Пример #1
0
nco_bool /* O [flg] File obeys CCM/CCSM/CF conventions */
nco_cnv_ccm_ccsm_cf_inq /* O [fnc] Check if file obeys CCM/CCSM/CF conventions */
(const int nc_id) /* I [id] netCDF file ID */
{
  /* Purpose: Check if file adheres to CCM/CCSM/CF history tape format */

  nco_bool CNV_CCM_CCSM_CF=False;

  char *att_val;
  char *cnv_sng=NULL_CEWI;

  /* netCDF standard is uppercase Conventions, though some models user lowercase */
  char cnv_sng_UC[]="Conventions"; /* Unidata standard     string (uppercase) */
  char cnv_sng_LC[]="conventions"; /* Unidata non-standard string (lowercase) */
  
  int rcd; /* [rcd] Return code */
  
  long att_sz;

  nc_type att_typ;

  /* Look for signature of a CCM/CCSM/CF-format file */
  cnv_sng=cnv_sng_UC;
  rcd=nco_inq_att_flg(nc_id,NC_GLOBAL,cnv_sng,&att_typ,&att_sz);
  if(rcd != NC_NOERR){
    /* Re-try with lowercase string because some models, e.g., CLM, user lowercase "conventions" */
    cnv_sng=cnv_sng_LC;
    rcd=nco_inq_att_flg(nc_id,NC_GLOBAL,cnv_sng,&att_typ,&att_sz);
  } /* endif lowercase */
  
  if(rcd == NC_NOERR && att_typ == NC_CHAR){
    /* Add one for NUL byte */
    att_val=(char *)nco_malloc(att_sz*nco_typ_lng(att_typ)+1L);
    (void)nco_get_att(nc_id,NC_GLOBAL,cnv_sng,att_val,att_typ);
    /* NUL-terminate convention attribute before using strcmp() */
    att_val[att_sz]='\0';
    /* CCM3, CCSM1 conventions */
    if(strstr(att_val,"NCAR-CSM")) CNV_CCM_CCSM_CF=True; /* Backwards compatibility */
    /* Climate-Forecast conventions */
    if(strstr(att_val,"CF-1.")) CNV_CCM_CCSM_CF=True; /* NB: Not fully implemented TODO nco145 */
    /* As of 20060514, CLM 3.0 uses CF1.0 not CF-1.0 (CAM gets it right) */
    if(strstr(att_val,"CF1.")) CNV_CCM_CCSM_CF=True; /* NB: Not fully implemented TODO nco145 */
    if(CNV_CCM_CCSM_CF && nco_dbg_lvl_get() >= nco_dbg_std){
      (void)fprintf(stderr,"%s: CONVENTION File \"%s\" attribute is \"%s\"\n",nco_prg_nm_get(),cnv_sng,att_val);
      if(cnv_sng == cnv_sng_LC) (void)fprintf(stderr,"%s: WARNING: This file uses a non-standard attribute (\"%s\") to indicate the netCDF convention. The correct attribute is \"%s\".\n",nco_prg_nm_get(),cnv_sng_LC,cnv_sng_UC);
      /* Only warn in arithmetic operators where conventions change behavior */
      if(nco_dbg_lvl_get() >= nco_dbg_fl && nco_dbg_lvl_get() != nco_dbg_dev && nco_is_rth_opr(nco_prg_id_get())) (void)fprintf(stderr,"%s: INFO NCO attempts to abide by many official and unofficial metadata conventions including ARM, CCM, CCSM, and CF. To adhere to these conventions, NCO implements variable-specific exceptions in certain operators, e.g., ncbo will not subtract variables named \"date\" or \"gw\", and many operators will always leave coordinate variables unchanged. The full list of exceptions is in the manual http://nco.sf.net/nco.html#CF\n",nco_prg_nm_get());
    } /* endif dbg */
    att_val=(char *)nco_free(att_val);
  } /* endif */

  return CNV_CCM_CCSM_CF;
  
} /* end nco_cnv_ccm_ccsm_cf_inq */
Пример #2
0
FILE * /* O [fl] Unformatted binary file handle */
nco_bnr_open /* [fnc] Open unformatted binary data file */
(const char * const fl_bnr, /* [sng] Unformatted binary file */
 const char * const fl_mode) /* [sng] Open-mode ("r", "w", ...) */
{
  /* Purpose: Open unformatted binary data file for writing */
  FILE *fp_bnr; /* [fl] Unformatted binary output file handle */
  /* Open output file */
  if((fp_bnr=fopen(fl_bnr,fl_mode)) == NULL){
    (void)fprintf(stderr,"%s: ERROR unable to open binary output file %s\n",nco_prg_nm_get(),fl_bnr);
    nco_exit(EXIT_FAILURE);
  } /* end if */
  if(nco_dbg_lvl_get() >= nco_dbg_fl) (void)fprintf(stdout,"%s: Opened binary file %s\n",nco_prg_nm_get(),fl_bnr);
  if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(stdout,"Variable(s): Name (native type, # elements x bytes per element):\n");
  return fp_bnr; /* O [fl] Unformatted binary output file handle */
} /* end nco_bnr_open() */
Пример #3
0
int /* [rcd] Return code */
nco_bnr_close /* [fnc] Close unformatted binary data file for writing */
(FILE *fp_bnr, /* I [fl] Unformatted binary output file handle */
 const char * const fl_bnr) /* [sng] Unformatted binary output file */
{
  /* Purpose: Close unformatted binary data file for writing */
  int rcd; /* [rcd] Return code */
  /* Close output file */
  rcd=fclose(fp_bnr);
  if(rcd != 0){
    (void)fprintf(stderr,"%s: ERROR unable to close binary output file %s\n",nco_prg_nm_get(),fl_bnr);
    nco_exit(EXIT_FAILURE);
  } /* end if */
  if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(stdout,"\n");
  if(nco_dbg_lvl_get() >= nco_dbg_fl) (void)fprintf(stdout,"%s: Closed binary file %s\n",nco_prg_nm_get(),fl_bnr);
  return rcd; /* O [rcd] Return code */
} /* end nco_bnr_close() */
Пример #4
0
size_t /* O [nbr] Number of elements successfully written */
nco_bnr_wrt /* [fnc] Write unformatted binary data */
(FILE * const fp_bnr, /* I [fl] Unformatted binary output file handle */
 const char * const var_nm, /* I [sng] Variable name */
 const long var_sz, /* I [nbr] Variable size */
 const nc_type var_typ, /* I [enm] Variable type */
 const void * const void_ptr) /* I [ptr] Data to write */
{
  /* Purpose: Write unformatted binary data */
  /* Testing:
     ncks -O -D 3 -B -b foo.bnr ~/nco/data/in.nc ~/foo.nc */

  long wrt_nbr; /* [nbr] Number of elements successfully written */
  /* Write unformatted data to binary output file */
  wrt_nbr=fwrite(void_ptr,(size_t)nco_typ_lng(var_typ),(size_t)var_sz,fp_bnr);
  if(wrt_nbr != var_sz){
    (void)fprintf(stderr,"%s: ERROR only succeeded in writing %ld of %ld elements of variable %s\n",nco_prg_nm_get(),wrt_nbr,var_sz,var_nm);
    nco_exit(EXIT_FAILURE);
  } /* end if */
  if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(stdout,"%s (%s, %ld x %lu b), ",var_nm,c_typ_nm(var_typ),var_sz,(unsigned long)nco_typ_lng(var_typ));
  if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fflush(stderr);
  return wrt_nbr; /* O [nbr] Number of elements successfully written */
} /* end nco_bnr_wrt() */
Пример #5
0
size_t /* O [nbr] Number of elements successfully read */
nco_bnr_rd /* [fnc] Read unformatted binary data */
(FILE * const fp_bnr, /* I [fl] Unformatted binary input file handle */
 const char * const var_nm, /* I [sng] Variable name */
 const long var_sz, /* I [nbr] Variable size */
 const nc_type var_typ, /* I [enm] Variable type */
 void * const void_ptr) /* O [ptr] Data to read */
{
  /* Purpose: Read unformatted binary data */
  /* Testing:
     ncks -O -D 73 ~/nco/data/in.nc ~/foo.nc */

  long rd_nbr; /* [nbr] Number of elements successfully written */
  /* Read unformatted data from binary input file */
  rd_nbr=fread(void_ptr,(size_t)nco_typ_lng(var_typ),(size_t)var_sz,fp_bnr);
  if(rd_nbr != var_sz){
    (void)fprintf(stderr,"%s: ERROR only succeeded in reading %ld of %ld elements into variable %s\n",nco_prg_nm_get(),rd_nbr,var_sz,var_nm);
    nco_exit(EXIT_FAILURE);
  } /* end if */
  if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(stdout,"Binary read of %s (%s, %ld x %lu b)",var_nm,c_typ_nm(var_typ),var_sz,(unsigned long)nco_typ_lng(var_typ));
  if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fflush(stderr);
  return rd_nbr; /* O [nbr] Number of elements successfully written */
} /* end nco_bnr_rd() */
Пример #6
0
int /* [rcd] Successful conversion returns NCO_NOERR */
nco_cln_prs_tm /* UDUnits2 Extract time stamp from parsed UDUnits string */
(const char *unt_sng, /* I [ptr] units attribute string */
 tm_cln_sct *tm_in) /* O [sct] Time structure to be populated */
{
  const char fnc_nm[]="nco_cln_prs_tm()"; /* [sng] Function name */

  /* 20141230: fxm figure out a better length */
  char bfr[200];

  char *dt_sng;

  int ut_rcd; /* [enm] UDUnits2 status */

  ut_system *ut_sys;
  ut_unit *ut_sct_in; /* UDUnits structure, input units */

  /* When empty, ut_read_xml() uses environment variable UDUNITS2_XML_PATH, if any
     Otherwise it uses default initial location hardcoded when library was built */
  if(nco_dbg_lvl_get() >= nco_dbg_vrb) ut_set_error_message_handler(ut_write_to_stderr); else ut_set_error_message_handler(ut_ignore);
  ut_sys=ut_read_xml(NULL);
  if(ut_sys == NULL){
    (void)fprintf(stdout,"%s: %s failed to initialize UDUnits2 library\n",nco_prg_nm_get(),fnc_nm);
    return NCO_ERR; /* Failure */
  } /* end if err */ 

  /* Units string to convert from */
  ut_sct_in=ut_parse(ut_sys,unt_sng,UT_ASCII); 
  if(ut_sct_in == NULL){ /* Problem with 'units' attribute */
    ut_rcd=ut_get_status(); /* [enm] UDUnits2 status */
    if(ut_rcd == UT_BAD_ARG) (void)fprintf(stderr,"ERROR: empty units attribute string\n");
    if(ut_rcd == UT_SYNTAX)  (void)fprintf(stderr,"ERROR: units attribute \"%s\" has a syntax error\n",unt_sng);
    if(ut_rcd == UT_UNKNOWN) (void)fprintf(stderr,"ERROR: units attribute \"%s\" is not listed in UDUnits2 SI system database\n",unt_sng);

    return NCO_ERR; /* Failure */
  } /* endif coordinate on disk has no units attribute */

  /* Print timestamp to buffer in standard, dependable format */
  ut_format(ut_sct_in,bfr,sizeof(bfr),UT_ASCII|UT_NAMES);

  /* Extract parsed time units from print string (kludgy)
     20141230 change to using ut_decode_time() instead? */
  dt_sng=strstr(bfr,"since");  
  sscanf(dt_sng,"%*s %d-%d-%d %d:%d:%f",&tm_in->year,&tm_in->month,&tm_in->day,&tm_in->hour,&tm_in->min,&tm_in->sec);

  ut_free_system(ut_sys); /* Free memory taken by UDUnits library */
  ut_free(ut_sct_in);

  return NCO_NOERR;
} /* end UDUnits2 nco_cln_prs_tm() */
Пример #7
0
int 
main(int argc,char **argv)
{
  aed_sct *aed_lst=NULL_CEWI;

  char **fl_lst_abb=NULL; /* Option n */
  char **fl_lst_in;
  char **gaa_arg=NULL; /* [sng] Global attribute arguments */
  char *aed_arg[NC_MAX_ATTRS];
  char *cmd_ln;
  char *fl_in=NULL;
  char *fl_out=NULL; /* Option o */
  char *fl_pth=NULL; /* Option p */
  char *fl_pth_lcl=NULL; /* Option l */
  char *opt_crr=NULL; /* [sng] String representation of current long-option name */
  char *sng_cnv_rcd=NULL_CEWI; /* [sng] strtol()/strtoul() return code */
  char trv_pth[]="/"; /* [sng] Root path of traversal tree */

  const char * const CVS_Id="$Id$"; 
  const char * const CVS_Revision="$Revision$";
  const char * const opt_sht_lst="Aa:D:hl:Oo:p:Rr-:";

#if defined(__cplusplus) || defined(PGI_CC)
  ddra_info_sct ddra_info;
  ddra_info.flg_ddra=False;
#else /* !__cplusplus */
  ddra_info_sct ddra_info={.flg_ddra=False};
#endif /* !__cplusplus */

  extern char *optarg;
  extern int optind;

  int abb_arg_nbr=0;
  int fl_nbr=0;
  int gaa_nbr=0; /* [nbr] Number of global attributes to add */
  int nbr_aed=0; /* Option a. NB: nbr_var_aed gets incremented */
  int nbr_var_fl;
  int nc_id;  
  int md_open; /* [enm] Mode flag for nc_open() call */
  int opt;
  int rcd=NC_NOERR; /* [rcd] Return code */

  nco_bool FL_RTR_RMT_LCN;
  nco_bool FL_LST_IN_FROM_STDIN=False; /* [flg] fl_lst_in comes from stdin */
  nco_bool FORCE_APPEND=False; /* Option A */
  nco_bool FORCE_OVERWRITE=False; /* Option O */
  nco_bool HISTORY_APPEND=True; /* Option h */
  nco_bool FL_OUT_NEW=False;
  nco_bool RAM_OPEN=False; /* [flg] Open (netCDF3-only) file(s) in RAM */
  nco_bool RM_RMT_FL_PST_PRC=True; /* Option R */
  nco_bool flg_cln=False; /* [flg] Clean memory prior to exit */

  size_t bfr_sz_hnt=NC_SIZEHINT_DEFAULT; /* [B] Buffer size hint */
  size_t hdr_pad=0UL; /* [B] Pad at end of header section */

  trv_tbl_sct *trv_tbl=NULL; /* [lst] Traversal table */

  nco_dmn_dne_t *flg_dne=NULL; /* [lst] Flag to check if input dimension -d "does not exist" */
 
#ifdef ENABLE_MPI
  /* Declare all MPI-specific variables here */
  MPI_Comm mpi_cmm=MPI_COMM_WORLD; /* [prc] Communicator */
  int prc_rnk; /* [idx] Process rank */
  int prc_nbr=0; /* [nbr] Number of MPI processes */
#endif /* !ENABLE_MPI */
  
  static struct option opt_lng[]={ /* Structure ordered by short option key if possible */
    /* Long options with no argument, no short option counterpart */
    {"cln",no_argument,0,0}, /* [flg] Clean memory prior to exit */
    {"clean",no_argument,0,0}, /* [flg] Clean memory prior to exit */
    {"mmr_cln",no_argument,0,0}, /* [flg] Clean memory prior to exit */
    {"drt",no_argument,0,0}, /* [flg] Allow dirty memory on exit */
    {"dirty",no_argument,0,0}, /* [flg] Allow dirty memory on exit */
    {"mmr_drt",no_argument,0,0}, /* [flg] Allow dirty memory on exit */
    {"hdf4",no_argument,0,0}, /* [flg] Treat file as HDF4 */
    {"ram_all",no_argument,0,0}, /* [flg] Open (netCDF3) and create file(s) in RAM */
    {"create_ram",no_argument,0,0}, /* [flg] Create file in RAM */
    {"open_ram",no_argument,0,0}, /* [flg] Open (netCDF3) file(s) in RAM */
    {"diskless_all",no_argument,0,0}, /* [flg] Open (netCDF3) and create file(s) in RAM */
    {"version",no_argument,0,0},
    {"vrs",no_argument,0,0},
    /* Long options with argument, no short option counterpart */
    {"bfr_sz_hnt",required_argument,0,0}, /* [B] Buffer size hint */
    {"buffer_size_hint",required_argument,0,0}, /* [B] Buffer size hint */
    {"gaa",required_argument,0,0}, /* [sng] Global attribute add */
    {"glb_att_add",required_argument,0,0}, /* [sng] Global attribute add */
    {"hdr_pad",required_argument,0,0},
    {"header_pad",required_argument,0,0},
    /* Long options with short counterparts */
    {"append",no_argument,0,'A'},
    {"attribute",required_argument,0,'a'},
    {"debug",required_argument,0,'D'},
    {"nco_dbg_lvl",required_argument,0,'D'},
    {"history",no_argument,0,'h'},
    {"hst",no_argument,0,'h'},
    {"local",required_argument,0,'l'},
    {"lcl",required_argument,0,'l'},
    {"overwrite",no_argument,0,'O'},
    {"ovr",no_argument,0,'O'},
    {"output",required_argument,0,'o'},
    {"fl_out",required_argument,0,'o'},
    {"path",required_argument,0,'p'},
    {"retain",no_argument,0,'R'},
    {"rtn",no_argument,0,'R'},
    {"help",no_argument,0,'?'},
    {"hlp",no_argument,0,'?'},
    {0,0,0,0}
  }; /* end opt_lng */
  int opt_idx=0; /* Index of current long option into opt_lng array */

  /* Start timer and save command line */ 
  ddra_info.tmr_flg=nco_tmr_srt;
  rcd+=nco_ddra((char *)NULL,(char *)NULL,&ddra_info);
  ddra_info.tmr_flg=nco_tmr_mtd;
  cmd_ln=nco_cmd_ln_sng(argc,argv);

  /* Get program name and set program enum (e.g., nco_prg_id=ncra) */
  nco_prg_nm=nco_prg_prs(argv[0],&nco_prg_id);

#ifdef ENABLE_MPI
  /* MPI Initialization */
  if(False) (void)fprintf(stdout,gettext("%s: WARNING Compiled with MPI\n"),nco_prg_nm);
  MPI_Init(&argc,&argv);
  MPI_Comm_size(mpi_cmm,&prc_nbr);
  MPI_Comm_rank(mpi_cmm,&prc_rnk);
#endif /* !ENABLE_MPI */
  
  /* Parse command line arguments */
  while(1){
    /* getopt_long_only() allows one dash to prefix long options */
    opt=getopt_long(argc,argv,opt_sht_lst,opt_lng,&opt_idx);
    /* NB: access to opt_crr is only valid when long_opt is detected */
    if(opt == EOF) break; /* Parse positional arguments once getopt_long() returns EOF */
    opt_crr=(char *)strdup(opt_lng[opt_idx].name);

    /* Process long options without short option counterparts */
    if(opt == 0){
      if(!strcmp(opt_crr,"bfr_sz_hnt") || !strcmp(opt_crr,"buffer_size_hint")){
        bfr_sz_hnt=strtoul(optarg,&sng_cnv_rcd,NCO_SNG_CNV_BASE10);
        if(*sng_cnv_rcd) nco_sng_cnv_err(optarg,"strtoul",sng_cnv_rcd);
      } /* endif cnk */
      if(!strcmp(opt_crr,"cln") || !strcmp(opt_crr,"mmr_cln") || !strcmp(opt_crr,"clean")) flg_cln=True; /* [flg] Clean memory prior to exit */
      if(!strcmp(opt_crr,"drt") || !strcmp(opt_crr,"mmr_drt") || !strcmp(opt_crr,"dirty")) flg_cln=False; /* [flg] Clean memory prior to exit */
      if(!strcmp(opt_crr,"gaa") || !strcmp(opt_crr,"glb_att_add")){
        gaa_arg=(char **)nco_realloc(gaa_arg,(gaa_nbr+1)*sizeof(char *));
        gaa_arg[gaa_nbr++]=(char *)strdup(optarg);
      } /* endif gaa */
      if(!strcmp(opt_crr,"hdf4")) nco_fmt_xtn=nco_fmt_xtn_hdf4; /* [enm] Treat file as HDF4 */
      if(!strcmp(opt_crr,"hdr_pad") || !strcmp(opt_crr,"header_pad")){
        hdr_pad=strtoul(optarg,&sng_cnv_rcd,NCO_SNG_CNV_BASE10);
        if(*sng_cnv_rcd) nco_sng_cnv_err(optarg,"strtoul",sng_cnv_rcd);
      } /* endif "hdr_pad" */
      if(!strcmp(opt_crr,"ram_all") || !strcmp(opt_crr,"open_ram") || !strcmp(opt_crr,"diskless_all")) RAM_OPEN=True; /* [flg] Create file in RAM */
      if(!strcmp(opt_crr,"vrs") || !strcmp(opt_crr,"version")){
        (void)nco_vrs_prn(CVS_Id,CVS_Revision);
        nco_exit(EXIT_SUCCESS);
      } /* endif "vrs" */
    } /* opt != 0 */
    /* Process short options */
    switch(opt){
    case 0: /* Long options have already been processed, return */
      break;
    case 'A': /* Toggle FORCE_APPEND */
      FORCE_APPEND=!FORCE_APPEND;
      break;
    case 'a': /* Copy argument for later processing */
      aed_arg[nbr_aed]=(char *)strdup(optarg);
      nbr_aed++;
      break;
    case 'D': /* Debugging level. Default is 0. */
      nco_dbg_lvl=(unsigned short int)strtoul(optarg,&sng_cnv_rcd,NCO_SNG_CNV_BASE10);
      if(*sng_cnv_rcd) nco_sng_cnv_err(optarg,"strtoul",sng_cnv_rcd);
      nc_set_log_level(nco_dbg_lvl);
      break;
    case 'h': /* Toggle appending to history global attribute */
      HISTORY_APPEND=!HISTORY_APPEND;
      break;
    case 'l': /* Local path prefix for files retrieved from remote file system */
      fl_pth_lcl=(char *)strdup(optarg);
      break;
    case 'O': /* Toggle FORCE_OVERWRITE */
      FORCE_OVERWRITE=!FORCE_OVERWRITE;
      break;
    case 'o': /* Name of output file */
      fl_out=(char *)strdup(optarg);
      break;
    case 'p': /* Common file path */
      fl_pth=(char *)strdup(optarg);
      break;
    case 'R': /* Toggle removal of remotely-retrieved-files. Default is True. */
      RM_RMT_FL_PST_PRC=!RM_RMT_FL_PST_PRC;
      break;
    case 'r': /* Print CVS program information and copyright notice */
      (void)nco_vrs_prn(CVS_Id,CVS_Revision);
      (void)nco_lbr_vrs_prn();
      (void)nco_cpy_prn();
      (void)nco_cnf_prn();
      nco_exit(EXIT_SUCCESS);
      break;
    case '?': /* Print proper usage */
      (void)nco_usg_prn();
      nco_exit(EXIT_SUCCESS);
      break;
    case '-': /* Long options are not allowed */
      (void)fprintf(stderr,"%s: ERROR Long options are not available in this build. Use single letter options instead.\n",nco_prg_nm_get());
      nco_exit(EXIT_FAILURE);
      break;
    default: /* Print proper usage */
      (void)fprintf(stdout,"%s ERROR in command-line syntax/options. Please reformulate command accordingly.\n",nco_prg_nm_get());
      (void)nco_usg_prn();
      nco_exit(EXIT_FAILURE);
    } /* end switch */
    if(opt_crr) opt_crr=(char *)nco_free(opt_crr);
  } /* end while loop */

  /* Process positional arguments and fill in filenames */
  fl_lst_in=nco_fl_lst_mk(argv,argc,optind,&fl_nbr,&fl_out,&FL_LST_IN_FROM_STDIN);
  if(fl_out) FL_OUT_NEW=True; else fl_out=(char *)strdup(fl_lst_in[0]);

  if(nbr_aed == 0){
    (void)fprintf(stdout,"%s: ERROR must specify an attribute to edit\n",nco_prg_nm);
    nco_usg_prn();
    nco_exit(EXIT_FAILURE);
  } /* end if */ 

  if(nco_dbg_lvl_get() >= nco_dbg_var) (void)fprintf(stdout,"%s: DEBUG attribute assumed to hold missing data is named \"%s\"\n",nco_prg_nm_get(),nco_mss_val_sng_get());

  /* Make uniform list of user-specified attribute edit structures */
  if(nbr_aed > 0) aed_lst=nco_prs_aed_lst(nbr_aed,aed_arg);

  /* We now have final list of attributes to edit */

  /* Parse filename */
  fl_in=nco_fl_nm_prs(fl_in,0,&fl_nbr,fl_lst_in,abb_arg_nbr,fl_lst_abb,fl_pth);
  /* Make sure file is on local system and is readable or die trying */
  fl_in=nco_fl_mk_lcl(fl_in,fl_pth_lcl,&FL_RTR_RMT_LCN);

  if(FL_OUT_NEW){
    /* Obtain user consent, if needed, to overwrite output file (or die trying) */
    if(!FORCE_OVERWRITE) nco_fl_overwrite_prm(fl_out);

    /* Copy input file to output file and then search through output, 
       changing names on the fly. This avoids possible XDR translation
       performance penalty of copying each variable with netCDF. */
    (void)nco_fl_cp(fl_in,fl_out);

    /* Ensure output file is user/owner-writable */
    (void)nco_fl_chmod(fl_out);
  } /* end if FL_OUT_NEW */

  /* Open file. Writing must be enabled and file should be in define mode for renaming */
  /*  if(nco_dbg_lvl == 8) md_open|=NC_SHARE;*/
  if(RAM_OPEN) md_open=NC_WRITE|NC_DISKLESS; else md_open=NC_WRITE;
  rcd+=nco_fl_open(fl_out,md_open,&bfr_sz_hnt,&nc_id);
  (void)nco_redef(nc_id);

  /* Get number of variables in file */
  (void)nco_inq(nc_id,(int *)NULL,&nbr_var_fl,(int *)NULL,(int *)NULL);

  /* Initialize traversal table */ 
  trv_tbl_init(&trv_tbl); 

  /* Construct GTT (Group Traversal Table) */
  (void)nco_bld_trv_tbl(nc_id,trv_pth,(int)0,NULL,(int)0,NULL,False,False,NULL,(int)0,NULL,(int) 0,False,False,False,False,True,nco_pck_plc_nil,&flg_dne,trv_tbl);

  /* Timestamp end of metadata setup and disk layout */
  rcd+=nco_ddra((char *)NULL,(char *)NULL,&ddra_info);
  ddra_info.tmr_flg=nco_tmr_rgl;

  for(int idx_aed=0;idx_aed<nbr_aed;idx_aed++){
    if(!aed_lst[idx_aed].var_nm){
      /* Variable name is blank so edit same attribute for all variables */
      (void)nco_aed_prc_var_all(nc_id,aed_lst[idx_aed],trv_tbl);
    }else if(strpbrk(aed_lst[idx_aed].var_nm,".*^$\\[]()<>+?|{}")){
      /* Variable name contains a "regular expression" (rx) */
      trv_tbl_sct *trv_tbl_rx;
      char **var_lst_in; /* I [sng] User-specified list of variables */
      int var_lst_in_nbr; /* I [nbr] Number of variables in list */
      var_lst_in=nco_lst_prs_2D(aed_lst[idx_aed].var_nm,",",&var_lst_in_nbr);
      trv_tbl_init(&trv_tbl_rx); 
      /* Use regular expressions in aed structure to construct traversal table
	 Variables marked for extraction will then have the attributes edited */
      (void)nco_bld_trv_tbl(nc_id,trv_pth,(int)0,NULL,(int)0,NULL,False,False,NULL,(int)0,var_lst_in,var_lst_in_nbr,False,False,False,False,False,nco_pck_plc_nil,&flg_dne,trv_tbl_rx); 
      /* Edit same attribute for all variables marked for extraction */
      (void)nco_aed_prc_var_xtr(nc_id,aed_lst[idx_aed],trv_tbl_rx);
      trv_tbl_free(trv_tbl_rx);
      var_lst_in=nco_sng_lst_free(var_lst_in,var_lst_in_nbr);
    }else if(!strcasecmp(aed_lst[idx_aed].var_nm,"group")){
      /* Variable name of "group" means edit group attributes */
      (void)nco_aed_prc_grp(nc_id,aed_lst[idx_aed],trv_tbl);
    }else if(!strcasecmp(aed_lst[idx_aed].var_nm,"global")){
      /* Variable name of "global" means edit global attributes */
      (void)nco_aed_prc_glb(nc_id,aed_lst[idx_aed],trv_tbl);
    }else{ 
      /* Regular ole' variable name means edits attributes that match absoluted and relative names */
      (void)nco_aed_prc_var_nm(nc_id,aed_lst[idx_aed],trv_tbl);
    } /* end var_nm */
  } /* end loop over aed structures */

  /* Catenate the time-stamped command line to the "history" global attribute */
  if(HISTORY_APPEND) (void)nco_hst_att_cat(nc_id,cmd_ln);

  /* Take output file out of define mode */
  if(hdr_pad == 0UL){
    (void)nco_enddef(nc_id);
  }else{
    (void)nco__enddef(nc_id,hdr_pad);
    if(nco_dbg_lvl >= nco_dbg_scl) (void)fprintf(stderr,"%s: INFO Padding header with %lu extra bytes\n",nco_prg_nm_get(),(unsigned long)hdr_pad);
  } /* hdr_pad */

  /* Close the open netCDF file */
  nco_close(nc_id);

  /* Remove local copy of file */
  if(FL_RTR_RMT_LCN && RM_RMT_FL_PST_PRC) (void)nco_fl_rm(fl_in);

  /* Clean memory unless dirty memory allowed */
  if(flg_cln){
    /* ncatted-specific memory */
    for(int idx=0;idx<nbr_aed;idx++) aed_arg[idx]=(char *)nco_free(aed_arg[idx]);
    for(int idx=0;idx<nbr_aed;idx++){
      if(aed_lst[idx].att_nm) aed_lst[idx].att_nm=(char *)nco_free(aed_lst[idx].att_nm);
      if(aed_lst[idx].var_nm) aed_lst[idx].var_nm=(char *)nco_free(aed_lst[idx].var_nm);
      aed_lst[idx].val.vp=(void *)nco_free(aed_lst[idx].val.vp);
    } /* end for */
    if(nbr_aed > 0) aed_lst=(aed_sct *)nco_free(aed_lst);

    /* NCO-generic clean-up */
    /* Free individual strings/arrays */
    if(cmd_ln) cmd_ln=(char *)nco_free(cmd_ln);
    if(fl_in) fl_in=(char *)nco_free(fl_in);
    if(fl_out) fl_out=(char *)nco_free(fl_out);
    if(fl_pth) fl_pth=(char *)nco_free(fl_pth);
    if(fl_pth_lcl) fl_pth_lcl=(char *)nco_free(fl_pth_lcl);
    /* Free lists of strings */
    if(fl_lst_in && fl_lst_abb == NULL) fl_lst_in=nco_sng_lst_free(fl_lst_in,fl_nbr); 
    if(fl_lst_in && fl_lst_abb) fl_lst_in=nco_sng_lst_free(fl_lst_in,1);
    if(fl_lst_abb) fl_lst_abb=nco_sng_lst_free(fl_lst_abb,abb_arg_nbr);
    if(gaa_nbr > 0) gaa_arg=nco_sng_lst_free(gaa_arg,gaa_nbr);

    trv_tbl_free(trv_tbl);
  } /* !flg_cln */

#ifdef ENABLE_MPI
  MPI_Finalize();
#endif /* !ENABLE_MPI */
  
  /* End timer */ 
  ddra_info.tmr_flg=nco_tmr_end; /* [enm] Timer flag */
  rcd+=nco_ddra((char *)NULL,(char *)NULL,&ddra_info);
  if(rcd != NC_NOERR) nco_err_exit(rcd,"main");

  nco_exit_gracefully();
  return EXIT_SUCCESS;
} /* end main() */
Пример #8
0
var_sct * /* O [sct] Pointer to conforming variable structure */
nco_var_cnf_dmn /* [fnc] Stretch second variable to match dimensions of first variable */
(const var_sct * const var, /* I [ptr] Pointer to variable structure to serve as template */
 var_sct * const wgt, /* I [ptr] Pointer to variable structure to make conform to var */
 var_sct *wgt_crr, /* I/O [ptr] Pointer to existing conforming variable structure, if any (destroyed when does not conform to var) */
 const nco_bool MUST_CONFORM, /* I [flg] Must wgt and var conform? */
 nco_bool *DO_CONFORM) /* O [flg] Do wgt and var conform? */
{
  /* Threads: Routine is thread safe and calls no unsafe routines */
  /* fxm: TODO 226. Is xrf in nco_var_cnf_dmn() really necessary? If not, remove it and make wgt arg const var_sct * const */

  /* Purpose: Stretch second variable to match dimensions of first variable
     Dimensions in var which are not in wgt will be present in wgt_out, with values
     replicated from existing dimensions in wgt.
     By default, wgt's dimensions must be subset of var's dimensions (MUST_CONFORM=true)
     Calling routine should set MUST_CONFORM=false if wgt and var need not conform
     When wgt and var do not conform then then nco_var_cnf_dmn sets *DO_CONFORM=False and returns copy of var with all values set to 1.0
     Calling procedure then decides what to do with unity output
     MUST_CONFORM is True for ncbo: Variables of like name to be, e.g., differenced, must conform
     MUST_CONFORM is False false for ncap, ncflint, ncwa: Some variables to be averaged may not conform to specified weight, e.g., lon will not conform to gw. This is fine and returned wgt_out may be discarded. */

  /* There are many inelegant ways to accomplish this (without using C++): */  

  /* Perhaps most efficient method in general case is to expand weight array until
     it is same size as variable array, and then multiply these arrays together 
     element-by-element in highly vectorized loop (possibly in Fortran or BLAS). 
     To enhance speed, (enlarged) weight-values array could be static, only re-made
     when dimensions of incoming variables change. */

  /* Another general method, though more expensive, is to use C to figure out the 
     multidimensional indices into the one dimensional hyperslab, a la ncks. 
     Knowing these indices, routine could loop over the one-dimensional array
     element by element, choosing the appropriate index into the weight array from 
     those same multidimensional indices. 
     This method can also create a static weight-value array that is only destroyed 
     when an incoming variable changes dimensions from the previous variable. */

  /* Another method, which is not completely general, but which may be good enough for
     governement work, is to create Fortran subroutines which expect variables of 
     a given number of dimensions as input. 
     Creating these functions for up to five dimensions would satisfy most situations
     C code would determine which branch to call based on number of dimensions
     C++ or Fortran9x overloading could construct this interface more elegantly */

  /* An (untested) simplification to some of these methods is to copy the 1-D array
     value pointer of variable and cast it to an N-D array pointer
     Then C could handle indexing 
     This method easily produce working, but non-general code
     Implementation would require ugly branches or hard-to-understand recursive function calls */
  
  /* Routine assumes weight will never have more dimensions than variable
     (otherwise which hyperslab of weight to use would be ill-defined). 
     However, weight may (and often will) have fewer dimensions than variable */

  nco_bool CONFORMABLE=False; /* [flg] wgt can be made to conform to var */
  nco_bool USE_DUMMY_WGT=False; /* [flg] Fool NCO into thinking wgt conforms to var */

  int idx; /* [idx] Counting index */
  int idx_dmn; /* [idx] Dimension index */
  int wgt_var_dmn_shr_nbr=0; /* [nbr] Number of dimensions wgt and var share */

  var_sct *wgt_out=NULL;

  /* Initialize flag to false. Overwrite by true after successful conformance */
  *DO_CONFORM=False;

  /* Does current weight (wgt_crr) conform to variable's dimensions? */
  if(wgt_crr){
    /* Test rank first because wgt_crr because of 19960218 bug (invalid dmn_id in old wgt_crr leads to match) */
    if(var->nbr_dim == wgt_crr->nbr_dim){
      /* Test whether all wgt and var dimensions match in sequence */
      for(idx=0;idx<var->nbr_dim;idx++){
	/* 20131002: nco_var_cnf_dmn() borken for groups as shown by dimension short-name strcmp() comparison here */
        if(strcmp(wgt_crr->dim[idx]->nm,var->dim[idx]->nm)) break;
      } /* end loop over dimensions */
      if(idx == var->nbr_dim) *DO_CONFORM=True;
    } /* end if ranks are equal */

    /* 20060425: Weight re-use will not occur if wgt_crr is free()'d here
       Some DDRA benchmarks need to know cost of broadcasting weights
       To turn off weight re-use and cause broadcasting, execute "else" block below
       by (temporarily) using 
              if(*DO_CONFORM && False){ ....instead of.... if(*DO_CONFORM){ 
       in following condition */
    if(*DO_CONFORM){
      wgt_out=wgt_crr;
    }else{
      wgt_crr=nco_var_free(wgt_crr);
      wgt_out=NULL;
    } /* !*DO_CONFORM */
  } /* wgt_crr == NULL */

  /* Does original weight (wgt) conform to variable's dimensions? */
  if(wgt_out == NULL){
    if(var->nbr_dim > 0){
      /* Test that all dimensions in wgt appear in var */
      for(idx=0;idx<wgt->nbr_dim;idx++){
        for(idx_dmn=0;idx_dmn<var->nbr_dim;idx_dmn++){
          /* Compare names, not dimension IDs */
	  /* 20131002: nco_var_cnf_dmn() borken for groups as shown by dimension short-name strcmp() comparison here */
          if(!strcmp(wgt->dim[idx]->nm,var->dim[idx_dmn]->nm)){
            wgt_var_dmn_shr_nbr++; /* wgt and var share this dimension */
            break;
          } /* endif */
        } /* end loop over var dimensions */
      } /* end loop over wgt dimensions */
      /* Decide whether wgt and var dimensions conform, are mutually exclusive, or are partially exclusive (an error) */ 
      if(wgt_var_dmn_shr_nbr == wgt->nbr_dim){
        /* wgt and var conform */
        CONFORMABLE=True;
      }else if(wgt_var_dmn_shr_nbr == 0){
        /* Dimensions in wgt and var are mutually exclusive */
        CONFORMABLE=False;
        if(MUST_CONFORM){
          (void)fprintf(stdout,"%s: ERROR %s and template %s share no dimensions\n",nco_prg_nm_get(),wgt->nm,var->nm);
          nco_exit(EXIT_FAILURE);
        }else{
          if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(stdout,"\n%s: DEBUG %s and template %s share no dimensions: Not broadcasting %s to %s\n",nco_prg_nm_get(),wgt->nm,var->nm,wgt->nm,var->nm);
          USE_DUMMY_WGT=True;
        } /* endif */
      }else if(wgt->nbr_dim > var->nbr_dim){
        /* wgt is larger rank than var---no possibility of conforming */
        CONFORMABLE=False;
        if(MUST_CONFORM){
          (void)fprintf(stdout,"%s: ERROR %s is rank %d but template %s is rank %d: Impossible to broadcast\n",nco_prg_nm_get(),wgt->nm,wgt->nbr_dim,var->nm,var->nbr_dim);
          nco_exit(EXIT_FAILURE);
        }else{
          if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(stdout,"\n%s: DEBUG %s is rank %d but template %s is rank %d: Not broadcasting %s to %s\n",nco_prg_nm_get(),wgt->nm,wgt->nbr_dim,var->nm,var->nbr_dim,wgt->nm,var->nm);
          USE_DUMMY_WGT=True;
        } /* endif */
      }else if(wgt_var_dmn_shr_nbr > 0 && wgt_var_dmn_shr_nbr < wgt->nbr_dim){
        /* Some, but not all, of wgt dimensions are in var */
        CONFORMABLE=False;
        if(MUST_CONFORM){
          (void)fprintf(stdout,"%s: ERROR %d dimensions of %s belong to template %s but %d dimensions do not\n",nco_prg_nm_get(),wgt_var_dmn_shr_nbr,wgt->nm,var->nm,wgt->nbr_dim-wgt_var_dmn_shr_nbr);
          nco_exit(EXIT_FAILURE);
        }else{
          if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(stdout,"\n%s: DEBUG %d dimensions of %s belong to template %s but %d dimensions do not: Not broadcasting %s to %s\n",nco_prg_nm_get(),wgt_var_dmn_shr_nbr,wgt->nm,var->nm,wgt->nbr_dim-wgt_var_dmn_shr_nbr,wgt->nm,var->nm);
          USE_DUMMY_WGT=True;
        } /* endif */
      } /* end if */
      if(USE_DUMMY_WGT){
        /* Variables do not truly conform, but this might be OK, depending on the application, so set DO_CONFORM flag to false and ... */
        *DO_CONFORM=False;
        /* ... return a dummy weight of 1.0, which allows program logic to pretend variable is weighted, but does not change answers */ 
        wgt_out=nco_var_dpl(var);
        (void)vec_set(wgt_out->type,wgt_out->sz,wgt_out->val,1.0);
      } /* endif */
      if(CONFORMABLE){
        if(var->nbr_dim == wgt->nbr_dim){
          /* var and wgt conform and are same rank */
          /* Test whether all wgt and var dimensions match in sequence */
          for(idx=0;idx<var->nbr_dim;idx++){
	    /* 20131002: nco_var_cnf_dmn() borken for groups as shown by dimension short-name strcmp() comparison here */
            if(strcmp(wgt->dim[idx]->nm,var->dim[idx]->nm)) break;
            /*	    if(wgt->dmn_id[idx] != var->dmn_id[idx]) break;*/
          } /* end loop over dimensions */
          /* If so, take shortcut and copy wgt to wgt_out */
          if(idx == var->nbr_dim) *DO_CONFORM=True;
        }else{
          /* var and wgt conform but are not same rank, set flag to proceed to generic conform routine */
          *DO_CONFORM=False;
        } /* end else */
      } /* endif CONFORMABLE */
    }else{
      /* var is scalar, if wgt is also then set flag to copy wgt to wgt_out else proceed to generic conform routine */
      if(wgt->nbr_dim == 0) *DO_CONFORM=True; else *DO_CONFORM=False;
    } /* end else */
    if(CONFORMABLE && *DO_CONFORM){
      wgt_out=nco_var_dpl(wgt);
      (void)nco_xrf_var(wgt,wgt_out);
    } /* end if */
  } /* end if */

  /* Set diagnostic DDRA information DDRA */
  /* ddra_info->wgt_brd_flg=(wgt_out == NULL) ? True : False; *//* [flg] Broadcast weight for this variable */

  if(wgt_out == NULL){
    /* Expand original weight (wgt) to match size of current variable */
    char * restrict wgt_cp;
    char * restrict wgt_out_cp;

    int idx_wgt_var[NC_MAX_DIMS];
    int wgt_nbr_dim;
    int var_nbr_dmn_m1;

    long * restrict var_cnt;
    long dmn_ss[NC_MAX_DIMS];
    long dmn_var_map[NC_MAX_DIMS];
    long dmn_wgt_map[NC_MAX_DIMS];
    long var_lmn;
    long wgt_lmn;
    long var_sz;

    size_t wgt_typ_sz;

    /* Copy main attributes of variable into output weight */
    wgt_out=nco_var_dpl(var);
    (void)nco_xrf_var(wgt,wgt_out);

    /* wgt_out variable was copied from template var
       Modify key fields so its name and type are based on wgt, not var
       wgt_out will then be hybrid between wgt and var 
       Remainder of routine fills wgt_out's var-dimensionality with wgt-values */
    wgt_out->nm=(char *)nco_free(wgt_out->nm);
    wgt_out->nm=(char *)strdup(wgt->nm);
    wgt_out->id=wgt->id;
    wgt_out->type=wgt->type;
    wgt_out->val.vp=(void *)nco_free(wgt_out->val.vp);
    wgt_out->val.vp=(void *)nco_malloc(wgt_out->sz*nco_typ_lng(wgt_out->type));
    wgt_cp=(char *)wgt->val.vp;
    wgt_out_cp=(char *)wgt_out->val.vp;
    wgt_typ_sz=nco_typ_lng(wgt_out->type);

    if(wgt_out->nbr_dim == 0){
      /* Variable (and weight) are scalars, not arrays */
      (void)memcpy(wgt_out_cp,wgt_cp,wgt_typ_sz);
    }else if(wgt->nbr_dim == 0){
      /* Lesser-ranked input variable is scalar 
	 Expansion in this degenerate case needs no index juggling (reverse-mapping)
	 Code as special case to speed-up important applications of ncap
	 for synthetic file creation */
      var_sz=var->sz;
      for(var_lmn=0;var_lmn<var_sz;var_lmn++){
        (void)memcpy(wgt_out_cp+var_lmn*wgt_typ_sz,wgt_cp,wgt_typ_sz);      
      } /* end loop over var_lmn */
    }else{
      /* Variable (and therefore wgt_out) are arrays, not scalars */
      
      /* Create forward and reverse mappings from variable's dimensions to weight's dimensions:
	 
	 dmn_var_map[i] is number of elements between one value of i_th 
	 dimension of variable and next value of i_th dimension, i.e., 
	 number of elements in memory between indicial increments in i_th dimension. 
	 This is computed as product of one (1) times size of all dimensions (if any) after i_th 
	 dimension in variable.
	 
	 dmn_wgt_map[i] contains analogous information, except for original weight variable
	 
	 idx_wgt_var[i] contains index into variable's dimensions of i_th dimension of original weight
	 idx_var_wgt[i] contains index into original weight's dimensions of i_th dimension of variable 
	 
	 Since weight is a subset of variable, some elements of idx_var_wgt may be "empty", or unused
	 
	 Since mapping arrays (dmn_var_map and dmn_wgt_map) are ultimately used for a
	 memcpy() operation, they could (read: should) be computed as byte offsets, not type offsets. 
	 This is why netCDF generic hyperslab routines (ncvarputg(), ncvargetg())
	 request imap vector to specify offset (imap) vector in bytes. */

      for(idx=0;idx<wgt->nbr_dim;idx++){
        for(idx_dmn=0;idx_dmn<var->nbr_dim;idx_dmn++){
          /* Compare names, not dimension IDs */
	  /* 20131002: nco_var_cnf_dmn() borken for groups as shown by dimension short-name strcmp() comparison here */
          if(!strcmp(var->dim[idx_dmn]->nm,wgt->dim[idx]->nm)){
            idx_wgt_var[idx]=idx_dmn;
            break;
          } /* end if */
          /* Sanity check */
          if(idx_dmn == var->nbr_dim-1){
            (void)fprintf(stdout,"%s: ERROR wgt %s has dimension %s but var %s does not deep in nco_var_cnf_dmn()\n",nco_prg_nm_get(),wgt->nm,wgt->dim[idx]->nm,var->nm);
            nco_exit(EXIT_FAILURE);
          } /* end if err */
        } /* end loop over variable dimensions */
      } /* end loop over weight dimensions */

      /* Figure out map for each dimension of variable */
      for(idx=0;idx<var->nbr_dim;idx++)	dmn_var_map[idx]=1L;
      for(idx=0;idx<var->nbr_dim-1;idx++)
        for(idx_dmn=idx+1;idx_dmn<var->nbr_dim;idx_dmn++)
          dmn_var_map[idx]*=var->cnt[idx_dmn];

      /* Figure out map for each dimension of weight */
      for(idx=0;idx<wgt->nbr_dim;idx++)	dmn_wgt_map[idx]=1L;
      for(idx=0;idx<wgt->nbr_dim-1;idx++)
        for(idx_dmn=idx+1;idx_dmn<wgt->nbr_dim;idx_dmn++)
          dmn_wgt_map[idx]*=wgt->cnt[idx_dmn];

      /* Define convenience variables to avoid repetitive indirect addressing */
      wgt_nbr_dim=wgt->nbr_dim;
      var_sz=var->sz;
      var_cnt=var->cnt;
      var_nbr_dmn_m1=var->nbr_dim-1;

      /* var_lmn is offset into 1-D array corresponding to N-D indices dmn_ss */
      for(var_lmn=0;var_lmn<var_sz;var_lmn++){
        /* dmn_ss are corresponding indices (subscripts) into N-D array */
        /* Operations: 1 modulo, 1 pointer offset, 1 user memory fetch
	   Repetitions: \lmnnbr
	   Total Counts: \rthnbr=2\lmnnbr, \mmrusrnbr=\lmnnbr
	   NB: LHS assumed compact and cached, counted RHS offsets and fetches only */
        dmn_ss[var_nbr_dmn_m1]=var_lmn%var_cnt[var_nbr_dmn_m1];
        for(idx=0;idx<var_nbr_dmn_m1;idx++){
          /* Operations: 1 divide, 1 modulo, 2 pointer offset, 2 user memory fetch
	     Repetitions: \lmnnbr(\dmnnbr-1)
	     Counts: \rthnbr=4\lmnnbr(\dmnnbr-1), \mmrusrnbr=2\lmnnbr(\dmnnbr-1)
	     NB: LHS assumed compact and cached, counted RHS offsets and fetches only
	     NB: Neglected loop arithmetic/compare */
          dmn_ss[idx]=(long int)(var_lmn/dmn_var_map[idx]);
          dmn_ss[idx]%=var_cnt[idx];
        } /* end loop over dimensions */
	
        /* Map (shared) N-D array indices into 1-D index into original weight data */
        wgt_lmn=0L;
        /* Operations: 1 add, 1 multiply, 3 pointer offset, 3 user memory fetch
	   Repetitions: \lmnnbr\rnkwgt
	   Counts: \rthnbr=5\lmnnbr\rnkwgt, \mmrusrnbr=3\lmnnbr\rnkwgt */
        for(idx=0;idx<wgt_nbr_dim;idx++) wgt_lmn+=dmn_ss[idx_wgt_var[idx]]*dmn_wgt_map[idx];
	
        /* Operations: 2 add, 2 multiply, 0 pointer offset, 1 system memory copy
	   Repetitions: \lmnnbr
	   Counts: \rthnbr=4\lmnnbr, \mmrusrnbr=0, \mmrsysnbr=1 */
        (void)memcpy(wgt_out_cp+var_lmn*wgt_typ_sz,wgt_cp+wgt_lmn*wgt_typ_sz,wgt_typ_sz);
	
      } /* end loop over var_lmn */

    } /* end if variable (and weight) are arrays, not scalars */

    *DO_CONFORM=True;
  } /* end if we had to stretch weight to fit variable */
Пример #9
0
int /* O [flg] Dimension exists in scope of group (if rcd != NC_NOERR) */
nco_inq_dmn_grp_id /* [fnc] Return location and ID of named dimension in specified group */
(const int nc_id, /* I [id] netCDF group ID */
 const char * const dmn_nm, /* I [sng] Dimension name */
 int * const dmn_id, /* O [id] Dimension ID in specified group */
 int * const grp_id_dmn) /* O [id] Group ID where dimension visible to specified group is defined */
{
  /* Purpose: Return location and ID of named dimension in specified group
     ncks -O -D 1 -v two_dmn_rec_var ~/nco/data/in_grp.nc ~/foo.nc */

  const char fnc_nm[]="nco_inq_dmn_grp_id()"; /* [sng] Function name */

  const int flg_prn=1; /* [flg] Retrieve all dimensions in all parent groups */        

  int dmn_ids[NC_MAX_DIMS]; /* [nbr] Dimensions IDs array */

  int dmn_idx; /* [idx] Dimension index */
  int dmn_nbr; /* [nbr] Number of dimensions for group */
  int rcd; /* [rcd] Return code */

  nco_bool grp_dfn_fnd=False; /* [flg] Group of definition has been found */

  /* Initialize search to start with specified group */
  *grp_id_dmn=nc_id;

  rcd=nco_inq_dimid_flg(*grp_id_dmn,dmn_nm,dmn_id);
  
  if(nco_dbg_lvl_get() >= nco_dbg_std){
    char *grp_nm_fll; /* [sng] Group name */
    char dmn_nm_lcl[NC_MAX_NAME]; /* [sng] Dimension name */
    size_t grp_nm_fll_lng; /* [nbr] Length of group name */
    (void)nco_inq_grpname_full(*grp_id_dmn,&grp_nm_fll_lng,(char *)NULL);
    grp_nm_fll=(char *)nco_malloc((grp_nm_fll_lng+1L)*sizeof(char));
    (void)nco_inq_grpname_full(*grp_id_dmn,(size_t *)NULL,grp_nm_fll);
    (void)nco_inq_dimids(*grp_id_dmn,&dmn_nbr,dmn_ids,flg_prn);
    (void)fprintf(stdout,"%s: %s nco_inq_dimids() reports following dimensions/IDs are visible to group %s:\n",nco_prg_nm_get(),fnc_nm,grp_nm_fll);
    for(dmn_idx=0;dmn_idx<dmn_nbr;dmn_idx++){
      (void)nco_inq_dimname(*grp_id_dmn,dmn_ids[dmn_idx],dmn_nm_lcl);
      (void)fprintf(stdout,"%s/%d,%s",dmn_nm_lcl,dmn_ids[dmn_idx],(dmn_idx == dmn_nbr-1) ? "\n" : ", ");
    } /* end loop over dmn */
    if(rcd == NC_NOERR) (void)fprintf(stdout,"%s: %s nco_inq_dimid() reports group %s sees dimension %s with ID = %d:\n",nco_prg_nm_get(),fnc_nm,grp_nm_fll,dmn_nm,*dmn_id); else (void)fprintf(stdout,"%s: %s reports group %s does not see dimension %s\n",nco_prg_nm_get(),fnc_nm,grp_nm_fll,dmn_nm);
    if(grp_nm_fll) grp_nm_fll=(char *)nco_free(grp_nm_fll);
  } /* endif dbg */

  /* If dimension is visible to output group, find exactly where it is defined
     Search ancestors until group of definition is found ... */
  while(!grp_dfn_fnd && (rcd == NC_NOERR)){
    /* ... obtain all dimension IDs in current group (_NOT_ in ancestor groups) ... */
    (void)nco_inq_dimids(*grp_id_dmn,&dmn_nbr,dmn_ids,0);
    /* ... and check each against ID of target dimension */
    for(dmn_idx=0;dmn_idx<dmn_nbr;dmn_idx++)
      if(dmn_ids[dmn_idx] == *dmn_id) break;
    
    if(nco_dbg_lvl_get() >= nco_dbg_std){
      char *grp_nm_fll; /* [sng] Group name */
      size_t grp_nm_fll_lng; /* [nbr] Length of group name */
      (void)nco_inq_grpname_full(*grp_id_dmn,&grp_nm_fll_lng,(char *)NULL);
      grp_nm_fll=(char *)nco_malloc((grp_nm_fll_lng+1L)*sizeof(char));
      (void)nco_inq_grpname_full(*grp_id_dmn,(size_t *)NULL,grp_nm_fll);
      (void)fprintf(stdout,"%s: %s reports dimension %s was%s defined in group %s\n",nco_prg_nm_get(),fnc_nm,dmn_nm,(dmn_idx < dmn_nbr) ? "" : " not",grp_nm_fll);
      if(grp_nm_fll) grp_nm_fll=(char *)nco_free(grp_nm_fll);
    } /* endif dbg */
    
    if(dmn_idx < dmn_nbr){
      grp_dfn_fnd=True;
    }else{
      /* Overwrite current group ID with parent group ID */
      rcd=nco_inq_grp_parent_flg(*grp_id_dmn,grp_id_dmn);
    } /* end else */
  } /* end while */
 
  return rcd;

} /* end nco_inq_dmn_grp_id */
Пример #10
0
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() */
Пример #11
0
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() */
Пример #12
0
int /* [rcd] Successful conversion returns NCO_NOERR */
nco_cln_clc_dff /* [fnc] UDUnits1 Difference between two co-ordinate units */
(const char *fl_unt_sng, /* I [ptr] units attribute string from disk  */     
 const char *fl_bs_sng, /* I [ptr] units attribute string from disk  */     
 double crr_val,
 double *og_val) /* O [ptr] */
{
  const char fnc_nm[]="nco_cln_clc_dff()"; /* [sng] Function name */
    
  double slp;
  double incpt;

  int rcd;

  utUnit udu_sct_in; /* UDUnits structure, input units */
  utUnit udu_sct_out; /* UDUnits structure, output units */

  /* Quick return if units identical */
  if(!strcmp(fl_unt_sng,fl_bs_sng) ){
    *og_val=crr_val;  
    return NCO_NOERR;
  } /* endif */

#ifdef UDUNITS_PATH
  /* UDUNITS_PATH macro expands to where autoconf found database file */
  rcd=utInit(UDUNITS_PATH);
#else /* !UDUNITS_PATH */
  /* When empty, utInit() uses environment variable UDUNITS_PATH, if any
     Otherwise it uses default initial location hardcoded when library was built */
  rcd=utInit("");
#endif /* !UDUNITS_PATH */

  if(rcd != UDUNITS_NOERR){
    (void)fprintf(stdout,"%s: %s failed to initialize UDUnits2 library\n",nco_prg_nm_get(),fnc_nm);
    return NCO_ERR;
  } /* end if err */ 

  /* units string to convert from */
  rcd=utScan(fl_unt_sng,&udu_sct_in); 
  if(rcd != UDUNITS_NOERR){
    if(rcd == UT_EINVALID) (void)fprintf(stderr,"ERROR: units attribute \"%s\" is invalid \n",fl_unt_sng);
    if(rcd == UT_ESYNTAX) (void)fprintf(stderr,"ERROR units attribute \"%s\" contains a syntax error",fl_unt_sng);
    if(rcd == UT_EUNKNOWN) (void)fprintf(stderr,"ERROR units attribute \"%s\" is not in udunits database",fl_unt_sng);
    (void)utTerm(); /* Free memory taken by UDUnits library */
    return NCO_ERR;
  } /* endif unkown type */

  /* units string to convert to */
  rcd=utScan(fl_bs_sng,&udu_sct_out); 
  if(rcd != UDUNITS_NOERR){
    if(rcd == UT_EINVALID) (void)fprintf(stderr,"ERROR: units attribute \"%s\" is invalid \n",fl_bs_sng);
    if(rcd == UT_ESYNTAX) (void)fprintf(stderr,"ERROR units attribute \"%s\" contains a syntax error",fl_bs_sng);
    if(rcd == UT_EUNKNOWN) (void)fprintf(stderr,"ERROR units attribute \"%s\" is not in udunits database",fl_bs_sng);
    (void)utTerm(); /* Free memory taken by UDUnits library */
    return NCO_ERR;
  } /* endif unkown type */

  rcd=utConvert(&udu_sct_in,&udu_sct_out,&slp,&incpt);
  if(rcd == UT_ECONVERT){
    (void)fprintf(stderr,"ERROR: user specified unit \"%s\" cannot be converted to units \"%s\"\n",fl_unt_sng,fl_bs_sng);
    (void)utTerm();
    return NCO_ERR;
  } /* endif */

  *og_val=crr_val*slp+incpt;

  /* debug stuff */
  if(nco_dbg_lvl_get() > nco_dbg_std) (void)fprintf(stderr,"%s: %s reports difference between systems \"%s\" and \"%s\" is %f\n",nco_prg_nm_get(),fnc_nm,fl_unt_sng,fl_bs_sng,*og_val);

  (void)utTerm();

  return NCO_NOERR;   
}  /* end UDUnits1 nco_cln_clc_dff() */
Пример #13
0
void
nco_ppc_ini /* Set PPC based on user specifications */
(const int nc_id, /* I [id] netCDF input file ID */
 int *dfl_lvl, /* O [enm] Deflate level */
 const int fl_out_fmt,  /* I [enm] Output file format */
 char * const ppc_arg[], /* I [sng] List of user-specified PPCs */
 const int ppc_arg_nbr, /* I [nbr] Number of PPC specified */
 trv_tbl_sct * const trv_tbl) /* I/O [sct] Traversal table */
{
  int ppc_arg_idx; /* [idx] Index over ppc_arg (i.e., separate invocations of "--ppc var1[,var2]=val") */
  int ppc_var_idx; /* [idx] Index over ppc_lst (i.e., all names explicitly specified in all "--ppc var1[,var2]=val" options) */
  int ppc_var_nbr=0;
  kvm_sct *ppc_lst; /* [sct] List of all PPC specifications */
  kvm_sct kvm;

  if(fl_out_fmt == NC_FORMAT_NETCDF4 || fl_out_fmt == NC_FORMAT_NETCDF4_CLASSIC){
    /* If user did not explicitly set deflate level for this file ... */
    if(*dfl_lvl == NCO_DFL_LVL_UNDEFINED){
      *dfl_lvl=1;
      if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fprintf(stderr,"%s: INFO Precision-Preserving Compression (PPC) automatically activating file-wide deflation level = %d\n",nco_prg_nm_get(),*dfl_lvl);
    } /* endif */
  }else{
    if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fprintf(stderr,"%s: INFO Requested Precision-Preserving Compression (PPC) on netCDF3 output dataset. Unlike netCDF4, netCDF3 does not support internal compression. To take full advantage of PPC consider writing file as netCDF4 enhanced (e.g., %s -4 ...) or classic (e.g., %s -7 ...). Or consider compressing the netCDF3 file afterwards with, e.g., gzip or bzip2. File must then be uncompressed with, e.g., gunzip or bunzip2 before netCDF readers will recognize it. See http://nco.sf.net/nco.html#ppc for more information on PPC strategies.\n",nco_prg_nm_get(),nco_prg_nm_get(),nco_prg_nm_get());
  } /* endelse */

  ppc_lst=(kvm_sct *)nco_malloc(NC_MAX_VARS*sizeof(kvm_sct));

  /* Parse PPCs */
  for(ppc_arg_idx=0;ppc_arg_idx<ppc_arg_nbr;ppc_arg_idx++){
    if(!strstr(ppc_arg[ppc_arg_idx],"=")){
      (void)fprintf(stdout,"%s: Invalid --ppc specification: %s. Must contain \"=\" sign.\n",nco_prg_nm_get(),ppc_arg[ppc_arg_idx]);
      if(ppc_lst) ppc_lst=(kvm_sct *)nco_free(ppc_lst);
      nco_exit(EXIT_FAILURE);
    } /* endif */
    kvm=nco_sng2kvm(ppc_arg[ppc_arg_idx]);
    /* nco_sng2kvm() converts argument "--ppc one,two=3" into kvm.key="one,two" and kvm.val=3
       Then nco_lst_prs_2D() converts kvm.key into two items, "one" and "two", with the same value, 3 */
    if(kvm.key){
      int var_idx; /* [idx] Index over variables in current PPC argument */
      int var_nbr; /* [nbr] Number of variables in current PPC argument */
      char **var_lst;
      var_lst=nco_lst_prs_2D(kvm.key,",",&var_nbr);
      for(var_idx=0;var_idx<var_nbr;var_idx++){ /* Expand multi-variable specification */
        ppc_lst[ppc_var_nbr].key=strdup(var_lst[var_idx]);
        ppc_lst[ppc_var_nbr].val=strdup(kvm.val);
        ppc_var_nbr++;
      } /* end for */
      var_lst=nco_sng_lst_free(var_lst,var_nbr);
    } /* end if */
  } /* end for */

  /* PPC "default" specified, set all non-coordinate variables to default first */
  for(ppc_var_idx=0;ppc_var_idx<ppc_var_nbr;ppc_var_idx++){
    if(!strcasecmp(ppc_lst[ppc_var_idx].key,"default")){
      nco_ppc_set_dflt(nc_id,ppc_lst[ppc_var_idx].val,trv_tbl);
      break; /* Only one default is needed */
    } /* endif */
  } /* end for */

  /* Set explicit, non-default PPCs that can overwrite default */
  for(ppc_var_idx=0;ppc_var_idx<ppc_var_nbr;ppc_var_idx++){
    if(!strcasecmp(ppc_lst[ppc_var_idx].key,"default")) continue;
    nco_ppc_set_var(ppc_lst[ppc_var_idx].key,ppc_lst[ppc_var_idx].val,trv_tbl);
  } /* end for */

  /* Unset PPC and flag for all variables with excessive PPC
     Operational definition of maximum PPC is maximum decimal precision of float/double = FLT_DIG/DBL_DIG = 7/15 */
  const int nco_max_ppc_dbl=15;
  const int nco_max_ppc_flt=7;
  /* Maximum digits for integers taken based on LONG_MAX ... from limits.h */
  const int nco_max_ppc_short=5;
  const int nco_max_ppc_ushort=5;
  const int nco_max_ppc_int=10;
  const int nco_max_ppc_uint=10;
  const int nco_max_ppc_int64=19;
  const int nco_max_ppc_uint64=20;
  int nco_max_ppc=int_CEWI;

  for(unsigned idx_tbl=0;idx_tbl<trv_tbl->nbr;idx_tbl++){
    if(trv_tbl->lst[idx_tbl].ppc != NC_MAX_INT){
      switch(trv_tbl->lst[idx_tbl].var_typ){
      case NC_FLOAT: nco_max_ppc=nco_max_ppc_flt; break;
      case NC_DOUBLE: nco_max_ppc=nco_max_ppc_dbl; break;
      case NC_SHORT: nco_max_ppc=nco_max_ppc_short; break;
      case NC_USHORT: nco_max_ppc=nco_max_ppc_ushort; break;
      case NC_INT: nco_max_ppc=nco_max_ppc_int; break;
      case NC_UINT: nco_max_ppc=nco_max_ppc_uint; break;
      case NC_INT64: nco_max_ppc=nco_max_ppc_int64; break;
      case NC_UINT64: nco_max_ppc=nco_max_ppc_uint64; break;
	/* Do nothing for non-numeric types ...*/
      case NC_CHAR:
      case NC_BYTE:
      case NC_UBYTE:
      case NC_STRING: break;
      default: 
	nco_dfl_case_nc_type_err();
	break;
      } /* end switch */

      switch(trv_tbl->lst[idx_tbl].var_typ){
	/* Floating point types */
      case NC_FLOAT: 
      case NC_DOUBLE: 
	if(trv_tbl->lst[idx_tbl].ppc > nco_max_ppc){
	  if(trv_tbl->lst[idx_tbl].flg_nsd) (void)fprintf(stdout,"%s: INFO Number of Significant Digits (NSD) requested = %d too high for variable %s which is of type %s. No quantization or rounding will be performed for this variable. HINT: Maximum precisions for NC_FLOAT and NC_DOUBLE are %d and %d, respectively.\n",nco_prg_nm_get(),trv_tbl->lst[idx_tbl].ppc,trv_tbl->lst[idx_tbl].nm,nco_typ_sng(trv_tbl->lst[idx_tbl].var_typ),nco_max_ppc_flt,nco_max_ppc_dbl);
	  trv_tbl->lst[idx_tbl].ppc=NC_MAX_INT;
	} /* endif */
	break;
	/* Integer types */
      case NC_SHORT:
      case NC_USHORT:
      case NC_INT:
      case NC_UINT:
      case NC_INT64:
      case NC_UINT64:
	if(
	   /* ...rounding requested with NSD or ... */
	   (trv_tbl->lst[idx_tbl].flg_nsd) ||
	   /* ...more rounding requested with DSD than available or ... */
	   (!trv_tbl->lst[idx_tbl].flg_nsd && (trv_tbl->lst[idx_tbl].ppc < -1*nco_max_ppc)) ||
	   /* ...more precision requested than integers have or ... */
	   (!trv_tbl->lst[idx_tbl].flg_nsd && (trv_tbl->lst[idx_tbl].ppc >= 0)) ||
	   False)
	trv_tbl->lst[idx_tbl].ppc=NC_MAX_INT;
	break;
      case NC_CHAR: /* Do nothing for non-numeric types ...*/
      case NC_BYTE:
      case NC_UBYTE:
      case NC_STRING:
	trv_tbl->lst[idx_tbl].ppc=NC_MAX_INT;
	break;
      default: 
	nco_dfl_case_nc_type_err();
	break;
      } /* end switch */
      /* For consistency reset flg_nsd as well */
      if(trv_tbl->lst[idx_tbl].ppc == NC_MAX_INT) trv_tbl->lst[idx_tbl].flg_nsd=True;
    } /* endif */
  } /* endfor */

  if(ppc_lst) ppc_lst=nco_kvm_lst_free(ppc_lst,ppc_var_nbr);
} /* end nco_ppc_ini() */
Пример #14
0
void
nco_ppc_around /* [fnc] Replace op1 values by their values rounded to decimal precision prc */
(const int ppc, /* I [nbr] Precision-preserving compression, i.e., number of total or decimal significant digits */
 const nc_type type, /* I [enm] netCDF type of operand */
 const long sz, /* I [nbr] Size (in elements) of operand */
 const int has_mss_val, /* I [flg] Flag for missing values */
 ptr_unn mss_val, /* I [val] Value of missing value */
 ptr_unn op1) /* I/O [val] Values of first operand */
{
  /* Threads: Routine is thread safe and calls no unsafe routines */

  /* Purpose: Replace op1 values by their values rounded to decimal precision ppc
     Similar to numpy.around() function, hence the name around()
     Based on implementation by Jeff Whitaker for netcdf4-python described here:
     http://netcdf4-python.googlecode.com/svn/trunk/docs/netCDF4-module.html
     which invokes the numpy.around() function documented here:
     http://docs.scipy.org/doc/numpy/reference/generated/numpy.around.html#numpy.around
     A practical discussion of rounding is at
     http://stackoverflow.com/questions/20388071/what-are-the-under-the-hood-differences-between-round-and-numpy-round
     This mentions the () NumPy source code:
     https://github.com/numpy/numpy/blob/7b2f20b406d27364c812f7a81a9c901afbd3600c/numpy/core/src/multiarray/calculation.c#L588

     Manually determine scale:
     ncap2 -O -v -s 'ppc=2;ppc_abs=abs(ppc);bit_nbr_xct=ppc_abs*ln(10.)/ln(2.);bit_nbr_int=ceil(bit_nbr_xct);scale=pow(2.0,bit_nbr_int);' ~/nco/data/in.nc ~/foo.nc 
     ncks -H ~/foo.nc

     Test full algorithm:
     ncks -4 -O -C -v ppc_dbl,ppc_big --ppc ppc_dbl=3 --ppc ppc_big=-2 ~/nco/data/in.nc ~/foo.nc

     Compare to Jeff Whitaker's nc3tonc4 results:
     nc3tonc4 -o --quantize=ppc_dbl=3,ppc_big=-2 ~/nco/data/in.nc ~/foo.nc
     ncks -H -C -v ppc_dbl,ppc_big ~/foo.nc */
  
  /* Rounding is currently defined as op1:=around(op1,ppc) */  
  
  /* Use constants defined in math.h */
  const double bit_per_dcm_dgt_prc=M_LN10/M_LN2; /* 3.32 [frc] Bits per decimal digit of precision */

  double scale; /* [frc] Number by which to scale data to achieve rounding */
  float scalef; /* [frc] Number by which to scale data to achieve rounding */

  int bit_nbr; /* [nbr] Number of bits required to exceed pow(10,-ppc) */
  int ppc_abs; /* [nbr] Absolute value of precision */

  long idx;
  
  /* Only numeric types can be quantized */
  if(type == NC_CHAR || type == NC_BYTE || type == NC_UBYTE || type == NC_STRING) return;

  ppc_abs=abs(ppc);
  assert(ppc_abs <= 16);
  switch(ppc_abs){
  case 0:
    bit_nbr=0;
    scale=1.0;
    break;
  case 1:
    bit_nbr=4;
    scale=16.0;
    break;
  case 2:
    bit_nbr=7;
    scale=128.0;
    break;
  case 3:
    bit_nbr=10;
    scale=1024.0;
    break;
  case 4:
    bit_nbr=14;
    scale=16384.0;
    break;
  case 5:
    bit_nbr=17;
    scale=131072.0;
    break;
  case 6:
    bit_nbr=20;
    scale=1048576.0;
    break;
  default:
    bit_nbr=(int)ceil(ppc_abs*bit_per_dcm_dgt_prc);
    scale=pow(2.0,bit_nbr);
    break;
  } /* end switch */   
  if(ppc < 0) scale=1.0/scale;

  if(nco_dbg_lvl_get() == nco_dbg_sbr) (void)fprintf(stdout,"%s: INFO nco_ppc_around() reports ppc = %d, bit_nbr= %d, scale = %g\n",nco_prg_nm_get(),ppc,bit_nbr,scale);

  /* Typecast pointer to values before access */
  (void)cast_void_nctype(type,&op1);
  if(has_mss_val) (void)cast_void_nctype(type,&mss_val);
  
  scalef=(float)scale;
  switch(type){
  case NC_FLOAT: 
    /* By default do float arithmetic in double precision before converting back to float
       Allow --flt to override
       NB: Use rint() not lrint()
       If ignoring this advice, be sure to bound calls to lrint(), e.g., 
       rint_arg=scale*op1.fp[idx];
       if(rint_arg > LONG_MIN && rint_arg < LONG_MAX) op1.fp[idx]=(float)lrint(scale*op1.fp[idx])/scale; */
    if(!has_mss_val){
      if(nco_rth_cnv_get() == nco_rth_flt_flt)
	for(idx=0L;idx<sz;idx++) op1.fp[idx]=rintf(scalef*op1.fp[idx])/scalef;
      else
	for(idx=0L;idx<sz;idx++) op1.fp[idx]=(float)(rint(scale*op1.fp[idx])/scale); /* Coerce to avoid implicit conversions warning */
    }else{
      const float mss_val_flt=*mss_val.fp;
      if(nco_rth_cnv_get() == nco_rth_flt_flt){
	for(idx=0;idx<sz;idx++)
	  if(op1.fp[idx] != mss_val_flt)
	    op1.fp[idx]=rintf(scalef*op1.fp[idx])/scalef;
      }else{
	for(idx=0;idx<sz;idx++)
	  if(op1.fp[idx] != mss_val_flt)
	    op1.fp[idx]=(float)(rint(scale*op1.fp[idx])/scale); /* Coerce to avoid implicit conversions warning */
      } /* end else */
    } /* end else */
    break;
  case NC_DOUBLE: 
    if(!has_mss_val){
      for(idx=0L;idx<sz;idx++) op1.dp[idx]=rint(scale*op1.dp[idx])/scale;
    }else{
      const double mss_val_dbl=*mss_val.dp;
      for(idx=0;idx<sz;idx++)
	if(op1.dp[idx] != mss_val_dbl) op1.dp[idx]=rint(scale*op1.dp[idx])/scale;
    } /* end else */
    break;
  case NC_SHORT:
    if(!has_mss_val){
      for(idx=0L;idx<sz;idx++) op1.sp[idx]=(short int)lrint(scale*op1.sp[idx])/scale;
    }else{
      const nco_int mss_val_short=*mss_val.sp;
      for(idx=0;idx<sz;idx++)
	if(op1.sp[idx] != mss_val_short) op1.sp[idx]=(short int)lrint(scale*op1.sp[idx])/scale;
    } /* end else */
    break;
  case NC_USHORT:
    if(!has_mss_val){
      for(idx=0L;idx<sz;idx++) op1.usp[idx]=(unsigned short int)lrint(scale*op1.usp[idx])/scale;
    }else{
      const nco_ushort mss_val_ushort=*mss_val.usp;
      for(idx=0;idx<sz;idx++)
	if(op1.usp[idx] != mss_val_ushort) op1.usp[idx]=(unsigned short int)lrint(scale*op1.usp[idx])/scale;
    } /* end else */
    break;
  case NC_INT:
    if(!has_mss_val){
      for(idx=0L;idx<sz;idx++) op1.ip[idx]=lrint(scale*op1.ip[idx])/scale;
    }else{
      const nco_int mss_val_int=*mss_val.ip;
      for(idx=0;idx<sz;idx++)
	if(op1.ip[idx] != mss_val_int) op1.ip[idx]=lrint(scale*op1.ip[idx])/scale;
    } /* end else */
    break;
  case NC_UINT:
    if(!has_mss_val){
      for(idx=0L;idx<sz;idx++) op1.uip[idx]=(unsigned int)lrint(scale*op1.uip[idx])/scale;
    }else{
      const nco_uint mss_val_uint=*mss_val.uip;
      for(idx=0;idx<sz;idx++)
	if(op1.uip[idx] != mss_val_uint) op1.uip[idx]=(unsigned int)lrint(scale*op1.uip[idx])/scale;
    } /* end else */
    break;
  case NC_INT64:
    if(!has_mss_val){
      for(idx=0L;idx<sz;idx++) op1.i64p[idx]=lrint(scale*op1.i64p[idx])/scale;
    }else{
      const nco_int64 mss_val_int64=*mss_val.i64p;
      for(idx=0;idx<sz;idx++)
	if(op1.i64p[idx] != mss_val_int64) op1.i64p[idx]=lrint(scale*op1.i64p[idx])/scale;
    } /* end else */
    break;
  case NC_UINT64:
    if(!has_mss_val){
      for(idx=0L;idx<sz;idx++) op1.ui64p[idx]=(unsigned long)lrint(scale*op1.ui64p[idx])/scale;
    }else{
      const nco_uint64 mss_val_uint64=*mss_val.ui64p;
      for(idx=0;idx<sz;idx++)
	if(op1.ui64p[idx] != mss_val_uint64) op1.ui64p[idx]=(unsigned long)lrint(scale*op1.ui64p[idx])/scale;
    } /* end else */
    break;
  case NC_CHAR: /* Do nothing for non-numeric types ...*/
  case NC_BYTE:
  case NC_UBYTE:
  case NC_STRING: break;
  default: 
    nco_dfl_case_nc_type_err();
    break;
  } /* end switch */

  /* NB: it is not neccessary to un-typecast pointers to values after access 
     because we have only operated on local copies of them. */
  
} /* end nco_ppc_around() */
Пример #15
0
int /* O [nbr] Thread number */
nco_openmp_ini /* [fnc] Initialize OpenMP threading environment */
(const int thr_nbr) /* I [nbr] User-requested thread number */
{
  /* Purpose: Initialize OpenMP multi-threading environment
     Honor user-requested thread number, balance against known code efficiency,
     print diagnostics
     Returns thr_nbr=1 in three situations:
     1. UP codes (not threaded)
     2. SMP codes compiled with compilers which lack OpenMP support
     3. SMP codes where single thread requested/advised
     Otherwise returns system-dependent thr_nbr */

  /* Using naked stdin/stdout/stderr in parallel region generates warning
     Copy appropriate filehandle to variable scoped shared in parallel clause */

  char *nvr_OMP_NUM_THREADS; /* [sng] Environment variable OMP_NUM_THREADS */
  char *sng_cnv_rcd=NULL_CEWI; /* [sng] strtol()/strtoul() return code */
  FILE * const fp_stderr=stderr; /* [fl] stderr filehandle CEWI */

  nco_bool USR_SPC_THR_RQS=False;

  int dyn_thr=1; /* [flg] Allow system to dynamically set number of threads */

  int ntg_OMP_NUM_THREADS=int_CEWI; // [nbr] OMP_NUM_THREADS environment variable
  int prc_nbr_max; /* [nbr] Maximum number of processors available */
  int thr_nbr_act; /* O [nbr] Number of threads NCO uses */
  int thr_nbr_max_fsh=4; /* [nbr] Maximum number of threads program can use efficiently */
  int thr_nbr_max=int_CEWI; /* [nbr] Maximum number of threads system allows */
  int thr_nbr_rqs=int_CEWI; /* [nbr] Number of threads to request */

#ifndef _OPENMP
  if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fprintf(fp_stderr,"%s: INFO Build compiler lacked (or user turned off) OpenMP support. Code will execute with single thread in Uni-Processor (UP) mode.\n",nco_prg_nm_get());
  return (int)1;
#endif /* !_OPENMP */

  /* Strategy: 
     0. Determine maximum number of threads system will allocate (thr_nbr_max)
     1. Command-line thread request, if any, overrides automatic algorithm
     2. If no command-line request then system allocates OMP_NUM_THREADS if possible
     3. Reduce maximum number of threads available to system to thr_nbr_max_fsh
     Many operators cannot use more than thr_nbr_max_fsh ~ 2--4 threads efficiently
     Play nice: Set dynamic threading so that system can make efficiency decisions
     When dynamic threads are set, system never allocates more than thr_nbr_max_fsh */
  if(thr_nbr < 0){
    (void)fprintf(fp_stderr,"%s: ERROR User-requested thread number = %d is less than zero\n",nco_prg_nm_get(),thr_nbr);
    nco_exit(EXIT_FAILURE);
  } /* endif err */

  if(thr_nbr == 0)
    if(nco_dbg_lvl_get() >= nco_dbg_scl && nco_dbg_lvl_get() != nco_dbg_dev )
      (void)fprintf(fp_stderr,"%s: INFO User did not specify thread request > 0 on command line. NCO will automatically assign threads based on OMP_NUM_THREADS environment and machine capabilities.\nHINT: Not specifiying any --thr_nbr (or specifying --thr_nbr=0) causes NCO to try to pick the optimal thread number. Specifying --thr_nbr=1 tells NCO to execute in Uni-Processor (UP) (i.e., single-threaded) mode.\n",nco_prg_nm_get());

  if(thr_nbr > 0) USR_SPC_THR_RQS=True;

  prc_nbr_max=omp_get_num_procs(); /* [nbr] Maximum number of processors available */
  if(omp_in_parallel()){
    (void)fprintf(fp_stderr,"%s: ERROR Attempted to get maximum thread number from within parallel region\n",nco_prg_nm_get());
    nco_exit(EXIT_FAILURE);
  }else{
    thr_nbr_max=omp_get_max_threads(); /* [nbr] Maximum number of threads system allows */
  } /* end error */

  if(nco_dbg_lvl_get() >= nco_dbg_scl && nco_dbg_lvl_get() != nco_dbg_dev){
    if((nvr_OMP_NUM_THREADS=getenv("OMP_NUM_THREADS"))) ntg_OMP_NUM_THREADS=(int)strtol(nvr_OMP_NUM_THREADS,&sng_cnv_rcd,NCO_SNG_CNV_BASE10); /* [sng] Environment variable OMP_NUM_THREADS */
    if(nvr_OMP_NUM_THREADS && *sng_cnv_rcd) nco_sng_cnv_err(nvr_OMP_NUM_THREADS,"strtol",sng_cnv_rcd);
    (void)fprintf(fp_stderr,"%s: INFO Environment variable OMP_NUM_THREADS ",nco_prg_nm_get());
    if(ntg_OMP_NUM_THREADS > 0) (void)fprintf(fp_stderr,"= %d\n",ntg_OMP_NUM_THREADS); else (void)fprintf(fp_stderr,"does not exist\n");
    (void)fprintf(fp_stderr,"%s: INFO omp_get_num_procs() reports number of processors available is %d\n",nco_prg_nm_get(),prc_nbr_max);
    (void)fprintf(fp_stderr,"%s: INFO omp_get_max_threads() reports maximum number of threads system allows is %d\n",nco_prg_nm_get(),thr_nbr_max);
  } /* endif dbg */

  if(USR_SPC_THR_RQS){
    /* Try to honor user-specified thread request... */
    thr_nbr_rqs=thr_nbr; /* [nbr] Number of threads to request */
    /* ...if possible... */
    if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(fp_stderr,"%s: INFO Command-line requests %d thread%s\n",nco_prg_nm_get(),thr_nbr,(thr_nbr > 1) ? "s" : "");
    if(thr_nbr > thr_nbr_max){
      (void)fprintf(fp_stderr,"%s: WARNING Reducing user-requested thread number = %d to maximum thread number allowed = %d\n",nco_prg_nm_get(),thr_nbr,thr_nbr_max);
      thr_nbr_rqs=thr_nbr_max; /* [nbr] Number of threads to request */
    } /* endif */
  }else{ /* !USR_SPC_THR_RQS */
    /* Otherwise use automatic thread allocation algorithm */

    /* Request maximum number of threads permitted */
    thr_nbr_rqs=thr_nbr_max; /* [nbr] Number of threads to request */

    /* Restrict threading on per-program basis to play nicely with others */
    switch(nco_prg_id_get()){
      /* Operators with pre-set thread limit
	 NB: All operators currently have default restrictions
	 2007: Only ncwa and ncap2 have a chance to scale on non-parallel filesystems
	 ncap2 may, one day, see a big performance boost from threading
	 However, as of 20090327, ncap2 threading may be buggy due to ANTLR
	 Moreover, we want to prevent hogging processes on 32-way nodes
	 until/unless clear benefits of threading are demonstrated.
	 2015: Threads improve ncks regridding performance by 2-3x on ACME ~1-20 GB netCDF3 files */
    case ncap: 
      /* 20090327: Restrict ncap2 to one thread until ANTLR threading resolved */
      thr_nbr_max_fsh=1;
      break;
    case ncecat: 
    case ncrcat: 
      /* ncecat and ncrcat are extremely I/O intensive 
	 Maximum efficiency when one thread reads from input file while other writes to output file */
      // 20140219: Turn-off OpenMP until thoroughly tested
      // thr_nbr_max_fsh=2;
      thr_nbr_max_fsh=1;
      break;
    case ncks: 
      // 20150529: Turn-on OpenMP for regridder
      thr_nbr_max_fsh=16;
      break;
    case ncwa: 
      // 20150530: Turn-on OpenMP for debugging
      // 20150610: Eight threads with ncwa seemed to work for a little while, then it got flaky. Turned-off for 4.5.0 release
      // 20150622: Allowing eight threads again for debugging with -D 3
      // 20150701: Firmly established that netCDF4 involvement hoses threading because HDF5 is not threadsafe by default
      // 20150710: Turned-off for 4.5.1 release
      // Symptoms of bugs, if any, show up with
      // cd ~/nco/bm;nco_bm.pl --regress ncwa;cd -
      thr_nbr_max_fsh=1;
      if(nco_dbg_lvl_get() >= nco_dbg_scl) thr_nbr_max_fsh=1;
      break;
      /* Operators with higher maximum pre-set thread limit (NB: not all of these are threaded!) */
    case ncra:
      thr_nbr_max_fsh=1;
      if(nco_dbg_lvl_get() >= nco_dbg_scl) thr_nbr_max_fsh=1;
      break;
    case ncbo: 
    case ncatted: 
    case ncfe:
    case ncflint: 
    case ncpdq: 
    case ncrename: 
    case ncge:
      // 20140219: Turn-off OpenMP until thoroughly tested
      // thr_nbr_max_fsh=4;
      thr_nbr_max_fsh=1;
      break;
    default: nco_dfl_case_prg_id_err(); break;
    } /* end case */
    
    /* Automatic algorithm tries to play nice with others */
    (void)omp_set_dynamic(dyn_thr); /* [flg] Allow system to dynamically set number of threads */
    if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fprintf(fp_stderr,"%s: INFO omp_set_dynamic() used to %s OS to dynamically set threads\n",nco_prg_nm_get(),(dyn_thr ? "ALLOW" : "DISALLOW"));
    dyn_thr=omp_get_dynamic(); /* [flg] Allow system to dynamically set number of threads */
    if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fprintf(fp_stderr,"%s: INFO omp_get_dynamic() reports system will%s utilize dynamic threading\n",nco_prg_nm_get(),(dyn_thr ? "" : " NOT"));

    /* Apply program/system limitations */
    if(thr_nbr_max > thr_nbr_max_fsh){
      if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fprintf(fp_stderr,"%s: INFO Reducing default thread number from %d to %d, an operator-dependent \"play-nice\" number set in nco_openmp_ini()\n",nco_prg_nm_get(),thr_nbr_max,thr_nbr_max_fsh);
      thr_nbr_rqs=thr_nbr_max_fsh; /* [nbr] Number of threads to request */
    } /* endif */      
  } /* !USR_SPC_THR_RQS */

#ifdef ENABLE_NETCDF4
  if(nco_prg_id_get() != ncks && nco_prg_id_get() != ncwa && nco_prg_id_get() != ncra && thr_nbr_rqs > 1){
    if(USR_SPC_THR_RQS && nco_dbg_lvl_get() >= nco_dbg_fl) (void)fprintf(stdout,"%s: WARNING This is TODO nco939. Requested threading with netCDF4 (HDF5) support. The NCO thread request algorithm considers user-input, environment variables, and software and hardware limitations in determining the number of threads to request, thr_nbr_rqs. At this point NCO would request result %d threads from a netCDF3-based library. However, this NCO was built with netCDF4, which relies on HDF5. netCDF4 is not thread-safe unless HDF5 is configured with the (non-default) --enable-threadsafe option. NCO currently has no way to know whether HDF5 was built thread-safe. Hence, all netCDF4-based operators are currently restricted to a single thread. The program will now automatically set thr_nbr_rqs = 1.\nThis unfortunate limitation is necessary to keep the NCO developers sane. If you want/need threading in netCDF4-based NCO, please politely yet firmly request of the Unidata netCDF developers that better thread support be built into netCDF4, and request of the HDF5 developers that they make the --enable-threadsafe option compatible with all HDF5 libraries and APIs, including Fortran (which, as of HDF5 1.8.0 in 2008, is incompatible with --enable-threadsafe).\n",nco_prg_nm_get(),thr_nbr_rqs);
    thr_nbr_rqs=1;
  } /* endif */
#endif /* !ENABLE_NETCDF4 */

  /* Set thread number */
  if(omp_in_parallel()){
    (void)fprintf(fp_stderr,"%s: ERROR Attempted to set thread number from within parallel region\n",nco_prg_nm_get());
    nco_exit(EXIT_FAILURE);
  }else{
    (void)omp_set_num_threads(thr_nbr_rqs); 
    if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fprintf(fp_stderr,"%s: INFO omp_set_num_threads() used to set execution environment to spawn teams of %d thread(s)\n",nco_prg_nm_get(),thr_nbr_rqs);
  } /* end error */

  thr_nbr_act=omp_get_max_threads();
  if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(fp_stderr,"%s: INFO After using omp_set_num_threads() to adjust for any user requests/NCO optimizations, omp_get_max_threads() reports that a parallel construct here/now would spawn %d thread(s)\n",nco_prg_nm_get(),thr_nbr_act);
#ifdef _OPENMP
  if(nco_dbg_lvl_get() >= nco_dbg_scl){
# pragma omp parallel default(none) shared(thr_nbr_act)
    { /* begin OpenMP parallel */
# pragma omp single nowait
      { /* begin OpenMP single */
	thr_nbr_act=omp_get_num_threads(); /* [nbr] Number of threads NCO uses */
	if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fprintf(fp_stderr,"%s: INFO Small parallel test region spawned team of %d thread(s)\n",nco_prg_nm_get(),thr_nbr_act);
      } /* end OpenMP single */
    } /* end OpenMP parallel */
  } /* end dbg */
#endif /* !_OPENMP */
  
  /* Issue any warnings about OpenMP credibility during debugging phase */
  if(True)
     if((nco_prg_id_get() == ncwa || nco_prg_id_get() == ncra) && thr_nbr_act > 1)
      if(nco_dbg_lvl_get() >= nco_dbg_std) (void)fprintf(fp_stderr,"%s: WARNING OpenMP threading active with %d threads but not guaranteed to work on this operator. If strange behavior (e.g., NaN results) ensues, manually turn-off multi-threading by specifying \"-t 1\" option.\n",nco_prg_nm_get(),thr_nbr_act);

  return thr_nbr_act; /* O [nbr] Number of threads NCO uses */
} /* end nco_openmp_ini() */
Пример #16
0
int /* [rcd] Return code */
nco_cln_sng_rbs /* [fnc] Rebase calendar string for legibility */
(const ptr_unn val, /* I [sct] Value to rebase */
 const long val_idx, /* I [idx] Index into 1-D array of values */
 const nc_type val_typ, /* I [enm] Value type */
 const char *unit_sng, /* I [sng] Units string */
 char *lgb_sng) /* O [sng] Legible version of input string */
{
  /* Purpose: Rebase calendar string for legibility
     Assumptions: Input units string unit_sng is a calendar date, i.e., contains "from", "since", or "after"

     ncdump handles this in nctime0.c
     dumplib.c/nctime_val_tostring() by Dave Allured, NOAA
     cdRel2Iso() from CDMS by Bob Drach, LLNL
     cdParseRelunits() from CDMS by Bob Drach, LLNL */

#ifdef HAVE_UDUNITS2_H

  const char fnc_nm[]="nco_cln_sng_rbs()"; /* [sng] Function name */
  
  double val_dbl; /* [day] Calendar offset converted to double */

  int ut_rcd; /* [enm] UDUnits2 status */
  
  ut_system *ut_sys;
  ut_unit *ut_sct_in; /* [sct] UDUnits structure, input units */
  ut_unit *ut_sct_out; /* [sct] UDUnits structure, output units */

  /* Quick return if units DNE */
  if(!unit_sng) return NCO_NOERR;
  
  /* When empty, ut_read_xml() uses environment variable UDUNITS2_XML_PATH, if any
     Otherwise it uses default initial location hardcoded when library was built */
  if(nco_dbg_lvl_get() >= nco_dbg_vrb) ut_set_error_message_handler(ut_write_to_stderr); else ut_set_error_message_handler(ut_ignore);
  ut_sys=ut_read_xml(NULL);
  if(!ut_sys){
    (void)fprintf(stdout,"%s: %s() failed to initialize UDUnits2 library\n",nco_prg_nm_get(),fnc_nm);
    return NCO_ERR; /* Failure */
  } /* end if err */ 

  /* Units string containing calendar origin converted to UDUnit structure */
  ut_sct_in=ut_parse(ut_sys,unit_sng,UT_ASCII); 
  if(!ut_sct_in){ /* Problem with 'units' attribute */
    ut_rcd=ut_get_status(); /* [enm] UDUnits2 status */
    if(ut_rcd == UT_BAD_ARG) (void)fprintf(stderr,"ERROR: empty units attribute string\n");
    if(ut_rcd == UT_SYNTAX) (void)fprintf(stderr,"ERROR: units attribute \"%s\" has a syntax error\n",unit_sng);
    if(ut_rcd == UT_UNKNOWN) (void)fprintf(stderr,"ERROR: units attribute \"%s\" is not listed in UDUnits2 SI system database\n",unit_sng);
    return NCO_ERR; /* Failure */
  } /* endif coordinate on disk has no units attribute */

  /* Convert time since calendar origin to double */
  val_dbl=ptr_unn_2_scl_dbl(val,val_typ); 
  
  /* Units string to convert to */
  ut_sct_out=ut_offset(ut_sct_in,val_dbl);
  if(!ut_sct_out){ /* Problem with 'units' attribute */
    ut_rcd=ut_get_status(); /* [enm] UDUnits2 status */
    if(ut_rcd == UT_BAD_ARG) (void)fprintf(stderr,"ERROR: Empty units attribute string\n");
    if(ut_rcd == UT_SYNTAX) (void)fprintf(stderr,"ERROR: units attribute  \"%s\" has a syntax error\n",unit_sng);
    if(ut_rcd == UT_UNKNOWN) (void)fprintf(stderr,"ERROR: units attribute \"%s\" is not listed in UDUnits2 SI system database\n",unit_sng);
    return NCO_ERR; /* Failure */
  } /* endif */

  val_dbl+=0*val_idx; /* CEWI */

  ut_free(ut_sct_in);
  ut_free(ut_sct_out);
  ut_free_system(ut_sys); /* Free memory taken by UDUnits library */
#endif /* !HAVE_UDUNITS2 */

  lgb_sng[0]='\0'; /* CEWI */

  return NCO_NOERR;

} /* end nco_cln_sng_rbs() */
Пример #17
0
int /* [rcd] Successful conversion returns NCO_NOERR */
nco_cln_clc_tm /* [fnc] Difference between two coordinate units */
(const char *fl_unt_sng, /* I [ptr] Units attribute string from disk */
 const char *fl_bs_sng, /* I [ptr] Units attribute string from disk */
 nco_cln_typ lmt_cln, /* [enum] Calendar type of coordinate variable */ 
 double *og_val){ /* O [ptr] */
  
  int rcd;
  int year;
  int month;
  char *lcl_unt_sng;
  /* 20141230 figure out better length */
  char tmp_sng[100];
  double crr_val;
  
  tm_typ bs_tm_typ; /* enum for units type in fl_bs_sng */
  tm_cln_sct unt_cln_sct;
  tm_cln_sct bs_cln_sct;
  
  if(nco_dbg_lvl_get() >= nco_dbg_scl) (void)fprintf(stderr,"%s: nco_cln_clc_tm() reports unt_sng=%s bs_sng=%s\n",nco_prg_nm_get(),fl_unt_sng,fl_bs_sng);
  
  /* Does fl_unt_sng look like a regular timestamp? */ 
  if(sscanf(fl_unt_sng,"%d-%d",&year,&month) == 2){
    lcl_unt_sng=(char *)nco_malloc((strlen(fl_unt_sng)+3L)*sizeof(char));
    strcpy(lcl_unt_sng,"s@");
    strcat(lcl_unt_sng,fl_unt_sng);
  }else{
    lcl_unt_sng=strdup(fl_unt_sng);
  } /* endelse */
  
  /* Temporary until we handle more calendar types */
  if(lmt_cln != cln_360 && lmt_cln != cln_365){
    rcd=nco_cln_clc_dff(lcl_unt_sng,fl_bs_sng,0.0,og_val);
    lcl_unt_sng=(char *)nco_free(lcl_unt_sng);
    return rcd;
  } /* endif */

  /* Obtain units type from fl_bs_sng */
  if(sscanf(fl_bs_sng,"%s",tmp_sng) != 1) return NCO_ERR;
  
  bs_tm_typ=nco_cln_get_tm_typ(tmp_sng);  
  
  /* Assume non-standard calendar */ 
  if(nco_cln_prs_tm(lcl_unt_sng,&unt_cln_sct) == NCO_ERR) return NCO_ERR;
  if(nco_cln_prs_tm(fl_bs_sng,&bs_cln_sct) == NCO_ERR) return NCO_ERR;
  
  unt_cln_sct.sc_typ=bs_tm_typ;
  bs_cln_sct.sc_typ=bs_tm_typ;
  
  unt_cln_sct.sc_cln=lmt_cln;
  bs_cln_sct.sc_cln=lmt_cln;
  
  (void)nco_cln_pop_val(&unt_cln_sct);
  (void)nco_cln_pop_val(&bs_cln_sct);
  
  crr_val=nco_cln_rel_val(unt_cln_sct.value-bs_cln_sct.value,lmt_cln,bs_tm_typ);                 
  
  *og_val=crr_val;
  
  lcl_unt_sng=(char *)nco_free(lcl_unt_sng);
  
  return NCO_NOERR;
} /* end nco_cln_clc_tm() */
Пример #18
0
int                                  /* O [rcd] Return code */
nco_cnv_cf_cll_mth_add               /* [fnc] Add cell_methods attributes */
(const int out_id,                   /* I [id] netCDF file ID */
 var_sct * const * const var,        /* I [sct] Variable to reduce (e.g., average) (destroyed) */
 const int var_nbr,                  /* I [nbr] Number of variables to be defined */
 dmn_sct * const * const dmn_rdc,    /* I [sct] Dimensions over which to reduce variable */
 const int dmn_nbr_rdc,              /* I [sct] Number of dimensions to reduce variable over */
 const int nco_op_typ,               /* I [enm] Operation type, default is average */
 gpe_sct *gpe,                       /* I [sng] Group Path Editing (GPE) structure */
 const clm_bnd_sct * const cb,       /* I [sct] Climatology bounds structure */
 const trv_tbl_sct * const trv_tbl)  /* I [sct] Traversal table */
{
  /* Purpose: Add/modify CF cell_methods attribute
     http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/build/cf-conventions.html#cell-methods
     
     cell_methods values and description:
     point	: Data values are representative of points in space or time (instantaneous)
     sum	: Data values are representative of a sum or accumulation over the cell
     maximum_absolute_value	: Maximum absolute value
     maximum	: Maximum
     median	: Median
     mid_range	: Average of maximum and minimum
     minimum	: Minimum
     mean	: Mean (average value)
     mode	: Mode (most common value)
     range	: Absolute difference between maximum and minimum
     standard_deviation : Standard deviation
     variance	: Variance
     
     NCO operation types:
     avg Mean value
     avgsqr Mean of sum of squares
     mabs Maximum absolute value
     max Maximum value
     mebs Mean absolute value
     mibs Minimum absolute value
     min Minimum value
     rms Root-mean-square (normalized by N)
     rmssdn Root-mean square (normalized by N-1)
     sqravg Square of the mean
     sqrt Square root of the mean
     ttl Sum of values */

  const char fnc_nm[]="nco_cnv_cf_cll_mth_add()"; /* [sng] Function name */

  aed_sct aed; /* [sct] Attribute-edit information */

  char att_op_sng[23]; /* [sng] Operation type (longest is nco_op_mabs which translates to "maximum_absolute_value") */

  char *att_val=NULL; /* [sng] Coordinates attribute */
  char *att_val_cpy; /* [sng] Copy of attribute */
  char *grp_out_fll=NULL; /* [sng] Group name */
  char *sbs_ptr; /* [sng] Pointer to substring */
  char *cll_mth_clm; /* [sng] Cell methods for climatology */
  
  int *dmn_mch; /* [idx] Indices of dimensions reduced in this variable */

  int dmn_idx_rdc;
  int dmn_idx_var;
  int dmn_nbr_mch; /* [nbr] Number of dimension names to add to cell_methods */
  int grp_out_id; /* [ID] Group ID (output) */
  int nco_op_typ_lcl; /* [enm] Operation type, default is average */
  int rcd=NC_NOERR; /* [rcd] Return code */
  int var_idx;
  int var_out_id; /* [ID] Variable ID (output) */

  long int att_lng; /* [nbr] Length of attribute string */
  
  nc_type att_typ; /* [nbr] Attribute type */

  nco_bool FIRST_WARNING=True;
  nco_bool flg_dpl; /* [flg] New cell_methods attribute duplicates old */
  nco_bool mlt_dmn_rdc; /* [flg] Multiple dimension reduction flag */

  size_t dmn_sng_lng; /* [nbr] Length of dimension string */
  size_t sbs_sng_lng; /* [nbr] Length of substring */

  trv_sct *var_trv=NULL;  /* [sct] Variable GTT object */

  /* Initialize unchanging structure members */
  aed.att_nm=strdup("cell_methods");
  aed.type=NC_CHAR;

  /* Allocate space for maximum number of matching dimensions */
  dmn_mch=(int *)nco_calloc(dmn_nbr_rdc,sizeof(int));

  if(cb){
    if(cb->bnd2clm || cb->clm2clm) cll_mth_clm=strdup("time: mean within years time: mean over years");
    if(cb->clm2bnd) cll_mth_clm=strdup("time: mean");
  } /* !cb */

  /* Process all variables */
  for(var_idx=0;var_idx<var_nbr;var_idx++){ 

    /* Obtain variable GTT object using full variable name */
    var_trv=trv_tbl_var_nm_fll(var[var_idx]->nm_fll,trv_tbl);

    /* Edit group name for output */
    if(gpe) grp_out_fll=nco_gpe_evl(gpe,var_trv->grp_nm_fll); else grp_out_fll=(char *)strdup(var_trv->grp_nm_fll);

    /* Obtain output group ID */
    (void)nco_inq_grp_full_ncid(out_id,grp_out_fll,&grp_out_id);

    /* Memory management after current extracted group */
    if(grp_out_fll) grp_out_fll=(char *)nco_free(grp_out_fll);

    /* Get variable ID */
    (void)nco_inq_varid(grp_out_id,var_trv->nm,&var_out_id);

    /* Initialize attribute-edit structure for this variable */
    aed.var_nm=var_trv->nm;
    aed.id=var_out_id;
    aed.sz=0L;
    dmn_nbr_mch=0;
    flg_dpl=False;

    if(cb){
      /* Does variable use time coordinate? */
      for(dmn_idx_var=0;dmn_idx_var<var_trv->nbr_dmn;dmn_idx_var++)
	if(!strcmp(var_trv->var_dmn[dmn_idx_var].dmn_nm,cb->tm_crd_nm)) break;
      if(dmn_idx_var < var_trv->nbr_dmn){
	/* Stamp with appropriate cell_methods temporal attribute */
	att_val=strdup(cll_mth_clm);
	aed.sz=strlen(att_val);
	aed.type=NC_CHAR;
	aed.val.cp=att_val;
	aed.mode=aed_overwrite;
	(void)nco_aed_prc(grp_out_id,var_out_id,aed);
	if(att_val) att_val=(char *)nco_free(att_val);
	continue;
      } /* !dmn_idx_var */
    } /* !cb */

    /* cell_methods format: blank-separated phrases of form "dmn1[, dmn2[...]]: op_typ", e.g., "lat, lon: mean" */ 
    for(dmn_idx_var=0;dmn_idx_var<var_trv->nbr_dmn;dmn_idx_var++){
      for(dmn_idx_rdc=0;dmn_idx_rdc<dmn_nbr_rdc;dmn_idx_rdc++){
        assert(dmn_rdc[dmn_idx_rdc]->nm_fll);
        /* Compare full names */
        if(!strcmp(var_trv->var_dmn[dmn_idx_var].dmn_nm_fll,dmn_rdc[dmn_idx_rdc]->nm_fll)){
          /* Add length of each matching dimension to accumulating attribute size */
          aed.sz+=strlen(dmn_rdc[dmn_idx_rdc]->nm);
          dmn_mch[dmn_nbr_mch++]=dmn_idx_rdc;
        } /* !match */
      } /* dmn_idx_rdc */
    } /* dmn_idx_var */

    assert(dmn_nbr_mch > 0);

    /* Preserve rule to always return averages (never extrema or other statistics) of coordinates */
    if(var[var_idx]->is_crd_var) nco_op_typ_lcl=nco_op_avg; else nco_op_typ_lcl=nco_op_typ;

    /* NUL-terminate before concatenation */
    att_op_sng[0]='\0';
    switch(nco_op_typ_lcl){
      /* Next five operations are defined in CF Conventions */
    case nco_op_avg: strcpy(att_op_sng,"mean"); break;
    case nco_op_min: strcpy(att_op_sng,"minimum"); break;
    case nco_op_max: strcpy(att_op_sng,"maximum"); break;
    case nco_op_ttl: strcpy(att_op_sng,"sum"); break;
    case nco_op_avgsqr: strcpy(att_op_sng,"variance"); break; /* Mean of sum of squares */
      /* Remaining operations are supported by NCO yet are not in CF Conventions */
    case nco_op_mabs: strcpy(att_op_sng,"maximum_absolute_value"); break; /* Maximum absolute value */
    case nco_op_mebs: strcpy(att_op_sng,"mean_absolute_value"); break; /* Mean absolute value */
    case nco_op_mibs: strcpy(att_op_sng,"minimum_absolute_value"); break; /* Minimum absolute value */
    case nco_op_sqravg: strcpy(att_op_sng,"square_of_mean"); break; /* Square of mean */
    case nco_op_sqrt: strcpy(att_op_sng,"square_root_of_mean"); break; /* Square root of mean */ 
    case nco_op_rms: strcpy(att_op_sng,"root_mean_square"); break; /* Root-mean-square (normalized by N) */
    case nco_op_rmssdn: strcpy(att_op_sng,"root_mean_square_nm1"); break; /* Root-mean square normalized by N-1 */
    case nco_op_nil: /* nco_op_nil, Undefined operation type */
      if(nco_dbg_lvl_get() >= nco_dbg_var) (void)fprintf(stdout,"%s: DEBUG %s reports variable %s cell_method not implemented for operation %d\n",nco_prg_nm_get(),fnc_nm,var_trv->nm_fll,nco_op_typ);
      continue;
    } /* End switch */

    /* Initialize to size of ": " plus length of operation string */
    aed.sz+=2L+strlen(att_op_sng);
    /* Add room for commas and spaces, i.e., "dmn1, dmn2, dmn3" */
    if(dmn_nbr_mch > 1) aed.sz+=2*(dmn_nbr_mch-1);
    /* Add room for NUL-terminator */
    aed.val.cp=(char *)nco_malloc((aed.sz+1L)*sizeof(char));
    aed.val.cp[0]='\0';

    /* Build single string by concatenating known matches */
    for(int dmn_idx_mch=0;dmn_idx_mch<dmn_nbr_mch;dmn_idx_mch++){
      (void)strcat(aed.val.cp,dmn_rdc[dmn_mch[dmn_idx_mch]]->nm);
      if(dmn_idx_mch<dmn_nbr_mch-1) (void)strcat(aed.val.cp,", ");
    } /* dmn_idx_mch>=dmn_nbr_mch */
    (void)strcat(aed.val.cp,": ");
    (void)strcat(aed.val.cp,att_op_sng);

    /* 20150625: Older versions of CAM, e.g., CAM3, used "cell_method" instead of "cell_methods" 
       If old attribute is not deleted then the output file will contain both attributes
       Does variable already have "cell_method" attribute? */
    strcpy(aed.att_nm,"cell_method");
    rcd=nco_inq_att_flg(grp_out_id,var_out_id,aed.att_nm,&att_typ,&att_lng);
    if(rcd == NC_NOERR){
      if(FIRST_WARNING) (void)fprintf(stderr,"%s: WARNING: Variable \"%s\" uses the non-standard attribute name \"cell_method\" instead of \"cell_methods\", the correct attribute name. The CAM3 model (and others?) have this problem. Expect \"double attributes\" in output. This message is printed only once per invocation, although the problem likely occurs in more variables.\n",nco_prg_nm_get(),aed.var_nm);
      FIRST_WARNING=False;
    } /* endif attribute exists */

    /* Does variable already have "cell_methods" attribute? */
    strcpy(aed.att_nm,"cell_methods");
    rcd=nco_inq_att_flg(grp_out_id,var_out_id,aed.att_nm,&att_typ,&att_lng);
    if(rcd == NC_NOERR){
      if(att_typ == NC_STRING) (void)fprintf(stderr,"%s: WARNING %s reports existing cell_methods attribute for variable %s is type NC_STRING. Unpredictable results...\n",nco_prg_nm_get(),fnc_nm,aed.var_nm);
      if(att_typ != NC_STRING && att_typ != NC_CHAR) (void)fprintf(stderr,"%s: WARNING %s reports existing cell_methods attribute for variable %s is type %s. Unpredictable results...\n",nco_prg_nm_get(),fnc_nm,aed.var_nm,nco_typ_sng(att_typ));

      /* Often climatologies are multiply-averaged over time
	 NCO's treatment of this has changed with time
	 pre-20140131: 
	 NCO has no special treatment of cell_methods
	 20140131: 
	 First NCO implementation (ncra, ncea, ncwa) of cell_methods with 
	 20150625: 
	 For example, climate model output is often archived as monthly means in each gridcell
	 cell_methods attributes of these monthly data begin as "time: mean" (i.e., monthly mean).
	 We then create a climatology by a sequence of one or two more temporal-averaging steps
	 The one-step method puts all the months in the hopper and averages those
	 Variables in the resultiing file may have cell_methods = "time: mean time: mean"
	 The two-step method first averages the months into four climatological seasons
	 Then it averages those four seasons into the climatological annual mean
	 Variables in the resultiing file may have cell_methods = "time: mean time: mean time: mean"
	 To avoid this redundancy, we check that the new cell_method does not duplicate the old 
	 If it would, then skip adding the new
	 20160418: 
	 Treatment of multiply-time-averaged quantities requires climatology bounds attribute
	 One-step methods (e.g., monthly mean) should have time-bounds attribute
	 cell_methods = "time: mean"
	 Two-step methods (e.g., climatological March) should have climatology-bounds attribute
	 cell_methods = "time: mean within years time: mean over years"
	 Three-step methods (e.g., climatological MAM) should have climatology-bounds attribute
	 cell_methods = "time: mean within years time: mean over years"
	 Four-step methods (e.g., climatological ANN) should have time-bounds attribute
	 cell_methods = "time: mean" */
      ptr_unn val_old; /* [sng] Old cell_methods attribute */
      val_old.vp=(void *)nco_malloc((att_lng+1L)*sizeof(char));
      (void)nco_get_att(grp_out_id,var_out_id,aed.att_nm,val_old.vp,NC_CHAR);
      val_old.cp[att_lng]='\0';
      if(strstr(val_old.cp,aed.val.cp)) flg_dpl=True;
      if(val_old.vp) val_old.vp=(void *)nco_free(val_old.vp);
      
      aed.mode=aed_append;
      /* Insert space between existing attribute and appended attribute */
      att_val_cpy=(char *)strdup(aed.val.cp);
      /* Add one for space character */
      aed.sz++;
      /* Add one for NUL-terminator */
      aed.val.cp=(char *)nco_realloc(aed.val.cp,(aed.sz+1L)*sizeof(char));
      aed.val.cp[0]=' ';
      aed.val.cp[1]='\0';
      (void)strncat(aed.val.cp,att_val_cpy,aed.sz-1L);
      if(att_val_cpy) att_val_cpy=(char *)nco_free(att_val_cpy);
    }else{ /* !cell_methods attribute already exists */
      aed.mode=aed_create;
    } /* !cell_methods attribute already exists */

    /* Edit attribute */
    if(!flg_dpl) (void)nco_aed_prc(grp_out_id,var_out_id,aed);

    /* 20150308 */
    /* Does variable already have "coordinates" attribute?
       NB: This reuses att_nm which has only enough space to hold "cell_methods" */
    strcpy(aed.att_nm,"coordinates");
    rcd=nco_inq_att_flg(grp_out_id,var_out_id,aed.att_nm,&att_typ,&att_lng);
    if(rcd == NC_NOERR && att_typ == NC_CHAR){
      /* Remove reduced dimensions from coordinates string
	 coordinates format: blank-separated names of form "dmn1 [dmn2 [...]] dmnN", e.g., "time lat lon" */ 
      /* Add room for NUL-terminator */
      att_val=(char *)nco_malloc((att_lng+1L)*sizeof(char));
      rcd=nco_get_att(grp_out_id,var_out_id,aed.att_nm,att_val,att_typ);
      /* Reset value from previous use */
      aed.val.cp[0]='\0';
      att_val[att_lng]='\0';
      mlt_dmn_rdc=False;
      assert(rcd == NC_NOERR);
      for(dmn_idx_var=0;dmn_idx_var<var_trv->nbr_dmn;dmn_idx_var++){
	for(dmn_idx_rdc=0;dmn_idx_rdc<dmn_nbr_rdc;dmn_idx_rdc++){
	  /* Is reduced dimension in variable? */
	  if(!strcmp(var_trv->var_dmn[dmn_idx_var].dmn_nm_fll,dmn_rdc[dmn_idx_rdc]->nm_fll)){
	    if(mlt_dmn_rdc){
	      /* At least one other dimension has already been reduced/excised
		 Hence multiple dimensions of this variable may be reduced
		 Start next excision from ending point of last excision, not from disk-values */
	      strcpy(att_val,aed.val.cp);
	      att_lng=strlen(aed.val.cp);
	    } /* endif */
	    /* Is dimension in current (possibly locally-modified) "coordinates" attribute? NB: Assume short name not full name */
	    if((sbs_ptr=strstr(att_val,dmn_rdc[dmn_idx_rdc]->nm))){
	      /* Is this the only dimension in "coordinates" attribute? */
	      if(!strcmp(dmn_rdc[dmn_idx_rdc]->nm,att_val)){
		/* Variable will become scalar so delete "coordinates" attribute */
		aed.mode=aed_delete;
	      }else{ /* endif scalar */
		/* Excise dimension from "coordinates" attribute */
		dmn_sng_lng=strlen(dmn_rdc[dmn_idx_rdc]->nm);
		sbs_sng_lng=(size_t)(sbs_ptr-att_val);
		aed.mode=aed_overwrite;
		/* Remove whitespace immediately following excised dimension, i.e., count it as part of dimension string
		   True for all dimensions except final dimension (trailed by a NUL, not a space) */
		if(sbs_ptr[dmn_sng_lng] == ' ') dmn_sng_lng++;
		aed.sz=att_lng-dmn_sng_lng;
		/* Add one for NUL-terminator */
		aed.val.cp=(char *)nco_realloc(aed.val.cp,(aed.sz+1L)*sizeof(char));
		strncpy(aed.val.cp,att_val,sbs_sng_lng);
		aed.val.cp[sbs_sng_lng]='\0';
		strcat(aed.val.cp,sbs_ptr+dmn_sng_lng);
	      } /* endelse scalar */
	      /* Edit attribute */
	      (void)nco_aed_prc(grp_out_id,var_out_id,aed);
	      mlt_dmn_rdc=True;
	    } /* !match attribute */
	  } /* !match variable */
	} /* dmn_idx_rdc */
      } /* dmn_idx_var */
    } /* endif attribute exists */

    if(att_val) att_val=(char *)nco_free(att_val);
    if(aed.val.cp) aed.val.cp=(char *)nco_free(aed.val.cp);

  } /* var_idx >= var_nbr */

  if(aed.att_nm) aed.att_nm=(char *)nco_free(aed.att_nm);
  if(dmn_mch) dmn_mch=(int *)nco_free(dmn_mch);

  return NC_NOERR;

} /* end nco_cnv_cf_cll_mth_add() */
Пример #19
0
int /* [rcd] Return code */
nco_cln_clc_dff /* [fnc] UDUnits2 Compute difference between two coordinate units */
(const char *fl_unt_sng, /* I [ptr] units attribute string from disk */
 const char *fl_bs_sng, /* I [ptr] units attribute string from disk */
 double crr_val,
 double *og_val) /* O [] Difference between two units strings */
{
  const char fnc_nm[]="nco_cln_clc_dff()"; /* [sng] Function name */
  
  cv_converter *ut_cnv; /* UDUnits converter */

  int ut_rcd; /* [enm] UDUnits2 status */
  
  ut_system *ut_sys;
  ut_unit *ut_sct_in; /* [sct] UDUnits structure, input units */
  ut_unit *ut_sct_out; /* [sct] UDUnits structure, output units */
  
  /* Quick return if units identical */
  if(!strcasecmp(fl_unt_sng,fl_bs_sng)){
    *og_val=crr_val;  
    return NCO_NOERR;
  } /* end if */
  
  /* When empty, ut_read_xml() uses environment variable UDUNITS2_XML_PATH, if any
     Otherwise it uses default initial location hardcoded when library was built */
  if(nco_dbg_lvl_get() >= nco_dbg_vrb) ut_set_error_message_handler(ut_write_to_stderr); else ut_set_error_message_handler(ut_ignore);
  ut_sys=ut_read_xml(NULL);
  if(ut_sys == NULL){
    (void)fprintf(stdout,"%s: %s() failed to initialize UDUnits2 library\n",nco_prg_nm_get(),fnc_nm);
    return NCO_ERR; /* Failure */
  } /* end if err */ 
  
  /* Units string to convert from */
  ut_sct_in=ut_parse(ut_sys,fl_unt_sng,UT_ASCII); 
  if(!ut_sct_in){ /* Problem with 'units' attribute */
    ut_rcd=ut_get_status(); /* [enm] UDUnits2 status */
    if(ut_rcd == UT_BAD_ARG) (void)fprintf(stderr,"ERROR: empty units attribute string\n");
    if(ut_rcd == UT_SYNTAX) (void)fprintf(stderr,"ERROR: units attribute \"%s\" has a syntax error\n",fl_unt_sng);
    if(ut_rcd == UT_UNKNOWN) (void)fprintf(stderr,"ERROR: units attribute \"%s\" is not listed in UDUnits2 SI system database\n",fl_unt_sng);
    return NCO_ERR; /* Failure */
  } /* endif coordinate on disk has no units attribute */

  /* Units string to convert to */
  ut_sct_out=ut_parse(ut_sys,fl_bs_sng,UT_ASCII); 
  if(!ut_sct_out){ /* Problem with 'units' attribute */
    ut_rcd=ut_get_status(); /* [enm] UDUnits2 status */
    if(ut_rcd == UT_BAD_ARG) (void)fprintf(stderr,"ERROR: Empty units attribute string\n");
    if(ut_rcd == UT_SYNTAX) (void)fprintf(stderr,"ERROR: units attribute  \"%s\" has a syntax error\n",fl_bs_sng);
    if(ut_rcd == UT_UNKNOWN) (void)fprintf(stderr,"ERROR: units attribute \"%s\" is not listed in UDUnits2 SI system database\n",fl_bs_sng);
    return NCO_ERR; /* Failure */
  } /* endif */

  /* Create converter */
  ut_cnv=ut_get_converter(ut_sct_in,ut_sct_out); /* UDUnits converter */
  if(!ut_cnv){
    ut_rcd=ut_get_status(); /* [enm] UDUnits2 status */
    if(ut_rcd == UT_BAD_ARG) (void)fprintf(stderr,"WARNING: One of units, %s or %s, is NULL\n",fl_bs_sng,fl_unt_sng);
    if(ut_rcd == UT_NOT_SAME_SYSTEM) (void)fprintf(stderr,"WARNING: Units %s and %s belong to different unit systems\n",fl_bs_sng,fl_unt_sng);
    if(ut_rcd == UT_MEANINGLESS) (void)fprintf(stderr,"WARNING: Conversion between user-specified unit \"%s\" and file units \"%s\" is meaningless\n",fl_bs_sng,fl_unt_sng);
    return NCO_ERR; /* Failure */
  } /* endif */

  /* Convert */
  *og_val=cv_convert_double(ut_cnv,crr_val);
  
  if(nco_dbg_lvl_get() >= nco_dbg_var) fprintf(stderr, "%s: INFO %s() reports conversion between systems \"%s\" and \"%s\" is %f\n",nco_prg_nm_get(),fnc_nm,fl_unt_sng,fl_bs_sng,*og_val);

  ut_free(ut_sct_in);
  ut_free(ut_sct_out);
  cv_free(ut_cnv);
  ut_free_system(ut_sys); /* Free memory taken by UDUnits library */

  return NCO_NOERR;
}  /* end UDUnits2 nco_cln_clc_dff() */