#!/bin/bash

# Purpose: Regrid (subsets of) variables from netCDF files
# Regrids all input files (possibly on different grids) to a single specified output grid

# Copyright (C) 2015-2016 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 
# GNU General Public License (GPL) Version 3.

# As a special exception to the terms of the GPL, you are permitted 
# to link the NCO source code with the HDF, netCDF, OPeNDAP, and UDUnits
# libraries and to distribute the resulting executables under the terms 
# of the GPL, 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 GNU General Public 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

# Source: https://github.com/nco/nco/tree/master/data/ncremap

# Prerequisites: Bash, NCO, ESMF_RegridWeightGen
# Script could use other shells, e.g., dash (Debian default) after re-writing function definition and looping constructs
# Script could use other weight-generators, e.g., TempestRemap by re-writing map-file command

# Script runs in one of four modes:
# 1. Free-will: Infer source and destination grids to generate map-file, then regrid
# 2. Old Grid: Use known-good destination grid to generate map-file then regrid
# 3. New Grid: Generate source-grid from ncks parameter string
# 4. Pre-Destination: Apply supplied map-file to all input files
# By default, ncremap deletes any intermediate grids and map-file that it generates
# Use Free-Will, Old-Grid, or New-Grid mode to process Swath-Like-Data (SLD) where each input may be a granule on a new grid, yet all inputs are to be regridded to the same output grid
# Use Pre-Destination mode to post-process models or analyses where all files are converted from the same source grid to the same destination grid so the map-file can be pre-generated and never change

# Additional Documentation:

# Configure paths at High-Performance Computer Centers (HPCCs) based on ${HOSTNAME}
if [ -z "${HOSTNAME}" ]; then
    if [ -f /bin/hostname ] && [ -x /bin/hostname ] ; then
	export HOSTNAME=`/bin/hostname`
    elif [ -f /usr/bin/hostname ] && [ -x /usr/bin/hostname ] ; then
	export HOSTNAME=`/usr/bin/hostname`
    fi # !hostname
fi # HOSTNAME
# Default input and output directory is ${DATA}
if [ -z "${DATA}" ]; then
    case "${HOSTNAME}" in 
	cooley* | cc* ) DATA="/projects/HiRes_EarthSys/${USER}" ; ;; # ALCF cooley compute nodes named ccNNN
	edison* | hopper* | nid* ) DATA="${SCRATCH}" ; ;; # NERSC edison compute nodes named nidNNNNN
	pileus* ) DATA="/lustre/atlas/proj-shared/cli115/${USER}" ; ;; # OLCF CADES
	rhea* ) DATA="/lustre/atlas/proj-shared/cli115/${USER}" ; ;; # OLCF rhea compute nodes named rheaNNN
	* ) DATA='/tmp' ; ;; # Other
    esac # !HOSTNAME
fi # DATA

# Test cases (examples for Charlie's machines):
# ls ${DATA}/sld/raw/*.nc | ncremap -a conserve -D 0 -d ${DATA}/dstmch90/dstmch90_clm.nc -O ~/rgr
# ncremap -a conserve -v FSNT -I ${DATA}/ne30/raw -s ${DATA}/grids/ne30np4_pentagons.091226.nc -d ${DATA}/dstmch90/dstmch90_clm.nc -O ~/rgr
# ls ${DATA}/essgcm14/essgcm14*cam*0007*.nc | ncremap -a conserve -M -d ${DATA}/dstmch90/dstmch90_clm.nc -O ~/rgr
# ncremap -a conserve -v FSNT -I ${DATA}/ -s ${DATA}/grids/ne30np4_pentagons.091226.nc -d ${DATA}/dstmch90/dstmch90_clm.nc -O ~/rgr
# ncremap -i AIRS.2014.10.01.202.L2.TSurfStd.Regrid010.1DLatLon.hole.nc -d ${DATA}/dstmch90/dstmch90_clm.nc -O ~/rgr
# ncremap -v TSurfAir -i ${DATA}/hdf/AIRS.2015.01.15.001.L2.RetStd.v6.0.11.0.G15015142014.hdf -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -v CloudFrc_A -i ${DATA}/hdf/AIRS.2002.08.01.L3.RetStd_H031.v4.0.21.0.G06104133732.hdf -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -i ${DATA}/hdf/MOD04_L2.A2000055.0005.006.2014307165927.hdf -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -i ${DATA}/hdf/OMI-Aura_L2-OMIAuraSO2_2012m1222-o44888_v01-00-2014m0107t114720.h5 -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -v T -i ${DATA}/hdf/wrfout_v2_Lambert_notime.nc -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -v StepTwoO3 -i ${DATA}/hdf/OMI-Aura_L2-OMTO3_2015m0731t0034-o58727_v003-2015m0731t080836.he5.nc -d ${DATA}/hdf/cam_time.nc -O ~/rgr
# ncremap -v TSurfStd -i ${DATA}/sld/raw/AIRS.2014.10.01.202.L2.TSurfStd.Regrid010.1DLatLon.hole.nc -G "--rgr grd_ttl='Default internally-generated grid' --rgr grid=~/rgr/ncremap_tmp_grd_dst.nc --rgr latlon=100,100 --rgr snwe=30.0,70.0,-130.0,-90.0" -O ~/rgr
# ncremap -x TSurfStd_ct -i ${DATA}/sld/raw/AIRS.2014.10.01.202.L2.TSurfStd.Regrid010.1DLatLon.hole.nc -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -i ${DATA}/hdf/cice_hi_flt.nc -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -i ${DATA}/hdf/cam_time.nc -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# CESM & ACME:
# ncremap -i ${DATA}/ne120/raw/b1850c5_m2a.cam.h0.0060-01.nc -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -i ${DATA}/ne120/raw/b1850c5_m2a.clm2.h0.0060-01.nc -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -i ${DATA}/ne120/raw/b1850c5_m2a.cice.h.0060-01.nc -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -i ${DATA}/ne120/raw/b1850c5_m2a.pop.h.0060-01.nc -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ncremap -i ${DATA}/ne120/raw/b1850c5_m2a.rtm.h0.0060-01.nc -g ${DATA}/grids/180x360_SCRIP.20150901.nc -O ~/rgr
# ACME benchmarks:
# ncremap -v FSNT,AODVIS -i ${DATA}/ne30/raw/famipc5_ne30_v0.3_00003.cam.h0.1979-01.nc -m map_ne30np4_to_fv129x256_aave.150418.nc   -O ~/rgr
# ncremap -v FSNT,AODVIS -i ${DATA}/ne30/raw/famipc5_ne30_v0.3_00003.cam.h0.1979-01.nc -m map_ne30np4_to_fv129x256_aave.20150901.nc -O ~/rgr
# ncremap -v FSNT,AODVIS -w esmf    -i ${DATA}/ne30/raw/famipc5_ne30_v0.3_00003.cam.h0.1979-01.nc -s ${DATA}/grids/ne30np4_pentagons.091226.nc -g ${DATA}/grids/129x256_SCRIP.20150901.nc -O ~/rgr
# ncremap -v FSNT,AODVIS -w tempest -i ${DATA}/ne30/raw/famipc5_ne30_v0.3_00003.cam.h0.1979-01.nc -s ${DATA}/grids/ne30np4_pentagons.091226.nc -g ${DATA}/grids/129x256_SCRIP.20150901.nc -O ~/rgr

