text: /* $Header$ */
  
  /* This single source file compiles into three separate executables:
     ncra -- netCDF record averager
     nces -- netCDF ensemble statistics
     ncrcat -- netCDF record concatenator */
  
  /* Purpose: Compute averages or extract series of specified hyperslabs of 
     specfied variables of multiple input netCDF files and output them 
     to a single file. */
  
  /* Copyright (C) 1995--present Charlie Zender
     This file is part of NCO, the netCDF Operators. NCO is free software.
     You may redistribute and/or modify NCO under the terms of the 
     3-Clause BSD License.
     You are permitted to link NCO with the HDF, netCDF, OPeNDAP, and UDUnits
     libraries and to distribute the resulting executables under the terms 
     of the BSD, but in addition obeying the extra stipulations of the 
     HDF, netCDF, OPeNDAP, and UDUnits licenses.
  
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     See the 3-Clause BSD License for more details.
     
     The original author of this software, Charlie Zender, seeks to improve
     it with your suggestions, contributions, bug-reports, and patches.
     Please contact the NCO project at http://nco.sf.net or write to
     Charlie Zender
     Department of Earth System Science
     University of California, Irvine
     Irvine, CA 92697-3100 */
  
  /* URL: https://github.com/nco/nco/tree/master/src/nco/ncra.c
     
     Usage:
     ncra -O -n 3,4,1 -p ${HOME}/nco/data h0001.nc ~/foo.nc
     ncra -O -n 3,4,1 -p ${HOME}/nco/data -l ${HOME} h0001.nc ~/foo.nc
     ncra -O -n 3,4,1 -p /ZENDER/tmp -l ${HOME}/nco/data h0001.nc ~/foo.nc
     ncrcat -O -C -d time,0,5,4,2 -v time -p ~/nco/data in.nc ~/foo.nc
     ncra -O -C -d time,0,5,4,2 -v time -p ~/nco/data in.nc ~/foo.nc
     ncra -O -C --mro -d time,0,5,4,2 -v time -p ~/nco/data in.nc ~/foo.nc
     ncra -O -w 1,2,3 -n 3,4,1 -p ${HOME}/nco/data h0001.nc ~/foo.nc
     ncra -O -w one_dmn_rec_var -n 3,4,1 -p ${HOME}/nco/data h0001.nc ~/foo.nc
     
     scp ~/nco/src/nco/ncra.c esmf.ess.uci.edu:nco/src/nco
     
     nces in.nc in.nc ~/foo.nc
     nces -O -n 3,4,1 -p ${HOME}/nco/data h0001.nc ~/foo.nc
     nces -O -n 3,4,1 -p ${HOME}/nco/data -l ${HOME} h0001.nc ~/foo.nc
     nces -O -n 3,4,1 -p /ZENDER/tmp -l ${HOME} h0001.nc ~/foo.nc
  
     ncra -Y ncge -O -p ~/nco/data mdl_1.nc ~/foo.nc
     ncra -Y ncge -O --nsm_sfx=_avg -p ~/nco/data mdl_1.nc ~/foo.nc */
  
  #ifdef HAVE_CONFIG_H
  # include <config.h> /* Autotools tokens */
  #endif /* !HAVE_CONFIG_H */
  
  /* Standard C headers */
  #include <math.h> /* sin cos cos sin 3.14159 */
  #include <stdio.h> /* stderr, FILE, NULL, etc. */
  #include <stdlib.h> /* atof, atoi, malloc, getopt */
  #include <string.h> /* strcmp() */
  #include <sys/stat.h> /* stat() */
  #include <time.h> /* machine time */
  #ifndef _MSC_VER
  # include <unistd.h> /* POSIX stuff */
  #endif
  #ifndef HAVE_GETOPT_LONG
  # include "nco_getopt.h"
  #else /* HAVE_GETOPT_LONG */
  # ifdef HAVE_GETOPT_H
  #  include <getopt.h>
  # endif /* !HAVE_GETOPT_H */ 
  #endif /* HAVE_GETOPT_LONG */
  
  #ifdef I18N
  # include <langinfo.h> /* nl_langinfo() */
  # include <libintl.h> /* Internationalization i18n */
  # include <locale.h> /* Locale setlocale() */
  # define _(sng) gettext (sng)
  # define gettext_noop(sng) (sng)
  # define N_(sng) gettext_noop(sng)
  #endif /* I18N */
  /* Supply stub gettext() function in case i18n failed */
  #ifndef _LIBINTL_H
  # define gettext(foo) foo
  #endif /* _LIBINTL_H */
  
  /* 3rd party vendors */	 
  #include <netcdf.h> /* netCDF definitions and C library */	 
  #ifdef ENABLE_MPI
  # include <mpi.h> /* MPI definitions */
  # include <netcdf_par.h> /* Parallel netCDF definitions */
  # include "nco_mpi.h" /* MPI utilities */
  #endif /* !ENABLE_MPI */
  
  /* Personal headers */
  /* #define MAIN_PROGRAM_FILE MUST precede #include libnco.h */
  #define MAIN_PROGRAM_FILE
  #include "nco.h" /* netCDF Operator (NCO) definitions */
  #include "libnco.h" /* netCDF Operator (NCO) library */
  
  
  /* forward declaration */
  nco_bool ra_lst_chk(char ***ra_lst, int nbr_lst, char *var_nm, char *bnds_nm);
  void ra_lst_free(char ***ra_lst, int nbr_lst);
  
  
  /* Define inline'd functions in header so source is visible to calling files
     C99 only: Declare prototype in exactly one header
     http://www.drdobbs.com/the-new-c-inline-functions/184401540 */
  extern int min_int(int a, int b);
  extern int max_int(int a, int b);
  inline int min_int(int a, int b){return (a < b) ? a : b;}
  inline int max_int(int a, int b){return (a > b) ? a : b;}
  extern long min_lng(long a, long b);
  extern long max_lng(long a, long b);
  inline long min_lng(long a, long b){return (a < b) ? a : b;}
  inline long max_lng(long a, long b){return (a > b) ? a : b;}
  
  int
  main(int argc,char **argv)
  {
    char **fl_lst_abb=NULL; /* Option n */
    char **fl_lst_in;
    char **gaa_arg=NULL; /* [sng] Global attribute arguments */
    char **grp_lst_in=NULL_CEWI;
    char **var_lst_in=NULL_CEWI;
    char **wgt_lst_in=NULL_CEWI;
    char *aux_arg[NC_MAX_DIMS];
    char *cmd_ln;
    char *cnk_arg[NC_MAX_DIMS];
    char *cnk_map_sng=NULL_CEWI; /* [sng] Chunking map */
    char *cnk_plc_sng=NULL_CEWI; /* [sng] Chunking policy */
    char *fl_in=NULL;
    char *fl_out=NULL; /* Option o */
    char *fl_out_tmp=NULL_CEWI;
    char *fl_pth=NULL; /* Option p */
    char *fl_pth_lcl=NULL; /* Option l */
    char *grp_out_fll=NULL; /* [sng] Group name */
    char *lmt_arg[NC_MAX_DIMS];
    char *nco_op_typ_sng=NULL_CEWI; /* [sng] Operation type Option y */
    char *nco_pck_plc_sng=NULL_CEWI; /* [sng] Packing policy Option P */
    char *nsm_sfx=NULL; /* [sng] Ensemble suffix */
    char *opt_crr=NULL; /* [sng] String representation of current long-option name */
    char *optarg_lcl=NULL; /* [sng] Local copy of system optarg */
    char *ppc_arg[NC_MAX_VARS]; /* [sng] PPC arguments */
    char *sng_cnv_rcd=NULL_CEWI; /* [sng] strtol()/strtoul() return code */
    char *wgt_nm=NULL_CEWI; /* [sng] Weight variable */
    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="34567ACcD:d:FG:g:HhL:l:Nn:Oo:p:P:rRt:v:w:X:xY:y:-:";
  
    clm_bnd_sct *cb=NULL;
    
    cnk_sct cnk; /* [sct] Chunking structure */
  
  #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 */
  
    dmn_sct **dim=NULL; /* CEWI */
    dmn_sct **dmn_out=NULL; /* CEWI */
  
    double *wgt_arr=NULL; /* Option w */
    double wgt_avg_scl=0.0; /* [frc] Scalar version of wgt_avg */
  
    extern char *optarg;
    extern int optind;
  
    /* Using naked stdin/stdout/stderr in parallel region generates warning
    Copy appropriate filehandle to variable scoped shared in parallel clause */
    FILE * const fp_stderr=stderr; /* [fl] stderr filehandle CEWI */
    FILE * const fp_stdout=stdout; /* [fl] stdout filehandle CEWI */
  
    gpe_sct *gpe=NULL; /* [sng] Group Path Editing (GPE) structure */
  
    int *in_id_arr;
  
    const int rec_dmn_idx=0; /* [idx] Assumed index of current record dimension where zero assumes record is leading dimension */
  
    int abb_arg_nbr=0;
    int aux_nbr=0; /* [nbr] Number of auxiliary coordinate hyperslabs specified */
    int cnk_map=nco_cnk_map_nil; /* [enm] Chunking map */
    int cnk_nbr=0; /* [nbr] Number of chunk sizes */
    int cnk_plc=nco_cnk_plc_nil; /* [enm] Chunking policy */
    int dfl_lvl=NCO_DFL_LVL_UNDEFINED; /* [enm] Deflate level */
    int dmn_rec_fl;
    int fl_idx;
    int fl_in_fmt; /* [enm] Input file format */
    int fl_nbr=0;
    int fl_out_fmt=NCO_FORMAT_UNDEFINED; /* [enm] Output file format */
    int flg_input_complete_nbr=0; /* [nbr] Number of record dimensions completed */
    int fll_md_old; /* [enm] Old fill mode */
    int gaa_nbr=0; /* [nbr] Number of global attributes to add */
    int grp_id;        /* [ID] Group ID */
    int grp_lst_in_nbr=0; /* [nbr] Number of groups explicitly specified by user */
    int grp_out_id;    /* [ID] Group ID (output) */
    int idx=int_CEWI;
    int idx_rec=0; /* [idx] Index that iterates over number of record dimensions */
    int in_id;
    int lmt_nbr=0; /* Option d. NB: lmt_nbr gets incremented */
    int log_lvl=0; /* [enm] netCDF library debugging verbosity [0..5] */
    int md_open; /* [enm] Mode flag for nc_open() call */
    int nbr_dmn_fl;
    int nbr_dmn_xtr=0;
    int nbr_rec; /* [nbr] (ncra) Number of record dimensions  */
    int nbr_var_fix; /* nbr_var_fix gets incremented */
    int nbr_var_fl;
    int nbr_var_prc; /* nbr_var_prc gets incremented */
    int nco_op_typ=nco_op_avg; /* [enm] Default operation is averaging */
    int nco_pck_plc=nco_pck_plc_nil; /* [enm] Default packing is none */
    int opt;
    int out_id;  
    int ppc_nbr=0; /* [nbr] Number of PPC arguments */
    int rcd=NC_NOERR; /* [rcd] Return code */
    int thr_idx; /* [idx] Index of current thread */
    int thr_nbr=int_CEWI; /* [nbr] Thread number Option t */
    int var_lst_in_nbr=0;
    int var_out_id; /* [ID] Variable ID (output) */
    int wgt_nbr=0; 
    int xtr_nbr=0; /* xtr_nbr won't otherwise be set for -c with no -v */
  
    lmt_sct **lmt_rec=NULL; /* [lst] (ncra) Record dimensions */
  
    long idx_rec_crr_in; /* [idx] Index of current record in current input file */
    long *idx_rec_out=NULL; /* [idx] Index of current record in output file (0 is first, ...) */
    long *rec_in_cml=NULL;  /* [nbr] Number of records, read or not, in all processed files */
    long *rec_usd_cml=NULL; /* [nbr] Cumulative number of input records used (catenated by ncrcat or operated on by ncra) */
    long rec_dmn_sz=0L; /* [idx] Size of record dimension, if any, in current file (increments by srd) */
    long rec_rmn_prv_ssc=0L; /* [idx] Records remaining to be read in current subcycle group */
  
    md5_sct *md5=NULL; /* [sct] MD5 configuration */
  
    nco_bool *REC_LST_DSR=NULL; /* [flg] Record is last desired from all input files */
    nco_bool *flg_input_complete=NULL; /* [flg] All requested records in record dimension have been read */
  
    nco_bool CNV_ARM;
    cnv_sct *cnv; /* [sct] Convention structure */
    nco_bool EXCLUDE_INPUT_LIST=False; /* Option c */
    nco_bool EXTRACT_ALL_COORDINATES=False; /* Option c */
    nco_bool EXTRACT_ASSOCIATED_COORDINATES=True; /* Option C */
    nco_bool EXTRACT_CLL_MSR=True; /* [flg] Extract cell_measures variables */
    nco_bool EXTRACT_FRM_TRM=True; /* [flg] Extract formula_terms variables */
    nco_bool FLG_BFR_NRM=False; /* [flg] Current output buffers need normalization */
    nco_bool FLG_MRO=False; /* [flg] Multi-Record Output */
    nco_bool FL_LST_IN_APPEND=True; /* Option H */
    nco_bool FL_LST_IN_FROM_STDIN=False; /* [flg] fl_lst_in comes from stdin */
    nco_bool FL_RTR_RMT_LCN;
    nco_bool FORCE_APPEND=False; /* Option A */
    nco_bool FORCE_OVERWRITE=False; /* Option O */
    nco_bool FORTRAN_IDX_CNV=False; /* Option F */
    nco_bool GRP_VAR_UNN=False; /* [flg] Select union of specified groups and variables */
    nco_bool HISTORY_APPEND=True; /* Option h */
    nco_bool HPSS_TRY=False; /* [flg] Search HPSS for unfound files */
    nco_bool MSA_USR_RDR=False; /* [flg] Multi-Slab Algorithm returns hyperslabs in user-specified order */
    nco_bool NORMALIZE_BY_WEIGHT=True; /* [flg] Normalize by command-line weight */
    nco_bool RAM_CREATE=False; /* [flg] Create file in RAM */
    nco_bool RAM_OPEN=False; /* [flg] Open (netCDF3-only) file(s) in RAM */
    nco_bool REC_APN=False; /* [flg] Append records directly to output file */
    nco_bool REC_FRS_GRP=False; /* [flg] Record is first in current group */
    nco_bool REC_LST_GRP=False; /* [flg] Record is last in current group */
    nco_bool REC_SRD_LST=False; /* [flg] Record belongs to last stride of current file */
    nco_bool RM_RMT_FL_PST_PRC=True; /* Option R */
    nco_bool WRT_TMP_FL=True; /* [flg] Write output to temporary file */
    nco_bool flg_cll_mth=True; /* [flg] Add/modify cell_methods attributes */
    nco_bool flg_cb=False; /* [flg] Climatology bounds */
    nco_bool flg_c2b=False; /* [flg] Climatology bounds-to-time bounds */
    nco_bool flg_mmr_cln=True; /* [flg] Clean memory prior to exit */
    nco_bool flg_skp1; /* [flg] Current record is not dimension of this variable */
    nco_bool flg_skp2; /* [flg] Current record is not dimension of this variable */
  
    nco_dmn_dne_t *flg_dne=NULL; /* [lst] Flag to check if input dimension -d "does not exist" */
  
    nco_int base_time_srt=nco_int_CEWI;
    nco_int base_time_crr=nco_int_CEWI;
  
    nc_type var_prc_typ_pre_prm=NC_NAT; /* [enm] Type of variable before promotion */
  
    scv_sct wgt_scv;
    scv_sct wgt_avg_scv;
    
    size_t bfr_sz_hnt=NC_SIZEHINT_DEFAULT; /* [B] Buffer size hint */
    size_t cnk_csh_byt=NCO_CNK_CSH_BYT_DFL; /* [B] Chunk cache size */
    size_t cnk_min_byt=NCO_CNK_SZ_MIN_BYT_DFL; /* [B] Minimize size of variable to chunk */
    size_t cnk_sz_byt=0UL; /* [B] Chunk size in bytes */
    size_t cnk_sz_scl=0UL; /* [nbr] Chunk size scalar */
    size_t hdr_pad=0UL; /* [B] Pad at end of header section */
  
    trv_sct *var_trv; /* [sct] Variable GTT object */
  
    trv_tbl_sct *trv_tbl; /* [lst] Traversal table */
  
    var_sct **var;
    var_sct **var_fix;
    var_sct **var_fix_out;
    var_sct **var_out=NULL_CEWI;
    var_sct **var_prc;
    var_sct **var_prc_out;
    var_sct *wgt=NULL; /* [sct] Raw weight on disk in input file */
    var_sct *wgt_out=NULL; /* [sct] Copy of wgt
  			    Tally and val members malloc'd & initialized
  			    IDs updated each new file by nco_var_mtd_refresh() in file loop
  			    Current record value obtained by nco_msa_var_get_rec_trv() in record loop
  			    One copy of wgt_out used for all variables */
    var_sct *wgt_avg=NULL; /* [sct] Copy of wgt_out created to mimic var_prc_out processing 
  			    Holds running total and tally of weight
  			    Acts as op2 for wgt_out averaging just before var_prc[nbr_var_prc-1] */
  #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 */
      {"cll_msr",no_argument,0,0}, /* [flg] Extract cell_measures variables */
      {"cell_measures",no_argument,0,0}, /* [flg] Extract cell_measures variables */
      {"no_cll_msr",no_argument,0,0}, /* [flg] Do not extract cell_measures variables */
      {"no_cell_measures",no_argument,0,0}, /* [flg] Do not extract cell_measures variables */
      {"frm_trm",no_argument,0,0}, /* [flg] Extract formula_terms variables */
      {"formula_terms",no_argument,0,0}, /* [flg] Extract formula_terms variables */
      {"no_frm_trm",no_argument,0,0}, /* [flg] Do not extract formula_terms variables */
      {"no_formula_terms",no_argument,0,0}, /* [flg] Do not extract formula_terms variables */
      {"cll_mth",no_argument,0,0}, /* [flg] Add/modify cell_methods attributes */
      {"cell_methods",no_argument,0,0}, /* [flg] Add/modify cell_methods attributes */
      {"no_cll_mth",no_argument,0,0}, /* [flg] Do not add/modify cell_methods attributes */
      {"no_cell_methods",no_argument,0,0}, /* [flg] Do not add/modify cell_methods attributes */
      {"clm_bnd",no_argument,0,0}, /* [sct] Climatology bounds */
      {"cb",no_argument,0,0}, /* [sct] Climatology bounds */
      {"clm2bnd",no_argument,0,0}, /* [sct] Climatology bounds-to-time bounds */
      {"c2b",no_argument,0,0}, /* [sct] Climatology bounds-to-time bounds */
      {"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 */
      {"dbl",no_argument,0,0}, /* [flg] Arithmetic convention: promote float to double */
      {"flt",no_argument,0,0}, /* [flg] Arithmetic convention: keep single-precision */
      {"rth_dbl",no_argument,0,0}, /* [flg] Arithmetic convention: promote float to double */
      {"rth_flt",no_argument,0,0}, /* [flg] Arithmetic convention: keep single-precision */
      {"hdf4",no_argument,0,0}, /* [flg] Treat file as HDF4 */
      {"hdf_upk",no_argument,0,0}, /* [flg] HDF unpack convention: unpacked=scale_factor*(packed-add_offset) */
      {"hdf_unpack",no_argument,0,0}, /* [flg] HDF unpack convention: unpacked=scale_factor*(packed-add_offset) */
      {"help",no_argument,0,0},
      {"hlp",no_argument,0,0},
      {"hpss_try",no_argument,0,0}, /* [flg] Search HPSS for unfound files */
      {"md5_dgs",no_argument,0,0}, /* [flg] Perform MD5 digests */
      {"md5_digest",no_argument,0,0}, /* [flg] Perform MD5 digests */
      {"mro",no_argument,0,0}, /* [flg] Multi-Record Output */
      {"multi_record_output",no_argument,0,0}, /* [flg] Multi-Record Output */
      {"msa_usr_rdr",no_argument,0,0}, /* [flg] Multi-Slab Algorithm returns hyperslabs in user-specified order */
      {"msa_user_order",no_argument,0,0}, /* [flg] Multi-Slab Algorithm returns hyperslabs in user-specified order */
      {"nsm_fl",no_argument,0,0},
      {"nsm_grp",no_argument,0,0},
      {"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 */
      {"rec_apn",no_argument,0,0}, /* [flg] Append records directly to output file */
      {"record_append",no_argument,0,0}, /* [flg] Append records directly to output file */
      {"wrt_tmp_fl",no_argument,0,0}, /* [flg] Write output to temporary file */
      {"write_tmp_fl",no_argument,0,0}, /* [flg] Write output to temporary file */
      {"no_tmp_fl",no_argument,0,0}, /* [flg] Do not write output to temporary file */
      {"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 */
      {"cnk_byt",required_argument,0,0}, /* [B] Chunk size in bytes */
      {"chunk_byte",required_argument,0,0}, /* [B] Chunk size in bytes */
      {"cnk_csh",required_argument,0,0}, /* [B] Chunk cache size in bytes */
      {"chunk_cache",required_argument,0,0}, /* [B] Chunk cache size in bytes */
      {"cnk_dmn",required_argument,0,0}, /* [nbr] Chunk size */
      {"chunk_dimension",required_argument,0,0}, /* [nbr] Chunk size */
      {"cnk_map",required_argument,0,0}, /* [nbr] Chunking map */
      {"chunk_map",required_argument,0,0}, /* [nbr] Chunking map */
      {"cnk_min",required_argument,0,0}, /* [B] Minimize size of variable to chunk */
      {"chunk_min",required_argument,0,0}, /* [B] Minimize size of variable to chunk */
      {"cnk_plc",required_argument,0,0}, /* [nbr] Chunking policy */
      {"chunk_policy",required_argument,0,0}, /* [nbr] Chunking policy */
      {"cnk_scl",required_argument,0,0}, /* [nbr] Chunk size scalar */
      {"chunk_scalar",required_argument,0,0}, /* [nbr] Chunk size scalar */
      {"fl_fmt",required_argument,0,0},
      {"file_format",required_argument,0,0},
      {"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},
      {"log_lvl",required_argument,0,0}, /* [enm] netCDF library debugging verbosity [0..5] */
      {"log_level",required_argument,0,0}, /* [enm] netCDF library debugging verbosity [0..5] */
      {"ppc",required_argument,0,0}, /* [nbr] Precision-preserving compression, i.e., number of total or decimal significant digits */
      {"precision_preserving_compression",required_argument,0,0}, /* [nbr] Precision-preserving compression, i.e., number of total or decimal significant digits */
      {"quantize",required_argument,0,0}, /* [nbr] Precision-preserving compression, i.e., number of total or decimal significant digits */
      {"nsm_sfx",required_argument,0,0},
      {"ensemble_suffix",required_argument,0,0},
      /* Long options with short counterparts */
      {"3",no_argument,0,'3'},
      {"4",no_argument,0,'4'},
      {"netcdf4",no_argument,0,'4'},
      {"5",no_argument,0,'5'},
      {"64bit_data",no_argument,0,'5'},
      {"cdf5",no_argument,0,'5'},
      {"pnetcdf",no_argument,0,'5'},
      {"64bit_offset",no_argument,0,'6'},
      {"7",no_argument,0,'7'},
      {"append",no_argument,0,'A'},
      {"coords",no_argument,0,'c'},
      {"crd",no_argument,0,'c'},
      {"xtr_ass_var",no_argument,0,'c'},
      {"xcl_ass_var",no_argument,0,'C'},
      {"no_coords",no_argument,0,'C'},
      {"no_crd",no_argument,0,'C'},
      {"debug",required_argument,0,'D'},
      {"nco_dbg_lvl",required_argument,0,'D'},
      {"dimension",required_argument,0,'d'},
      {"dmn",required_argument,0,'d'},
      {"fortran",no_argument,0,'F'},
      {"ftn",no_argument,0,'F'},
      {"fl_lst_in",no_argument,0,'H'},
      {"file_list",no_argument,0,'H'},
      {"history",no_argument,0,'h'},
      {"hst",no_argument,0,'h'},
      {"dfl_lvl",required_argument,0,'L'}, /* [enm] Deflate level */
      {"deflate",required_argument,0,'L'}, /* [enm] Deflate level */
      {"local",required_argument,0,'l'},
      {"lcl",required_argument,0,'l'},
      {"no-normalize-by-weight",no_argument,0,'N',},
      {"no_nrm_by_wgt",no_argument,0,'N',},
      {"nintap",required_argument,0,'n'},
      {"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'},
      {"pack",required_argument,0,'P'},
      {"retain",no_argument,0,'R'},
      {"rtn",no_argument,0,'R'},
      {"revision",no_argument,0,'r'},
      {"thr_nbr",required_argument,0,'t'},
      {"threads",required_argument,0,'t'},
      {"omp_num_threads",required_argument,0,'t'},
      {"variable",required_argument,0,'v'},
      {"wgt",required_argument,0,'w'},
      {"weight",required_argument,0,'w'},
      {"auxiliary",required_argument,0,'X'},
      {"exclude",no_argument,0,'x'},
      {"xcl",no_argument,0,'x'},
      {"pseudonym",required_argument,0,'Y'},
      {"program",required_argument,0,'Y'},
      {"prg_nm",required_argument,0,'Y'},
      {"math",required_argument,0,'y'},
      {"operation",required_argument,0,'y'},
      {"op_typ",required_argument,0,'y'},
      {0,0,0,0}
    }; /* end opt_lng */
    int opt_idx=0; /* Index of current long option into opt_lng array */
    
  #ifdef _LIBINTL_H
    setlocale(LC_ALL,""); /* LC_ALL sets all localization tokens to same value */
    bindtextdomain("nco","/home/zender/share/locale"); /* ${LOCALEDIR} is e.g., /usr/share/locale */
    /* MO files should be in ${LOCALEDIR}/es/LC_MESSAGES */
    textdomain("nco"); /* PACKAGE is name of program or library */
  #endif /* not _LIBINTL_H */
  
    /* 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,"baa") || !strcmp(opt_crr,"bit_alg")){
  	nco_baa_cnv=(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);
        } /* endif baa */
        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,"cnk_byt") || !strcmp(opt_crr,"chunk_byte")){
          cnk_sz_byt=strtoul(optarg,&sng_cnv_rcd,NCO_SNG_CNV_BASE10);
          if(*sng_cnv_rcd) nco_sng_cnv_err(optarg,"strtoul",sng_cnv_rcd);
        } /* endif cnk_byt */
        if(!strcmp(opt_crr,"cnk_csh") || !strcmp(opt_crr,"chunk_cache")){
          cnk_csh_byt=strtoul(optarg,&sng_cnv_rcd,NCO_SNG_CNV_BASE10);
          if(*sng_cnv_rcd) nco_sng_cnv_err(optarg,"strtoul",sng_cnv_rcd);
        } /* endif cnk_csh_byt */
        if(!strcmp(opt_crr,"cnk_min") || !strcmp(opt_crr,"chunk_min")){
          cnk_min_byt=strtoul(optarg,&sng_cnv_rcd,NCO_SNG_CNV_BASE10);
          if(*sng_cnv_rcd) nco_sng_cnv_err(optarg,"strtoul",sng_cnv_rcd);
        } /* endif cnk_min */
        if(!strcmp(opt_crr,"cnk_dmn") || !strcmp(opt_crr,"chunk_dimension")){
          /* Copy limit argument for later processing */
          cnk_arg[cnk_nbr]=(char *)strdup(optarg);
          cnk_nbr++;
        } /* endif cnk */
        if(!strcmp(opt_crr,"cnk_scl") || !strcmp(opt_crr,"chunk_scalar")){
          cnk_sz_scl=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,"cnk_map") || !strcmp(opt_crr,"chunk_map")){
          /* Chunking map */
          cnk_map_sng=(char *)strdup(optarg);
          cnk_map=nco_cnk_map_get(cnk_map_sng);
        } /* endif cnk */
        if(!strcmp(opt_crr,"cnk_plc") || !strcmp(opt_crr,"chunk_policy")){
          /* Chunking policy */
          cnk_plc_sng=(char *)strdup(optarg);
          cnk_plc=nco_cnk_plc_get(cnk_plc_sng);
        } /* endif cnk */
        if(!strcmp(opt_crr,"cll_msr") || !strcmp(opt_crr,"cell_measures")) EXTRACT_CLL_MSR=True; /* [flg] Extract cell_measures variables */
        if(!strcmp(opt_crr,"no_cll_msr") || !strcmp(opt_crr,"no_cell_measures")) EXTRACT_CLL_MSR=False; /* [flg] Do not extract cell_measures variables */
        if(!strcmp(opt_crr,"frm_trm") || !strcmp(opt_crr,"formula_terms")) EXTRACT_FRM_TRM=True; /* [flg] Extract formula_terms variables */
        if(!strcmp(opt_crr,"no_frm_trm") || !strcmp(opt_crr,"no_formula_terms")) EXTRACT_FRM_TRM=False; /* [flg] Do not extract formula_terms variables */
        if(!strcmp(opt_crr,"cll_mth") || !strcmp(opt_crr,"cell_methods")) flg_cll_mth=True; /* [flg] Add/modify cell_methods attributes */
        if(!strcmp(opt_crr,"no_cll_mth") || !strcmp(opt_crr,"no_cell_methods")) flg_cll_mth=False; /* [flg] Add/modify cell_methods attributes */
        if(!strcmp(opt_crr,"mmr_cln") || !strcmp(opt_crr,"clean")) flg_mmr_cln=True; /* [flg] Clean memory prior to exit */
        if(!strcmp(opt_crr,"drt") || !strcmp(opt_crr,"mmr_drt") || !strcmp(opt_crr,"dirty")) flg_mmr_cln=False; /* [flg] Clean memory prior to exit */
        if(!strcmp(opt_crr,"clm_bnd") || !strcmp(opt_crr,"cb")) flg_cb=True; /* [sct] Climatology bounds */
        if(!strcmp(opt_crr,"clm2bnd") || !strcmp(opt_crr,"c2b")) flg_c2b=flg_cb=True; /* [sct] Climatology bounds-to-time bounds */
        if(!strcmp(opt_crr,"fl_fmt") || !strcmp(opt_crr,"file_format")) rcd=nco_create_mode_prs(optarg,&fl_out_fmt);
        if(!strcmp(opt_crr,"dbl") || !strcmp(opt_crr,"rth_dbl")) nco_rth_cnv=nco_rth_flt_dbl; /* [flg] Arithmetic convention: promote float to double */
        if(!strcmp(opt_crr,"flt") || !strcmp(opt_crr,"rth_flt")) nco_rth_cnv=nco_rth_flt_flt; /* [flg] Arithmetic convention: keep single-precision */
        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,"hdf_upk") || !strcmp(opt_crr,"hdf_unpack")) nco_upk_cnv=nco_upk_HDF_MOD10; /* [flg] HDF unpack convention: unpacked=scale_factor*(packed-add_offset) */
        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,"help") || !strcmp(opt_crr,"hlp")){
  	(void)nco_usg_prn();
  	nco_exit(EXIT_SUCCESS);
        } /* endif "help" */
        if(!strcmp(opt_crr,"hpss_try")) HPSS_TRY=True; /* [flg] Search HPSS for unfound files */
        if(!strcmp(opt_crr,"log_lvl") || !strcmp(opt_crr,"log_level")){
  	log_lvl=(int)strtol(optarg,&sng_cnv_rcd,NCO_SNG_CNV_BASE10);
  	if(*sng_cnv_rcd) nco_sng_cnv_err(optarg,"strtol",sng_cnv_rcd);
  	nc_set_log_level(log_lvl);
        } /* !log_lvl */
        if(!strcmp(opt_crr,"md5_dgs") || !strcmp(opt_crr,"md5_digest")){
          if(!md5) md5=nco_md5_ini();
          md5->dgs=True;
          if(nco_dbg_lvl >= nco_dbg_std) (void)fprintf(stderr,"%s: INFO Will perform MD5 digests of input and output hyperslabs\n",nco_prg_nm_get());
        } /* endif "md5_dgs" */
        if(!strcmp(opt_crr,"mro") || !strcmp(opt_crr,"multi_record_output")) FLG_MRO=True; /* [flg] Multi-Record Output */
        if(!strcmp(opt_crr,"msa_usr_rdr") || !strcmp(opt_crr,"msa_user_order")) MSA_USR_RDR=True; /* [flg] Multi-Slab Algorithm returns hyperslabs in user-specified order */
        if(!strcmp(opt_crr,"nsm_fl") || !strcmp(opt_crr,"nsm_file") || !strcmp(opt_crr,"ensemble_file")){
  	if(nco_prg_nm) nco_prg_nm=(char *)nco_free(nco_prg_nm);
  	nco_prg_nm=nco_prg_prs("ncfe",&nco_prg_id);
        } /* endif nsm_fl */
        if(!strcmp(opt_crr,"nsm_grp") || !strcmp(opt_crr,"nsm_group") || !strcmp(opt_crr,"ensemble_group")){
  	if(nco_prg_nm) nco_prg_nm=(char *)nco_free(nco_prg_nm);
  	nco_prg_nm=nco_prg_prs("ncge",&nco_prg_id);
        } /* endif nsm_grp */
        if(!strcmp(opt_crr,"nsm_sfx") || !strcmp(opt_crr,"ensemble_suffix")) nsm_sfx=(char *)strdup(optarg);
        if(!strcmp(opt_crr,"ppc") || !strcmp(opt_crr,"precision_preserving_compression") || !strcmp(opt_crr,"quantize")){
          ppc_arg[ppc_nbr]=(char *)strdup(optarg);
          ppc_nbr++;
        } /* endif "ppc" */
        if(!strcmp(opt_crr,"ram_all") || !strcmp(opt_crr,"create_ram") || !strcmp(opt_crr,"diskless_all")) RAM_CREATE=True; /* [flg] Open (netCDF3) file(s) in RAM */
        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,"rec_apn") || !strcmp(opt_crr,"record_append")){
          REC_APN=True; /* [flg] Append records directly to output file */
          FORCE_APPEND=True;
        } /* endif "rec_apn" */
        if(!strcmp(opt_crr,"vrs") || !strcmp(opt_crr,"version")){
          (void)nco_vrs_prn(CVS_Id,CVS_Revision);
          nco_exit(EXIT_SUCCESS);
        } /* endif "vrs" */
        if(!strcmp(opt_crr,"wrt_tmp_fl") || !strcmp(opt_crr,"write_tmp_fl")) WRT_TMP_FL=True;
        if(!strcmp(opt_crr,"no_tmp_fl")) WRT_TMP_FL=False;
      } /* opt != 0 */
      /* Process short options */
      switch(opt){
      case 0: /* Long options have already been processed, return */
        break;
      case '3': /* Request netCDF3 output storage format */
        fl_out_fmt=NC_FORMAT_CLASSIC;
        break;
      case '4': /* Request netCDF4 output storage format */
        fl_out_fmt=NC_FORMAT_NETCDF4; 
        break;
      case '5': /* Request netCDF3 64-bit offset+data storage (i.e., pnetCDF) format */
        fl_out_fmt=NC_FORMAT_CDF5;
        break;
      case '6': /* Request netCDF3 64-bit offset output storage format */
        fl_out_fmt=NC_FORMAT_64BIT_OFFSET;
        break;
      case '7': /* Request netCDF4-classic output storage format */
        fl_out_fmt=NC_FORMAT_NETCDF4_CLASSIC;
        break;
      case 'A': /* Toggle FORCE_APPEND */
        FORCE_APPEND=True;
        break;
      case 'C': /* Extract all coordinates associated with extracted variables? */
        EXTRACT_ASSOCIATED_COORDINATES=False;
        break;
      case 'c':
        EXTRACT_ALL_COORDINATES=True;
        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);
        break;
      case 'd': /* Copy limit argument for later processing */
        lmt_arg[lmt_nbr]=(char *)strdup(optarg);
        lmt_nbr++;
        break;
      case 'F': /* Toggle index convention. Default is 0-based arrays (C-style). */
        FORTRAN_IDX_CNV=!FORTRAN_IDX_CNV;
        break;
      case 'G': /* Apply Group Path Editing (GPE) to output group */
        /* NB: GNU getopt() optional argument syntax is ugly (requires "=" sign) so avoid it
        http://stackoverflow.com/questions/1052746/getopt-does-not-parse-optional-arguments-to-parameters */
        gpe=nco_gpe_prs_arg(optarg);
        fl_out_fmt=NC_FORMAT_NETCDF4; 
        break;
      case 'g': /* Copy group argument for later processing */
        /* Replace commas with hashes when within braces (convert back later) */
        optarg_lcl=(char *)strdup(optarg);
        (void)nco_rx_comma2hash(optarg_lcl);
        grp_lst_in=nco_lst_prs_2D(optarg_lcl,",",&grp_lst_in_nbr);
        optarg_lcl=(char *)nco_free(optarg_lcl);
        break;
      case 'H': /* Toggle writing input file list attribute */
        FL_LST_IN_APPEND=!FL_LST_IN_APPEND;
        break;
      case 'h': /* Toggle appending to history global attribute */
        HISTORY_APPEND=!HISTORY_APPEND;
        break;
      case 'L': /* [enm] Deflate level. Default is 0. */
        dfl_lvl=(int)strtol(optarg,&sng_cnv_rcd,NCO_SNG_CNV_BASE10);
        if(*sng_cnv_rcd) nco_sng_cnv_err(optarg,"strtol",sng_cnv_rcd);
        break;
      case 'l': /* Local path prefix for files retrieved from remote file system */
        fl_pth_lcl=(char *)strdup(optarg);
        break;
      case 'N':
        NORMALIZE_BY_WEIGHT=False;
        break;
      case 'n': /* NINTAP-style abbreviation of files to average */
        fl_lst_abb=nco_lst_prs_2D(optarg,",",&abb_arg_nbr);
        if(abb_arg_nbr < 1 || abb_arg_nbr > 6){
          (void)fprintf(stdout,gettext("%s: ERROR Incorrect abbreviation for file list\n"),nco_prg_nm_get());
          (void)nco_usg_prn();
          nco_exit(EXIT_FAILURE);
        } /* end if */
        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 'P': /* Packing policy */
        nco_pck_plc_sng=(char *)strdup(optarg);
        nco_pck_plc=nco_pck_plc_get(nco_pck_plc_sng);
        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 't': /* Thread number */
        thr_nbr=(int)strtol(optarg,&sng_cnv_rcd,NCO_SNG_CNV_BASE10);
        if(*sng_cnv_rcd) nco_sng_cnv_err(optarg,"strtol",sng_cnv_rcd);
        break;
      case 'v': /* Variables to extract/exclude */
        /* Replace commas with hashes when within braces (convert back later) */
        optarg_lcl=(char *)strdup(optarg);
        (void)nco_rx_comma2hash(optarg_lcl);
        var_lst_in=nco_lst_prs_2D(optarg_lcl,",",&var_lst_in_nbr);
        optarg_lcl=(char *)nco_free(optarg_lcl);
        xtr_nbr=var_lst_in_nbr;
        break;
      case 'w': /* Per-file and per-record weights */
        if(isalpha(optarg[0]) || optarg[0] == '/'){
  	wgt_nm=(char *)strdup(optarg);
        }else{ /* !wgt_nm */
  	optarg_lcl=(char *)strdup(optarg);
  	wgt_lst_in=nco_lst_prs_2D(optarg_lcl,",",&wgt_nbr);
  	optarg_lcl=(char *)nco_free(optarg_lcl);
  	wgt_arr=(double *)nco_malloc(wgt_nbr*sizeof(double));
  	for(idx=0L;idx<wgt_nbr;idx++){
  	  wgt_arr[idx]=strtod(wgt_lst_in[idx],&sng_cnv_rcd);
  	  if(*sng_cnv_rcd) nco_sng_cnv_err(wgt_lst_in[idx],"strtod",sng_cnv_rcd);
  	  wgt_avg_scl+=wgt_arr[idx];
  	} /* end loop over elements */
  	if(NORMALIZE_BY_WEIGHT) wgt_avg_scl/=wgt_nbr; else wgt_avg_scl=1.0/wgt_nbr;
  	assert(wgt_avg_scl != 0.0);
  	for(idx=0L;idx<wgt_nbr;idx++) wgt_arr[idx]/=wgt_avg_scl;
        } /* !wgt_nm */
        break;
      case 'X': /* Copy auxiliary coordinate argument for later processing */
        aux_arg[aux_nbr]=(char *)strdup(optarg);
        aux_nbr++;
        MSA_USR_RDR=True; /* [flg] Multi-Slab Algorithm returns hyperslabs in user-specified order */      
        break;
      case 'x': /* Exclude rather than extract variables specified with -v */
        EXCLUDE_INPUT_LIST=True;
        break;
      case 'Y': /* Pseudonym */
        /* Call nco_prg_prs() to reset pseudonym */
        optarg_lcl=(char *)strdup(optarg);
        if(nco_prg_nm) nco_prg_nm=(char *)nco_free(nco_prg_nm);
        nco_prg_nm=nco_prg_prs(optarg_lcl,&nco_prg_id);
        optarg_lcl=(char *)nco_free(optarg_lcl);
        break;
      case 'y': /* Operation type */
        nco_op_typ_sng=(char *)strdup(optarg);
        if(nco_prg_id == ncra || nco_prg_id == ncfe) nco_op_typ=nco_op_typ_get(nco_op_typ_sng);
        break;
      case '?': /* Question mark means unrecognized option, print proper usage then EXIT_FAILURE */
        (void)fprintf(stdout,"%s: ERROR in command-line syntax/options. Missing or unrecognized option. Please reformulate command accordingly.\n",nco_prg_nm_get());
        (void)nco_usg_prn();
        nco_exit(EXIT_FAILURE);
        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);
        break;
      } /* end switch */
      if(opt_crr) opt_crr=(char *)nco_free(opt_crr);
    } /* end while loop */
  
    /* Set/report global chunk cache */
    rcd+=nco_cnk_csh_ini(cnk_csh_byt);
  
    /* 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,FORCE_OVERWRITE);
  
    /* Initialize thread information */
    thr_nbr=nco_openmp_ini(thr_nbr);
    in_id_arr=(int *)nco_malloc(thr_nbr*sizeof(int));
  
    /* 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,HPSS_TRY,&FL_RTR_RMT_LCN);
    /* Open file using appropriate buffer size hints and verbosity */
    if(RAM_OPEN) md_open=NC_NOWRITE|NC_DISKLESS; else md_open=NC_NOWRITE;
    rcd+=nco_fl_open(fl_in,md_open,&bfr_sz_hnt,&in_id);
    (void)nco_inq_format(in_id,&fl_in_fmt);
  
    /* Initialize traversal table */ 
    trv_tbl_init(&trv_tbl); 
  
    /* Construct GTT, Group Traversal Table (groups,variables,dimensions, limits) */
    (void)nco_bld_trv_tbl(in_id,trv_pth,lmt_nbr,lmt_arg,aux_nbr,aux_arg,MSA_USR_RDR,FORTRAN_IDX_CNV,grp_lst_in,grp_lst_in_nbr,var_lst_in,var_lst_in_nbr,EXTRACT_ALL_COORDINATES,GRP_VAR_UNN,False,EXCLUDE_INPUT_LIST,EXTRACT_ASSOCIATED_COORDINATES,EXTRACT_CLL_MSR,EXTRACT_FRM_TRM,nco_pck_plc_nil,&flg_dne,trv_tbl);
    
    /* Were all user-specified dimensions found? */ 
    (void)nco_chk_dmn(lmt_nbr,flg_dne);     
  
    /* Store ncge ensemble suffix in table */
    if(nco_prg_id == ncge && nsm_sfx) trv_tbl->nsm_sfx=nsm_sfx;
  
    /* Get number of variables, dimensions, and global attributes in file, file format */
    (void)trv_tbl_inq((int *)NULL,(int *)NULL,(int *)NULL,&nbr_dmn_fl,&dmn_rec_fl,(int *)NULL,(int *)NULL,(int *)NULL,&nbr_var_fl,trv_tbl);
  
    /* Record handling operators only */
    if(nco_prg_id == ncra || nco_prg_id == ncrcat){
  
      /* Build record dimensions array */
      (void)nco_bld_rec_dmn(in_id,FORTRAN_IDX_CNV,&lmt_rec,&nbr_rec,trv_tbl);  
  
      /* Allocate arrays for multi-records cases */
      flg_input_complete=(nco_bool *)nco_malloc(nbr_rec*sizeof(nco_bool));
      idx_rec_out=(long *)nco_malloc(nbr_rec*sizeof(long));
      rec_in_cml=(long *)nco_malloc(nbr_rec*sizeof(long));
      rec_usd_cml=(long *)nco_malloc(nbr_rec*sizeof(long));
      REC_LST_DSR=(nco_bool *)nco_malloc(nbr_rec*sizeof(nco_bool));
  
      /* Initialize arrays for multi-records cases */
      for(idx_rec=0;idx_rec<nbr_rec;idx_rec++){
        flg_input_complete[idx_rec]=False;
        idx_rec_out[idx_rec]=0L;
        rec_in_cml[idx_rec]=0L;
        rec_usd_cml[idx_rec]=0L;
        REC_LST_DSR[idx_rec]=False;
      } /* Initialize arrays */
  
    } /* Record handling operators only */
  
    /* Is this an ARM-format data file? */
    CNV_ARM=nco_cnv_arm_inq(in_id);
    /* NB: nco_cnv_arm_base_time_get() with same nc_id contains OpenMP critical region */
    if(CNV_ARM) base_time_srt=nco_cnv_arm_base_time_get(in_id);
  
    /* Fill-in variable structure list for all extracted variables */
    var=nco_fll_var_trv(in_id,&xtr_nbr,trv_tbl);
  
    /* Duplicate to output array */
    var_out=(var_sct **)nco_malloc(xtr_nbr*sizeof(var_sct *));
    for(idx=0;idx<xtr_nbr;idx++){
      var_out[idx]=nco_var_dpl(var[idx]);
      (void)nco_xrf_var(var[idx],var_out[idx]);
      (void)nco_xrf_dmn(var_out[idx]);
    } /* end loop over xtr */
  
    /* Refresh var_out with dim_out data */
    (void)nco_var_dmn_refresh(var_out,xtr_nbr);
  
    /* Determine conventions (ARM/CCM/CCSM/CF/MPAS) for treating file */
    cnv=nco_cnv_ini(in_id);
  
    /* Divide variable lists into lists of fixed variables and variables to be processed */
    (void)nco_var_lst_dvd(var,var_out,xtr_nbr,cnv,True,nco_pck_plc_nil,nco_pck_map_nil,(dmn_sct **)NULL,0,&var_fix,&var_fix_out,&nbr_var_fix,&var_prc,&var_prc_out,&nbr_var_prc,trv_tbl);
  
    /* Store processed and fixed variables info into GTT */
    (void)nco_var_prc_fix_trv(nbr_var_prc,var_prc,nbr_var_fix,var_fix,trv_tbl);
  
    /* Make output and input files consanguinous */
    if(fl_out_fmt == NCO_FORMAT_UNDEFINED) fl_out_fmt=fl_in_fmt;
  
    /* Initialize, decode, and set PPC information */
    if(ppc_nbr > 0) nco_ppc_ini(in_id,&dfl_lvl,fl_out_fmt,ppc_arg,ppc_nbr,trv_tbl);
  
    /* Verify output file format supports requested actions */
    (void)nco_fl_fmt_vet(fl_out_fmt,cnk_nbr,dfl_lvl);
  
    /* Open output file */
    fl_out_tmp=nco_fl_out_open(fl_out,&FORCE_APPEND,FORCE_OVERWRITE,fl_out_fmt,&bfr_sz_hnt,RAM_CREATE,RAM_OPEN,WRT_TMP_FL,&out_id);
  
    /* Initialize chunking from user-specified inputs */
    if(fl_out_fmt == NC_FORMAT_NETCDF4 || fl_out_fmt == NC_FORMAT_NETCDF4_CLASSIC) rcd+=nco_cnk_ini(in_id,fl_out,cnk_arg,cnk_nbr,cnk_map,cnk_plc,cnk_csh_byt,cnk_min_byt,cnk_sz_byt,cnk_sz_scl,&cnk);
  
    /* Define dimensions, extracted groups, variables, and attributes in output file */
    (void)nco_xtr_dfn(in_id,out_id,&cnk,dfl_lvl,gpe,md5,!FORCE_APPEND,!REC_APN,False,nco_pck_plc_nil,(char *)NULL,trv_tbl);
  
    /* Define ensemble fixed variables (True parameter) */
    if(nco_prg_id_get() == ncge) (void)nco_nsm_dfn_wrt(in_id,out_id,&cnk,dfl_lvl,gpe,True,trv_tbl); 
  
    /* Catenate time-stamped command line to "history" global attribute */
    if(HISTORY_APPEND) (void)nco_hst_att_cat(out_id,cmd_ln);
    if(HISTORY_APPEND && FORCE_APPEND) (void)nco_prv_att_cat(fl_in,in_id,out_id);
    if(gaa_nbr > 0) (void)nco_glb_att_add(out_id,gaa_arg,gaa_nbr);
    if(HISTORY_APPEND) (void)nco_vrs_att_cat(out_id);
    if(thr_nbr > 1 && HISTORY_APPEND) (void)nco_thr_att_cat(out_id,thr_nbr);
  
    /* Add input file list global attribute */
    if(FL_LST_IN_APPEND && HISTORY_APPEND && FL_LST_IN_FROM_STDIN) (void)nco_fl_lst_att_cat(out_id,fl_lst_in,fl_nbr);
  
    /* Turn-off default filling behavior to enhance efficiency */
    (void)nco_set_fill(out_id,NC_NOFILL,&fll_md_old);
  
    /* Add climatology_bounds attribute to output file (before cell_methods) */
    if(flg_cb && nco_prg_id == ncra){
      char bnd_sng[]="bounds"; /* CF-standard time bounds attribute name */
      char clm_sng[]="climatology"; /* CF-standard climatology bounds attribute name */
      char cln_sng[]="calendar"; /* CF-standard calendar attribute name */
      char unt_sng[]="units"; /* NUG-standard units attribute name */
      long att_sz;
      nc_type att_typ;
  
      cb=(clm_bnd_sct *)nco_malloc(sizeof(clm_bnd_sct));
      cb->bnd2clm=False; /* [flg] Convert time bounds to climatology bounds */
      cb->clm2bnd=False; /* [flg] Convert climatology bounds to time bounds */
      cb->clm2clm=False; /* [flg] Convert climatology bounds to climatology bounds */
      cb->clm_bnd_id_in=NC_MIN_INT; /* [id] Climatology bounds ID */
      cb->clm_bnd_id_out=NC_MIN_INT; /* [id] Climatology bounds ID */
      cb->clm_bnd_in=False; /* [flg] Climatology bounds appear in input */
      cb->clm_bnd_nm=NULL; /* [sng] Climatology bounds name */
      cb->dmn_srt_srt[0]=0L;cb->dmn_srt_srt[1]=0L;
      cb->dmn_srt_end[0]=0L;cb->dmn_srt_end[1]=1L;
      cb->tm_bnd_id_in=NC_MIN_INT; /* [id] Time bounds ID */
      cb->tm_bnd_in=False; /* [flg] Time bounds appear in input */
      cb->tm_bnd_nm=NULL; /* [sng] Time bounds name */
      cb->tm_crd_id_in=NC_MIN_INT; /* [id] Time coordinate ID */
      cb->tm_crd_nm=NULL; /* [sng] Time coordinate name */
      cb->type=NC_NAT; /* [enm] Time coordinate type */
      cb->val[0]=NC_MIN_DOUBLE;
      cb->val[1]=NC_MIN_DOUBLE;
  
      if((rcd=nco_inq_varid_flg(in_id,"time",&cb->tm_crd_id_in)) == NC_NOERR) cb->tm_crd_nm=strdup("time");
      else if((rcd=nco_inq_varid_flg(in_id,"Time",&cb->tm_crd_id_in)) == NC_NOERR) cb->tm_crd_nm=strdup("Time");
      if(cb->tm_crd_id_in != NC_MIN_INT){
        rcd=nco_inq_vartype(in_id,cb->tm_crd_id_in,&cb->type);
  
        rcd=nco_inq_att_flg(in_id,cb->tm_crd_id_in,clm_sng,&att_typ,&att_sz);
        if(rcd == NC_NOERR && att_typ == NC_CHAR){
  	cb->clm_bnd_nm=(char *)nco_malloc((att_sz+1L)*nco_typ_lng(att_typ));
  	rcd+=nco_get_att(in_id,cb->tm_crd_id_in,clm_sng,cb->clm_bnd_nm,att_typ);
  	/* NUL-terminate attribute before using strstr() */
  	cb->clm_bnd_nm[att_sz]='\0';
  	cb->clm_bnd_in=True;
        }else{
  	cb->clm_bnd_nm=strdup("climatology_bounds");
  	rcd=NC_NOERR;
        }	/* !rcd && att_typ */
  
        rcd=nco_inq_att_flg(in_id,cb->tm_crd_id_in,bnd_sng,&att_typ,&att_sz);
        if(rcd == NC_NOERR && att_typ == NC_CHAR){
  	cb->tm_bnd_nm=(char *)nco_malloc((att_sz+1L)*nco_typ_lng(att_typ));
  	rcd+=nco_get_att(in_id,cb->tm_crd_id_in,bnd_sng,cb->tm_bnd_nm,att_typ);
  	/* NUL-terminate attribute before using strstr() */
  	cb->tm_bnd_nm[att_sz]='\0';
  	cb->tm_bnd_in=True;
        }else{
  	cb->tm_bnd_nm=strdup("time_bnds");
  	rcd=NC_NOERR;
        } /* !rcd && att_typ */
  
        /* Input file must have either (but not both) time bounds or climatology bounds */
        if(cb->tm_bnd_in && cb->clm_bnd_in){
  	(void)fprintf(stderr,"%s: WARNING Climatology bounds invoked on time coordinate with both time bounds attribute \"%s\" (value = \"%s\") and climatology bounds attribute \"%s\" (value = \"%s\"). Results would be ambiguous. Turning-off climatology bounds mode.\n",nco_prg_nm_get(),bnd_sng,cb->tm_bnd_nm,clm_sng,cb->clm_bnd_nm);
  	flg_cb=False;
  	goto skp_cb;
        } /* !(cb->tm_bnd_in && cb->clm_bnd_in) */
        if(!cb->tm_bnd_in && !cb->clm_bnd_in){
  	(void)fprintf(stderr,"%s: WARNING Climatology bounds invoked on time coordinate with neither time bounds attribute \"%s\" nor climatology bounds attribute \"%s\". No way to obtain bounding time values. Turning-off climatology bounds mode.\n",nco_prg_nm_get(),bnd_sng,clm_sng);
  	flg_cb=False;
  	goto skp_cb;
        } /* !cb->tm_bnd_in && !cb->clm_bnd_in */
  
      }else{ /* !tm_crd_id_in */
        if(nco_dbg_lvl >= nco_dbg_std) (void)fprintf(stderr,"%s: WARNING Climatology bounds invoked on dataset with unknown time coordinate. Turning-off climatology bounds mode.\n",nco_prg_nm_get());
        flg_cb=False;
        rcd=NC_NOERR;
        goto skp_cb; 
      } /* !tm_crd_in */
  
      if(flg_c2b && cb->clm_bnd_in) cb->clm2bnd=True;
      if(cb->clm_bnd_in) cb->clm2clm=True;
      if(cb->tm_bnd_in) cb->bnd2clm=True;
  
      if(cb->tm_bnd_in){
        rcd=nco_inq_varid_flg(in_id,cb->tm_bnd_nm,&cb->tm_bnd_id_in); 
        if(cb->tm_bnd_id_in == NC_MIN_INT){
  	if(nco_dbg_lvl >= nco_dbg_std) (void)fprintf(stderr,"%s: WARNING Climatology bounds invoked on dataset with missing time bounds variable \"%s\". Turning-off climatology bounds mode.\n",nco_prg_nm_get(),cb->tm_bnd_nm);
  	flg_cb=False;
  	rcd=NC_NOERR;
  	goto skp_cb; 
        } /* !tm_bnd_id_in */
      } /* !tm_bnd_in */
  
      if(cb->clm_bnd_in){
        rcd=nco_inq_varid_flg(in_id,cb->clm_bnd_nm,&cb->clm_bnd_id_in); 
        if(cb->clm_bnd_id_in == NC_MIN_INT){
  	if(nco_dbg_lvl >= nco_dbg_std) (void)fprintf(stderr,"%s: WARNING Climatology bounds invoked on dataset with missing climatology bounds variable \"%s\". Turning-off climatology bounds mode.\n",nco_prg_nm_get(),cb->tm_bnd_nm);
  	flg_cb=False;
  	rcd=NC_NOERR;
  	goto skp_cb; 
        } /* !tm_bnd_id_in */
      } /* !clm_bnd_in */
  
      rcd=nco_inq_varid_flg(out_id,cb->tm_crd_nm,&cb->tm_crd_id_out); 
      if(rcd != NC_NOERR){
        if(nco_dbg_lvl >= nco_dbg_std) (void)fprintf(stderr,"%s: ERROR Climatology bounds did not find time coordinate in output file\n",nco_prg_nm_get());
        nco_exit(EXIT_FAILURE);
      } /* !tm_crd_id_out */
  
      if(cb->bnd2clm){
        rcd=nco_inq_varid_flg(out_id,cb->tm_bnd_nm,&cb->tm_bnd_id_out); 
        if(rcd != NC_NOERR){
  	if(nco_dbg_lvl >= nco_dbg_std) (void)fprintf(stderr,"%s: ERROR Time bounds variable %s was not copied to output file\n",nco_prg_nm_get(),cb->tm_bnd_nm);
  	nco_exit(EXIT_FAILURE);
        } /* !tm_bnd_id_out */
        /* Write climatology bounds to time bounds then rename */
        cb->clm_bnd_id_out=cb->tm_bnd_id_out;
      } /* !bnd2clm */
      
      if(cb->clm2clm || cb->clm2bnd){
        rcd=nco_inq_varid_flg(out_id,cb->clm_bnd_nm,&cb->clm_bnd_id_out); 
        if(rcd != NC_NOERR){
  	if(nco_dbg_lvl >= nco_dbg_std) (void)fprintf(stderr,"%s: ERROR Climatology bounds variable %s was not copied to output file\n",nco_prg_nm_get(),cb->clm_bnd_nm);
  	nco_exit(EXIT_FAILURE);
        } /* !clm_bnd_id_out */
        /* Write time bounds to climatology bounds then rename */
        if(cb->clm2bnd) cb->tm_bnd_id_out=cb->clm_bnd_id_out;
      } /* !clm2clm */
  
      if(cb->bnd2clm || cb->clm2bnd){
        aed_sct aed_mtd;
        char *att_nm;
        char *att_val;
  
        /* Add new bounds attribute */
        att_nm = cb->bnd2clm ? strdup(clm_sng) : strdup(bnd_sng);
        att_val= cb->bnd2clm ? strdup(cb->clm_bnd_nm) : strdup(cb->tm_bnd_nm);
        aed_mtd.att_nm=att_nm;
        aed_mtd.var_nm=cb->tm_crd_nm;
        aed_mtd.id=cb->tm_crd_id_out;
        aed_mtd.sz=strlen(att_val);
        aed_mtd.type=NC_CHAR;
        aed_mtd.val.cp=att_val;
        aed_mtd.mode=aed_create;
        (void)nco_aed_prc(out_id,cb->tm_crd_id_out,aed_mtd);
        if(att_nm) att_nm=(char *)nco_free(att_nm);
        if(att_val) att_val=(char *)nco_free(att_val);
        
        /* Delete old bounds attribute */
        att_nm= cb->bnd2clm ? strdup(bnd_sng) : strdup(clm_sng);
        aed_mtd.att_nm=att_nm;
        aed_mtd.var_nm=cb->tm_crd_nm;
        aed_mtd.id=cb->tm_crd_id_out;
        aed_mtd.mode=aed_delete;
        (void)nco_aed_prc(out_id,cb->tm_crd_id_out,aed_mtd);
        if(att_nm) att_nm=(char *)nco_free(att_nm);
  
        /* Copy units attribute from coordinate to new bounds if necessary */
        if(cb->tm_bnd_in) rcd=nco_inq_att_flg(out_id,cb->tm_bnd_id_out,unt_sng,&att_typ,&att_sz);
        if(cb->clm_bnd_in) rcd=nco_inq_att_flg(out_id,cb->clm_bnd_id_out,unt_sng,&att_typ,&att_sz);
        if(rcd != NC_NOERR && att_typ == NC_CHAR){
  	rcd=nco_inq_att_flg(out_id,cb->tm_crd_id_out,unt_sng,&att_typ,&att_sz);
  	if(rcd == NC_NOERR && att_typ == NC_CHAR){
  	  cb->unt_val=(char *)nco_malloc((att_sz+1L)*nco_typ_lng(att_typ));
  	  rcd+=nco_get_att(out_id,cb->tm_crd_id_out,unt_sng,cb->unt_val,att_typ);
  	  /* NUL-terminate attribute before using strstr() */
  	  cb->unt_val[att_sz]='\0';
  	  
  	  /* Add units attribute */
  	  att_nm=strdup(unt_sng);
  	  att_val=cb->unt_val;
  	  aed_mtd.att_nm=att_nm;
  	  aed_mtd.var_nm=cb->bnd2clm ? cb->tm_bnd_nm : cb->clm_bnd_nm;
  	  aed_mtd.id=cb->bnd2clm ? cb->tm_bnd_id_out : cb->clm_bnd_id_out;
  	  aed_mtd.sz=strlen(att_val);
  	  aed_mtd.type=NC_CHAR;
  	  aed_mtd.val.cp=att_val;
  	  aed_mtd.mode=aed_create;
  	  if(cb->bnd2clm) (void)nco_aed_prc(out_id,cb->tm_bnd_id_out,aed_mtd); else (void)nco_aed_prc(out_id,cb->clm_bnd_id_out,aed_mtd);
  	  if(att_nm) att_nm=(char *)nco_free(att_nm);
  	  if(att_val) att_val=cb->unt_val=(char *)nco_free(att_val);
  	} /* !rcd && att_typ */
  	rcd=NC_NOERR;
        } /* !rcd && att_typ */
  
        /* Copy calendar attribute from coordinate to new bounds if necessary */
        if(cb->tm_bnd_in) rcd=nco_inq_att_flg(out_id,cb->tm_bnd_id_out,cln_sng,&att_typ,&att_sz);
        if(cb->clm_bnd_in) rcd=nco_inq_att_flg(out_id,cb->clm_bnd_id_out,cln_sng,&att_typ,&att_sz);
        if(rcd != NC_NOERR && att_typ == NC_CHAR){
  	rcd=nco_inq_att_flg(out_id,cb->tm_crd_id_out,cln_sng,&att_typ,&att_sz);
  	if(rcd == NC_NOERR && att_typ == NC_CHAR){
  	  cb->cln_val=(char *)nco_malloc((att_sz+1L)*nco_typ_lng(att_typ));
  	  rcd+=nco_get_att(out_id,cb->tm_crd_id_out,cln_sng,cb->cln_val,att_typ);
  	  /* NUL-terminate attribute before using strstr() */
  	  cb->cln_val[att_sz]='\0';
  	  
  	  /* Add calendar attribute */
  	  att_nm=strdup(cln_sng);
  	  att_val=cb->cln_val;
  	  aed_mtd.att_nm=att_nm;
  	  aed_mtd.var_nm=cb->bnd2clm ? cb->tm_bnd_nm : cb->clm_bnd_nm;
  	  aed_mtd.id=cb->bnd2clm ? cb->tm_bnd_id_out : cb->clm_bnd_id_out;
  	  aed_mtd.sz=strlen(att_val);
  	  aed_mtd.type=NC_CHAR;
  	  aed_mtd.val.cp=att_val;
  	  aed_mtd.mode=aed_create;
  	  if(cb->bnd2clm) (void)nco_aed_prc(out_id,cb->tm_bnd_id_out,aed_mtd); else (void)nco_aed_prc(out_id,cb->clm_bnd_id_out,aed_mtd);
  	  if(att_nm) att_nm=(char *)nco_free(att_nm);
  	  if(att_val) att_val=cb->cln_val=(char *)nco_free(att_val);
  	} /* !rcd && att_typ */
  	rcd=NC_NOERR;
        } /* !rcd && att_typ */
        
      } /* !bnd2clm !clm2bnd */
    } /* !flg_cb */
    
    /* goto skp_cb */
   skp_cb: 
    /* free() any abandoned cb structure now or it will be inadvertently used in nco_cnv_cf_cll_mth_add() */
    if(!flg_cb)
      if(cb) cb=(clm_bnd_sct *)nco_free(cb);
  
    /* Add cell_methods attributes (before exiting define mode) */
    if(nco_prg_id == ncra){
      dmn_sct **dmn=NULL_CEWI;
      int nbr_dmn=nbr_rec;
      dmn=(dmn_sct **)nco_malloc(nbr_dmn*sizeof(dmn_sct *));
      /* Make dimension array from limit records array */
      (void)nco_dmn_lmt(lmt_rec,nbr_dmn,&dmn);
      /* Add cell_methods attributes (pass as dimension argument a records-only array) */
      if(flg_cll_mth) rcd+=nco_cnv_cf_cll_mth_add(out_id,var_prc_out,nbr_var_prc,dmn,nbr_dmn,nco_op_typ,gpe,cb,trv_tbl); 
      if(nbr_dmn > 0) dmn=nco_dmn_lst_free(dmn,nbr_dmn);
    } /* !ncra */
  
    /* Take output file out of define mode */
    if(hdr_pad == 0UL){
      (void)nco_enddef(out_id);
    }else{
      (void)nco__enddef(out_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 */
  
    /* Zero start and stride vectors for all output variables */
    (void)nco_var_srd_srt_set(var_out,xtr_nbr);
  
    /* Copy variable data for non-processed variables */
    (void)nco_cpy_fix_var_trv(in_id,out_id,gpe,trv_tbl); 
  
    /* Write ensemble fixed variables (False parameter) */
    if(nco_prg_id_get() == ncge) (void)nco_nsm_dfn_wrt(in_id,out_id,&cnk,dfl_lvl,gpe,False,trv_tbl); 
  
    /* Allocate and, if necesssary, initialize accumulation space for processed variables */
    for(idx=0;idx<nbr_var_prc;idx++){
      /* Record operators only need space for one record, not entire variable */
      if(nco_prg_id == ncra || nco_prg_id == ncrcat) var_prc[idx]->sz=var_prc[idx]->sz_rec=var_prc_out[idx]->sz=var_prc_out[idx]->sz_rec;
      if(nco_prg_id == ncra || nco_prg_id == ncfe || nco_prg_id == ncge){
        if((wgt_arr || wgt_nm) && var_prc[idx]->has_mss_val) var_prc_out[idx]->wgt_sum=var_prc[idx]->wgt_sum=(double *)nco_calloc(var_prc_out[idx]->sz,sizeof(double));
        var_prc_out[idx]->tally=var_prc[idx]->tally=(long *)nco_calloc(var_prc_out[idx]->sz,sizeof(long));
        var_prc_out[idx]->val.vp=(void *)nco_calloc(var_prc_out[idx]->sz,nco_typ_lng(var_prc_out[idx]->type));
      } /* end if */
    } /* end loop over idx */
  
    if(wgt_nm && (nco_op_typ == nco_op_avg || nco_op_typ == nco_op_mebs)){
      /* Find weight variable that matches current variable */
      wgt=nco_var_get_wgt_trv(in_id,lmt_nbr,lmt_arg,MSA_USR_RDR,FORTRAN_IDX_CNV,wgt_nm,var_prc[0],trv_tbl);
      assert(wgt->nbr_dim < 2);
      /* Change wgt from a normal full variable to one that only holds one record at a time
         This differs from ncwa wgt treatment
         20150708: nco_var_dpl() calls below generate valgrind invalid read errors. Not sure why. */
      wgt->val.vp=(void *)nco_realloc(wgt->val.vp,wgt->sz_rec*nco_typ_lng(wgt->type));
      wgt->tally=(long *)nco_realloc(wgt->tally,wgt->sz_rec*sizeof(long));
      (void)nco_var_zero(wgt->type,wgt->sz_rec,wgt->val);
      (void)nco_zero_long(wgt->sz_rec,wgt->tally);
      wgt_out=nco_var_dpl(wgt);
      wgt_avg=nco_var_dpl(wgt_out);
    } /* !wgt_nm */
  
    /* Close first input netCDF file */
    nco_close(in_id);
  
    /* Timestamp end of metadata setup and disk layout */
    rcd+=nco_ddra((char *)NULL,(char *)NULL,&ddra_info);
    ddra_info.tmr_flg=nco_tmr_rgl;
  
    /* Loop over input files */
    for(fl_idx=0;fl_idx<fl_nbr;fl_idx++){
  
      /* Parse filename */
      if(fl_idx != 0) fl_in=nco_fl_nm_prs(fl_in,fl_idx,(int *)NULL,fl_lst_in,abb_arg_nbr,fl_lst_abb,fl_pth);
      if(nco_dbg_lvl >= nco_dbg_fl) (void)fprintf(stderr,gettext("%s: INFO Input file %d is %s"),nco_prg_nm_get(),fl_idx,fl_in);
      /* Make sure file is on local system and is readable or die trying */
      if(fl_idx != 0) fl_in=nco_fl_mk_lcl(fl_in,fl_pth_lcl,HPSS_TRY,&FL_RTR_RMT_LCN);
      if(nco_dbg_lvl >= nco_dbg_fl && FL_RTR_RMT_LCN) (void)fprintf(stderr,gettext(", local file is %s"),fl_in);
      if(nco_dbg_lvl >= nco_dbg_fl) (void)fprintf(stderr,"\n");
  
      /* Open file once per thread to improve caching */
      for(thr_idx=0;thr_idx<thr_nbr;thr_idx++) rcd+=nco_fl_open(fl_in,NC_NOWRITE,&bfr_sz_hnt,in_id_arr+thr_idx);
      in_id=in_id_arr[0];
  
      /* Do ncge ensemble refresh */
      if(nco_prg_id == ncge){
        /* Refresh ensembles */
        if(fl_idx > 0) (void)nco_nsm_ncr(in_id,trv_tbl);
     
        /* Check if ensembles are valid */
        (void)nco_chk_nsm(in_id,fl_idx,trv_tbl); 
  
      }else{ /* ! ncge */
        /* Variables may have different ID, missing_value, type, in each file */
        for(idx=0;idx<nbr_var_prc;idx++){
          /* Obtain variable GTT object using full variable name */
          trv_sct *trv=trv_tbl_var_nm_fll(var_prc[idx]->nm_fll,trv_tbl);
          /* Obtain group ID */
          (void)nco_inq_grp_full_ncid(in_id,trv->grp_nm_fll,&grp_id);
          (void)nco_var_mtd_refresh(grp_id,var_prc[idx]);
        } /* end loop over variables */
      } /* ! ncge */
  
      if(wgt_nm && (nco_op_typ == nco_op_avg || nco_op_typ == nco_op_mebs)){
        /* Get variable ID in this file */
        trv_sct *trv=trv_tbl_var_nm_fll(wgt_out->nm_fll,trv_tbl);
        (void)nco_inq_grp_full_ncid(in_id,trv->grp_nm_fll,&grp_id);
        (void)nco_var_mtd_refresh(grp_id,wgt_out);
      } /* !wgt_nm */
  
      if(nco_prg_id == ncra || nco_prg_id == ncrcat){ /* ncfe and ncge jump to else branch */
  
        /* Loop over number of different record dimensions in file */
        for(idx_rec=0;idx_rec<nbr_rec;idx_rec++){
          char *fl_udu_sng=NULL_CEWI;
          char ***ra_bnds_lst=NULL_CEWI;
          char ***ra_climo_lst=NULL_CEWI;
          int ra_bnds_nbr=0;
          int ra_climo_nbr=0;
  
          /* Obtain group ID */
          (void)nco_inq_grp_full_ncid(in_id,lmt_rec[idx_rec]->grp_nm_fll,&grp_id);
  
          /* Fill record array */
          (void)nco_lmt_evl(grp_id,lmt_rec[idx_rec],rec_usd_cml[idx_rec],FORTRAN_IDX_CNV);
  
          if(lmt_rec[idx_rec]->is_rec_dmn){
            int mid=-1;
            if(nco_inq_varid_flg(grp_id,lmt_rec[idx_rec]->nm,&mid) == NC_NOERR && mid >= 0){
              fl_udu_sng=nco_lmt_get_udu_att(grp_id,mid,"units"); /* Units attribute of coordinate variable */
              ra_bnds_lst=nco_lst_cf_att(grp_id,"bounds",&ra_bnds_nbr);
              ra_climo_lst=nco_lst_cf_att(grp_id,"climatology",&ra_climo_nbr);
            } /* !mid */
          } /* !is_rec_dmn */
  
          if(REC_APN){
  	  int rec_var_out_id;
            /* Append records directly to output file */
            int rec_dmn_out_id=NCO_REC_DMN_UNDEFINED;
            /* Get group ID using record group full name */
            (void)nco_inq_grp_full_ncid(out_id,lmt_rec[idx_rec]->grp_nm_fll,&grp_out_id);
            /* Get dimension ID (rec_dmn_out_id) of current record from its name */
            (void)nco_inq_dimid(grp_out_id,lmt_rec[idx_rec]->nm,&rec_dmn_out_id);
            /* Get current size of record dimension */
            (void)nco_inq_dimlen(grp_out_id,rec_dmn_out_id,&idx_rec_out[idx_rec]);
  	  /* 20181212: Re-base relative to calendar units in output file, not first input file */
  	  if(nco_inq_varid_flg(grp_out_id,lmt_rec[idx_rec]->nm,&rec_var_out_id) == NC_NOERR){
  	    if(nco_dbg_lvl_get() >= nco_dbg_fl) (void)fprintf(fp_stderr,"%s: DEBUG REC_APN mode changing re-base units string of variable \"%s\" from input units \"%s\" ",nco_prg_nm_get(),lmt_rec[idx_rec]->nm,lmt_rec[idx_rec]->rbs_sng);
  	    lmt_rec[idx_rec]->rbs_sng=nco_lmt_get_udu_att(grp_out_id,rec_var_out_id,"units"); 
  	    if(nco_dbg_lvl_get() >= nco_dbg_fl) (void)fprintf(fp_stderr,"to output units \"%s\"\n",lmt_rec[idx_rec]->rbs_sng);
  	  } /* endif record coordinate exists in output file */
  	} /* !REC_APN */
  
          if(nco_dbg_lvl_get() >= nco_dbg_crr)  (void)fprintf(fp_stdout,"%s: DEBUG record %d id %d name %s rec_dmn_sz %ld units=\"%s\"\n",nco_prg_nm_get(),idx_rec,lmt_rec[idx_rec]->id,lmt_rec[idx_rec]->nm_fll,lmt_rec[idx_rec]->rec_dmn_sz,fl_udu_sng);
          /* Two distinct ways to specify MRO are --mro and -d dmn,a,b,c,d,[m,M] */
          if(FLG_MRO) lmt_rec[idx_rec]->flg_mro=True;
          if(lmt_rec[idx_rec]->flg_mro) FLG_MRO=True;
  
          /* NB: nco_cnv_arm_base_time_get() with same nc_id contains OpenMP critical region */
          if(CNV_ARM) base_time_crr=nco_cnv_arm_base_time_get(in_id);
  
          /* Perform various error-checks on input file */
          if(False) (void)nco_fl_cmp_err_chk();
  
          /* This file may be superfluous though valid data will be found in upcoming files */
          if(nco_dbg_lvl >= nco_dbg_std)
            if((lmt_rec[idx_rec]->srt > lmt_rec[idx_rec]->end) && (lmt_rec[idx_rec]->rec_rmn_prv_ssc == 0L))
              (void)fprintf(fp_stdout,"%s: INFO %s (input file %d) is superfluous\n",nco_prg_nm_get(),fl_in,fl_idx);
  
          rec_dmn_sz=lmt_rec[idx_rec]->rec_dmn_sz;
          rec_rmn_prv_ssc=lmt_rec[idx_rec]->rec_rmn_prv_ssc; /* Local copy may be decremented later */
          idx_rec_crr_in= (rec_rmn_prv_ssc > 0L) ? 0L : lmt_rec[idx_rec]->srt;
  
          /* Master while loop over records in current file */
          while(idx_rec_crr_in >= 0L && idx_rec_crr_in < rec_dmn_sz){
            /* Following logic/assumptions built-in to this loop:
  	     idx_rec_crr_in points to valid record before loop is entered
  	     Loop is never entered if this file has no valid records
  	     Much conditional logic needed to prescribe group position and next record
  
  	     Index juggling:
  	     idx_rec_crr_in: Index of current record in current input file (increments by 1 for ssc then srd-ssc ...)
  	     idx_rec_out: Index of record in output file
  	     lmt_rec->rec_rmn_prv_ssc: Structure member, at start of this while loop, contains records remaining-to-be-read to complete subcycle group from previous file. Structure member remains constant until next file is read.
  	     rec_in_cml: Cumulative number of records, read or not, in all files opened so far. Similar to lmt_rec->rec_in_cml but augmented at end of record loop, rather than prior to record loop.
  	     rec_rmn_prv_ssc: Local copy initialized from lmt_rec structure member begins with above, and then is set to and tracks number of records remaining remaining in current group. This means it is decremented from ssc_nbr->0 for each group contained in current file.
  	     rec_usd_cml: Cumulative number of input records used (catenated by ncrcat or operated on by ncra)
  
  	     Flag juggling:
  	     REC_LST_DSR is "sloppy"---it is only set in last input file. If last file(s) is/are superfluous, REC_LST_DSR is never set and final normalization is done outside file and record loops (along with nces normalization). FLG_BFR_NRM indicates these situations and allow us to be "sloppy" in setting REC_LST_DSR. */
  
            /* Last stride in file has distinct index-augmenting behavior */
            if(idx_rec_crr_in >= lmt_rec[idx_rec]->end) REC_SRD_LST=True; else REC_SRD_LST=False;
            /* Even strides commence group beginnings */
            if(rec_rmn_prv_ssc == 0L) REC_FRS_GRP=True; else REC_FRS_GRP=False;
            /* Each group comprises SSC records */
            if(REC_FRS_GRP) rec_rmn_prv_ssc=lmt_rec[idx_rec]->ssc;
            /* Final record triggers normalization regardless of its location within group */
            if(fl_idx == fl_nbr-1 && idx_rec_crr_in == min_int(lmt_rec[idx_rec]->end+lmt_rec[idx_rec]->ssc-1L,rec_dmn_sz-1L)) REC_LST_DSR[idx_rec]=True;
            /* ncra normalization/writing code must know last record in current group (LRCG) for both MRO and non-MRO */
            if(rec_rmn_prv_ssc == 1L) REC_LST_GRP=True; else REC_LST_GRP=False;
  
  	  /* Retrieve this record of weight variable */
  	  if(wgt_nm && (nco_op_typ == nco_op_avg || nco_op_typ == nco_op_mebs))
  	    (void)nco_msa_var_get_rec_trv(in_id,wgt_out,lmt_rec[idx_rec]->nm_fll,idx_rec_crr_in,trv_tbl);
  
            /* Process all variables in current record */
            if(nco_dbg_lvl >= nco_dbg_scl) (void)fprintf(fp_stdout,"%s: INFO Record %ld of %s contributes to output record %ld\n",nco_prg_nm_get(),idx_rec_crr_in,fl_in,idx_rec_out[idx_rec]);
  
  #ifdef _OPENMP
  #pragma omp parallel for private(idx,in_id) shared(CNV_ARM,FLG_BFR_NRM,FLG_MRO,NORMALIZE_BY_WEIGHT,REC_FRS_GRP,REC_LST_DSR,base_time_crr,base_time_srt,fl_idx,fl_in,fl_nbr,fl_out,flg_skp1,flg_skp2,gpe,grp_id,grp_out_fll,grp_out_id,idx_rec,idx_rec_crr_in,idx_rec_out,in_id_arr,lmt_rec,md5,nbr_dmn_fl,nbr_rec,nbr_var_prc,nco_dbg_lvl,nco_op_typ,nco_prg_id,out_id,rcd,rec_usd_cml,trv_tbl,var_out_id,var_prc,var_prc_out,var_prc_typ_pre_prm,var_trv,wgt_arr,wgt_avg,wgt_avg_scl,wgt_nbr,wgt_nm,wgt_out,wgt_scv,fl_udu_sng,ra_bnds_lst,ra_climo_lst,ra_bnds_nbr,ra_climo_nbr,thr_nbr)
  #endif /* !_OPENMP */
            for(idx=0;idx<nbr_var_prc;idx++){
  
              /* Skip variable if does not relate to current record */
              flg_skp1=nco_skp_var(var_prc[idx],lmt_rec[idx_rec]->nm_fll,trv_tbl);
              if(flg_skp1) continue;
  
  	    if(thr_nbr > 1) in_id=in_id_arr[omp_get_thread_num()]; else in_id=in_id_arr[0];
              if(nco_dbg_lvl >= nco_dbg_var) rcd+=nco_var_prc_crr_prn(idx,var_prc[idx]->nm_fll);
              if(nco_dbg_lvl >= nco_dbg_var) (void)fflush(fp_stderr);
  
              /* Obtain variable GTT object using full variable name */
              var_trv=trv_tbl_var_nm_fll(var_prc[idx]->nm_fll,trv_tbl);
              /* Obtain group ID */
              (void)nco_inq_grp_full_ncid(in_id,var_trv->grp_nm_fll,&grp_id);
              /* Edit group name for output */
              grp_out_fll=NULL;
              if(gpe) grp_out_fll=nco_gpe_evl(gpe,var_trv->grp_nm_fll); else grp_out_fll=var_trv->grp_nm_fll;
              /* Obtain output group ID */
              (void)nco_inq_grp_full_ncid(out_id,grp_out_fll,&grp_out_id);
              /* Get variable ID */
              (void)nco_inq_varid(grp_out_id,var_trv->nm,&var_out_id);
              /* Memory management after current extracted group */
              if(gpe && grp_out_fll) grp_out_fll=(char *)nco_free(grp_out_fll);
  
              /* Store the output variable ID */
              var_prc_out[idx]->id=var_out_id;
  
              /* Retrieve this record of this variable. NB: Updates hyperslab start indices to idx_rec_crr_in */
              (void)nco_msa_var_get_rec_trv(in_id,var_prc[idx],lmt_rec[idx_rec]->nm_fll,idx_rec_crr_in,trv_tbl);
  
              if(nco_prg_id == ncra) FLG_BFR_NRM=True; /* [flg] Current output buffers need normalization */
  
              /* Re-base record coordinate and bounds if necessary (e.g., time, time_bnds) */
              /* if(var_prc[idx]->is_crd_var|| nco_is_spc_in_cf_att(grp_id,"bounds",var_prc[idx]->id) || nco_is_spc_in_cf_att(grp_id,"climatology",var_prc[idx]->id)) */
  
              /* Re-base coordinate variable to units of coordinate in the first input file
  	       If record hyperslab indice(s) are double or strings then coordinate variable and limits
  	       are (re)-read earlier by nco_lmt_evl() and if units between files are incompatible 
  	       then ncra will die in that call and not in nco_cln_clc_dbl_var_dff() below */
              if(var_prc[idx]->is_crd_var){
                nco_bool do_rebase=False;
                if(!strcmp(var_prc[idx]->nm,lmt_rec[idx_rec]->nm) ||
  		 ra_lst_chk(ra_bnds_lst,ra_bnds_nbr,lmt_rec[idx_rec]->nm,var_prc[idx]->nm) ||
  		 ra_lst_chk(ra_climo_lst,ra_climo_nbr,lmt_rec[idx_rec]->nm,var_prc[idx]->nm))
  		do_rebase=True;
                if(do_rebase && fl_udu_sng && lmt_rec[idx_rec]->rbs_sng){
                  if(nco_cln_clc_dbl_var_dff(fl_udu_sng,lmt_rec[idx_rec]->rbs_sng,lmt_rec[idx_rec]->lmt_cln,(double*)NULL,var_prc[idx]) != NCO_NOERR){
                    (void)fprintf(fp_stderr,"%s: ERROR in nco_cln_clc_dbl_var_dff() when attempting to re-base variable \"%s\" from units \"%s\" to \"%s\"\n",nco_prg_nm_get(),var_prc[idx]->nm,fl_udu_sng,lmt_rec[idx_rec]->rbs_sng);
                    nco_exit(EXIT_FAILURE);
                  } /* !nco_cln_clc_dbl_var_dff() */
                  //nco_free(fl_udu_sng);
                } /* end !do_rebase */
              } /* !crd_var */
                
              if(nco_prg_id == ncra){
                nco_bool flg_rth_ntl;
                if(!rec_usd_cml[idx_rec] || (FLG_MRO && REC_FRS_GRP)) flg_rth_ntl=True; else flg_rth_ntl=False;
                /* Initialize tally and accumulation arrays when appropriate */
                if(flg_rth_ntl){
                  (void)nco_zero_long(var_prc_out[idx]->sz,var_prc_out[idx]->tally);
                  (void)nco_var_zero(var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->val);
                } /* end if flg_rth_ntl */
  
                if(var_prc[idx]->type == NC_CHAR || var_prc[idx]->type == NC_STRING){
  		/* Do not promote un-averagable types (NC_CHAR, NC_STRING)
  		   Stuff their first record into output buffer regardless of nco_op_typ, and ignore later records (rec_usd_cml > 1)
  		   Temporarily fixes TODO nco941 */
                  if(flg_rth_ntl) nco_opr_drv((long)0L,nco_op_min,var_prc[idx],var_prc_out[idx]);
                }else{
                  /* Convert char, short, long, int, and float types to doubles before arithmetic
  		   Output variable type is "sticky" so only convert on first record */
                  if(flg_rth_ntl) var_prc_out[idx]=nco_typ_cnv_rth(var_prc_out[idx],nco_op_typ);
  		var_prc_typ_pre_prm=var_prc[idx]->type; /* [enm] Type of variable before promotion */
                  var_prc[idx]=nco_var_cnf_typ(var_prc_out[idx]->type,var_prc[idx]);
  
  		/* Weight current record */
  		if((wgt_arr || wgt_nm) && (nco_op_typ == nco_op_avg || nco_op_typ == nco_op_mebs) && !var_prc[idx]->is_crd_var){
  		  if(wgt_arr){
  		    /* Per-file weight */
  		    wgt_scv.type=NC_DOUBLE;
  		    wgt_scv.val.d=wgt_arr[fl_idx];
  		  } /* !wgt_arr */
  		  if(wgt_nm){
  		    wgt_scv.type=wgt_out->type;
  		    wgt_scv.val.d=wgt_out->val.dp[0]; /* Per-record weight */
  		  } /* !wgt_nm */
  		  if(var_prc[idx]->wgt_sum) var_prc[idx]->wgt_crr=wgt_scv.val.d;
  		  nco_scv_cnf_typ(var_prc[idx]->type,&wgt_scv);
  		  if(nco_dbg_lvl > nco_dbg_std && (wgt_nm || wgt_arr)) (void)fprintf(fp_stdout,"wgt_nm = %s, var_nm = %s, idx = %li, typ = %s, wgt_val = %g, wgt_crr = %g, var_val=%g\n",wgt_nm ? wgt_out->nm_fll : "NULL",var_prc[idx]->nm,idx_rec_crr_in,nco_typ_sng(wgt_scv.type),wgt_scv.val.d,var_prc[idx]->wgt_crr,var_prc[idx]->val.dp[0]);
  		  (void)nco_var_scv_mlt(var_prc[idx]->type,var_prc[idx]->sz,var_prc[idx]->has_mss_val,var_prc[idx]->mss_val,var_prc[idx]->val,&wgt_scv);
  		  if(wgt_nm && var_prc[idx]->has_mss_val){
  		    (void)fprintf(fp_stdout,"%s: ERROR %s -w wgt_nm does not yet work on variables that contain missing values and variable %s contains a missing value attribute. This is TODO nco1124. %s will now quit rather than compute possibly erroneous values. HINT: Restrict the %s -w wgt_nm operation to variables with no missing value attributes.\n",nco_prg_nm_get(),nco_prg_nm_get(),nco_prg_nm_get(),var_prc[idx]->nm,nco_prg_nm_get());
  		    nco_exit(EXIT_FAILURE);
  		  } /* !wgt_nm */
  		  /* Increment running total of wgt_out after its application to last processed variable for this record */
  		  if(wgt_nm && (idx == nbr_var_prc-1)){
  		    if(flg_rth_ntl) nco_opr_drv((long)0L,nco_op_typ,wgt_out,wgt_avg); else nco_opr_drv((long)1L,nco_op_typ,wgt_out,wgt_avg);
  		  } /* !wgt_nm */
  		} /* !wgt */
  		/* Perform arithmetic operations: avg, min, max, ttl, ... */
  		if(flg_rth_ntl) nco_opr_drv((long)0L,nco_op_typ,var_prc[idx],var_prc_out[idx]); else nco_opr_drv((long)1L,nco_op_typ,var_prc[idx],var_prc_out[idx]);
  	      } /* end else */ 
              } /* end if ncra */
  
              /* All processed variables contain record dimension and both ncrcat and ncra write records singly */
              var_prc_out[idx]->srt[rec_dmn_idx]=var_prc_out[idx]->end[rec_dmn_idx]=idx_rec_out[idx_rec];
              var_prc_out[idx]->cnt[rec_dmn_idx]=1L;
  
              /* Append current record to output file */
              if(nco_prg_id == ncrcat){
                /* Replace this time_offset value with time_offset from initial file base_time */
                if(CNV_ARM && !strcmp(var_prc[idx]->nm,"time_offset")) var_prc[idx]->val.dp[0]+=(base_time_crr-base_time_srt);
  	      if(var_trv->ppc != NC_MAX_INT){
  		if(var_trv->flg_nsd) (void)nco_ppc_bitmask(var_trv->ppc,var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc[idx]->val); else (void)nco_ppc_around(var_trv->ppc,var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc[idx]->val);
  	      } /* endif ppc */
  	      if(nco_is_xcp(var_trv->nm)) nco_xcp_prc(var_trv->nm,var_prc_out[idx]->type,var_prc_out[idx]->sz,(char *)var_prc[idx]->val.vp);
  #ifdef _OPENMP
  #pragma omp critical
  #endif /* _OPENMP */
                if(var_prc_out[idx]->sz_rec > 1L) (void)nco_put_vara(grp_out_id,var_prc_out[idx]->id,var_prc_out[idx]->srt,var_prc_out[idx]->cnt,var_prc[idx]->val.vp,var_prc_out[idx]->type); else (void)nco_put_var1(grp_out_id,var_prc_out[idx]->id,var_prc_out[idx]->srt,var_prc[idx]->val.vp,var_prc_out[idx]->type);
                /* Perform MD5 digest of input and output data if requested */
                if(md5) (void)nco_md5_chk(md5,var_prc_out[idx]->nm,var_prc_out[idx]->sz*nco_typ_lng(var_prc_out[idx]->type),grp_out_id,var_prc_out[idx]->srt,var_prc_out[idx]->cnt,var_prc[idx]->val.vp);
              } /* end if ncrcat */
  
              /* Warn if record coordinate, if any, is not monotonic */
              if(nco_prg_id == ncrcat && var_prc[idx]->is_crd_var) (void)rec_crd_chk(var_prc[idx],fl_in,fl_out,idx_rec_crr_in,idx_rec_out[idx_rec]);
              /* Convert missing_value, if any, back to unpacked or unpromoted type
  	       Otherwise missing_value will be double-promoted when next record read in nco_msa_var_get_trv()
  	       Do not convert after last record otherwise normalization fails
  	       due to wrong missing_value type (needs promoted type, not unpacked type)
  	       20140930: This is (too?) confusing and hard-to-follow, a better solution is to add a field mss_val_typ 
  	       to var_sct and then separately and explicitly track types of both val and mss_val members. */
              if(var_prc[idx]->has_mss_val && /* If there is a missing value and... */
  	       !REC_LST_DSR[idx_rec] && /* ...no more records will be read (thus no more calls to nco_msa_var_get_trv()) and... */
  	       !(var_prc[idx]->pck_dsk && var_prc_typ_pre_prm != var_prc_out[idx]->type) && /* Exclude conversion on situations like regression test ncra #32 */
  	       var_prc[idx]->type != var_prc[idx]->typ_upk) /* ...the variable was auto-promoted (e.g., --dbl) then */
  	      var_prc[idx]=nco_cnv_mss_val_typ(var_prc[idx],var_prc[idx]->typ_upk); /* Demote missing value */
  
              /* Free current input buffer */
              var_prc[idx]->val.vp=nco_free(var_prc[idx]->val.vp);
            } /* end (OpenMP parallel for) loop over variables */
  
            if(nco_prg_id == ncra && ((FLG_MRO && REC_LST_GRP) || REC_LST_DSR[idx_rec])){
              /* Normalize, multiply, etc where necessary: ncra and nces normalization blocks are identical, 
  	       except ncra normalizes after every ssc records, while nces normalizes once, after files loop. 
  	       20131210: nco_cnv_mss_val_typ() can cause type of var_prc to be out-of-sync with var_prc_out
  	       nco_cnv_mss_val_typ() above works correctly for case of packing/unpacking, not for rth_dbl
  	       Options:
  	       1. Avoid nco_cnv_mss_val_typ() above if rth_dbl is invoked. Keep it for packing.
  	       2. In nco_opr_nrm() below, use mss_val from var_prc_out not var_prc
  	       Problem is var_prc[idx]->mss_val is typ_upk while var_prc_out is type, so normalization
  	       sets missing var_prc_out value to var_prc[idx]->mss_val read as type */
              (void)nco_opr_nrm(nco_op_typ,nbr_var_prc,var_prc,var_prc_out,lmt_rec[idx_rec]->nm_fll,trv_tbl);
              FLG_BFR_NRM=False; /* [flg] Current output buffers need normalization */
  
  	    for(idx=0;idx<nbr_var_prc;idx++){
  	      if(var_prc[idx]->wgt_sum){
  		if(NORMALIZE_BY_WEIGHT) (void)nco_var_nrm_wgt(var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc_out[idx]->tally,var_prc_out[idx]->wgt_sum,var_prc_out[idx]->val);
  	      } /* !wgt_sum */
  	    } /* !idx */
  	      
  	    if(wgt_nm && (nco_op_typ == nco_op_avg || nco_op_typ == nco_op_mebs)){
  	      /* Compute mean of per-record weight, by normalizing running sum of weight by tally
  		 Then normalize all numerical record variables by mean of per-record weight
  		 Still ill-defined when MRO is invoked with --wgt 
  		 Same logic applies in two locations in this code:
  		 1. During SSC normalization inside record loop when REC_LST_DSR is true
  		 2. After file loop for nces, and for ncra with superfluous trailing files */
  	      wgt_avg_scv.type=NC_DOUBLE;
  	      wgt_avg->val.dp[0]/=wgt_out->tally[0]; /* NB: wgt_avg tally is kept in wgt_out */
  	      wgt_avg_scv.val.d=wgt_avg->val.dp[0];
  	      for(idx=0;idx<nbr_var_prc;idx++){
  		if(var_prc_out[idx]->is_crd_var || var_prc[idx]->type == NC_CHAR || var_prc[idx]->type == NC_STRING) continue;
  		nco_scv_cnf_typ(var_prc_out[idx]->type,&wgt_avg_scv);
  		if(NORMALIZE_BY_WEIGHT) (void)nco_var_scv_dvd(var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc_out[idx]->val,&wgt_avg_scv);
  	      } /* end loop over var */
  	    } /* !wgt_nm */
  
              /* Copy averages to output file */
              for(idx=0;idx<nbr_var_prc;idx++){
  
                /* Skip variables that do not contain current record dimension */
                flg_skp2=nco_skp_var(var_prc[idx],lmt_rec[idx_rec]->nm_fll,trv_tbl);
                if(flg_skp2) continue;
  
                /* Obtain variable GTT object using full variable name */
                var_trv=trv_tbl_var_nm_fll(var_prc_out[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);
  
                var_prc_out[idx]=nco_var_cnf_typ(var_prc_out[idx]->typ_upk,var_prc_out[idx]);
                /* Packing/Unpacking */
                if(nco_pck_plc == nco_pck_plc_all_new_att) var_prc_out[idx]=nco_put_var_pck(grp_out_id,var_prc_out[idx],nco_pck_plc);
  	      if(var_trv->ppc != NC_MAX_INT){
  		if(var_trv->flg_nsd) (void)nco_ppc_bitmask(var_trv->ppc,var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc_out[idx]->val); else (void)nco_ppc_around(var_trv->ppc,var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc_out[idx]->val);
  	      } /* endif ppc */
  	      if(nco_is_xcp(var_trv->nm)) nco_xcp_prc(var_trv->nm,var_prc_out[idx]->type,var_prc_out[idx]->sz,(char *)var_prc_out[idx]->val.vp);
                if(var_prc_out[idx]->nbr_dim == 0) (void)nco_put_var1(grp_out_id,var_prc_out[idx]->id,var_prc_out[idx]->srt,var_prc_out[idx]->val.vp,var_prc_out[idx]->type); else (void)nco_put_vara(grp_out_id,var_prc_out[idx]->id,var_prc_out[idx]->srt,var_prc_out[idx]->cnt,var_prc_out[idx]->val.vp,var_prc_out[idx]->type);
              } /* end loop over idx */
              idx_rec_out[idx_rec]++; /* [idx] Index of current record in output file (0 is first, ...) */
            } /* end if normalize and write */
  
            /* Prepare indices and flags for next iteration */
            if(nco_prg_id == ncrcat) idx_rec_out[idx_rec]++; /* [idx] Index of current record in output file (0 is first, ...) */
            rec_usd_cml[idx_rec]++; /* [nbr] Cumulative number of input records used (catenated by ncrcat or operated on by ncra) */
            if(nco_dbg_lvl >= nco_dbg_var) (void)fprintf(fp_stderr,"\n");
  
            /* Finally, set index for next record or get outta' Dodge */
            if(REC_SRD_LST){
              /* Last index depends on whether user-specified end was exact, sloppy, or caused truncation */
              long end_max_crr;
              end_max_crr=min_lng(lmt_rec[idx_rec]->idx_end_max_abs-rec_in_cml[idx_rec],min_lng(lmt_rec[idx_rec]->end+lmt_rec[idx_rec]->ssc-1L,rec_dmn_sz-1L));
              if(--rec_rmn_prv_ssc > 0L && idx_rec_crr_in < end_max_crr) idx_rec_crr_in++; else break;
            }else{ /* !REC_SRD_LST */
              if(--rec_rmn_prv_ssc > 0L) idx_rec_crr_in++; else idx_rec_crr_in+=lmt_rec[idx_rec]->srd-lmt_rec[idx_rec]->ssc+1L;
            } /* !REC_SRD_LST */
  
          } /* end idx_rec_crr_in master while loop over records in current file */
  
          rec_in_cml[idx_rec]+=rec_dmn_sz; /* [nbr] Cumulative number of records in all files opened so far */
          lmt_rec[idx_rec]->rec_rmn_prv_ssc=rec_rmn_prv_ssc;
  
          if(fl_idx == fl_nbr-1){
            /* Warn if other than number of requested records were read */
            if(lmt_rec[idx_rec]->lmt_typ == lmt_dmn_idx && lmt_rec[idx_rec]->is_usr_spc_min && lmt_rec[idx_rec]->is_usr_spc_max){
              long ssc_grp_nbr_max; /* [nbr] Subcycle groups that start within range */
              long rec_nbr_rqs; /* Number of records user requested */
              long rec_nbr_rqs_max; /* [nbr] Records that would be used by ssc_grp_nbr_max groups */
              long rec_nbr_spn_act; /* [nbr] Records available within user-specified range */
              long rec_nbr_spn_max; /* [nbr] Minimum record number spanned by ssc_grp_nbr_max groups */
              long rec_nbr_trn; /* [nbr] Records truncated in last group */
              long srd_nbr_flr; /* [nbr] Whole strides that fit within specified range */
              /* Number of whole strides that fit within specified range */
              srd_nbr_flr=(lmt_rec[idx_rec]->max_idx-lmt_rec[idx_rec]->min_idx)/lmt_rec[idx_rec]->srd;
              ssc_grp_nbr_max=1L+srd_nbr_flr;
              /* Number of records that would be used by N groups */
              rec_nbr_rqs_max=ssc_grp_nbr_max*lmt_rec[idx_rec]->ssc;
              /* Minimum record number spanned by N groups of size D is N-1 strides, plus D-1 trailing members of last group */
              rec_nbr_spn_max=lmt_rec[idx_rec]->srd*(ssc_grp_nbr_max-1L)+lmt_rec[idx_rec]->ssc;
              /* Actual number of records available within range */
              rec_nbr_spn_act=1L+lmt_rec[idx_rec]->max_idx-lmt_rec[idx_rec]->min_idx;
              /* Number truncated in last group */
              rec_nbr_trn=max_int(rec_nbr_spn_max-rec_nbr_spn_act,0L);
              /* Records requested is maximum minus any truncated in last group */
              rec_nbr_rqs=rec_nbr_rqs_max-rec_nbr_trn;
              if(rec_nbr_rqs != rec_usd_cml[idx_rec]) (void)fprintf(fp_stdout,"%s: WARNING User requested %li records but only %li were found and used\n",nco_prg_nm_get(),rec_nbr_rqs,rec_usd_cml[idx_rec]);
            } /* end if */
            /* ... and die if no records were read ... */
            if(rec_usd_cml[idx_rec] <= 0){
              (void)fprintf(fp_stdout,"%s: ERROR No records lay within specified hyperslab\n",nco_prg_nm_get());
              nco_exit(EXIT_FAILURE);
            } /* end if */
          } /* end if */
  
          if(fl_udu_sng) fl_udu_sng=(char*)nco_free(fl_udu_sng);
  
          ra_lst_free(ra_bnds_lst,ra_bnds_nbr);
          ra_lst_free(ra_climo_lst,ra_climo_nbr);
        } /* end idx_rec loop over different record variables to process */
  
        if(flg_cb && nco_prg_id == ncra){
  	/* Obtain climatology bounds from input file
  	   20160824: Currently dmn_srt_srt and dmn_srt_end indices are 0 and 1, respectively
  	   This means values are always/only taken for first record in input file
  	   Thus climatology_bounds are only correct for input files with single timestep
  	   To fix this requires updating dmn_srt_srt and dmn_srt_end with correct indices
  	   Correct indices must account for multiple input records per file and hyperslabbing (e.g., -d time,3,5) */
  	int var_id_in;
  	double val_dbl;
  	var_id_in= cb->tm_bnd_in ? cb->tm_bnd_id_in : cb->clm_bnd_id_in;
  	rcd=nco_get_var1(in_id,var_id_in,cb->dmn_srt_srt,&val_dbl,(nc_type)NC_DOUBLE);
  	if(fl_idx == 0) cb->val[0]=val_dbl;
  	if(val_dbl < cb->val[0]) cb->val[0]=val_dbl;
  	rcd=nco_get_var1(in_id,var_id_in,cb->dmn_srt_end,&val_dbl,(nc_type)NC_DOUBLE);
  	if(val_dbl > cb->val[1]) cb->val[1]=val_dbl;
        } /* !flg_cb */
  
        /* End ncra, ncrcat section */
      }else if(nco_prg_id == ncfe){ /* ncfe */
  
  #ifdef _OPENMP
  #pragma omp parallel for private(idx,in_id) shared(nco_dbg_lvl,fl_idx,FLG_BFR_NRM,in_id_arr,nbr_var_prc,nco_op_typ,rcd,var_prc,var_prc_out,nbr_dmn_fl,trv_tbl,var_trv,grp_id,gpe,grp_out_fll,grp_out_id,out_id,var_out_id,thr_nbr)
  #endif /* !_OPENMP */
        for(idx=0;idx<nbr_var_prc;idx++){ /* Process all variables in current file */
  
  	if(thr_nbr > 1) in_id=in_id_arr[omp_get_thread_num()]; else in_id=in_id_arr[0];
          if(nco_dbg_lvl >= nco_dbg_var) rcd+=nco_var_prc_crr_prn(idx,var_prc[idx]->nm);
          if(nco_dbg_lvl >= nco_dbg_var) (void)fflush(fp_stderr);
  
          /* Obtain variable GTT object using full variable name */
          var_trv=trv_tbl_var_nm_fll(var_prc[idx]->nm_fll,trv_tbl);
          /* Obtain group ID */
          (void)nco_inq_grp_full_ncid(in_id,var_trv->grp_nm_fll,&grp_id);
          /* 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);
  
          /* Store the output variable ID */
          var_prc_out[idx]->id=var_out_id;
  
          /* Retrieve variable from disk into memory */
          (void)nco_msa_var_get_trv(in_id,var_prc[idx],trv_tbl);
  
          /* Convert char, short, long, int, and float types to doubles before arithmetic
  	   Output variable type is "sticky" so only convert on first record */
          if(fl_idx == 0) var_prc_out[idx]=nco_typ_cnv_rth(var_prc_out[idx],nco_op_typ);
          var_prc[idx]=nco_var_cnf_typ(var_prc_out[idx]->type,var_prc[idx]);
          /* Perform arithmetic operations: avg, min, max, ttl, ... */ /* Note: fl_idx not rec_usd_cml! */
          nco_opr_drv(fl_idx,nco_op_typ,var_prc[idx],var_prc_out[idx]);
          FLG_BFR_NRM=True; /* [flg] Current output buffers need normalization */
  
          /* Free current input buffer */
          var_prc[idx]->val.vp=nco_free(var_prc[idx]->val.vp);
        } /* end (OpenMP parallel for) loop over idx */
        /* End ncfe section */
      }else if(nco_prg_id == ncge){ /* ncge */
  
        trv_tbl_sct *trv_tbl1;    /* [lst] Traversal table (needed for multi-file cases)  */
  
        /* Initialize traversal table */ 
        trv_tbl_init(&trv_tbl1); 
  
        /* Construct GTT using current file ID */
        (void)nco_bld_trv_tbl(in_id,trv_pth,lmt_nbr,lmt_arg,aux_nbr,aux_arg,MSA_USR_RDR,FORTRAN_IDX_CNV,grp_lst_in,grp_lst_in_nbr,var_lst_in,var_lst_in_nbr,EXTRACT_ALL_COORDINATES,GRP_VAR_UNN,False,EXCLUDE_INPUT_LIST,EXTRACT_ASSOCIATED_COORDINATES,EXTRACT_CLL_MSR,EXTRACT_FRM_TRM,nco_pck_plc_nil,&flg_dne,trv_tbl1);
  
        /* Were all user-specified dimensions found? */ 
        (void)nco_chk_dmn(lmt_nbr,flg_dne);  
  
        /* Loop over ensembles in current file */
        for(int idx_nsm=0;idx_nsm<trv_tbl->nsm_nbr;idx_nsm++){ 
  
          if(nco_dbg_lvl > nco_dbg_std) (void)fprintf(stdout,"%s: ensemble %d: %s\n",nco_prg_nm_get(),idx_nsm,trv_tbl->nsm[idx_nsm].grp_nm_fll_prn);
  
          int mbr_srt=trv_tbl->nsm[idx_nsm].mbr_srt;
          int mbr_end=trv_tbl->nsm[idx_nsm].mbr_end;
  
          /* Loop over ensemble members in current file (use start and end members, multi-file cases) */
          for(int idx_mbr=mbr_srt;idx_mbr<mbr_end;idx_mbr++){
  
            /* Loop over all variables */
            for(int idx_prc=0;idx_prc<nbr_var_prc;idx_prc++){ 
  
              /* Obtain variable GTT object for member variable in ensemble */
              var_trv=trv_tbl_var_nm_fll(var_prc[idx_prc]->nm_fll,trv_tbl);
              assert(var_trv);
  
              /* Skip if from different ensembles */
              if(strcmp(var_trv->nsm_nm,trv_tbl->nsm[idx_nsm].grp_nm_fll_prn)) continue;
  
              /* Build new variable name */
              char *grp_nm_fll=trv_tbl->nsm[idx_nsm].mbr[idx_mbr].mbr_nm_fll; 
              char *var_nm_fll=nco_bld_nm_fll(grp_nm_fll,var_prc[idx_prc]->nm);;
              char *nm_fll=strdup(var_prc[idx_prc]->nm_fll);
  
              var_prc[idx_prc]->nm_fll=(char *)nco_free(var_prc[idx_prc]->nm_fll);
              var_prc[idx_prc]->nm_fll=nco_bld_nm_fll(grp_nm_fll,var_prc[idx_prc]->nm);
              if(nco_dbg_lvl > nco_dbg_std) (void)fprintf(fp_stdout,"%s:\t variable <%s>\n",nco_prg_nm_get(),var_prc[idx_prc]->nm_fll);
  
              /* Obtain group ID */
              (void)nco_inq_grp_full_ncid(in_id,grp_nm_fll,&grp_id);
              (void)nco_var_mtd_refresh(grp_id,var_prc[idx_prc]);
  
              /* Retrieve variable from disk into memory. NB: Using table in file loop */
              (void)nco_msa_var_get_trv(in_id,var_prc[idx_prc],trv_tbl1);
  
              /* Convert char, short, long, int, and float types to doubles before arithmetic
  	       Output variable type is "sticky" so only convert on first member */
              if(fl_idx == 0 && idx_mbr == 0) var_prc_out[idx_prc]=nco_typ_cnv_rth(var_prc_out[idx_prc],nco_op_typ);
              var_prc[idx_prc]=nco_var_cnf_typ(var_prc_out[idx_prc]->type,var_prc[idx_prc]);
              /* Perform arithmetic operations: avg, min, max, ttl, ... */
              nco_opr_drv(fl_idx+idx_mbr,nco_op_typ,var_prc[idx_prc],var_prc_out[idx_prc]);
              FLG_BFR_NRM=True; /* [flg] Current output buffers need normalization */
  
              /* Put old name back */
              var_prc[idx_prc]->nm_fll=(char *)nco_free(var_prc[idx_prc]->nm_fll);
              var_prc[idx_prc]->nm_fll=strdup(nm_fll);
  
              /* Free current input buffer */
              var_prc[idx_prc]->val.vp=nco_free(var_prc[idx_prc]->val.vp);
  
              /* Free built variable name */
              var_nm_fll=(char *)nco_free(var_nm_fll);
              nm_fll=(char *)nco_free(nm_fll);
  
            } /* end loop over var_prc */
          } /* end loop over mbr */
  
        } /* !idx_mbr */
  
        (void)trv_tbl_free(trv_tbl1);
  
      } /* End ncge section */
  
      /* For ncge, save helpful metadata for later handling by ncbo */
      if(nco_prg_id == ncge && fl_idx == 0) (void)nco_nsm_wrt_att(in_id,out_id,gpe,trv_tbl);   
  
      if(nco_dbg_lvl >= nco_dbg_scl) (void)fprintf(fp_stderr,"\n");
  
      /* Close input netCDF file */
      for(thr_idx=0;thr_idx<thr_nbr;thr_idx++) nco_close(in_id_arr[thr_idx]);
  
      /* Dispose local copy of file */
      if(FL_RTR_RMT_LCN && RM_RMT_FL_PST_PRC) (void)nco_fl_rm(fl_in);
  
      /* Are all our data tanks already full? */
      if(nco_prg_id == ncra || nco_prg_id == ncrcat){
        for(idx_rec=0;idx_rec<nbr_rec;idx_rec++){
  	if(!flg_input_complete[idx_rec]){
  	  if((flg_input_complete[idx_rec]=lmt_rec[idx_rec]->flg_input_complete)){
  	    /* NB: TODO nco1066 move input_complete break to precede record loop but remember to close open filehandles */
  	    /* 20131209: Rewritten so file skipped only once all record dimensions have flg_input_complete
  	       Warnings about superfluous files printed only once per dimension
  	       fxm: use flg_input_complete[idx_rec] to skip completed entries in main record dimension loop above */
  	    if(nco_dbg_lvl >= nco_dbg_std) (void)fprintf(fp_stderr,"%s: INFO All requested records for record dimension #%d (%s) were found within the first %d input file%s, next file was opened then skipped, and remaining %d input file%s need not be opened\n",nco_prg_nm_get(),idx_rec,lmt_rec[idx_rec]->nm_fll,fl_idx,(fl_idx == 1) ? "" : "s",fl_nbr-fl_idx-1,(fl_nbr-fl_idx-1 == 1) ? "" : "s");
  	    flg_input_complete_nbr++;
  	  } /* endif superfluous */
  	} /* endif not already known to be complete */
        } /* end loop over record dimensions */
        /* Once all record dimensions are complete, break-out of file loop */
        if(flg_input_complete_nbr == nbr_rec) break;
      } /* endif ncra || ncrcat */
  
    } /* end loop over fl_idx */
  
    /* Subcycle argument warning */
    if(nco_prg_id == ncra || nco_prg_id == ncrcat){ /* fxm: Remove this or make DBG when crd_val SSC/MRO is predictable? */
      for(idx_rec=0;idx_rec<nbr_rec;idx_rec++){
        /* Check subcycle for each record */
        if(lmt_rec[idx_rec]->ssc != 1L && (lmt_rec[idx_rec]->lmt_typ == lmt_crd_val || lmt_rec[idx_rec]->lmt_typ == lmt_udu_sng)){
          (void)fprintf(stderr,"\n%s: WARNING Subcycle argument SSC used in hyperslab specification for %s which will be determined based on coordinate values rather than dimension indices. The behavior of the subcycle hyperslab argument is ambiguous for coordinate-based hyperslabs---it could mean select the first SSC elements that are within the min and max coordinate values beginning with each strided point, or it could mean always select the first _consecutive_ SSC elements beginning with each strided point (regardless of their values relative to min and max). For such hyperslabs, NCO adopts the latter definition and always selects the group of SSC records beginning with each strided point. Strided points are themselves guaranteed to be within the min and max coordinates, though the subsequent members of each group are not. This is only the case when the record coordinate is not monotonic. The record coordinate is usually monotonic, so unpleasant surprises are only expected in corner cases unlikely to affect the majority of users.\n",nco_prg_nm_get(),lmt_rec[idx_rec]->nm);
        } /* Check subcycle for each record */
      } /* !idx_rec */
    } /* Subcycle argument warning */
  
    /* Normalize, multiply, etc where necessary: ncra and nces normalization blocks are identical, 
       except ncra normalizes after every SSC records, while nces normalizes once, after all files.
       Occassionally last input file(s) is/are superfluous so REC_LST_DSR never set
       In such cases FLG_BFR_NRM is still true, indicating ncra still needs normalization
       FLG_BFR_NRM is always true here for ncfe and ncge */
    if(FLG_BFR_NRM){
      (void)nco_opr_nrm(nco_op_typ,nbr_var_prc,var_prc,var_prc_out,(char *)NULL,(trv_tbl_sct *)NULL);
      
      for(idx=0;idx<nbr_var_prc;idx++){
        if(var_prc[idx]->wgt_sum){
  	if(NORMALIZE_BY_WEIGHT) (void)nco_var_nrm_wgt(var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc_out[idx]->tally,var_prc_out[idx]->wgt_sum,var_prc_out[idx]->val);
        } /* !wgt_sum */
      } /* !idx */
  
      if(wgt_nm && (nco_op_typ == nco_op_avg || nco_op_typ == nco_op_mebs)){
        /* Compute mean of per-record weight, by normalizing running sum of weight by tally
  	 Then normalize all numerical record variables by mean of per-record weight
  	 Still ill-defined when MRO is invoked with --wgt 
  	 Same logic applies in two locations in this code:
  	 1. During SSC normalization inside record loop when REC_LST_DSR is true
  	 2. After file loop for nces, and for ncra with superfluous trailing files */
        wgt_avg_scv.type=NC_DOUBLE;
        wgt_avg->val.dp[0]/=wgt_out->tally[0]; /* NB: wgt_avg tally is kept in wgt_out */
        wgt_avg_scv.val.d=wgt_avg->val.dp[0];
        for(idx=0;idx<nbr_var_prc;idx++){
  	if(var_prc_out[idx]->is_crd_var || var_prc[idx]->type == NC_CHAR || var_prc[idx]->type == NC_STRING) continue;
  	nco_scv_cnf_typ(var_prc_out[idx]->type,&wgt_avg_scv);
  	if(NORMALIZE_BY_WEIGHT) (void)nco_var_scv_dvd(var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc_out[idx]->val,&wgt_avg_scv);
        } /* end loop over var */
      } /* !wgt_nm */
    } /* !FLG_BFR_NRM */
  
    /* Manually fix YYMMDD date which was mangled by averaging */
    if(cnv->CCM_CCSM_CF && nco_prg_id == ncra) (void)nco_cnv_ccm_ccsm_cf_date(grp_out_id,var_out,xtr_nbr);
  
    /* Add time variable to output file
       NB: nco_cnv_arm_time_install() contains OpenMP critical region */
    if(CNV_ARM && nco_prg_id == ncrcat) (void)nco_cnv_arm_time_install(grp_out_id,base_time_srt,dfl_lvl);
  
    /* Copy averages to output file for ncfe and ncge always and for ncra when trailing file(s) was/were superfluous */
    if(FLG_BFR_NRM){
      for(idx=0;idx<nbr_var_prc;idx++){
  
        /* Obtain variable GTT object using full variable name */
        var_trv=trv_tbl_var_nm_fll(var_prc_out[idx]->nm_fll,trv_tbl);
  
        /* For ncge, group to save is ensemble parent group */
        if(nco_prg_id == ncge){
  
          /* Check if suffix needed. Appends to default name */
          if(trv_tbl->nsm_sfx){
            /* Define (append) then use and forget new name */
            char *nm_fll_sfx=nco_bld_nsm_sfx(var_trv->grp_nm_fll_prn,trv_tbl);
            /* Use new name */
            if(gpe) grp_out_fll=nco_gpe_evl(gpe,nm_fll_sfx); else grp_out_fll=(char *)strdup(nm_fll_sfx);
            nm_fll_sfx=(char *)nco_free(nm_fll_sfx);
          }else{ /* Non suffix case */
            if(gpe) grp_out_fll=nco_gpe_evl(gpe,var_trv->nsm_nm); else grp_out_fll=(char *)strdup(var_trv->nsm_nm);
          } /* !trv_tbl->nsm_sfx */
  
        }else if(nco_prg_id == ncfe){
          /* 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);
        } /* end else */
  
        /* Obtain output group ID */
        (void)nco_inq_grp_full_ncid(out_id,grp_out_fll,&grp_out_id);
        /* Get output variable ID */
        (void)nco_inq_varid(grp_out_id,var_prc_out[idx]->nm,&var_out_id);   
        /* Store the output variable ID */
        var_prc_out[idx]->id=var_out_id;
  
        var_prc_out[idx]=nco_var_cnf_typ(var_prc_out[idx]->typ_upk,var_prc_out[idx]);
        /* Packing/Unpacking */
        if(nco_pck_plc == nco_pck_plc_all_new_att) var_prc_out[idx]=nco_put_var_pck(grp_out_id,var_prc_out[idx],nco_pck_plc);
        if(var_trv->ppc != NC_MAX_INT){
  	if(var_trv->flg_nsd) (void)nco_ppc_bitmask(var_trv->ppc,var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc_out[idx]->val); else (void)nco_ppc_around(var_trv->ppc,var_prc_out[idx]->type,var_prc_out[idx]->sz,var_prc_out[idx]->has_mss_val,var_prc_out[idx]->mss_val,var_prc_out[idx]->val);
        } /* endif ppc */
        if(var_prc_out[idx]->nbr_dim == 0) (void)nco_put_var1(grp_out_id,var_prc_out[idx]->id,var_prc_out[idx]->srt,var_prc_out[idx]->val.vp,var_prc_out[idx]->type); else (void)nco_put_vara(grp_out_id,var_prc_out[idx]->id,var_prc_out[idx]->srt,var_prc_out[idx]->cnt,var_prc_out[idx]->val.vp,var_prc_out[idx]->type);
  
      } /* end loop over idx */
    } /* end if ncfe and ncge */
  
    /* Free averaging, tally, and weight buffers */
    if(nco_prg_id == ncra || nco_prg_id == ncfe || nco_prg_id == ncge){
      for(idx=0;idx<nbr_var_prc;idx++){
        if((wgt_arr || wgt_nm) && var_prc[idx]->has_mss_val) var_prc_out[idx]->wgt_sum=var_prc[idx]->wgt_sum=(double *)nco_free(var_prc[idx]->wgt_sum);
        var_prc_out[idx]->tally=var_prc[idx]->tally=(long *)nco_free(var_prc[idx]->tally);
        var_prc_out[idx]->val.vp=nco_free(var_prc_out[idx]->val.vp);
      } /* end loop over idx */
    } /* endif ncra || nces */
  
    if(flg_cb && nco_prg_id == ncra) rcd=nco_put_var(out_id,cb->clm_bnd_id_out,cb->val,(nc_type)NC_DOUBLE);
  
    if(flg_cb && (cb->bnd2clm || cb->clm2bnd)){
      /* Rename time bounds as climatology bounds, or visa-versa
         Otherwise wrong bounds will remain orphaned in output file
         Also, this ensures same dimensions are used
         Rename at end of procedure so that traversal table does not get out-of-sync
         Avoiding renaming would mean creating the new and deleting the old bounds variable 
         That would entail significant modifications to traversal table logic
         Renaming seems simpler and less error prone */
      rcd+=nco_redef(out_id);
      if(cb->bnd2clm) rcd+=nco_rename_var(out_id,cb->tm_bnd_id_out,cb->clm_bnd_nm);
      if(cb->clm2bnd) rcd+=nco_rename_var(out_id,cb->clm_bnd_id_out,cb->tm_bnd_nm);
      rcd+=nco_enddef(out_id);
    } /* !flg_cb */
  
    /* Close output file and move it from temporary to permanent location */
    (void)nco_fl_out_cls(fl_out,fl_out_tmp,out_id);
  
    /* Clean memory unless dirty memory allowed */
    if(flg_mmr_cln){
      /* NCO-generic clean-up */
      /* Free individual strings/arrays */
      if(cmd_ln) cmd_ln=(char *)nco_free(cmd_ln);
      if(cnk_map_sng) cnk_map_sng=(char *)nco_free(cnk_map_sng);
      if(cnk_plc_sng) cnk_plc_sng=(char *)nco_free(cnk_plc_sng);
      if(fl_in) fl_in=(char *)nco_free(fl_in);
      if(fl_out) fl_out=(char *)nco_free(fl_out);
      if(fl_out_tmp) fl_out_tmp=(char *)nco_free(fl_out_tmp);
      if(fl_pth) fl_pth=(char *)nco_free(fl_pth);
      if(fl_pth_lcl) fl_pth_lcl=(char *)nco_free(fl_pth_lcl);
      if(in_id_arr) in_id_arr=(int *)nco_free(in_id_arr);
      if(wgt_arr) wgt_arr=(double *)nco_free(wgt_arr);
      if(wgt_nm) wgt_nm=(char *)nco_free(wgt_nm);
      /* Free lists of strings */
      if(fl_lst_in && !fl_lst_abb) 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);
      if(var_lst_in_nbr > 0) var_lst_in=nco_sng_lst_free(var_lst_in,var_lst_in_nbr);
      if(wgt_nbr > 0) wgt_lst_in=nco_sng_lst_free(wgt_lst_in,wgt_nbr);
      /* Free limits */
      for(idx=0;idx<aux_nbr;idx++) aux_arg[idx]=(char *)nco_free(aux_arg[idx]);
      for(idx=0;idx<lmt_nbr;idx++) lmt_arg[idx]=(char *)nco_free(lmt_arg[idx]);
      for(idx=0;idx<ppc_nbr;idx++) ppc_arg[idx]=(char *)nco_free(ppc_arg[idx]);
      /* Free chunking information */
      for(idx=0;idx<cnk_nbr;idx++) cnk_arg[idx]=(char *)nco_free(cnk_arg[idx]);
      if(cnk_nbr > 0 && (fl_out_fmt == NC_FORMAT_NETCDF4 || fl_out_fmt == NC_FORMAT_NETCDF4_CLASSIC)) cnk.cnk_dmn=(cnk_dmn_sct **)nco_cnk_lst_free(cnk.cnk_dmn,cnk_nbr);
      /* Free dimension lists */
      if(nbr_dmn_xtr > 0) dim=nco_dmn_lst_free(dim,nbr_dmn_xtr);
      if(nbr_dmn_xtr > 0) dmn_out=nco_dmn_lst_free(dmn_out,nbr_dmn_xtr);
      /* Free variable lists */
      if(xtr_nbr > 0) var=nco_var_lst_free(var,xtr_nbr);
      if(xtr_nbr > 0) var_out=nco_var_lst_free(var_out,xtr_nbr);
      var_prc=(var_sct **)nco_free(var_prc);
      var_prc_out=(var_sct **)nco_free(var_prc_out);
      var_fix=(var_sct **)nco_free(var_fix);
      var_fix_out=(var_sct **)nco_free(var_fix_out);
      if(md5) md5=(md5_sct *)nco_md5_free(md5);
      if(wgt) wgt=(var_sct *)nco_var_free(wgt);
      if(wgt_out) wgt_out=(var_sct *)nco_var_free(wgt_out);
      if(wgt_avg) wgt_avg=(var_sct *)nco_var_free(wgt_avg);
      if(cb){
        if(cb->tm_bnd_nm) cb->tm_bnd_nm=(char *)nco_free(cb->tm_bnd_nm);
        if(cb->tm_crd_nm) cb->tm_crd_nm=(char *)nco_free(cb->tm_crd_nm);
        if(cb->clm_bnd_nm) cb->clm_bnd_nm=(char *)nco_free(cb->clm_bnd_nm);
        if(cb) cb=(clm_bnd_sct *)nco_free(cb);
      } /* !cb */
  
      (void)trv_tbl_free(trv_tbl);
      for(idx=0;idx<lmt_nbr;idx++) flg_dne[idx].dim_nm=(char *)nco_free(flg_dne[idx].dim_nm);
      if(flg_dne) flg_dne=(nco_dmn_dne_t *)nco_free(flg_dne);
      if(flg_input_complete) flg_input_complete=(nco_bool *)nco_free(flg_input_complete);
      if(idx_rec_out) idx_rec_out=(long *)nco_free(idx_rec_out);
      if(rec_in_cml) rec_in_cml=(long *)nco_free(rec_in_cml);
      if(rec_usd_cml) rec_usd_cml=(long *)nco_free(rec_usd_cml);
      if(REC_LST_DSR) REC_LST_DSR=(nco_bool *)nco_free(REC_LST_DSR);
  
    } /* !flg_mmr_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() */
  
  /* ra_lst is a list of ragged arrays
     First element is var_nm, second element is the cf_nm, and the remaining elements are variable names mentioned in attribute var_nm@cf_nm */
  nco_bool
  ra_lst_chk(char ***ra_lst,int nbr_lst,char *var_nm,char *bnds_nm)
  {
    int idx=0;
    int jdx=0;
  
    for(idx=0;idx<nbr_lst;idx++)
      if(!strcmp(var_nm, ra_lst[idx][0]))
        break;
  
    if(idx == nbr_lst)
      return False;
  
    jdx=2;
    while(strlen(ra_lst[idx][jdx]) > 0)
      if(!strcmp(ra_lst[idx][jdx++],bnds_nm))
        return True;
  
    return False;
  } /* !ra_lst_chk() */
  
  /* remember ra_lst is a list of ragged arrays */
  /* the end of the ragged array is indicated by a zero-length (not null) string */
  void ra_lst_free(char ***ra_lst,int nbr_lst){
    int sz=1;
    int fdx;
    for(fdx=0;fdx<nbr_lst;fdx++){
      while(strlen(ra_lst[fdx][sz]) > 0) sz++;
      ra_lst[fdx]=nco_sng_lst_free(ra_lst[fdx],sz);
    } /* !fdx */
  } /* !ra_lst_free() */
  
  
  
  
  
  