# Debugging and Benchmarking:
# ncremap -D 1 -i ${DATA}/sld/raw/AIRS.2014.10.01.202.L2.TSurfStd.Regrid010.1DLatLon.hole.nc -d ${DATA}/dstmch90/dstmch90_clm.nc -O ~/rgr > ~/ncremap.out 2>&1 &

# dbg_lvl: 0 = Quiet, print basic status during evaluation
#          1 = Print configuration, full commands, and status to output during evaluation
#          2 = As in dbg_lvl=1, but _do not evaluate commands_
#          3 = As in dbg_lvl=1, and pass debug level through to NCO/ncks
# thr_nbr: Thread number to use in NCO regridder, '-t 1' for one thread, '-t 2' for two threads...

# Set script name and run directory
drc_pwd=${PWD}
spt_nm=$(basename ${0}) # [sng] Script name
nco_version=$(ncks --version 2>&1 >/dev/null | grep NCO | awk '{print $5}')

# Set fonts for legibility
fnt_nrm=`tput sgr0` # Normal
fnt_bld=`tput bold` # Bold
fnt_rvr=`tput smso` # Reverse

# Defaults for command-line options and some derived variables
# Modify these defaults to save typing later
alg_typ='conserve' # [nbr] Algorithm for ESMF interpolation (bilinear|patch|neareststod|nearestdtos|conserve)
dbg_lvl=0 # [nbr] Debugging level
#drc_in="${drc_pwd}" # [sng] Input file directory
drc_in='' # [sng] Input file directory
drc_in_xmp='~/drc_in' # [sng] Input file directory for examples
drc_out="${drc_pwd}" # [sng] Output file directory
drc_out_xmp="~/rgr" # [sng] Output file directory for examples
dst_fl='' # [sng] Destination file
dst_xmp='dst.nc' # [sng] Destination file for examples
#esmf_opt='--src_regional --dst_regional --ignore_unmapped' # [sng] ESMF_RegridWeightGen options
esmf_opt='--ignore_unmapped' # [sng] ESMF_RegridWeightGen options
#fml_nm='' # [sng] Family name (e.g., 'amip', 'control', 'experiment')
fl_nbr=0 # [nbr] Number of files to remap
gaa_sng="--gaa rgr_script=${spt_nm} --gaa rgr_hostname=${HOSTNAME} --gaa rgr_version=${nco_version}" # [sng] Global attributes to add
grd_dst='' # [sng] Destination grid-file
grd_dst_glb="${DATA}/grids/180x360_SCRIP.20150901.nc" # [sng] Grid-file (destination) global
grd_dst_xmp='grd_dst.nc' # [sng] Destination grid-file for examples
grd_sng='' # [sng] Grid string
grd_src='' # [sng] Source grid-file
grd_src_xmp='grd_src.nc' # [sng] Source grid-file for examples
hdr_pad='1000' # [B] Pad at end of header section
in_fl='' # [sng] Input file
#in_fl='AIRS.2014.10.01.202.L2.TSurfStd.Regrid010.1DLatLon.hole.nc' # [sng] Input file
in_xmp='in.nc' # [sng] Input file for examples
map_fl='' # [sng] Map-file
map_xmp='map.nc' # [sng] Map-file for examples
mlt_map_flg='Yes' # [sng] Multi-map flag
mpi_flg='No' # [sng] Parallelize over nodes
msh_fl='' # [sng] Mesh-file (for Tempest)
nco_opt='-O --no_tmp_fl' # [sng] NCO defaults (e.g., '-O -6 -t 1')
nco_usr='' # [sng] NCO user-configurable options (e.g., '-D 1')
out_fl='' # [sng] Output file
out_xmp='out.nc' # [sng] Output file for examples
par_typ='' # [sng] Parallelism type
rgr_opt='--rgr lat_nm_out=lat --rgr lon_nm_out=lon' # [sng] Regridding options (e.g., '--rgr col_nm=lndgrid')
tps_opt='' # [sng] TempestRemap options
thr_nbr=2 # [nbr] Thread number for regridder
#var_lst='FSNT,AODVIS' # [sng] Variables to process (empty means all)
tmp_out_fl='ncremap_grd_tmp.nc' # [sng] Temporary output file
var_lst='' # [sng] Variables to process (empty means all)
var_xmp='FSNT' # [sng] Variable list for examples
wgt_gnr='esmf' # [sng] Weight-generator program
xtn_var='' # [sng] Extensive variables (e.g., 'TSurfStd_ct')

function fnc_usg_prn { # NB: dash supports fnc_nm (){} syntax, not function fnc_nm{} syntax
    # Print usage
    printf "\nQuick documentation for ${fnt_bld}${spt_nm}${fnt_nrm} (more at http://nco.sf.net/nco.html#ncremap)\n\n"
    printf "${fnt_rvr}Basic usage:${fnt_nrm} ${fnt_bld}$spt_nm -i in_fl -d dst_fl -o out_fl${fnt_nrm}\n\n"
    echo "Command-line options:"
    echo "${fnt_rvr}-a${fnt_nrm} ${fnt_bld}alg_typ${fnt_nrm}  Algorithm for ESMF weight generation (default ${fnt_bld}${alg_typ}${fnt_nrm})"
    echo "${fnt_rvr}-d${fnt_nrm} ${fnt_bld}dst_fl${fnt_nrm}   Data file to infer destination grid from (empty means none, i.e., use grd_fl, grd_sng or map_fl)) (default ${fnt_bld}${dst_fl}${fnt_nrm})"
    echo "${fnt_rvr}-D${fnt_nrm} ${fnt_bld}dbg_lvl${fnt_nrm}  Debugging level (default ${fnt_bld}${dbg_lvl}${fnt_nrm})"
    echo "${fnt_rvr}-E${fnt_nrm} ${fnt_bld}esmf_opt${fnt_nrm} ESMF ESMF_RegridWeightGen options (default ${fnt_bld}${esmf_opt}${fnt_nrm})"
#    echo "${fnt_rvr}-f${fnt_nrm} ${fnt_bld}fml_nm${fnt_nrm}   Family name (empty means none) (default ${fnt_bld}${fml_nm}${fnt_nrm})"
    echo "${fnt_rvr}-g${fnt_nrm} ${fnt_bld}grd_dst${fnt_nrm}  Grid-file (destination) (empty means none, i.e., infer from dst_fl or use grd_sng or map_fl) (default ${fnt_bld}${grd_dst}${fnt_nrm})"
    echo "${fnt_rvr}-G${fnt_nrm} ${fnt_bld}grd_sng${fnt_nrm}  Grid generation argument string (empty means none) (default ${fnt_bld}${grd_sng}${fnt_nrm})"
    echo "${fnt_rvr}-I${fnt_nrm} ${fnt_bld}drc_in${fnt_nrm}   Input directory (empty means none) (default ${fnt_bld}${drc_in}${fnt_nrm})"
    echo "${fnt_rvr}-i${fnt_nrm} ${fnt_bld}in_fl${fnt_nrm}    Input file (empty means use stdin or drc_in) (default ${fnt_bld}${in_fl}${fnt_nrm})"
    echo "${fnt_rvr}-M${fnt_nrm}          Multi-map-file toggle (unset means generate one map-file per input file)"
    echo "${fnt_rvr}-m${fnt_nrm} ${fnt_bld}map_fl${fnt_nrm}   Map-file (empty means generate internally) (default ${fnt_bld}${map_fl}${fnt_nrm})"
    echo "${fnt_rvr}-n${fnt_nrm} ${fnt_bld}nco_opt${fnt_nrm}  NCO options (empty means none) (default ${fnt_bld}${nco_opt}${fnt_nrm})"
    echo "${fnt_rvr}-O${fnt_nrm} ${fnt_bld}drc_out${fnt_nrm}  Output directory (default ${fnt_bld}${drc_out}${fnt_nrm})"
    echo "${fnt_rvr}-o${fnt_nrm} ${fnt_bld}out_fl${fnt_nrm}   Output-file (regridded file) (empty copies Input filename) (default ${fnt_bld}${out_fl}${fnt_nrm})"
    echo "${fnt_rvr}-p${fnt_nrm} ${fnt_bld}par_typ${fnt_nrm}  Parallelism type (default ${fnt_bld}${par_typ}${fnt_nrm})"
    echo "${fnt_rvr}-R${fnt_nrm} ${fnt_bld}rgr_opt${fnt_nrm}  Regridding options (empty means none) (default ${fnt_bld}${rgr_opt}${fnt_nrm})"
    echo "${fnt_rvr}-s${fnt_nrm} ${fnt_bld}grd_src${fnt_nrm}  Grid-file (source) (empty means infer or use map_fl) (default ${fnt_bld}${grd_src}${fnt_nrm})"
    echo "${fnt_rvr}-t${fnt_nrm} ${fnt_bld}thr_nbr${fnt_nrm}  Thread number for regridder (default ${fnt_bld}${thr_nbr}${fnt_nrm})"
    echo "${fnt_rvr}-T${fnt_nrm} ${fnt_bld}tps_opt${fnt_nrm}  TempestRemap GenerateOfflineMap options (default ${fnt_bld}${tps_opt}${fnt_nrm})"
    echo "${fnt_rvr}-v${fnt_nrm} ${fnt_bld}var_lst${fnt_nrm}  Variable list (empty means all) (default ${fnt_bld}${var_lst}${fnt_nrm})"
    echo "${fnt_rvr}-w${fnt_nrm} ${fnt_bld}wgt_gnr${fnt_nrm}  Weight-generator (default ${fnt_bld}${wgt_gnr}${fnt_nrm})"
    echo "${fnt_rvr}-x${fnt_nrm} ${fnt_bld}xtn_var${fnt_nrm}  Extensive variables (empty means none) (default ${fnt_bld}${xtn_var}${fnt_nrm})"
    printf "\n"
    printf "Examples: ${fnt_bld}$spt_nm -i ${in_xmp} -m ${map_xmp} -o ${out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -i ${in_xmp} -d ${dst_xmp} -o ${out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -i ${in_xmp} -g ${grd_dst_xmp} -o ${out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -a bilinear -i ${in_xmp} -d ${dst_xmp} -o ${out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -a conserve -i ${in_xmp} -d ${dst_xmp} -o ${out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -w tempest  -i ${in_xmp} -d ${dst_xmp} -o ${out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -v ${var_xmp} -i ${in_xmp} -m ${map_xmp} -o ${out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -I ${drc_in_xmp} -m ${map_xmp} -O ${drc_out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -M -I ${drc_in_xmp} -d ${dst_xmp} -O ${drc_out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -M -I ${drc_in_xmp} -g ${grd_dst_xmp} -O ${drc_out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -I ${drc_in_xmp} -s ${grd_src_xmp} -d ${dst_xmp} -O ${drc_out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -I ${drc_in_xmp} -s ${grd_src_xmp} -g ${grd_dst_xmp} -O ${drc_out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -I ${drc_in_xmp} -d ${dst_xmp} -O ${drc_out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}$spt_nm -I ${drc_in_xmp} -g ${grd_dst_xmp} -O ${drc_out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}ls mdl*2005*nc | $spt_nm -m ${map_xmp} -O ${drc_out_xmp} ${fnt_nrm}\n"
    printf "          ${fnt_bld}ls mdl*2005*nc | $spt_nm -d ${dst_xmp} -O ${drc_out_xmp} ${fnt_nrm}\n"
    exit 1
} # end fnc_usg_prn()

function dst_is_grd {
    # Is destination grid specified as SCRIP grid-file?
    # Usage: dst_is_grd ${fl}
    fl=${1}
    flg='Yes'
    #flg='No'
} # end dst_is_grd()

# Check argument number and complain accordingly
arg_nbr=$#
#printf "\ndbg: Number of arguments: ${arg_nbr}"
if [ ${arg_nbr} -eq 0 ]; then
  fnc_usg_prn
fi # !arg_nbr

# Parse command-line options:
# http://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options
# http://tuxtweaks.com/2014/05/bash-getopts
cmd_ln="${@}"
while getopts :a:D:d:E:f:g:G:h:I:i:Mm:n:O:o:p:R:s:T:t:v:w:x: OPT; do
    case ${OPT} in
	a) alg_typ=${OPTARG} ;; # Algorithm
	D) dbg_lvl=${OPTARG} ;; # Debugging level
	d) dst_fl=${OPTARG} ;; # Destination file
	E) esmf_opt=${OPTARG} ;; # ESMF options
#	f) fml_nm=${OPTARG} ;; # Family name
	g) grd_dst=${OPTARG} ;; # Destination grid-file
	G) grd_sng=${OPTARG} ;; # Grid generation string
	I) drc_in=${OPTARG} ;; # Input directory
	i) in_fl=${OPTARG} ;; # Input file
	M) mlt_map_flg='No' ;; # Multi-map flag
	m) map_fl=${OPTARG} ;; # Map-file
	n) nco_usr=${OPTARG} ;; # NCO options
	O) drc_out=${OPTARG} ;; # Output directory
	o) out_fl=${OPTARG} ;; # Output file
	p) par_typ=${OPTARG} ;; # Parallelism type
	R) rgr_opt=${OPTARG} ;; # Regridding options
	s) grd_src=${OPTARG} ;; # Source grid-file
	T) tps_opt=${OPTARG} ;; # Tempest options
	t) thr_usr=${OPTARG} ;; # Thread number
	v) var_lst=${OPTARG} ;; # Variables
	w) wgt_usr=${OPTARG} ;; # Weight-generator
	x) xtn_var=${OPTARG} ;; # Extensive variables
	\?) # Unrecognized option
	    printf "\nERROR: Option ${fnt_bld}-$OPTARG${fnt_nrm} not allowed"
	    fnc_usg_prn ;;
    esac
done
shift $((OPTIND-1)) # Advance one argument

# Derived variables
grd_dst_dfl="${drc_out}/ncremap_tmp_grd_dst.nc" # [sng] Grid-file (destination) default
grd_src_dfl="${drc_out}/ncremap_tmp_grd_src.nc" # [sng] Grid-file (source) default
tmp_out_fl="${drc_out}/${tmp_out_fl}" # [sng] Temporary output file
if [ ${alg_typ} = 'bilinear' ] || [ ${alg_typ} = 'bln' ] ; then 
    # ESMF algorithms are bilinear|patch|neareststod|nearestdtos|conserve
    alg_opt='bilinear'
elif [ ${alg_typ} = 'conserve' ] || [ ${alg_typ} = 'conservative' ] || [ ${alg_typ} = 'cns' ] ; then 
    alg_opt='conserve'
elif [ ${alg_typ} = 'nearestdtos' ] || [ ${alg_typ} = 'nds' ] || [ ${alg_typ} = 'dtos' ] ; then 
    alg_opt='nearestdtos'
elif [ ${alg_typ} = 'neareststod' ] || [ ${alg_typ} = 'nsd' ] || [ ${alg_typ} = 'stod' ] ; then 
    alg_opt='nearestdtos'
elif [ ${alg_typ} = 'patch' ] || [ ${alg_typ} = 'pch' ] || [ ${alg_typ} = 'ptc' ] ; then 
    alg_opt='patch'
fi # !alg_typ
if [ -z "${drc_in}" ]; then
    drc_in="${drc_pwd}"
else # !drc_in
    drc_in_usr_flg='Yes'
fi # !drc_in
if [ ${dbg_lvl} -ge 2 ]; then
    nco_opt="-D ${dbg_lvl} ${nco_opt}"
fi # !dbg_lvl
if [ -n "${nco_usr}" ]; then 
    nco_opt="${nco_usr} ${nco_opt}"
fi # !var_lst
if [ -n "${gaa_sng}" ]; then 
    nco_opt="${nco_opt} ${gaa_sng}"
fi # !var_lst
if [ -n "${hdr_pad}" ]; then 
    nco_opt="${nco_opt} --hdr_pad=${hdr_pad}"
fi # !hdr_pad
if [ -n "${var_lst}" ]; then 
    nco_var_lst="-v ${var_lst}"
fi # !var_lst
if [ -n "${xtn_var}" ]; then 
    rgr_opt="${rgr_opt} --xtn=${xtn_var}"
fi # !var_lst
if [ -n "${par_typ}" ]; then
    if [ "${par_typ}" != 'bck' ] && [ "${par_typ}" != 'mpi' ] && [ "${par_typ}" != 'nil' ]; then 
	    echo "ERROR: Invalid -p par_typ option = ${par_typ}"
	    echo "HINT: Valid par_typ arguments are 'bck', 'mpi', and 'nil'"
	    exit 1
    fi # !par_typ
fi # !par_typ
if [ "${par_typ}" = 'bck' ]; then 
    par_opt=' &'
elif [ "${par_typ}" = 'mpi' ]; then 
    mpi_flg='Yes'
    par_opt=' &'
fi # !par_typ
if [ -n "${out_fl}" ]; then 
    out_usr_flg='Yes'
fi # !out_fl
if [ -n "${thr_usr}" ]; then 
    thr_nbr="${thr_usr}"
fi # !thr_usr
if [ -n "${wgt_usr}" ]; then 
    wgt_gnr="${wgt_usr}"
fi # !wgt_usr    
if [ "${wgt_gnr}" != 'esmf' ] && [ "${wgt_gnr}" != 'tempest' ] ; then 
	    echo "ERROR: Invalid -w wgt_gnr option = ${wgt_gnr}"
	    echo "HINT: Valid wgt_gnr arguments are 'esmf' and 'tempest'"
	    exit 1
fi # !wgt_gnr

if [ -n "${in_fl}" ]; then
    # Single file argument
    fl_in[${fl_nbr}]=${in_fl}
    let fl_nbr=${fl_nbr}+1
else # !in_fl
    # Detecting input on stdin:
    # http://stackoverflow.com/questions/2456750/detect-presence-of-stdin-contents-in-shell-script
    # ls ${DATA}/ne30/raw/famipc5*1979*.nc | ncremap -D 1 -m ${DATA}/maps/map_ne30np4_to_fv129x256_aave.20150901.nc -O ~/rgr
    if [ -t 0 ]; then 
	if [ "${drc_in_usr_flg}" = 'Yes' ]; then
	    for fl in "${drc_in}"/*.nc "${drc_in}"/*.nc3 "${drc_in}"/*.nc4 "${drc_in}"/*.cdf "${drc_in}"/*.hdf "${drc_in}"/*.he5 "${drc_in}"/*.h5 ; do
		if [ -f "${fl}" ]; then
		    fl_in[${fl_nbr}]=${fl}
		    let fl_nbr=${fl_nbr}+1
		fi # !file
	    done
	else # !drc_in
	    echo "ERROR: Must specify input file with -i or with stdin"
	    echo "HINT: Pipe file list to script via stdin with, e.g., 'ls *.nc | ${spt_nm}'"
	    exit 1
	fi # !drc_in
    else
	# Input awaits on unit 0, i.e., on stdin
	while read -r line; do # NeR05 p. 179
	    fl_in[${fl_nbr}]=${line}
	    let fl_nbr=${fl_nbr}+1
	done < /dev/stdin
    fi # stdin
fi # !in_fl
if [ -n "${dst_fl}" ]; then 
    if [ ! -e "${dst_fl}" ]; then
	echo "ERROR: Unable to find specified destination-file ${dst_fl}"
	echo "HINT: Supply the full path-name for the destination-file"
	exit 1
    fi # ! -e
    dst_usr_flg='Yes'
fi # !dst_fl
if [ -n "${grd_dst}" ]; then 
    if [ ! -e "${grd_dst}" ]; then
	echo "ERROR: Unable to find specified destination grid-file ${grd_dst}"
	echo "HINT: Supply the full path-name for the destination grid, or generate one automatically with -G"
	exit 1
    fi # ! -e
    grd_dst_usr_flg='Yes'
else
    grd_dst=${grd_dst_dfl} # [sng] Grid-file default
fi # !grd_dst
if [ -n "${grd_src}" ]; then 
    if [ ! -e "${grd_src}" ]; then
	echo "ERROR: Unable to find specified source grid-file ${grd_src}"
	exit 1
    fi # ! -e
    grd_src_usr_flg='Yes'
else
    grd_src=${grd_src_dfl} # [sng] Grid-file default
fi # !grd_src
if [ -z "${grd_sng}" ]; then 
    grd_sng_dfl="--rgr grd_ttl='Default internally-generated grid' --rgr grid=${grd_dst_dfl} --rgr latlon=100,100 --rgr snwe=30.0,70.0,-130.0,-90.0" # [sng] Grid string default
    grd_sng="${grd_sng_dfl}"
else
    grd_sng_usr_flg='Yes'
fi # !grd_sng
if [ -n "${map_fl}" ]; then 
    if [ ! -e "${map_fl}" ]; then
	echo "ERROR: Unable to find specified regrid map ${map_fl}"
	echo "HINT: Supply the full path-name for the regridding map"
	exit 1
    fi # ! -e
    map_usr_flg='Yes'
else
    if [ "${wgt_gnr}" = 'esmf' ]; then 
	map_fl_dfl="${drc_out}/ncremap_tmp_map_${wgt_gnr}_${alg_opt}.nc" # [sng] Map-file default
    fi # !esmf
    if [ "${wgt_gnr}" = 'tempest' ]; then 
	map_fl_dfl="${drc_out}/ncremap_tmp_map_${wgt_gnr}.nc" # [sng] Map-file default
	msh_fl_dfl="${drc_out}/ncremap_tmp_msh_ovr_${wgt_gnr}.g" # [sng] Mesh-file default
	msh_fl=${msh_fl_dfl}
    fi # !tempest
    map_fl=${map_fl_dfl}
fi # !map_fl

# Print initial state
if [ ${dbg_lvl} -ge 2 ]; then
    printf "dbg: alg_opt  = ${alg_opt}\n"
    printf "dbg: dbg_lvl  = ${dbg_lvl}\n"
    printf "dbg: drc_in   = ${drc_in}\n"
    printf "dbg: drc_out  = ${drc_out}\n"
    printf "dbg: dst_fl   = ${dst_fl}\n"
#    printf "dbg: fml_nm   = ${fml_nm}\n"
    printf "dbg: gaa_sng  = ${gaa_sng}\n"
    printf "dbg: grd_dst  = ${grd_dst}\n"
    printf "dbg: grd_sng  = ${grd_sng}\n"
    printf "dbg: grd_src  = ${grd_src}\n"
    printf "dbg: hdr_pad  = ${hdr_pad}\n"
    printf "dbg: in_fl    = ${in_fl}\n"
    printf "dbg: map_fl   = ${map_fl}\n"
    printf "dbg: mlt_map  = ${mlt_map_flg}\n"
    printf "dbg: mpi_flg  = ${mpi_flg}\n"
    printf "dbg: nco_opt  = ${nco_opt}\n"
    printf "dbg: nd_nbr   = ${nd_nbr}\n"
    printf "dbg: out_fl   = ${out_fl}\n"
    printf "dbg: par_typ  = ${par_typ}\n"
    printf "dbg: thr_nbr  = ${thr_nbr}\n"
    printf "dbg: var_lst  = ${var_lst}\n"
    printf "Asked to regrid ${fl_nbr} files:\n"
    for ((fl_idx=0;fl_idx<${fl_nbr};fl_idx++)); do
	printf "${fl_in[${fl_idx}]}\n"
    done # !fl_idx
fi # !dbg
if [ ${dbg_lvl} -ge 2 ]; then
    if [ ${mpi_flg} = 'Yes' ]; then
	for ((nd_idx=0;nd_idx<${nd_nbr};nd_idx++)); do
	    printf "dbg: nd_nm[${nd_idx}] = ${nd_nm[${nd_idx}]}\n"
	done # !nd
    fi # !mpi
fi # !dbg

# Create output directory
mkdir -p ${drc_out}

# Human-readable summary
if [ ${dbg_lvl} -ge 1 ]; then
    printf "NCO regridder invoked with command:\n"
    echo "${spt_nm} ${cmd_ln}"
fi # !dbg
date_srt=$(date +"%s")

if [ -f 'PET0.RegridWeightGen.Log' ]; then
    printf "${spt_nm}: Removing PET0.RegridWeightGen.Log file from current directory before running\n"
    /bin/rm -f PET0.RegridWeightGen.Log
fi # !PETO
printf "Started processing at `date`.\n"
printf "NCO version is ${nco_version}\n"
if [ "${map_usr_flg}" = 'Yes' ] && [ -n "${wgt_usr}" ] ; then
    printf "${spt_nm}: ERROR Specifying both '-m map_fl' and '-w wgt_gnr' is not allowed (the weight-generator is unnecessary)\n"
    exit 1
fi # wgt_usr
if [ "${grd_src_usr_flg}" = 'Yes' ]; then
    if [ "${map_usr_flg}" = 'Yes' ]; then 
	printf "${spt_nm}: ERROR Specifying both '-s grd_src' and '-m map_fl' is ambiguous and forbidden\n"
	exit 1
    fi # !map_usr_flg
fi # !grd_src_usr_flg
    
if [ "${dst_usr_flg}" = 'Yes' ]; then 
    if [ "${map_usr_flg}" = 'Yes' ]; then 
	printf "${spt_nm}: ERROR Specify either '-d dst_fl' or '-m map_fl' not both\n"
	exit 1
    fi # !map_usr_flg
    if [ "${grd_dst_usr_flg}" = 'Yes' ]; then 
	printf "${spt_nm}: ERROR Specify either '-d dst_fl' or '-g grd_dst' not both\n"
	exit 1
    fi # !grd_dst_usr_flg
else # !dst_usr_flg
    if [ "${grd_dst_usr_flg}" = 'Yes' ]; then 
	if [ "${map_usr_flg}" = 'Yes' ]; then 
	    printf "${spt_nm}: ERROR Specify either '-g grd_dst' or '-m map_fl' not both\n"
	    exit 1
	fi # !map_usr_flg
    else
	if [ "${map_usr_flg}" != 'Yes' ] && [ "${grd_sng_usr_flg}" != 'Yes' ] ; then 
	    printf "${spt_nm}: ERROR Must use one of '-d dst_fl', '-g grd_dst', '-G grd_sng', or '-m map_fl'\n"
	    exit 1
	fi # !map_usr_flg
    fi # !grd_dst_usr_flg
fi # !dst_usr_flg

# Generate destination grid, if necessary, once (only) before loop over input files
# Block 1: Destination grid
# Unlike source grid, same destination grid is used for all files
# Generate destination grid at most one-time
# Eventually we will allow destination grid to be provided as grid-file, map-file, or data-file
# Currently we require user to know (and specify) means by which destination grid is provided
if [ "${map_usr_flg}" = 'Yes' ]; then 
    printf "Source and destination grids will both be read from supplied map-file ${map_fl}\n"
else # !map_usr_flg
    fl_idx=0 # [idx] Current file index
    if [ "${dst_usr_flg}" = 'Yes' ]; then 
	# Block 1 Loop 1: Generate, check, and store (but do not yet execute) commands
	# Infer destination grid-file from data file
	printf "Destination grid will be inferred from data-file ${dst_fl} and stored as ${grd_dst}\n"
	cmd_dst[${fl_idx}]="ncks ${nco_opt} --rgr nfr=y --rgr grid=${grd_dst} ${dst_fl} ${tmp_out_fl}"
    else # !dst_usr_flg
	if [ "${grd_dst_usr_flg}" = 'Yes' ]; then 
	    printf "Destination grid supplied by user as ${grd_dst}\n"
	else
	    if [ "${grd_sng_usr_flg}" = 'Yes' ]; then 
		printf "Destination grid will be generated from NCO grid string ${grd_sng}\n"
		cmd_dst[${fl_idx}]="ncks ${nco_opt} ${grd_sng} ${fl_in[0]} ${tmp_out_fl}"
	    else
		printf "${spt_nm}: ERROR Grid string grd_sng not provided\n"
		exit 1
	    fi # !grd_sng_usr_flg
	fi # !grd_dst_usr_flg
    fi # !dst_usr_flg
    if [ "${dst_usr_flg}" = 'Yes' ] || [ "${grd_dst_usr_flg}" != 'Yes' ]; then 
	# Block 1 Loop 2: Execute and/or echo commands
	if [ ${dbg_lvl} -ge 1 ]; then
	    echo ${cmd_dst[${fl_idx}]}
	fi # !dbg
	if [ ${dbg_lvl} -ne 2 ]; then
	    eval ${cmd_dst[${fl_idx}]}
	    if [ $? -ne 0 ]; then
		printf "${spt_nm}: ERROR Failed to generate destination grid. Debug this:\n${cmd_dst[${fl_idx}]}\n"
		exit 1
	    fi # !err
	    if [ "${grd_sng_usr_flg}" = 'Yes' ]; then 
		/bin/rm -f ${tmp_out_fl}
	    fi # !grd_sng_usr_flg
	fi # !dbg
    fi # !dst_usr_flg || grd_dst_usr_flg
    printf "Weight-generation type: ${wgt_gnr}\n"
    if [ "${wgt_gnr}" = 'esmf' ]; then 
	printf "ESMF's ESMF_RegridWeightGen will generate map-file internally and store it as ${map_fl}\n"
	printf "Algorithm used to generate weights in map-file is: ${alg_opt}\n"
    fi # !esmf
    if [ "${wgt_gnr}" = 'tempest' ]; then 
	printf "TempestRemap's GenerateOverlapMesh and GenerateOfflineMap will generate map-file internally and store it as ${map_fl}\n"
    fi # !tempest
    if [ ${fl_nbr} -ge 2 ]; then 
	if [ "${mlt_map_flg}" = 'Yes' ]; then 
	    printf "Input files assumed to use unique input grids, one map-file will be generated per input file\n"
	else # !mlt_map_flg
	    printf "Input files assumed to use same input grid, only one map-file will be generated\n"
	fi # !mlt_map_flg
    fi # !fl_nbr
fi # !map_usr

# If user provides source gridfile, assume it applies to every input file
# Do not infer source gridfiles from input files within file loop
# Generate map-file once outside of file loop, and re-use it for every input file
if [ "${grd_src_usr_flg}" = 'Yes' ]; then
    printf "Source grid supplied by user as ${grd_src}\n"
    fl_idx=0
    if [ "${wgt_gnr}" = 'esmf' ]; then 
	cmd_map[${fl_idx}]="ESMF_RegridWeightGen -s ${grd_src} -d ${grd_dst} -w ${map_fl} --method ${alg_opt} ${esmf_opt} > /dev/null"
    fi # !esmf
    if [ "${wgt_gnr}" = 'tempest' ]; then 
	cmd_msh[${fl_idx}]="GenerateOverlapMesh --a ${grd_src} --b ${grd_dst} --out ${msh_fl} > /dev/null"
	cmd_map[${fl_idx}]="GenerateOfflineMap --in_mesh ${grd_src} --out_mesh ${grd_dst} --ov_mesh ${msh_fl} --out_map ${map_fl} ${tps_opt} > /dev/null"
	# 20160101: TempestRemap programs have false positive error returns (failures reported as successes) so remove old map/mesh before generating new
	/bin/rm -f ${msh_fl} ${map_fl}
	if [ ${dbg_lvl} -ge 1 ]; then
	    echo ${cmd_msh[${fl_idx}]}
	fi # !dbg
	if [ ${dbg_lvl} -ne 2 ]; then
	    eval ${cmd_msh[${fl_idx}]}
	    if [ $? -ne 0 ] || [ ! -f ${msh_fl} ] ; then
		printf "${spt_nm}: ERROR Failed to generate mesh-file. Debug this:\n${cmd_msh[${fl_idx}]}\n"
		exit 1
	    fi # !err
	fi # !dbg
    fi # !tempest
    if [ ${dbg_lvl} -ge 1 ]; then
	echo ${cmd_map[${fl_idx}]}
    fi # !dbg
    if [ ${dbg_lvl} -ne 2 ]; then
	eval ${cmd_map[${fl_idx}]}
	if [ $? -ne 0 ] || [ ! -f ${map_fl} ] ; then
	    printf "${spt_nm}: ERROR Failed to generate map-file. Debug this:\n${cmd_map[${fl_idx}]}\n"
	    if [ "${wgt_gnr}" = 'esmf' ]; then 
		printf "${spt_nm}: HINT When ESMF fails to generate map-files, it often puts additional debugging information in the file named PET0.RegridWeightGen.Log in the invocation directory (${drc_pwd})\n"
	    fi # !esmf
	    exit 1
	fi # !err
    fi # !dbg
    # Set map_usr_flg to 'Yes' here to avoid re-generating map within file loop
    map_usr_flg='Yes'
fi # !grd_src_usr_flg

# Begin loop over input files
for ((fl_idx=0;fl_idx<${fl_nbr};fl_idx++)); do
    in_fl=${fl_in[${fl_idx}]}
    if [ "$(basename ${in_fl})" = "${in_fl}" ]; then
	in_fl="${drc_pwd}/${in_fl}"
    fi # !basename
    if [ "${out_usr_flg}" = 'Yes' ]; then 
	if [ ${fl_nbr} -ge 2 ]; then 
	    echo "ERROR: Single output filename specified with -o for multiple input files"
	    echo "HINT: For multiple input files use -O option to specify output directory and do not use -o option. Output files will have same name as input files, but will be in different directory."
	    exit 1
	fi # !fl_nbr
    else # !out_usr_flg
	out_fl="${drc_out}/$(basename ${in_fl})" # [sng] Output-file default
    fi # !out_fl
    if [ "${in_fl}" = "${out_fl}" ]; then
	echo "ERROR: Input file = Output file = ${in_fl}"
	echo "HINT: To prevent inadvertent data loss, ${spt_nm} insists that Input file and Output filenames differ"
	exit 1
    fi # !basename

    # Generate new map unless map-file was supplied or already-generated (as indicated by map_usr_flg)
    if [ "${map_usr_flg}" != 'Yes' ]; then
	# Block 2: Source grid
	# Block 2 Loop 1: Source gridfile command
	if [ ! -e "${in_fl}" ]; then
	    echo "${spt_nm}: ERROR Unable to find Input file ${in_fl}"
	    echo "HINT: All files implied to exist must be in the directory specified by their filename or in ${drc_in} before ${spt_nm} will proceed"
	    exit 1
	fi # ! -e
	# Infer source grid-file from input data file
	cmd_src[${fl_idx}]="ncks ${nco_opt} --rgr nfr=y --rgr grid=${grd_src} ${in_fl} ${tmp_out_fl}"
	
	# Block 2 Loop 2: Execute and/or echo commands
	if [ ${dbg_lvl} -ge 1 ]; then
	    echo ${cmd_src[${fl_idx}]}
	fi # !dbg
	if [ ${dbg_lvl} -ne 2 ]; then
	    eval ${cmd_src[${fl_idx}]}
	    if [ $? -ne 0 ]; then
		printf "${spt_nm}: ERROR Failed to generate source grid. Debug this:\n${cmd_src[${fl_idx}]}\n"
		exit 1
	    fi # !err
	fi # !dbg
	
	# Block 3: Source->destination maps
	# Block 3 Loop 1: Map-file commands
	printf "Grid(src): ${grd_src}\n"
	printf "Grid(dst): ${grd_dst}\n"
	printf "Map-File : ${map_fl}\n"
	if [ "${wgt_gnr}" = 'esmf' ]; then 
	    cmd_map[${fl_idx}]="ESMF_RegridWeightGen -s ${grd_src} -d ${grd_dst} -w ${map_fl} --method ${alg_opt} ${esmf_opt} > /dev/null"
	fi # !esmf
	if [ "${wgt_gnr}" = 'tempest' ]; then 
	    printf "Mesh-File: ${msh_fl}\n"
	    cmd_msh[${fl_idx}]="GenerateOverlapMesh --a ${grd_src} --b ${grd_dst} --out ${msh_fl} ${tps_opt} > /dev/null"
	    cmd_map[${fl_idx}]="GenerateOfflineMap --in_mesh ${grd_src} --out_mesh ${grd_dst} --ov_mesh ${msh_fl} --out_map ${map_fl} ${tps_opt} > /dev/null"
	    # 20160101: TempestRemap programs have false negative error returns (failures reported as successes) so remove old map/mesh before generating new
	    /bin/rm -f ${msh_fl} ${map_fl}
	    if [ ${dbg_lvl} -ge 1 ]; then
		echo ${cmd_msh[${fl_idx}]}
	    fi # !dbg
	    if [ ${dbg_lvl} -ne 2 ]; then
		eval ${cmd_msh[${fl_idx}]}
		if [ $? -ne 0 ] || [ ! -f ${msh_fl} ] ; then
		    printf "${spt_nm}: ERROR Failed to generate mesh-file. Debug this:\n${cmd_msh[${fl_idx}]}\n"
		    exit 1
		fi # !err
	    fi # !dbg
	fi # !tempest
	
	# Block 3 Loop 2: Execute and/or echo commands
	if [ ${dbg_lvl} -ge 1 ]; then
	    echo ${cmd_map[${fl_idx}]}
	fi # !dbg
	if [ ${dbg_lvl} -ne 2 ]; then
	    eval ${cmd_map[${fl_idx}]}
	    if [ $? -ne 0 ] || [ ! -f ${map_fl} ] ; then
		printf "${spt_nm}: ERROR Failed to generate map-file. Debug this:\n${cmd_map[${fl_idx}]}\n"
		if [ "${wgt_gnr}" = 'esmf' ]; then 
		    printf "${spt_nm}: HINT When ESMF fails to generate map-files, it often puts additional debugging information in the file named PET0.RegridWeightGen.Log in the invocation directory (${drc_pwd})\n"
		fi # !esmf
		exit 1
	    fi # !err
	fi # !dbg

	# Prevent creating new source gridfile and map-file after first iteration
	if [ "${mlt_map_flg}" = 'No' ] && [ ${fl_idx} -eq 0 ] ; then 
	    map_usr_flg='Yes'
	fi # !mlt_map_flg

    fi # !map_usr_flg
    
    # Block 4: Regrid
    printf "Input : ${in_fl}\n"
    printf "Output: ${out_fl}\n"
    cmd_rgr[${fl_idx}]="ncks -t ${thr_nbr} ${nco_opt} ${nco_var_lst} ${rgr_opt} --map=${map_fl} ${in_fl} ${out_fl}"
    
    # Block 4 Loop 2: Execute and/or echo commands
    if [ ${dbg_lvl} -ge 1 ]; then
	echo ${cmd_rgr[${fl_idx}]}
    fi # !dbg
    if [ ${dbg_lvl} -ne 2 ]; then
	eval ${cmd_rgr[${fl_idx}]}
	if [ -z "${par_opt}" ]; then
	    eval ${cmd_rgr[${fl_idx}]}
	    if [ $? -ne 0 ]; then
		printf "${spt_nm}: ERROR Failed to regrid. cmd_rgr[${fl_idx}] failed. Debug this:\n${cmd_rgr[${fl_idx}]}\n"
		exit 1
	    fi # !err
	else # !par_typ
	    eval ${cmd_rgr[${fl_idx}]} ${par_opt}
	    rgr_pid[${fl_idx}]=$!
	fi # !par_typ
    fi # !dbg
done # !fl_idx

# wait() for parallel regridding, if any, to finish
if [ -n "${par_opt}" ]; then
    for ((fl_idx=0;fl_idx<${fl_nbr};fl_idx++)); do
	wait ${rgr_pid[${fl_idx}]}
	if [ $? -ne 0 ]; then
	    printf "${spt_nm}: ERROR Failed to regrid. cmd_rgr[${fl_idx}] failed. Debug this:\n${cmd_rgr[${fl_idx}]}\n"
	    exit 1
	fi # !err
    done # !fl_idx
fi # !par_typ

date_end=$(date +"%s")
printf "Completed processing of ${fl_nbr} file(s) at `date`.\n"
date_dff=$((date_end-date_srt))
echo "Quick plots of results from last regridded file:"
echo "ncview  ${out_fl} &"
echo "panoply ${out_fl} &"
echo "Elapsed time $((date_dff/60))m$((date_dff % 60))s"

exit 0
