#!/usr/bin/env perl
########################################################################
#
# Create a customized CCCma job string
# The initial job string may be read from a file or generated internally.
# In either case variable assignments may be added/altered/removed and
# arbitrary substitutions made based on user input from the command line.
#
# Larry Solheim Aug 8,2005
########################################################################
#

require 5.003;

BEGIN {
  use Cwd;

  # Add site local lib
  unshift(@INC,"/usr/local/lib/site_perl");

  # add a default path to @INC for local libraries
  my $defpath = (getpwnam("acrnrls"))[7]."/pub/cccjob/lib";
  if ($defpath eq "/pub/cccjob/lib") {
    # acrnrls is not a valid user, possibly because this is a laptop
    # Try a few hard coded locations
    if (-d "/home/rls/pub/cccjob/lib" ) {
      $defpath = "/home/rls/pub/cccjob/lib";
    } elsif (-d "/HOME/rls/pub/cccjob/lib" ) {
      $defpath = "/HOME/rls/pub/cccjob/lib";
    } elsif (-d "/users/tor/acrn/rls/pub/cccjob/lib" ) {
      $defpath = "/users/tor/acrn/rls/pub/cccjob/lib";
    } else {
      unless ($ENV{CCCJOB_ROOT}) {
        # If none of the above then issue a warning
        warn "*** WARNING *** Unable to locate default library.\n";
      }
    }
  }
  $CCCJOB_LIB = $defpath;

  # If defined, a user specified path will override the default path
  if ($ENV{CCCJOB_ROOT}) {
    $CCCJOB_LIB = "$ENV{CCCJOB_ROOT}/lib";
    # ensure $CCCJOB_LIB is a full path name
    unless ($CCCJOB_LIB =~ /^\//) {
      my $dir = cwd();
      chdir $CCCJOB_LIB or die "Cannot access ${CCCJOB_LIB}: $!\n";
      $CCCJOB_LIB = cwd();
      chdir $dir;
    }
  }

  # This dir must exist
  die "Unable to determine path to default library.\nCCCJOB_LIB=$CCCJOB_LIB\n"
    unless (-d "$CCCJOB_LIB");
  unshift(@INC,$CCCJOB_LIB);
  # Add a missing dir as the last in the stack to hold stubs for
  # missing packages such as DB_File.pm
  push(@INC,"$CCCJOB_LIB/missing");

  # Look for command line args to define JGIT->repo, JGIT->rev, JGIT->path
  foreach (@ARGV) {
    next unless /^--?jdef-/;
    if ( /^--?jdef-rep/ ) {
      # JGIT{repo} is a full pathname to the git repository
      # JGIT{repo} must be of the form [rem_mach:]path_to_repo (rem_mach is optional)
      ($trash, $JGIT{repo}) = split(/=/);
      undef $trash;
    }
    if ( /^--?jdef-rev/ ) {
      # JGIT{rev} is the revision of the git repository to use
      ($trash, $JGIT{rev}) = split(/=/);
      undef $trash;
    }
    if ( /^--?jdef-p/ ) {
      # JGIT{path} is a path name relative to the root of the git repository
      # that points to a directory containing jobdef files named *_jobdef
      ($trash, $JGIT{path}) = split(/=/);
      undef $trash;
    }
  }

  $Verbose = 1;
}

# Declare global variables
use vars qw( $CCCJOB_LIB $Verbose %usrset @warnings %par_def %agcm %parin
             %diag_deck $add_ext_delim %ext_delim %DEFINE %UNDEF %JGIT );

use Sys::Hostname;

use Cwd qw(cwd abs_path);
use CCCjobs;
use cccjob_subs;
use shell_var_subs;
use CLhist;

# print "@INC\n";

chomp($Runame = `basename $0`);
chomp($stamp = `date "+%j"$$`);

my ($hname, $dname, $alias, $subnet) = hostid();
$host{name} = $alias;
if ($subnet =~ /cccma/i) {
  $host{location} = "victoria";
} elsif ($subnet =~ /cmc/i) {
  $host{location} = "dorval";
} else {
  $host{location} = $subnet;
};

# Invoked_name, if set in the environment, will be the name of the
# script invoked by the user. Otherwise it defaults to Runame.
$Invoked_name = $Runame unless $Invoked_name = $ENV{INVOKED_NAME};

# Set defaults
$Extended_usage=0;
$Outfile = "r${stamp}_job";
$Quiet = 0;
@Updates_list = ();
$Initial_conditions = 0;
$Restart = 1;
$Restart_fname = '';
$Show_jobdef=0;
$LOG = 0;
$show_diag_deck = 0;
$replace_diag_decks = 1;
$Override_total_months_check = 0;

# $add_ext_delim is set when one of the command line options
# --begin_ext, --end_ext or --parallel_ext is invoked.
$add_ext_delim = 0;

# $ext_delim{add_begin_delim} is a reference to a list of job names to which
# "#BEGIN_EXTENDED_SECTION" delimiters will be prepended when
# $add_ext_delim is set.
@{$ext_delim{add_begin_delim}} = ();

# $ext_delim{add_end_delim} is a reference to a list of job names to which
# "#END_EXTENDED_SECTION" delimiters will be appended when
# $add_ext_delim is set.
@{$ext_delim{add_end_delim}} = ();

# $ext_delim{add_parallel_delim} is a reference to a list of job names to which
# "#PARALLEL_JOB" delimiters will be prepended when
# $add_ext_delim is set.
@{$ext_delim{add_parallel_delim}} = ();

# Parc_check flags the check of critical parameters that is done in gcmjcl.
# If unset then updates will be added that disable the critical parameter check.
# Eventually these updates should be made a part of gcmjcl so that the parc
# check may be controlled by an environment variable.
# The default is to check parc
$Parc_check = 1;

# default values for the "add" and "remove" entries in the VARS hash table
$Addvar = 0;
$Remvar = 1;

# The contents of CUSTOM_fname will be inserted into the "custom" job when that
# job module is used
$CUSTOM_fname = '';

# $Year_range, $Month_range and $Day_range will contain a space or comma separated
# pair of numbers. For $Year_range this will be the first and last year in
# the job string. For $Month_range this will be the first month in the first
# year and the last month in the last year. For $Day_range this will be the first
# day in the first year and month and the last day in the last year and month.
$Year_range = '';
$Month_range = '';
$Day_range = '';

# if set on the command line, either/both $Start_time_cmdl and $Stop_time_cmdl
# will each contain a colon separated list of the form year:month:day:hour
# indicating the user's preference for start and stop times
$Start_time_cmdl = '';
$Stop_time_cmdl = '';

# These variables flag substitution and/or variable replacement/add/remove in
# an update section of the job string. If set to zero then no substitution
# or replacement will occur inside an "### update script ###",
# "### update model ###" or "### update sub ###" section in any job string.
# If non-zero then substitution or replacement will occur in all these sections.
$Sub_in_update_section = 0;
$Rep_in_update_section = 0;

# Always_add_updates flags the insertion of updates into an update section
# regardless of the value of samerun in that job string. Normally, if samerun=on
# or is not present in a particular job, then updates supplied on the command
# line via the --updates option will not be inserted into the job. If this
# flag is set then updates will always be inserted in every job, provided that
# job has an ### update model ### (or sub or script or ocean) line. If that
# line is not present in a job then updates will never be inserted.
$Always_add_updates = 0;

# Reorder_updates flags the reordering of updates such that all %df update
# directives are at the top of the update section followed by all %c directives.
# When Reorder_updates is true any duplicate %df or %c update directives will
# also be removed from the update section.
$Reorder_updates = 0;

# These variables are flags used when a job string template is processed.
# $Template_Expand_Global flags the use of a sub shell to expand variable
# definitions found in the "global" section of the template.
# $Template_Insert_Global flags the insertion of the "global" section
# into other sections found in the template. These insertions are done after
# lines of the form /#.*Parmsub Parameters/ found in the other sections.
# Setting $Template_Insert_Global false is equivalent to ignoring the "global"
# section of the job string template.
$Template_Expand_Global = 0;
$Template_Insert_Global = 1;

# Quote_all_vval flags the addition of single or double quotes around variable
# values for all variable replacements done in the var_replace subroutine
$Quote_all_vval = 0;

# Quote_number_vval flags the addition of single or double quotes around variable
# values for all numeric variable replacements done in the var_replace subroutine
$Quote_number_vval = 0;

# VARS is a hash containing relevant info for each variable definition
# that is to be replaced/added/removed in the job string
%VARS = ();

# auxcode will contain user supplied code snippets etc that are to be inserted
# in specific modules after the job string has been created
%auxcode = ();

# Runid_substitute flags the global substitution of the string which is the
# value of runid in the initial job with the string which is the value of runid
# for the new job. This will only happen if the initial job string is read
# from a file.
$Runid_substitute = 0;

# maintain a list of temporary files that are to be deleted when the job finishes
my @TMPflist;

# Joblist is a list of job names that will be put in the job string
# when the job string is generated internally. Each job in this list must have
# a corresponding entry in the JOBDEF hash.
@Joblist = ();

# In_vlist will return the value of vname if the first argument is a vname
# in vlist and vname has a non-null value. It will return false otherwise.
# vlist is a previously defined list of vname=value pairs.
$In_vlist = sub {
  my ($vin)=shift;
  foreach (@vlist) {
    my ($vn,$vv) = split '=',$_,2;
    if ($vn eq $vin) {
      return wantarray ? ($vv, 1) : $vv;
    };
  };
  return wantarray ? () : undef;
};

# reset the value of an existing variable in vlist or append vname=val
# to vlist if that variable is not found in vlist
$set_vlist = sub {
  my ($varval)=shift;  # input a vname=value pair
  my ($vn,$vv,$vni,$vvi,@vl,$found);
  die "Error in vname=value pair: $varval.\n  Stopped"
    unless ($varval =~ /=/);
  ($vni,$vvi)=split '=',$varval,2;
  $vni =~ s/\s*//g;
  # Add this variable to the parin hash
  $parin{$vni} = "$vvi";
  $found=0;
  foreach (@vlist) {
    ($vn,$vv)=split '=',$_,2;
    $vn =~ s/\s*//g;
    if ($vn eq $vni) {
      push @vl,"${vn}=$vvi";
      $found = 1;
    } else {
      push @vl,$_;
    }
  }
  unless ($found) {push @vl,$varval};
  @vlist = @vl;
  return wantarray ? @vl : $found;
};

# Delete an existing variable from vlist and parin
# If the variable is not found in vlist, del_vlist will return false
$del_vlist = sub {
  my ($vni) = shift;  # input a variable name
  die "del_vlist: No variable name supplied.\n  Stopped" unless $vni;
  my $status = 0;
  my @vl = ();
  foreach (@vlist) {
    # Determine current variable name in list
    my ($vn,$vv) = split '=',$_,2;
    $vn =~ s/\s*//g;
    if ($vn eq $vni) {
      # set a flag to indicate that the variable was found in vlist
      $status=1;
      # Also remove this variable from the parin hash
      delete $parin{$vni} if exists $parin{$vni}
    } else {
      # otherwise copy the current entry to the output list
      push @vl,$_;
    }
  }
  # redefine vlist
  @vlist = @vl;
  # In scalar context:
  #   return true if the variable was found and false otherwise
  # In list context:
  #   return the new vlist
  return wantarray ? @vl : $status;
};

# Define a function that will return true (1) or false (0) given a string value
$ToF = sub {
  my ($str) = shift;
  my $ret = -1;
  $ret = 0 if $str =~ /^no$/i;
  $ret = 0 if $str =~ /^off$/i;
  $ret = 0 if $str =~ /^0$/i;
  $ret = 1 if $str =~ /^yes$/i;
  $ret = 1 if $str =~ /^on$/i;
  $ret = 1 if $str =~ /^1$/i;
  if ($ret < 0) {
    die "ToF: Invalid input value --> $str <--\n Stopped";
  }
  return wantarray ? ($ret) : $ret;
};

# job_string will hold a working copy of the job string to be created
@job_string = ();

# JOBS is an array of hashes. Each element will contain one subsection of
# the entire job string along with associated information
@JOBS = ();

# SUBS will contain global substitutions to be made to the job string
@SUBS = ();

# vlist will contain a list of "var_name=var_value" pairs
# The initial values set here may be superceeded by values set on the command line
@vlist = ();

# The $preset{var} hash identifies values for variables that are set internally
# before user has an opportunity to define them. These preset variables may
# be redefined on the command line by the user, but need not be.
# $preset{changed} flags whether the user has changed the preset value, or not.

# runid is typically used to identify the current job
$runid="r$stamp";
push @vlist, "runid=$runid";
$preset{var}{runid}=$runid;
$preset{changed}{runid}=0;

# get the login name for the real user id of invoker
$user = (getpwuid($<))[0];
if ($user) {
  # in vlist, username should be the login name while user may be anything
  # that will identify the user to people, such as the surname
  push @vlist, "username=$user";
  $preset{var}{username}=$user;
  $preset{changed}{username}=0;
  # the gcos entry in the pw file as returned in element 6 of getpwuid should
  # contain the users real name possibly followed by a comma separated list
  # (real_name, office_number, work_phone, home_phone)
  ($real_name) = split ',',(getpwuid($<))[6];
  $real_name =~ s/^\s*//;  # strip leading white space
  $real_name =~ s/\s*$//;  # strip trailing white space
  $real_name =~ s/.*\s(\w+)$/$1/;  # select only the last word
  $real_name =~ tr/a-z/A-Z/;       # translate to upper case
  unless ($real_name) {($real_name = $user) =~ tr/a-z/A-Z/}; # default to login name
  push @vlist, "user=$real_name";
  $preset{var}{user}="$real_name";
  $preset{changed}{user}=0;
  # push @vlist, "cfsuser=$user";
  # uxxx may be used to form part of the file name prefix
  if (($id) = $user =~ /(...)$/) {$uxxx="u$id"} else {$uxxx='ma'};
};
$uxxx='ma' unless defined $uxxx;
push @vlist, "uxxx=$uxxx";
$preset{var}{uxxx}=$uxxx;
$preset{changed}{uxxx}=0;

# Set up a hash to contain job specific variable definitions. These variable defs
# will override any other definitions specified via the command line or elsewhere.
$job_specific_vars{dump_list}{cfsuser} = $user;
$job_specific_vars{dump_sublist}{cfsuser} = $user;
$job_specific_vars{mdump}{cfsuser} = $user;

# Define a tag to insert at the top of each output file when $AddTAG is set
# The command line option --notag will unset $AddTAG
$AddTAG = 1;
$CMDLINE = '';

# Create a list of command line options in clist
# with any spaces or dollar signs quoted
$cline[0] = $Invoked_name;
$i=0;
# Identify all command line options that require an option value
$require_val{output} = 1;
$require_val{substitute} = 1;
$require_val{joblist} = 1;
$require_val{year_range} = 1;
$require_val{month_range} = 1;
$require_val{day_range} = 1;
$require_val{start_time} = 1;
$require_val{stop_time} = 1;
$require_val{deck} = 1;
$require_val{agcm} = 1;
$require_val{updates} = 1;
$last_arg = '';
foreach (@ARGV) {
  my $arg = $_;
  my $wantval = 0;

  # Check for command line options (preceeded by - or --)
  my ($opt) = /^ *-?-(.*)/;
  if ($opt) {
    # strip any trailing option value (=.*) and store in $oval
    my ($oval) = $opt =~ /=(.*)$/;
    $opt =~ s/=.*$//;
    if ($oval) {
      # The option value is supplied in oval
      # quote oval if it contains and space or dollar sign
      if ($oval =~ /[$ ]/) {
        if ($oval !~ /'/) {
          # use single quotes if it does not already contain a one
          $oval =~ s/(.*)/'$1'/;
        } elsif ($oval !~ /"/) {
          # use double quotes if it does not already contain one
          $oval =~ s/(.*)/"$1"/;
        } else {
          # quote all meta characters in oval with a back slash
          $oval= quotemeta ($oval);
        };
      };
      # always put 2 dashes in front of these options
      my $opt = " --${opt}=$oval";
      if (length($cline[$i].$opt) > 80) {$i++};
      $cline[$i] .= $opt;
      next;
    };
    # Determine if this option requires an option value
    my $pat = quotemeta ($opt);
    my @hits = grep (/^$pat/, keys %require_val);
    if (scalar(@hits) == 1) {
      $wantval = 1;
    } elsif (scalar(@hits) > 1) {
      die "Ambiguous option $arg specified on command line.\n  Stopped";
    };
    # ensure that $arg always starts with 2 dashes if it is
    # a command line option
    $arg = "--$opt";
  };

  # This arg is either a file name, a variable definition or an option value
  if ($arg =~ /[a-zA-Z_0-9-.]+/) {
    # If it consists entirely of alphanumeric chars plus - or . then check
    # if it is a file name and if so push it onto a list of files
    # to be written to the log dir.
    # Note: this precludes spaces in file names
    if (-s $arg) {push @files_to_log,$arg};
  };

  # quote arg if it contains and space or dollar sign
  if ($arg =~ /[$ ]/) {
    if ($arg =~ /=/) {
      # This arg contains an equal sign but is not a command line option
      # because that possibility has already been delt with. Therefore
      # it must be a variable assignment. Only quote the value.
      my ($var,$val) = split '=',$arg,2;
      if ($var =~ /[$ ]/) {
        if ($var !~ /'/) {
          # use single quotes if it does not already contain one
          $var =~ s/(.*)/'$1'/;
        } elsif ($var !~ /"/) {
          # use double quotes if it does not already contain one
          $var =~ s/(.*)/"$1"/;
        } else {
          # quote all meta characters in var with a back slash
          $var = quotemeta ($var);
        };
      };
      if ($val =~ /[$ ]/) {
        if ($val !~ /'/) {
          # use single quotes if it does not already contain one
          $val =~ s/(.*)/'$1'/;
        } elsif ($val !~ /"/) {
          # use double quotes if it does not already contain one
          $val =~ s/(.*)/"$1"/;
        } else {
          # quote all meta characters in val with a back slash
          $val = quotemeta ($val);
        };
      };
      $arg = $var.'='.$val;
    } else {
      if ($arg !~ /'/) {
        # use single quotes if it does not already contain one
        $arg =~ s/(.*)/'$1'/;
      } elsif ($arg !~ /"/) {
        # use double quotes if it does not already contain one
        $arg =~ s/(.*)/"$1"/;
      } else {
        # quote all meta characters in arg with a back slash
        $arg = quotemeta ($arg);
      };
    };
  };

  if ($wantval) {
    # The current command line arg requires an option value
    # This option value will be the next command line arg
    $last_arg = " $arg";
  } else {
    # Append to the cline array
    if (length($cline[$i] . "$last_arg $arg") > 80) {$i++};
    $cline[$i] .= "$last_arg $arg";
    $last_arg = '';
  };
};

# These variables are used by the subroutine history
# $CLstring will be the command line as a single string
$CLstring = join(' ',@cline);
# $display_hist flags display of the command line history
$display_hist = 0;
# $edit_hist flags editing previously stored commmand lines
$edit_hist = 0;

$i=0;
foreach (@cline) {
  if ($i++ == 0) {$CMDLINE .= "\n# $_"}
  else {$CMDLINE .= "\n#   $_"}
};
chomp($date_and_time = `date "+%c"`);
$full_name = (getpwuid($<))[6];
$TAG = "# Created $date_and_time by $full_name\n";
$TAG .= "# using $Invoked_name with the following options:$CMDLINE";
$TAG = sprintf("%s\n%s\n", '#'x80, $TAG);

# Empty_vlist is used to set vlist to null when invoked on the command line.
# It will also unset the Set_vlist_def flag which means no values will be
# defined for vlist internally.
$Set_vlist_def = 1;
$Empty_vlist = sub {@vlist = (); $Set_vlist_def=0};

# unused_clargs will contain any unknown command line arguments
# after the call to &GetOptions
@unused_clargs = ();

# This hash will be used to identify variables that have
# been set by the user via command line options
my %optset;

# Process command line arguments
use Getopt::Long;
$Getopt::Long::ignorecase = 0;
$Getopt::Long::order = $PERMUTE;
&GetOptions("help"                    => \$Extended_usage,
            "show_jobdef"             => \$Show_jobdef,
            "output=s"                => \$Outfile,
            "substitute=s"            => \@SUBS,
            "joblist=s"               => sub {
               push @Joblist,(split /\s+/,$_[1]);
               # push @Joblist,(split /\s+|\s*,\s*/,$_[1]);
             },
            "jdef-repo=s"             => sub {$JGIT{repo} = $_[1] unless $JGIT{repo}},
            "jdef-rev=s"              => sub {$JGIT{rev}  = $_[1] unless $JGIT{rev} },
            "jdef-path=s"             => sub {$JGIT{path} = $_[1] unless $JGIT{path}},
            "updates=s"               => \@Updates_list,
            "reorder_updates!"        => \$Reorder_updates,
            "parc_check!"             => \$Parc_check,
            "begin_ext=s"             => sub {
               push my @args, (split /\s+/, $_[1]);
               shift @args unless $args[0];
               die "Empty arg list for option --begin_ext\n"
                 unless scalar @args;
               foreach (@args) {
                 # strip any options appended to this job name
                 # Options are anything following a :
                 # Multiple : separated options are allowed
                 my ($opts) = /:(.*)$/;
                 s/:.*$//;
                 my $jobname = $_;
                 $ext_delim{not_at_start}{$jobname} = 0
                   if not defined $ext_delim{not_at_start}{$jobname};
                 $ext_delim{not_at_end}{$jobname} = 0
                   if not defined $ext_delim{not_at_end}{$jobname};
                 next unless $opts;
                 # Process options
                 foreach (split /\s*:\s*/, $opts) {
                   /^notf/i and do {$ext_delim{not_at_start}{$jobname} = 1; next};
                   /^notl/i and do {$ext_delim{not_at_end}{$jobname} = 1; next};
                   die "Invalid option \":$_\" in --begin_ext=${jobname}:$_\n  ";
                 }
               }
               push @{$ext_delim{add_begin_delim}}, @args;
               $add_ext_delim = 1;
            },
            "end_ext=s"               => sub {
               push my @args, (split /\s+/, $_[1]);
               shift @args unless $args[0];
               die "Empty arg list for option --end_ext\n"
                 unless scalar @args;
               foreach (@args) {
                 # strip any options appended to this job name
                 # Options are anything following a :
                 # Multiple : separated options are allowed
                 my ($opts) = /:(.*)$/;
                 s/:.*$//;
                 my $jobname = $_;
                 $ext_delim{not_at_start}{$jobname} = 0
                   if not defined $ext_delim{not_at_start}{$jobname};
                 $ext_delim{not_at_end}{$jobname} = 0
                   if not defined $ext_delim{not_at_end}{$jobname};
                 next unless $opts;
                 # Process options
                 foreach (split /\s*:\s*/, $opts) {
                   /^notf/i and do {$ext_delim{not_at_start}{$jobname} = 1; next};
                   /^notl/i and do {$ext_delim{not_at_end}{$jobname} = 1; next};
                   die "Invalid option \":$_\" in --end_ext=${jobname}:$_\n  ";
                 }
               }
               push @{$ext_delim{add_end_delim}}, @args;
               $add_ext_delim = 1;
            },
            "parallel_ext=s"           => sub {
               push my @args, (split /\s+/, $_[1]);
               shift @args unless $args[0];
               die "Empty arg list for option --parallel_ext\n"
                 unless scalar @args;
               foreach (@args) {
                 # strip any options appended to this job name
                 # Options are anything following a :
                 # Multiple : separated options are allowed
                 my ($opts) = /:(.*)$/;
                 s/:.*$//;
                 my $jobname = $_;
                 $ext_delim{not_at_start}{$jobname} = 0
                   if not defined $ext_delim{not_at_start}{$jobname};
                 $ext_delim{not_at_end}{$jobname} = 0
                   if not defined $ext_delim{not_at_end}{$jobname};
                 next unless $opts;
                 # Process options
                 foreach (split /\s*:\s*/, $opts) {
                   /^notf/i and do {$ext_delim{not_at_start}{$jobname} = 1; next};
                   /^notl/i and do {$ext_delim{not_at_end}{$jobname} = 1; next};
                   die "Invalid option \":$_\" in --parallel_ext=${jobname}:$_\n  ";
                 }
               }
               push @{$ext_delim{add_parallel_delim}}, @args;
               $add_ext_delim = 1;
            },
            "agcm=s"                  => \&read_agcm_config,
            "decks=s"                 => \&read_diag_deck,
            "year_range=s"            => \$Year_range,
            "month_range=s"           => \$Month_range,
            "day_range=s"             => \$Day_range,
            "start_time=s"            => sub {
               $Start_time_cmdl = $_[1];
               $optset{Start_time_cmdl} = 1 },
            "stop_time=s"             => sub {
               $Stop_time_cmdl = $_[1];
               $optset{Stop_time_cmdl} = 1 },
            "initial_conditions!"     => sub{
               $optset{Initial_conditions} = 1;
               $optset{Restart} = 1;
               if ($_[1]) {$Initial_conditions = 1; $Restart = 0}
               else       {$Initial_conditions = 0; $Restart = 1} },
            "restart:s"               => sub{
               $optset{Initial_conditions} = 1;
               $optset{Restart} = 1;
               $Initial_conditions = 0; $Restart = 1;
               if ($_[1]) {$Restart_fname = $_[1]} },
            "Define=s"                => sub {
               my ($x) = $_[1] =~ /^\s*(\w+)/;
               die "Invalid argument for --Define = $_[1]\n" unless $x;
               my ($y) = $_[1] =~ /^\s*$x(?:\s*=\s*|\s+)(.*)/;
               $y =~ s/\s*$// if $y;
               my ($z) = $_[1] =~ /^\s*$x(.*)/;
               $z =~ s/^(\s*=\s*|\s+)// if $z;
               $z =~ s/\s*$// if $z;
               die "Invalid name for --Define = $_[1]\n" if (not $y and $z);
               $DEFINE{$x} = $y ? $y : ''},
            "Undef=s"                 => sub {
               my ($x) = $_[1] =~ /^\s*(\w+)/;
               die "Invalid argument for --Undef = $_[1]\n" unless $x;
               $UNDEF{$x} = 1},
            "addvar!"                 => \$Addvar,
            "remvar!"                 => \$Remvar,
            "always_add_updates!"     => \$Always_add_updates,
            "runid_substitute!"       => \$Runid_substitute,
            "emptyvlist"              => \&$Empty_vlist,
            "Override_total_months_check!" => \$Override_total_months_check,
            "template_expand_global!" => \$Template_Expand_Global,
            "template_insert_global!" => \$Template_Insert_Global,
            "quote_all_vval!"         => \$Quote_all_vval,
            "tag!"                    => \$AddTAG,
            "log!"                    => \$LOG,
            "history!"                => \$display_hist,
            "edit_hist!"              => \$edit_hist,
            "verbose"                 => sub {$Verbose++},
            "quiet"                   => \$Quiet,
            "<>"                      => \&cmd_line)
  or die "Error on command line. Stopped";

if (@unused_clargs) {show_usage "Unknown command line args :: @unused_clargs"};
if (@ARGV) {print "Command line args remaining in ARGV :: @ARGV\n"};

$Verbose = 0 if $Quiet;

if ($Verbose > 2) {
  print "hname=$hname  dname=$dname  alias=$alias  subnet=$subnet\n";
}

if ($Initial_conditions and $Restart) {
  die "Both initial conditions and restart are set. Stopped";
}
if (not $Initial_conditions and not $Restart) {
  die "Neither initial conditions nor restart are set. Stopped";
}

if ($Restart_fname and $Verbose > 0) {
  print "Starting from restart file $Restart_fname\n";
}

foreach (@vlist) {
  my ($var,$val) = split '=',$_,2;
  # Assign parin with definitions for all variables input by the user
  # as well as those set internally, at this point
  $parin{$var} = $val;

  # Adjust $preset{changed} for preset variables
  # that were defined on the command line
  if (exists $preset{var}{$var}) {
    unless ("$preset{var}{$var}" eq "$val") {
      $preset{var}{$var} = $val;
      $preset{changed}{$var} = 1;
    }
  }
}
if ($Verbose > 2) {
  foreach (sort keys %parin) {print "parin:  $_ = $parin{$_}\n"}
  foreach (sort keys %{$preset{var}}) {
    print "preset:  $_ = $preset{var}{$_} $preset{changed}{$_}\n";
  }
}

# Ensure that the variable $runid has the correct value
$runid = &$In_vlist("runid");

# Rationalize agcm hash
&config_agcm({VERBOSE=>$Verbose});

# In diagnostic strings:
if ("$agcm{version}" eq "13e") {
  # Use eng_stat4
  $diag_deck{eng_stat}  = 0;
  $diag_deck{eng_stat2} = 0;
  $diag_deck{eng_stat3} = 0;
  $diag_deck{eng_stat4} = 1;
}
elsif ("$agcm{version}" lt "15f") {
  # AGCM version 15f and above should use eng_stat3
  # AGCM version 15e and below should use eng_stat2
  # Historically versions prior to 15d used eng_stat
  # but they can use eng_stat2 so we let them
  $diag_deck{eng_stat}  = 0;
  $diag_deck{eng_stat2} = 1;
  $diag_deck{eng_stat3} = 0;
  $diag_deck{eng_stat4} = 0;
}

# Ensure that only one version of eng_stat is used
my $eng_stats = 0;
foreach (keys %diag_deck) {
  next unless /^eng_stat/i;
  $eng_stats += $diag_deck{$_};
}
if ($eng_stats > 1) {
  print "More than one version of eng_stat was requested.\n";
  print "The versions that were requested are: ";
  foreach (keys %diag_deck) {
    next unless /^eng_stat/i;
    print " $_" if $diag_deck{$_};
  }
  print "\n";
  die;
}

if ($show_diag_deck) {
  unless ($replace_diag_decks) {
    print "Diagnostic decks will be as provided by input file\n";
    exit;
  }
  print "================================\n";
  print "Diagnostic decks in call order\n";
  print "================================\n";
  # print decks in call order, ignoring keys rank, ocean and comment
  foreach (sort {$diag_deck{rank}{$a} <=> $diag_deck{rank}{$b}}
              grep !/^(rank|ocean|comment)$/, keys %diag_deck) {
    my $isused = ' :: not used';
    $isused = ' ' if $diag_deck{$_};
    if ($Verbose < 2 and not $diag_deck{$_}) {next}
    if (/^par_def_/i) {
      # This is  a special case to insert a variable definition
      my ($var) = /^par_def_(.*)$/i;
      unless ($var) {
        warn "No variable name was appended to par_def_\n";
        next;
      }
      unless ($par_def{$var}) {
        warn "No definition exists for par_def parameter $var\n";
        next;
      }
      print "#par_def\n    ${var}=\"$par_def{$var}\"\n";
    } else {
      my $isocn = ' ';
      if ($diag_deck{ocean}{$_}) {$isocn = 'ocean diag'}
      printf "%-12s%20s%-20s\n",$isocn,$_,$isused;
    }
  }
  print "================================\n";
  exit;
}

# invoke the history mechanism
unless ($CLstring eq $Invoked_name) {
  history($CLstring,
          {DISPLAY_HIST=>$display_hist,
              EDIT_HIST=>$edit_hist,
           INVOKED_NAME=>$Invoked_name });
};
exit if $display_hist or $edit_hist;

$clone_check = sub {
  my ($modname) = shift;  # input the name of the current module
  # Determine if this job is to be a clone of an existing jobdef.
  # Cloned module names are the name of the existing jobdef module with
  # 1 or more alphanumeric characters appended to it.
  # Only those modules that appear in the following conditional are
  # allowed to be cloned.
  my $clone_id = '';
  if (($clone_id) = $modname =~ /^cloop(\w+)$/ or
      ($clone_id) = $modname =~ /^custom(\w+)$/ or
      ($clone_id) = $modname =~ /^mload(\w+)$/ or
      ($clone_id) = $modname =~ /^mdump(\w+)$/ or
      ($clone_id) = $modname =~ /^mdelete(\w+)$/ or
      ($clone_id) = $modname =~ /^load_list(\w+)$/ or
      ($clone_id) = $modname =~ /^load_sublist(\w+)$/ or
      ($clone_id) = $modname =~ /^par_mdump(\w+)$/ or
      ($clone_id) = $modname =~ /^dump_list(\w+)$/ or
      ($clone_id) = $modname =~ /^dump_sublist(\w+)$/ or
      ($clone_id) = $modname =~ /^del_list(\w+)$/ or
      ($clone_id) = $modname =~ /^sdump(\w+)$/ or
      ($clone_id) = $modname =~ /^sdelete(\w+)$/ or
      ($clone_id) = $modname =~ /^rtrans(\w+)$/ or
      ($clone_id) = $modname =~ /^pool_mul(\w+)$/ or
      ($clone_id) = $modname =~ /^gztsdiag(\w+)$/ or
      ($clone_id) = $modname =~ /^spltdiag(\w+)$/ or
      ($clone_id) = $modname =~ /^block(\w+)$/ or
      ($clone_id) = $modname =~ /^next_block(\w+)$/ or
      ($clone_id) = $modname =~ /^resavejhome(\w+)$/ or
      ($clone_id) = $modname =~ /^tseries(\w+)$/ or
      ($clone_id) = $modname =~ /^tscat(\w+)$/ or
      ($clone_id) = $modname =~ /^tsdiag(\w+)$/ or
      ($clone_id) = $modname =~ /^ccc2nc(\w+)$/ or
      ($clone_id) = $modname =~ /^nccrip(\w+)$/ or
      ($clone_id) = $modname =~ /^xc2ppp(\w+)$/ or
      ($clone_id) = $modname =~ /^canesm_dump_hist(\w+)$/ or
      ($clone_id) = $modname =~ /^canesm_load_hist(\w+)$/ or
      ($clone_id) = $modname =~ /^canesm_nemo_rbld(\w+)$/ or
      ($clone_id) = $modname =~ /^plotmix(\w+)$/ or
      ($clone_id) = $modname =~ /^anomaly(\w+)$/ ) {
    # curr_module should be the name of an existing entry in %JOBDEF
    my ($curr_module) = $modname =~ /(.*)$clone_id$/;
    # clone will be the name of the new entry
    my $clone = "$curr_module$clone_id";
    # This should never happen but just in case...
    die "Unable to clone module $curr_module.\n  Stopped"
      if ("$curr_module" eq "$clone");
    # attempt to clone this module
    if (exists $JOBDEF{job}{$curr_module}) {
      unless (exists $JOBDEF{job}{$clone}) {
        print "Cloning $curr_module as $clone\n" if $Verbose > 5;
        $JOBDEF{job}{$clone} = $JOBDEF{job}{$curr_module};
        if (exists $JOBDEF{description}{$curr_module}) {
          $JOBDEF{description}{$clone} = $JOBDEF{description}{$curr_module}
        }
        # append $clone_id to all occurences of the string $curr_module
        # found in the job string just cloned
        my $tmpdef = '';
        foreach my $line (split "\n",$JOBDEF{job}{$clone}) {
          if ($line =~ /^\s*\.\s*/) {
            # Avoid changing file names of sourced files
            $tmpdef .= "$line\n";
            next;
          }
          $line =~ s/($curr_module)/$1$clone_id/mig;
          $tmpdef .= "$line\n";
        }
        $JOBDEF{job}{$clone} = $tmpdef;
        # add/append an entry to curr_module for this clone
        push @{$clone{$curr_module}}, $clone;
        # set misc attributes
        $JOBDEF{path}{$clone} = "internal clone of $curr_module";
        $JOBDEF{clone}{$clone} = 1;
        # For the plotmix and anomaly modules add values for either
        # plotmix${clone_id}_days or anomaly${clone_id}_days to vlist
        if ($curr_module =~ /^plotmix/ or $curr_module =~ /^anomaly/) {
          ($DAYS = $clone_id) =~ tr/a-z/A-Z/; # ensure upper case
          $DAYS =~ s/^_+//;  # strip leading underscore, if any
          # define a list of valid values for the variable days
          my @allow_days = ("DJF","MAM","JJA","SON","ANN","JAN","FEB","MAR",
            "APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC","M01",
            "M02","M03","M04","M05","M06","M07","M08","M09","M10","M11","M12");
          unless (grep /^$DAYS$/, @allow_days) {
            print "An invalid suffix \"$clone_id\" was found on module $curr_module.\n";
            print "Valid values are:\n  @allow_days\n";
            print "These values are case insensitive and may optionally be preceeded by an underscore.\n";
            die "Stopped";
          }
          push @vlist, "$curr_module${clone_id}_days=$DAYS";
        }
      } else {
        print "Attempting to clone module $curr_module as $clone";
        print " but $clone already exists.\n";
        print "description: $JOBDEF{description}{$clone}\n";
        print "       path: $JOBDEF{path}{$clone}\n";
        print "      clone: $JOBDEF{clone}{$clone}\n";
        # print "        job:\n$JOBDEF{job}{$clone}\n";
        die "Stopped";
      }
    } else {
      die "Attempting to clone $curr_module but it does not exist in JOBDEF\n";
    }
  }
  return 1;
};

# Define a hash containing info about each of the predefined job sequences.
# This hash will only be used to document what each special value means.
# Be careful to keep Predef_job_seq in sync with what happens in the loop below.
$Predef_job_seq{a13d}{cmd_line} =
  "--job='gcm13d:m mdump:y mdelete:y' --start=1 --stop=5 months=12";
$Predef_job_seq{a13d}{description} =
  "atm v.13d model run  ...default to 5 years in 12 month chunks";
$Predef_job_seq{a15c}{cmd_line} =
  "--job='gcm15c:m mdump:y mdelete:y' --start=1 --stop=5 months=6";
$Predef_job_seq{a15c}{description} =
  "atm v.15c model run  ...default to 5 years in 6 month chunks";
$Predef_job_seq{a15d}{cmd_line} =
  "--job='gcm15d:m mdump:y mdelete:y' --start=1 --stop=5 months=6";
$Predef_job_seq{a15d}{description} =
  "atm v.15d model run  ...default to 5 years in 6 month chunks";
$Predef_job_seq{a15e}{cmd_line} =
  "--job='gcm15e:m mdump:y mdelete:y' --start=1 --stop=6 months=6";
$Predef_job_seq{a15e}{description} =
  "atm v.15e model run  ...default to 6 years in 6 month chunks";
$Predef_job_seq{a15f}{cmd_line} =
  "--job='gcm15f:m mdump:y mdelete:y' --start=1 --stop=6 months=12";
$Predef_job_seq{a15f}{description} =
  "atm v.15f model run  ...default to 6 years in 12 month chunks";
$Predef_job_seq{a15g}{description} =
  "atm v.15g model run (t63)  ...default to 6 years in 6 month chunks";
$Predef_job_seq{rcm_data}{description} =
  "create RCM forcing data  ...default to 5 years in 1 month chunks";
$Predef_job_seq{rcm_run}{description} =
  "run RCM  ...default to 5 years in 1 month chunks";
$Predef_job_seq{diag4}{description} =
  "agcm IV diagnostics  ...default to 5 years from year 2 to year 6";
$Predef_job_seq{cgcm}{description} =
  "coupled ocn+atm model run";
$Predef_job_seq{cgcm}{comment} =
  "Run the coupled atm+ocn model with a t63 atm, 4 months per submission";
$Predef_job_seq{cgcm_15g}{description} =
  "coupled ocn+atm model run (atm=v15g ocn=v061)";
$Predef_job_seq{cdiag}{description} =
  "diagnostics for coupled model";
$Predef_job_seq{pool_all}{description} =
  "multi year pool of all months and all seasons";
$Predef_job_seq{pool_all_mon}{description} =
  "multi year pool of all months";
$Predef_job_seq{pool_all_sea}{description} =
  "multi year pool of all seasons";
$Predef_job_seq{apool}{description} =
  "pool diagnostic files from an AGCM only run";
$Predef_job_seq{cpool}{description} =
  "pool diagnostic files from a coupled run";

# Expand any special values indicating predefined job sequences
# that are found in the Joblist array
if (@Joblist) {
  my @jl = ();
  if ($Verbose > 10) {
    print "Number of jobs found in Joblist: ",scalar(@Joblist),"\n";
  }
  foreach (@Joblist) {
    if ($Verbose > 2) {print "JOB:: $_\n"};
    next unless $_;

    my $opt = '';
    # look for options, which are anything following a ":" in the keyword
    $opt = '' unless ($opt) = /(:.*)$/;
    # strip any options from the end of the keyword
    s/:.*$//;

    my $user_xopts = '';
    if ($opt) {
      # Extract user supplied definitions enclosed in angle brackets at the end of $opt
      #
      # Note: opt will still contain these angle bracket delimited defs and these
      # defs will get passed to specific jobs and/or aliases unless they are
      # explicitly removed below, prior to being pushed onto the Joblist
      ($user_xopts) = $opt =~ m/(<.*>)\s*$/;
    }

    # look for any file names following an "=" sign
    undef @curr_fnames;
    my $fspec_contains_percent = 0;
    $keymod = '' unless ($keymod) = /(=.*)$/;
    if (/=/) {
      my ($fspec) = /=(.*)$/;
      $fspec_contains_percent = 1 if ($fspec =~ /%/);
      @curr_fnames = split '%',$fspec;
      unless (scalar(@curr_fnames)) {
        print "The joblist keyword $_ requires a name specification following =\n";
        print "A name specification is a list of file names";
        print ' separated by "%" characters',"\n";
        die;
      };
      # strip the file name specification from the end of the keyword
      s/=.*$//;
    }

    # Determine if this job is to be a clone of an existing jobdef.
    # If so, add an entry to JOBDEF for the clone
    &$clone_check($_);

    ############################################################################
    # check for special values that indicate predefined job sequences
    ############################################################################

    /^a13d$/ and do {
      # default to run for 5 years
      # These may be changed via --start_time and --stop_time
      $Start_time_cmdl = '1:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '5:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # Run in 12 month chunks unless months is set on the command line
      push @vlist, "months=12" unless &$In_vlist("months");
      # add this job sequence to the job string
      push @jl, ("gcm13d:m","mdump:y","mdelete:y");
      next
    };
    ############################################################################

    /^a15c$/ and do {
      # default to run for 5 years
      # These may be changed via --start_time and --stop_time
      $Start_time_cmdl = '1:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '5:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # Run in 6 month chunks unless months is set on the command line
      push @vlist, "months=6" unless &$In_vlist("months");
      # add this job sequence to the job string
      push @jl, ("gcm15c:m","mdump:y","mdelete:y");
      next
    };
    ############################################################################

    /^a15d$/ and do {
      # default to run for 5 years
      # These may be changed via --start_time and --stop_time
      $Start_time_cmdl = '1:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '5:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # Run in 6 month chunks unless months is set on the command line
      push @vlist, "months=6" unless &$In_vlist("months");
      # add this job sequence to the job string
      push @jl, ("gcm15d:m","mdump:y","mdelete:y");
      next
    };
    ############################################################################

    /^a15e$/ and do {
      # default to run for 6 years
      # These may be changed via --start_time and --stop_time
      $Start_time_cmdl = '1:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '6:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # Run in 6 month chunks unless months is set on the command line
      push @vlist, "months=6" unless &$In_vlist("months");
      # add this job sequence to the job string
      push @jl, ("gcm15e:m","mdump:y","mdelete:y");
      next
    };
    ############################################################################

    /^a15f$/ and do {
      # default to run for 6 years
      # These may be changed via --start_time and --stop_time
      $Start_time_cmdl = '1:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '6:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # Run in 12 month chunks unless months is set on the command line
      push @vlist, "months=12" unless &$In_vlist("months");
      # add this job sequence to the job string
      push @jl, ("gcm15f:m","mdump:y","mdelete:y");
      next
    };
    ############################################################################

    /^a15g$/ and do {
      # default to run for 6 years
      # These may be changed via --start_time and --stop_time
      $Start_time_cmdl = '1:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '6:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # Run in 6 month chunks unless months is set on the command line
      push @vlist, "months=6" unless &$In_vlist("months");
      # add this job sequence to the job string
      push @jl, ("gcm15g_t63:m","mdump:y","mdelete:y");
      next
    };
    ############################################################################

    /^diag4$/ and do {
      # default to run from year 1:1 to year 6:12
      # These may be changed via --start_time and --stop_time
      $Start_time_cmdl = '2:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '6:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # ensure we can start from some time other than year 1 month 1
      $Initial_conditions = 0;
      $Restart = 1;
      # Run in 1 month chunks unless months is set on the command line
      push @vlist, "months=1" unless &$In_vlist("months");
      # delete gs and ss files with mdelete
      push @vlist, "mdelete_suffix_list=gs ss"
        unless &$In_vlist("mdelete_suffix_list");
      # dump gp and xp files with mdump
      push @vlist, "mdump_suffix_list=gp xp"
        unless &$In_vlist("mdump_suffix_list");
      # load gs and ss files with mload
      push @vlist, "mload_suffix_list=gs ss"
        unless &$In_vlist("mload_suffix_list");
      # make sure files are loaded onto pollux
      push @vlist, "sv=on" unless &$In_vlist("sv");
      # include annual average files in sdump
      push @vlist, "sdump_ann=on"
        unless &$In_vlist("sdump_ann");

      # Determine the name of the file containing the diagnostic string
      if (@curr_fnames) {
        # take the name of the string from diag4=fname on command line
        $diag_string = $curr_fnames[0];
      } elsif (defined $agcm{hres}) {
        # defined based on the horizontal resolution in the agcm
        if ("$agcm{hres}" eq "T63") {
          $diag_string = 'mdiag_t63';
        } elsif ("$agcm{hres}" eq "T47") {
          $diag_string = 'mdiag_t47';
        } else {
          die "A diagnostic string for $agcm{hres} is not available.\n Stopped";
        }
      } else {
        # this is the default
        $diag_string = 'mdiag_t47';
      }

      # Ensure that a JOBDEF for $diag_string exists
      unless (exists $JOBDEF{job}{$diag_string}) {
        # Find the file and define a JOBDEF entry for it
        my @fbody = file_body($diag_string,{VERBOSE=>1});
        if ($fbody[0]) {
          $JOBDEF{clone}{$diag_string} = 0;
          $JOBDEF{description}{$diag_string} = "Diagostic string";
          $JOBDEF{hide}{$diag_string} = '';
          $JOBDEF{job}{$diag_string} = $fbody[0];
          $JOBDEF{path}{$diag_string} = $fbody[1];
          $JOBDEF{user_supplied}{$diag_string} = 1;
        } else {
          die "Unable to locate diagnostic string $diag_string.\n Stopped";
	}
      }

      if ($replace_diag_decks) {
        # define a list of diagnostic decks to use
        $diag_decks_used = '';
        if ($Verbose > 0) {print "Diagnostic decks used in ${diag_string}:\n"}
        # cycle through decks in call order (ignoring keys rank, ocean
        # and comment) and build a string that will become the body of
        # the "Deck Definition" section of the diagnostic job.
        foreach (sort {$diag_deck{rank}{$a} <=> $diag_deck{rank}{$b}}
                    grep !/^(rank|ocean|comment)$/, keys %diag_deck) {
          next unless $diag_deck{$_};
          # Do not insert ocean diagnostic decks
          next if $diag_deck{ocean}{$_};
          $diag_decks_used .= "\n";
          if ($diag_deck{comment}{$_}) {
            my $comment = $diag_deck{comment}{$_};
            $comment =~ s/\n/\n  #/g;
            $diag_decks_used .= '  # ' . "$comment\n";
          }
          s/\s*//;
          if (/^par_def_/i) {
            # This is  a special case to insert a variable definition
            my ($var) = /^par_def_(.*)$/i;
            die "No variable name was appended to par_def_\n Stopped"
              unless $var;
            die "No definition exists for par_def parameter $var\n Stopped"
              unless $par_def{$var};
            $diag_decks_used .= "#par_def\n    ${var}=\"$par_def{$var}\"\n";
            if ($Verbose > 0) {
              print "#par_def\n    ${var}=\"$par_def{$var}\"\n";
            }
          } else {
            # Insert the source call for this deck
            $diag_decks_used .= '  . ' . "$_" . '.dk' . "\n";
            if ($Verbose > 0) {print "          $_\n"}
          }
        }
        if (exists $JOBDEF{job}{$diag_string}) {
          # insert $diag_decks_used into the diagnostic job
          # These regexes identify the start and end
          # of the deck definition section
          my $arex = '\n# *\* *\.+ *Deck +Definition[^\n]*\n';
          my $brex = '\n#end_of_job[^\n]*\n';
          $JOBDEF{job}{$diag_string} =~ s/($arex).*($brex)/$1$diag_decks_used$2/s;
          if ($Verbose > 10) {
            print "mdiag:\n$JOBDEF{job}{$diag_string}\n";
          }
        } else {
          die "Diagnostic string $diag_string is missing.\n Stopped";
        }
      }

      # add this job sequence to the job string
      push @jl, ("mload:-1y","${diag_string}:m","mdelete:m","mdump:y",
        "pool_sea:DMJS","pool_ann:y","pool_mul=ann:s",
        "pool_mul=djf:s","pool_mul=mam:s","pool_mul=jja:s","pool_mul=son:s",
        "sdump:s","sdelete:s");
      next
    };
    ############################################################################

    /^cgcm$/ and do {
      #----------- coupled atm+ocean
      # default to run starting at the beginning of year 7
      # These may be changed via command line options --start_time and --stop_time
      $Start_time_cmdl = '7:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '46:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # set shortermdir
      push @vlist, "shortermdir=off"
        unless &$In_vlist("shortermdir");
      # ksteps must be set to the number of time steps in 1 day
      push @vlist, "ksteps=96"
        unless &$In_vlist("ksteps");
      # Run in 4 month chunks unless months is set on the command line
      push @vlist, "months=4"
        unless &$In_vlist("months");
      # set the global file prefix to "mc" overwriting any existing uxxx value
      unless (&$In_vlist("uxxx")) {&$set_vlist("uxxx=mc")};
      # dump ocean and atm history files
      push @vlist, "mdump_suffix_list=cm gz gs ss os+12 cs+12 rs+12 ab+12 ob+12 an+12"
        unless &$In_vlist("mdump_suffix_list");
      # delete ocean and atm files
      push @vlist, "mdelete_suffix_list=cm gz gs ss os cs rs ab ob an _script"
        unless &$In_vlist("mdelete_suffix_list");
      # add this job sequence to the job string
      push @jl, ("cgcm15f:m","mdump:4m","mdelete:4m");
      next
    };
    ############################################################################

    /^cgcm_15g$/ and do {
      #############################################################################
      #----------- coupled atm+ocean  (atm=v15g  ocn=v061)
      # default to run starting at the beginning of year 7
      # These may be changed via command line options --start_time and --stop_time
      #############################################################################
      $Start_time_cmdl = '7:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '46:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # set shortermdir
      push @vlist, "shortermdir=off"
        unless &$In_vlist("shortermdir");
      # ksteps must be set to the number of time steps in 1 day
      &$set_vlist("ksteps=96");
      &$set_vlist("delt=900");
      &$set_vlist("float1=off");
#      push @vlist, "ksteps=96"
#        unless &$In_vlist("ksteps");
      # Run in 6 month chunks unless months is set on the command line
      push @vlist, "months=6"
        unless &$In_vlist("months");
      # Always recompile unless explicitly told not to by setting samerun on cmd line
      push @vlist, "samerun=off"
        unless &$In_vlist("samerun");
      # set the global file prefix to "mc" overwriting any existing uxxx value
      push @vlist, "uxxx=mc" unless exists $parin{uxxx};
#        unless (&$In_vlist("uxxx"));
      # dump ocean and atm history files
      push @vlist, "mdump_suffix_list=cm gz gs ss os+12 cs+12 rs+12 ab+12 ob+12 an+12"
        unless &$In_vlist("mdump_suffix_list");
      # delete ocean and atm files
      push @vlist, "mdelete_suffix_list=cm gz gs ss os cs rs ab ob an _script"
        unless &$In_vlist("mdelete_suffix_list");
      # add this job sequence to the job string
      push @jl, ("cgcm15g:m","mdump:m","mdelete:m");
      next
    };
    ############################################################################

    /^[amcrdf](diag|pool|plot)$/ and do {
      #############################################################################
      # Diagnostics/pooling/plotting using the "offical" naming convention (or not)
      #############################################################################

      my $this_job = $_;

      # runid is required to be set on the command line
      die "*** runid must be set on the command line when invoking $_.\n  Stopped"
        unless $preset{changed}{runid};

      # myp_freq is the multi year pooling frequency for pool files
      # The default interval is 5 years
      my $myp_freq = "5";
      my $mypx = "${myp_freq}y";
      my $myp_unit = "y";

      # user_iopts will contain any user supplied definitions enclosed within
      # angle brackets, found appended to $opt
      my $user_iopts = '';

      # Strip any angle bracketed defs from the end of $opt and assign them,
      # as a string without the angle brackets, to $user_iopts
      ($user_iopts) = $opt =~ m/\s*<(.*)>\s*$/ if $opt;
      $opt =~ s/\s*<(.*)>\s*$// if $opt;

      if ($opt) {
        # The user has specified a pooling frequency on the command line via
        # something like cpool:10y. The modifier (10y in this case) must be
        # a positive integer greater than 1, followed by the letters "y" or "m".
        # Any other form of modifier will result in an error.
        ($mypx) = $opt =~ /^:(.*)/;
        ($myp_freq) = $mypx =~ /^([+-]?\d*)/;
        $myp_freq = 1 unless $myp_freq;
        $myp_freq = 1 * $myp_freq;
        $mypx =~ s/^[+]//;
        ($myp_unit) = $mypx =~ /^[+-]?\d*(.*)/;
        die "$_${opt} --- Invalid pooling frequency $mypx (must be in years or months).\n"
          unless ($myp_unit eq "y" or $myp_unit eq "m");
        die "$_${opt} --- Pooling frequency $mypx must be greater than or equal to 1.\n"
          if ($myp_freq < 1);
      }

      # Ensure that myp_freq is of the form "\d+m" so that
      # the pooling can start from months other than JAN
      if ($mypx =~ /^\s*\d+y/) {
        # Convert <N>y to <M>m
        my ($freq_in_years, $subfreq) = $mypx =~ /^\s*(\d+)y(.*)$/;
        my $freq_in_months = 12*$freq_in_years;
        $mypx = "${freq_in_months}m$subfreq";
        $myp_freq = $freq_in_months;
        $myp_unit = "m";
      }

      # Determine a value for coupled from the first letter of the name
      # under which this alias was invoked
      my $coupled = "off";
      $coupled = "on" if /^c/ or /^f/;
      # Add this definition to vlist if not already there
      push @vlist, "coupled=$coupled" unless &$In_vlist("coupled");

      # Determine how this alias was invoked (ie as diag, pool or plot)
      # and set defaults for inclusion of diagnostics, pooling and plotting.
      my ($invoked_as_diag, $invoked_as_pool, $invoked_as_plot) = (0,0,0);
      my ($with_diag, $with_pool, $with_plot) = (0,0,0);
      my $crawork="$parin{runid}_diag_$stamp";
      if ( /^.diag$/ ) {
        $invoked_as_diag = 1;
        $with_diag = 1;
        $with_pool = 1;
        $with_plot = 1;
        $crawork="$parin{runid}_diag_$stamp";
      }
      if ( /^.pool$/ ) {
        $invoked_as_pool = 1;
        $with_diag = 0;
        $with_pool = 1;
        $with_plot = 0;
        $crawork="$parin{runid}_pool_$stamp";
      }
      if ( /^.plot$/ ) {
        $invoked_as_plot = 1;
        $with_diag = 0;
        $with_pool = 0;
        $with_plot = 1;
        $crawork="$parin{runid}_plot_$stamp";
      }
      die "Problem invoking alias $_\n  Stopped"
        if ($invoked_as_diag==0 and $invoked_as_pool==0 and $invoked_as_plot==0);

      # Set a value for crawork
      # The user may overide this with a crawork assignment on the command line.
      &$set_vlist("crawork=$crawork") unless exists $parin{crawork};

      if ( ($myp_unit eq "y" and $myp_freq == 1) or
           ($myp_unit eq "m" and $myp_freq <= 12) ) {
        # It does not make sense to pool files with a frequency of 1 year or less
        # Set the default for no pooling and no plotting
        $with_pool = 0;
        $with_plot = 0;
        die "An invalid frequency of 1y was requested with --> $_ <--\n  Stopped"
          unless $with_diag;
      }

      if (exists $parin{with_diag}) {
        # Setting with_diag from the command line may be used to control
        # inclusion or exclusion of diagnostic decks
        $with_diag = &$ToF($parin{with_diag});
        # Remove this variable from vlist/parin
        &$del_vlist("with_diag");
      }

      if (exists $parin{with_pool}) {
        # Setting with_pool from the command line may be used to control
        # inclusion or exclusion of pooling decks
        $with_pool = &$ToF($parin{with_pool});
        # Remove this variable from vlist/parin
        &$del_vlist("with_pool");
      }

      if (exists $parin{with_plot}) {
        # Setting with_plot from the command line may be used to control
        # inclusion or exclusion of plotting decks
        $with_plot = &$ToF($parin{with_plot});
        # Remove this variable from vlist/parin
        &$del_vlist("with_plot");
      }

      # with_cp is used to indicate whether or not the cp file is to be used
      # to create time series, regardless of whether it exists or not
      my $with_cp = &$ToF($coupled);
      if (exists $parin{pool_with_cp}) {
        # Setting pool_with_cp on the command line will override the default
        $with_cp = &$ToF($parin{pool_with_cp});
        # Remove this variable from vlist/parin
        &$del_vlist("pool_with_cp");
      } elsif (exists $parin{with_cp}) {
        # Setting with_cp on the command line will override the default
        # unless pool_with_cp is also set on the command line
        $with_cp = &$ToF($parin{with_cp});
      }
      if ($with_cp) {
        $with_cp = "cp";
      } else {
        $with_cp = "";
      }

      # Set the default to be exclusion of time series decks
      my $with_tseries = 0;

      # If the user has specified a time series config file on the command line
      # then assume that the tseries deck is to be called.
      # Note that only the gp, xp and possibly cp files may be accessed by the
      # time series deck unless history files are loaded for the diagnostics.
      if (exists $parin{TS_config}) {$with_tseries = 1}
      if (exists $parin{with_tseries}) {
        # Setting with_tseries from the command line may be used to control
        # inclusion or exclusion of time series
        $with_tseries = &$ToF($parin{with_tseries});
        # Remove this variable from vlist/parin
        &$del_vlist("with_tseries");
      }

      # Determine if radiative forcing fields are present
      my $radforce = 0;
      if (exists $parin{radforce}) {
        # Setting radforce from the command line may be used to control
        # inclusion or exclusion of radiative forcing diagnostics
        $radforce = &$ToF($parin{radforce});
        # Remove this variable from vlist/parin
        &$del_vlist("radforce");
      }

      # load_hist_freq is a positive integer followed by "y" or "m" indicating
      # the frequency at which history files are loaded/deleted
      my $load_hist_freq = "12m";
      if (exists $parin{load_hist_freq}) {
        # load_hist_freq was set on the command line
        my $str1 = "Invalid command line assignment load_hist_freq=$parin{load_hist_freq}";
        my $str2 = "  load_hist_freq must be assigned a positive integer";
        my $str3 = " followed by the letter \"y\" or the letter \"m\".";
        die "$str1\n$str2$str3\n  Stopped"
          unless ($parin{load_hist_freq} =~ /^\s*\d*(y|m)\s*$/);

        # The value assigned to load_hist_freq on the command line must be
        # a positive integer followed by the letter "y" or the letter "m".
        # "y" or "m" alone indicate a frequency of 1 year or 1 month.
        $load_hist_freq = $parin{load_hist_freq};
        unless ($parin{load_hist_freq} =~ /^\s*\d+/) {
          $load_hist_freq = "1y" if $parin{load_hist_freq} =~ /y\s*$/;
          $load_hist_freq = "1m" if $parin{load_hist_freq} =~ /m\s*$/;
        }

        # Remove this variable from vlist/parin
        &$del_vlist("load_hist_freq");
      }

      # Ensure that load_hist_freq is of the form "\d+m" so that loading
      # can happpen on months other than JAN
      if ($load_hist_freq =~ /^\s*\d+y/) {
        # Convert Ny to Mm
        my ($freq_in_years, $subfreq) = $load_hist_freq =~ /^\s*(\d+)y(.*)$/;
        my $freq_in_months = 12*$freq_in_years;
        $load_hist_freq = "${freq_in_months}m$subfreq";
      }

      # load_diag_freq is a positive integer followed by "y" or "m" indicating
      # the frequency at which diagnostic files are loaded
      my $load_diag_freq = $mypx;
      if (exists $parin{load_diag_freq}) {
        # load_diag_freq was set on the command line
        my $str1 = "Invalid command line assignment load_diag_freq=$parin{load_diag_freq}";
        my $str2 = "  load_diag_freq must begin with a positive integer";
        my $str3 = " followed by the letter \"y\" or the letter \"m\".";
        die "$str1\n$str2$str3\n  Stopped"
          unless ($parin{load_diag_freq} =~ /^\s*\d+(y|m)(:\d+(y|m))?\s*$/);

        $load_diag_freq = $parin{load_diag_freq};

        # Remove this variable from vlist/parin
        &$del_vlist("load_diag_freq");
      }

      # Ensure that load_diag_freq is of the form "\d+m" so that loading
      # can happpen on months other than JAN
      if ($load_diag_freq =~ /^\s*\d+y/) {
        # Convert Ny to Mm
        my ($freq_in_years, $subfreq) = $load_diag_freq =~ /^\s*(\d+)y(.*)$/;
        my $freq_in_months = 12*$freq_in_years;
        $load_diag_freq = "${freq_in_months}m$subfreq";
      }

      # dump_diag_freq is a positive integer followed by "y" or "m" indicating
      # the frequency at which diagnostic files are dumped to tape
      my $dump_diag_freq = "60m";
      if ( $myp_unit eq "y" ) {
        if ($myp_freq < 5) {
          $dump_diag_freq = "y";
        }
      } elsif ( $myp_unit eq "m" ) {
        if ($myp_freq < 60) {
          $dump_diag_freq = "12m";
        }
      } else {
        die "Invalid units for pooling frequency --> $mypx <--\n";
      }
      if (exists $parin{dump_diag_freq}) {
        # dump_diag_freq was set on the command line
        my $str1 = "Invalid command line assignment dump_diag_freq=$parin{dump_diag_freq}";
        my $str2 = "  dump_diag_freq must be assigned a positive integer";
        my $str3 = " followed by the letter \"y\" or the letter \"m\".";
        die "$str1\n$str2$str3\n  Stopped"
          unless ($parin{dump_diag_freq} =~ /^\s*\d*(y|m)\s*$/);

        # The value assigned to dump_diag_freq on the command line must be
        # a positive integer followed by the letter "y" or the letter "m".
        # "y" or "m" alone indicate a frequency of 1 year or 1 month.
        $dump_diag_freq = $parin{dump_diag_freq};
        unless ($parin{dump_diag_freq} =~ /^\s*\d+/) {
          $dump_diag_freq = "1y" if $parin{dump_diag_freq} =~ /y\s*$/;
          $dump_diag_freq = "1m" if $parin{dump_diag_freq} =~ /m\s*$/;
        }

        # Remove this variable from vlist/parin
        &$del_vlist("dump_diag_freq");
      }

      # Ensure that dump_diag_freq is of the form "\d+m" so that dumping
      # can happpen on months other than DEC
      if ($dump_diag_freq =~ /^\s*\d+y/) {
        # Convert Ny to Mm
        my ($freq_in_years, $subfreq) = $dump_diag_freq =~ /^\s*(\d+)y(.*)$/;
        my $freq_in_months = 12*$freq_in_years;
        $dump_diag_freq = "${freq_in_months}m$subfreq";
      }

      # Set default start and stop dates if one or both of "--start", "--stop"
      # were not specified on the command line
      $Start_time_cmdl = '1:1' unless $Start_time_cmdl;
      if ( $myp_unit eq "y" ) {
        $Stop_time_cmdl = "${myp_freq}:12" unless $Stop_time_cmdl;
      } elsif ( $myp_unit eq "m" ) {
        my ($new_mon,$new_year) = new_mon_year(1,1,$myp_freq);
        $Stop_time_cmdl = "${new_year}:$new_mon" unless $Stop_time_cmdl;
      } else {
        die "Invalid units for pooling frequency --> $mypx <--\n";
      }
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;

      # Ensure we can start from some time other than year 1 month 1
      $Initial_conditions = 0;
      $Restart = 1;

      ######## BEGIN define pool_iopts ##########

      # Assign the variable used to pass internal options to pooling jobs
      my $pool_iopts = '';

      # Determine the year and month on which pooling will begin
      # The default will be the time set by --start=... on the command line
      # or year=1 mon=1 otherwise
      my ($pool_start_year,$pool_start_mon)  = split(/\s*:\s*/, $Start_time_cmdl);
      $pool_start_year = 1 unless $pool_start_year;
      $pool_start_mon  = 1 unless $pool_start_mon;
      my $user_supplied_pool_start = 0;
      if (exists $parin{pool_start}) {
        # Use the value supplied by the user via the pool_start=... command line def
        $user_supplied_pool_start = 1;
        ($pool_start_year,$pool_start_mon) = split(/\s*:\s*/, $parin{pool_start});
        $pool_start_year = 1 unless $pool_start_year;
        $pool_start_mon  = 1 unless $pool_start_mon;
        # This must be added to pool_iopts if it differs from the default
        $pool_iopts .= ",start_year=$pool_start_year" if $pool_start_year;
	if ($pool_start_mon) {
          if ($pool_start_mon < 1 or $pool_start_mon > 12) {
            my $msg = "Invalid value for --> pool_start=$parin{pool_start} <--";
            $msg .= "set on command line.\n";
            die "$msg";
          }
          # This must be added to pool_iopts if it differs from the default
          $pool_iopts .= ",start_mon=$pool_start_mon";
        }
        # Remove this variable from vlist/parin
        &$del_vlist("pool_start");
      }

      # Determine the year and month, beyond which pooling will not be included in
      # the job string, and add the appropriate stop_year and stop_mon to pool_iopts.
      my ($pool_stop_year,$pool_stop_mon)  = split(/\s*:\s*/, $Stop_time_cmdl);
      $pool_stop_year =  1 unless $pool_stop_year;
      $pool_stop_mon  = 12 unless $pool_stop_mon;
      my $user_supplied_pool_stop = 0;
      if (exists $parin{pool_stop}) {
        # Use the value supplied by the user via the pool_stop=... command line def
         $user_supplied_pool_stop = 1;
        ($pool_stop_year,$pool_stop_mon) = split(/\s*:\s*/, $parin{pool_stop});
        $pool_stop_year =  1 unless $pool_stop_year;
        $pool_stop_mon  = 12 unless $pool_stop_mon;
        # This must be added to pool_iopts if it differs from the default
        $pool_iopts .= ",stop_year=$pool_stop_year" if $pool_stop_year;
	if ($pool_stop_mon) {
          if ($pool_stop_mon < 1 or $pool_stop_mon > 12) {
            my $msg = "Invalid value for --> pool_stop=$parin{pool_stop} <--";
            $msg .= "set on command line.\n";
            die "$msg";
          }
          # This must be added to pool_iopts if it differs from the default
          $pool_iopts .= ",stop_mon=$pool_stop_mon";
        }
        # Remove this variable from vlist/parin
        &$del_vlist("pool_stop");
      } else {
        # Set stop year and mon such that the maximum number of complete pooling
        # intervals will fit between pool_start_(year/mon) and the end of the job
        my $total_months = 1 + diff_in_months($pool_start_year,$pool_start_mon,
                                              $pool_stop_year,$pool_stop_mon);
        my $max_pool_intervals = int($total_months/$myp_freq);
        my $max_months = $max_pool_intervals*$myp_freq;
        if ($max_pool_intervals <= 0) {
          my $msg = " *** ERROR *** ";
          $msg .= "You cannot fit any pooling intervals between the start";
          $msg .= " of pooling (${pool_start_year}:$pool_start_mon) and end";
          $msg .= " of the run (${pool_stop_year}:$pool_stop_mon)";
          die "$msg\n";
        }
        ($pool_stop_mon,$pool_stop_year) =
           new_mon_year($pool_start_mon, $pool_start_year, $max_months - 1);
        $pool_iopts .= ",stop_year=$pool_stop_year";
        $pool_iopts .= ",stop_mon=$pool_stop_mon";
      }

      # Add an option that will ensure that there will be no job insertion
      # at the end of the first pooling interval (ie between the start
      # of the job string and the pool start date) if that period of time
      # is less than the length of a pooling interval
      $pool_iopts .= ",full_first_interval=1";

      if ($pool_iopts) {
        # Strip the leading comma (if any) from this option list
        $pool_iopts =~ s/\s*,//;
        # Enclose these options within angle brackets
        substr($pool_iopts,0,0) = '<';
        $pool_iopts .= '>';
      }

      # Check that pool_start_(year/mon) is less than pool_stop_(year/mon)
      my $delta_months = diff_in_months($pool_start_year,$pool_start_mon,
                                        $pool_stop_year, $pool_stop_mon);
      if ($delta_months <= 0) {
        print " *** ERROR *** ";
        print "The pooling start date";
        print " (${pool_start_year}:$pool_start_mon)";
        print " is later than or equal to the pooling end date";
        print " (${pool_stop_year}:$pool_stop_mon).\n";
        die "  Stopped";
      }

      # Detemine the users supplied start year/month
      my ($user_set_run_start_year, $user_set_run_start_mon) =
        split(/\s*:\s*/, $Start_time_cmdl);
      $user_set_run_start_year = 1 unless $user_set_run_start_year;
      $user_set_run_start_mon  = 1 unless $user_set_run_start_mon;

      # Detemine the users supplied stop year/month
      my ($user_set_run_stop_year, $user_set_run_stop_mon) =
        split(/\s*:\s*/, $Stop_time_cmdl);
      $user_set_run_stop_year =  1 unless $user_set_run_stop_year;
      $user_set_run_stop_mon  = 12 unless $user_set_run_stop_mon;

      # Define a month offset as the difference, in months, between the
      # start of the pooling interval and the start of the run
      my $pool_mon_offset = diff_in_months($user_set_run_start_year,$user_set_run_start_mon,
                                           $pool_start_year,$pool_start_mon);

      # Check that pool_start_(year/mon) does not fall outside the job run time
      my $delta_months_start = diff_in_months($pool_start_year,$pool_start_mon,
                                $user_set_run_start_year,$user_set_run_start_mon);
      my $delta_months_stop = diff_in_months($pool_start_year,$pool_start_mon,
                                $user_set_run_stop_year,$user_set_run_stop_mon);
      if ($delta_months_start > 0) {
        if ($user_supplied_pool_start) {
          print " *** WARNING *** ";
        } else {
          print " *** ERROR *** ";
        }
        print "The pooling start date";
        print " (${pool_start_year}:$pool_start_mon)";
        print " is earlier than the start of the run";
        print " (${user_set_run_start_year}:$user_set_run_start_mon).\n";
        die "  Stopped" unless $user_supplied_pool_start;
      }
      if ($delta_months_stop <= 0) {
        print " *** ERROR *** ";
        print "The pooling start date";
        print " (${pool_start_year}:$pool_start_mon)";
        print " is later than or equal to the end of the run";
        print " (${user_set_run_stop_year}:$user_set_run_stop_mon).\n";
        die "  Stopped";
      }

      # Check that pool_stop_(year/mon) does not fall outside the job run time
      $delta_months_start = diff_in_months($pool_stop_year,$pool_stop_mon,
                                $user_set_run_start_year,$user_set_run_start_mon);
      $delta_months_stop = diff_in_months($pool_stop_year,$pool_stop_mon,
                                $user_set_run_stop_year,$user_set_run_stop_mon);
      if ($delta_months_start >= 0) {
        print " *** ERROR *** ";
        print "The pooling stop date";
        print " (${pool_stop_year}:$pool_stop_mon)";
        print " is earlier than or equal to the start of the run";
        print " (${user_set_run_start_year}:$user_set_run_start_mon).\n";
        die "  Stopped";
      }
      if ($delta_months_stop < 0) {
        if ($user_supplied_pool_stop) {
          print " *** WARNING *** ";
        } else {
          print " *** ERROR *** ";
        }
        print "The pooling stop date";
        print " (${pool_stop_year}:$pool_stop_mon)";
        print " is later than the end of the run";
        print " (${user_set_run_stop_year}:$user_set_run_stop_mon).\n";
        die "  Stopped" unless $user_supplied_pool_stop;
      }

      # Verify that the difference between pool_start_(year/mon) and
      # pool_stop_(year/mon) is an integer number of pooling intervals
      my $total_months = 1 + diff_in_months($pool_start_year,$pool_start_mon,
                                            $pool_stop_year,$pool_stop_mon);
      unless ($total_months % $myp_freq == 0) {
        if ($user_supplied_pool_stop) {
          print " *** WARNING *** ";
        } else {
          print " *** ERROR *** ";
        }
        print "There is not an integer number of pooling intervals";
        print " between pooling start";
        print " (${pool_start_year}:$pool_start_mon) and";
        print " stop (${pool_stop_year}:$pool_stop_mon) dates.\n";
        my $total_pool_intervals = $total_months/$myp_freq;
        print "                 There are $total_pool_intervals";
        print " pooling intervals of $myp_freq months in this range.\n";
        # This is only fatal if the user did not explicitly
        # set an end date for pooling
        die "  Stopped" unless $user_supplied_pool_stop;
      }

      # Possibly reorder the pool_iopts string
      $pool_iopts = ref2str_iopts( str2ref_iopts($pool_iopts) );

      ######## END define pool_iopts ##########

      unless ($with_diag) {
        # If there are no diagnostics being run then add the internal options
        # for pooling to load_diag_freq so that diagnostic files are loaded
        # on the pooling interval
        $load_diag_freq .= $pool_iopts;
      }

      # Determine the name of the file containing the diagnostic string
      if ($curr_fnames[0]) {
        # take the name of the string from the [amcrd]diag=fname arg for
        # "--joblist=" on the cccjob command line
        $diag_string = $curr_fnames[0];
        # Unset replace_diag_decks so that decks from the input file
        # will be used "as is".
        $replace_diag_decks = 0;
      } else {
        # this is the default one month diagnostic string
        $diag_string = 'stdiag';
        # Unset replace_diag_decks so that decks from the input file
        # will be used "as is". This may be changed in the future.
        $replace_diag_decks = 0;
        if ( exists $JOBDEF{job}{$diag_string} ) {
          if ($coupled =~ /^\s*off\s*/i ) {
            # Comment out the line that sources cmstats.dk
            # This is used in coupled run diags to create the "cp" file from the "cm" file
            $JOBDEF{job}{$diag_string} =~ s/^(\s*\.\s+cmstats\d*\.dk.*)/  #$1/m;
          }
          if ( exists $parin{xtrachem} ) {
            if ( $parin{xtrachem} =~ /^\s*on\s*$/i ) {
              # Uncomment the line that sources xtrachem.dk
              # This deck will generate extra tracer sources/sinks info
              $JOBDEF{job}{$diag_string} =~ s/^\s*#\s*(\.\s+xtrachem\d*\.dk.*)/  $1/m;
            }
            # Remove this variable from vlist/parin
            &$del_vlist("xtrachem");
          }
          if ( exists $parin{xtraconv} ) {
            if ( $parin{xtraconv} =~ /^\s*on\s*$/i ) {
              # Uncomment the line that sources xtraconv.dk
              # This deck will generate extra cloud convection info
              $JOBDEF{job}{$diag_string} =~ s/^\s*#\s*(\.\s+xtraconv\d*\.dk.*)/  $1/m;
            }
            # Remove this variable from vlist/parin
            &$del_vlist("xtraconv");
          }
          if ( $radforce ) {
            # Insert a line to source radforce.dk
            # This is used to add radiative forcing fields to the gp/xp files
            my $rfcomm = "  # --------------------- Radiative forcing";
            my $cleanall = q[((\n[\s\t]*#[^\n]*)*\n[\s\t]*\.[\s\t]+cleanall.dk[^\n]*)];
            $JOBDEF{job}{$diag_string} =~ s/$cleanall/\n$rfcomm\n  . radforce.dk$1/s;
          }
        }
      }

      # Ensure that a JOBDEF for $diag_string exists
      unless (exists $JOBDEF{job}{$diag_string}) {
        # Find the file and define a JOBDEF entry for it
        my @fbody = file_body($diag_string,{VERBOSE=>1});
        if ($fbody[0]) {
          $JOBDEF{clone}{$diag_string} = 0;
          $JOBDEF{description}{$diag_string} = "diagostic string";
          $JOBDEF{hide}{$diag_string} = '';
          $JOBDEF{job}{$diag_string} = $fbody[0];
          $JOBDEF{path}{$diag_string} = $fbody[1];
          $JOBDEF{user_supplied}{$diag_string} = 1;
        } else {
          die "Unable to locate diagnostic string $diag_string.\n Stopped";
	}
      }

      # The user may supply names for any or all of the 1 month diagnostic string,
      # plotmix, anomaly or heatrans deck that will be used in the diagnostic string
      # created here. This is accomplished by adding a "%" separated list
      # of names following the [ca]diag= in the --jobdef argument.
      # The first name in this list will be used for the 1 month diagnostic
      # string (see above). This file must be accessible when cccjob is run
      # as it will be copied directly into the job string created below.
      # The second name, if present, is the name of the plotmix deck. This
      # file name will replace the name that appears in the plotmix jobdef
      # on the last line of that job, where plotmix.dk is sourced.
      # Similarily, the third name will be the name of the anomaly deck that
      # is sourced in the anomaly jobdef and the fourth name, if present, will
      # be the name of the heatrans deck that is sourced in the heatrans jobdef.
      # Any of the file names may be missing (null). The number of "%" characters
      # that preceed a file name will determine whether it is the diag, plotmix,
      # anomaly or heatrans name.
      my $plotmix_deck = '';
      my $anomaly_deck = '';
      my $heatrans_deck = '';
      $plotmix_deck = $curr_fnames[1] if $curr_fnames[1];
      $anomaly_deck = $curr_fnames[2] if $curr_fnames[2];
      $heatrans_deck = $curr_fnames[3] if $curr_fnames[3];
      if ($plotmix_deck and exists $JOBDEF{job}{plotmix}) {
        $plotmix_deck .= '.dk' unless $plotmix_deck =~ /\.dk\s*$/;
        $JOBDEF{job}{plotmix} =~ s/^\s*\.\s+plotmix.*\n/ . $plotmix_deck\n/im;
        print "Replacing default plotmix deck with $plotmix_deck\n";
      }
      if ($anomaly_deck and exists $JOBDEF{job}{anomaly}) {
        $anomaly_deck .= '.dk' unless $anomaly_deck =~ /\.dk\s*$/;
        $JOBDEF{job}{anomaly} =~ s/^\s*\.\s+anomaly.*\n/ . $anomaly_deck\n/im;
        print "Replacing default anomaly deck with $anomaly_deck\n";
      }
      if ($heatrans_deck and exists $JOBDEF{job}{heatrans}) {
        $heatrans_deck .= '.dk' unless $heatrans_deck =~ /\.dk\s*$/;
        $JOBDEF{job}{heatrans} =~ s/^\s*\.\s+heatrans.*\n/ . $heatrans_deck\n/im;
        print "Replacing default heatrans deck with $heatrans_deck\n";
      }

      # Default resolution is T63
      my $resol = "128_64";
      if (defined $agcm{hres}) {
        # define resol from the agcm hash
        push @vlist, "hres=$agcm{hres}" unless &$In_vlist("hres");
        if ("$agcm{hres}" eq "T31") {
          $resol = '64_32';
        } elsif ("$agcm{hres}" eq "T47") {
          $resol = '96_48';
        } elsif ("$agcm{hres}" eq "T63") {
          $resol = '128_64';
        } elsif ("$agcm{hres}" eq "T95") {
          $resol = '192_96';
        } elsif ("$agcm{hres}" eq "T127") {
          $resol = '256_128';
        } else {
          die "$_ Invalid horizontal resolution $agcm{hres}.\n Stopped";
        }
      }
      # Set resol to define horizontal resolution in plotting decks etc
      push @vlist, "resol=$resol" unless &$In_vlist("resol");

      if ($replace_diag_decks) {
        # define a list of diagnostic decks to use
        $diag_decks_used = '';
        if ($Verbose > 0) {print "Diagnostic decks used:\n"}
        # cycle through decks in call order (ignoring keys rank, ocean
        # and comment) and build a string that will become the body of
        # the "Deck Definition" section of the diagnostic job.
        foreach (sort {$diag_deck{rank}{$a} <=> $diag_deck{rank}{$b}}
                    grep !/^(rank|ocean|comment)$/, keys %diag_deck) {
          next unless $diag_deck{$_};
          if ($coupled eq "off") {
            # Do not insert ocean diagnostic decks in an agcm diag string
            next if $diag_deck{ocean}{$_};
          }
          $diag_decks_used .= "\n";
          if ($diag_deck{comment}{$_}) {
            my $comment = $diag_deck{comment}{$_};
            $comment =~ s/\n/\n  #/g;
            $diag_decks_used .= '  # ' . "$comment\n";
          }
          s/\s*//;
          if (/^par_def_/i) {
            # This is  a special case to insert a variable definition
            my ($var) = /^par_def_(.*)$/i;
            die "No variable name was appended to par_def_\n Stopped"
              unless $var;
            die "No definition exists for par_def parameter $var\n Stopped"
              unless $par_def{$var};
            $diag_decks_used .= "#par_def\n    ${var}=\"$par_def{$var}\"\n";
            if ($Verbose > 0) {
              print "#par_def\n    ${var}=\"$par_def{$var}\"\n";
            }
          } else {
            # Insert the source call for this deck
            $diag_decks_used .= '  . ' . "$_" . '.dk' . "\n";
            if ($Verbose > 0) {print "          $_\n"}
          }
        }
        if (exists $JOBDEF{job}{$diag_string}) {
          # insert $diag_decks_used into the diagnostic job
          # These regexes identify the start and end
          # of the deck definition section
          my $arex = '\n# *\* *\.+ *Deck +Definition[^\n]*\n';
          my $brex = '\n#end_of_job[^\n]*\n';
          $JOBDEF{job}{$diag_string} =~ s/($arex).*($brex)/$1$diag_decks_used$2/s;
          if ($Verbose > 10) {
            print "$_:\n$JOBDEF{job}{$diag_string}\n";
          }
        } else {
          die "Diagnostic string $diag_string is missing.\n Stopped";
        }
      }

      # Define a prefix for all file types
      #
      # Use the "offical" naming convention by default.
      # This may be overidden by assiging one or more of model_uxxx, diag_uxxx,
      # pool_uxxx or tseries_uxxx on the command line.
      #
      # The "offical" naming convention implies certain prefixes
      #   -- monthly history file names begin with the letter "m"
      #   -- monthly diagnostic file names begin with the letter "d"
      #   -- pooled diagnostic file names begin with the letter "p"
      #   -- time series file names begin with the letter "s"
      #   -- AGCM only file names have the second letter "a"
      #   -- COUPLED files names have the second letter "c"
      #   -- MAM files names have the second letter "m"
      #   -- RCM files names have the second letter "r"
      #   -- ANALYSIS files names have the second letter "d"
      #
      # The first letter in the alias [amcrd](diag|pool|plot) will be the second
      # letter of file names that follow the "official" naming convention
      my ($lett2) = $this_job =~ /^(.)/;
      &$set_vlist("uxxx=m$lett2")         unless exists $parin{uxxx};
      &$set_vlist("model_uxxx=m$lett2")   unless exists $parin{model_uxxx};
      &$set_vlist("diag_uxxx=d$lett2")    unless exists $parin{diag_uxxx};
      &$set_vlist("pool_uxxx=p$lett2")    unless exists $parin{pool_uxxx};
      &$set_vlist("tseries_uxxx=s$lett2") unless exists $parin{tseries_uxxx};

      # Other parameters that could be set on the command line that would
      # override pool_uxxx, diag_uxxx or model_uxxx in specific modules include:
      #    pool_mul_model_uxxx    pool_mul_flabel_uxxx
      #    pool_sea_model_uxxx    pool_sea_flabel_uxxx
      #    pool_ann_model_uxxx    pool_ann_flabel_uxxx

      # Set stat2nd to turn off second order statistics in diag/plotting decks
      push @vlist, "stat2nd=off" unless &$In_vlist("stat2nd");

      # Set splitfiles to turn on on file splitting in diagnostics
      push @vlist, "splitfiles=on" unless &$In_vlist("splitfiles");

      if ($Verbose > 3) {
        print "model_uxxx=$parin{model_uxxx}   tseries_uxxx=$parin{tseries_uxxx}   ";
        print "diag_uxxx=$parin{diag_uxxx}   pool_uxxx=$parin{pool_uxxx}\n";
      }

      if ($Verbose > 3) {
        foreach (@vlist) {
          my ($var,$val) = split '=',$_,2;
          print "vlist value:  $var = $val\n";
        }
      }

      # Define alias specific flags to determine which parts
      # of the job string are included or excluded
      my $load_hist = 0;
      my $delete_hist = 0;
      my $load_diag = 0;
      my $dump_diag = 0;
      my $delete_diag = 0;
      my $dump_pooled = 0;
      my $transfer_pooled = 0;
      my $delete_pooled = 0;
      my $dump_impooled = 0;
      my $delete_impooled = 0;
      my $pool_months = 0;
      my $pool_seasons = 0;
      my $pool_annual = 0;
      my $pool_ignore_first_year = 0;
      if ($with_pool) {
        $load_diag = 1;
        $delete_diag = 1;
        $dump_pooled = 1;
        $delete_pooled = 1;
        $delete_impooled = 1;
        $pool_months = 1;
        $pool_seasons = 1;
        $pool_annual = 1;
      }
      if ($with_diag) {
        $load_hist = 1;
        $load_diag = 0;
        $dump_diag = 1;
        $delete_hist = 1;
        $delete_diag = 1;
      }

      if (exists $parin{load_hist}) {
        # Setting load_hist on the command line
        # will override the default value
        $load_hist = &$ToF($parin{load_hist});
        # Remove this variable from vlist/parin
        &$del_vlist("load_hist");
      }
      if (exists $parin{delete_hist}) {
        # Setting delete_hist on the command line
        # will override the default value
        $delete_hist = &$ToF($parin{delete_hist});
        # Remove this variable from vlist/parin
        &$del_vlist("delete_hist");
      }
      if (exists $parin{load_diag}) {
        # Setting load_diag on the command line
        # will override the default value
        $load_diag = &$ToF($parin{load_diag});
        # Remove this variable from vlist/parin
        &$del_vlist("load_diag");
      }
      if (exists $parin{dump_diag}) {
        # Setting dump_diag on the command line
        # will override the default value
        $dump_diag = &$ToF($parin{dump_diag});
        # Remove this variable from vlist/parin
        &$del_vlist("dump_diag");
      }
      if (exists $parin{delete_diag}) {
        # Setting delete_diag on the command line
        # will override the default value
        $delete_diag = &$ToF($parin{delete_diag});
        # Remove this variable from vlist/parin
        &$del_vlist("delete_diag");
      }
      if (exists $parin{dump_pooled}) {
        # Setting dump_pooled on the command line
        # will override the default value
        $dump_pooled = &$ToF($parin{dump_pooled});
        # Remove this variable from vlist/parin
        &$del_vlist("dump_pooled");
      }
      if (exists $parin{transfer_pooled}) {
        # Setting transfer_pooled on the command line will override the default
        $transfer_pooled = &$ToF($parin{transfer_pooled});
        # Remove this variable from vlist/parin
        &$del_vlist("transfer_pooled");
      }
      if (exists $parin{delete_pooled}) {
        # Setting delete_pooled on the command line
        # will override the default value
        $delete_pooled = &$ToF($parin{delete_pooled});
        # Remove this variable from vlist/parin
        &$del_vlist("delete_pooled");
      }
      if (exists $parin{dump_impooled}) {
        # Setting dump_impooled on the command line
        # will override the default value
        $dump_impooled = &$ToF($parin{dump_impooled});
        # Remove this variable from vlist/parin
        &$del_vlist("dump_impooled");
      }
      if (exists $parin{delete_impooled}) {
        # Setting delete_impooled on the command line
        # will override the default value
        $delete_impooled = &$ToF($parin{delete_impooled});
        # Remove this variable from vlist/parin
        &$del_vlist("delete_impooled");
      }
      if (exists $parin{pool_months}) {
        # Setting pool_months on the command line
        # will override the default value
        $pool_months = &$ToF($parin{pool_months});
        # Remove this variable from vlist/parin
        &$del_vlist("pool_months");
      }
      if (exists $parin{pool_seasons}) {
        # Setting pool_seasons on the command line
        # will override the default value
        $pool_seasons = &$ToF($parin{pool_seasons});
        # Remove this variable from vlist/parin
        &$del_vlist("pool_seasons");
      }
      if (exists $parin{pool_annual}) {
        # Setting pool_annual on the command line
        # will override the default value
        $pool_annual = &$ToF($parin{pool_annual});
        # Remove this variable from vlist/parin
        &$del_vlist("pool_annual");
      }
      if (exists $parin{pool_ignore_first_year}) {
        # Setting pool_ignore_first_year on the command line
        # will override the default value
        $pool_ignore_first_year = &$ToF($parin{pool_ignore_first_year});
      }

      my $pool_ann_incomplete_start_year = 0;
      my $pool_ann_incomplete_stop_year  = 0;
      if (exists $parin{pool_ann_extend}) {
        if ( $parin{pool_ann_extend} eq "start" ) {
          # Extend annual pooling prior to the start of the pooling period
          $pool_ann_incomplete_start_year = 1;
	} elsif ( $parin{pool_ann_extend} eq "stop" ) {
          # Extend annual pooling past the end of the pooling period
          $pool_ann_incomplete_stop_year = 1;
        } else {
          die "Invalid value for pool_ann_extend --> $parin{pool_ann_extend} <--\n";
        }
        # Remove this variable from vlist/parin
        &$del_vlist("pool_ann_extend");
      }
      if (exists $parin{force_first_ann}) {
        # Ensure pool_ann_incomplete_start_year has
        # the same "truth" as force_first_ann
        # This will override pool_ann_extend = start, if set by the user
        $pool_ann_incomplete_start_year = &$ToF($parin{force_first_ann});
      }

      # Define start and stop dates specific to annual pooling
      my $pool_ann_start_year = $pool_start_year;
      my $pool_ann_start_mon  = $pool_start_mon;
      my $pool_ann_stop_year  = $pool_stop_year;
      my $pool_ann_stop_mon   = $pool_stop_mon;
      if ($pool_ann_incomplete_start_year) {
        $pool_ann_start_year = $pool_start_year;
        $pool_ann_start_mon  = 1;
        $pool_ann_stop_year  = $pool_stop_year;
        $pool_ann_stop_mon   = $pool_stop_mon;
      }
      if ($pool_ann_incomplete_stop_year) {
        $pool_ann_start_year = 1 + $pool_start_year;
        $pool_ann_start_mon  = 1;
        $pool_ann_stop_year  = $pool_stop_year;
        $pool_ann_stop_mon   = 12;
      }

      # Set options specific to annual pooling
      my $set_opts = {start_mon =>1,
                      start_year=>$pool_ann_start_year,
                      stop_mon  =>$pool_ann_stop_mon,
                      stop_year =>$pool_ann_stop_year,
                      pool_start_year=>$pool_ann_start_year,
                      pool_start_mon=>$pool_ann_start_mon,
                      pool_stop_year=>$pool_ann_stop_year,
                      pool_stop_mon=>$pool_ann_stop_mon};
      my $pool_ann_iopts = '';
      $pool_ann_iopts = set_iopts($pool_ann_iopts, $set_opts);

      if ($with_diag) {
        # Insert jobs to create diagnostic files

        if ($load_hist) {
          # Load history files

          # Define a clone of mload for loading history files
          &$clone_check("mload_hist");

          # Set defaults for mload_hist
          &$set_vlist("mload_hist_uxxx=$parin{model_uxxx}")
            unless (exists $parin{mload_hist_uxxx});
          unless (exists $parin{mload_hist_suffix_list}) {
            if ($coupled eq "on") {
              &$set_vlist(q(mload_hist_suffix_list="gs ss gz cm"))
            } else {
              &$set_vlist(q(mload_hist_suffix_list="gs ss"))
            }
          }
          &$set_vlist("mload_hist_shortermdir=on")
            unless (exists $parin{load_shortermdir} or exists $parin{mload_hist_shortermdir});
          &$set_vlist("mload_hist_masterdir=off")
            unless (exists $parin{load_masterdir} or exists $parin{mload_hist_masterdir});
          &$set_vlist("mload_hist_sv=on")
            unless (exists $parin{load_sv} or exists $parin{mload_hist_sv});
          push @jl, ("mload_hist:-${load_hist_freq}");
        }

        # Create monthly diagnostic files
        push @jl, ("${diag_string}:m");

        if ($delete_hist) {
          # Delete history files
          # Define a clone of mdelete to delete history files
          &$clone_check("mdelete_hist");
          &$set_vlist("mdelete_hist_uxxx=$parin{model_uxxx}")
            unless (exists $parin{mdelete_hist_uxxx});
          unless (exists $parin{mdelete_hist_suffix_list}) {
            if ($coupled eq "on") {
              &$set_vlist(q(mdelete_hist_suffix_list="gs ss gz cm"))
            } else {
              &$set_vlist(q(mdelete_hist_suffix_list="gs ss"))
            }
          }
          &$set_vlist("mdelete_hist_leave_last_mon=0")
            unless (exists $parin{mdelete_hist_leave_last_mon});
          push @jl, ("mdelete_hist:${load_hist_freq}");
        }

        if ($dump_diag) {
          # Dump monthly diagnostic files to tape

          # Define a clone of mdump to dump diagnostic files
          &$clone_check("mdump_diag");

          # Set defaults for mdump_diag
          &$set_vlist("mdump_diag_uxxx=$parin{diag_uxxx}")
            unless (exists $parin{mdump_diag_uxxx});
          unless (exists $parin{mdump_diag_suffix_list}) {
            if ($coupled eq "on" and $with_cp) {
              &$set_vlist(q(mdump_diag_suffix_list="gp xp cp"));
            } else {
              &$set_vlist(q(mdump_diag_suffix_list="gp xp"))
            }
          }
          &$set_vlist("mdump_diag_shortermdir=on")
            unless (exists $parin{dump_shortermdir} or exists $parin{mdump_diag_shortermdir});
          &$set_vlist("mdump_diag_masterdir=off")
            unless (exists $parin{dump_masterdir} or exists $parin{mdump_diag_masterdir});
          &$set_vlist("mdump_diag_sv=on")
            unless (exists $parin{dump_sv} or exists $parin{mdump_diag_sv});

          push @jl, ("mdump_diag:$dump_diag_freq");
        }

      } else {
        # Load diagnostic files from tape, unless they are already on disk

        if ($load_diag) {
          # Load diagnostic files

          # Define a clone of mload for loading diagnostic files
          &$clone_check("mload_diag");

          # Set defaults for mload_diag
          &$set_vlist("mload_diag_uxxx=$parin{diag_uxxx}")
            unless (exists $parin{mload_diag_uxxx});
          unless (exists $parin{mload_diag_suffix_list}) {
            if ($coupled eq "on" and $with_cp) {
              &$set_vlist(q(mload_diag_suffix_list="gp xp cp"));
            } else {
              &$set_vlist(q(mload_diag_suffix_list="gp xp"))
            }
          }
          &$set_vlist("mload_diag_shortermdir=off")
            unless (exists $parin{load_shortermdir} or exists $parin{mload_diag_shortermdir});
          &$set_vlist("mload_diag_masterdir=off")
            unless (exists $parin{load_masterdir} or exists $parin{mload_diag_masterdir});
          &$set_vlist("mload_diag_sv=on")
            unless (exists $parin{load_sv} or exists $parin{mload_diag_sv});
          push @jl, ("mload_diag:-$load_diag_freq");
        }

      }

      # Define a frequency suffix that contains any pooling specific internal options
      # This will be of the form e.g. 60m<start_year=1984,start_mon=11>
      my $pfrq = "$mypx$pool_iopts";

      if ($with_pool) {

        unless (exists $parin{pool_suffix_list}) {
          if ($coupled eq "on" and $with_cp) {
            &$set_vlist(q(pool_suffix_list="gp xp cp"))
          } else {
            &$set_vlist(q(pool_suffix_list="gp xp"))
          }
        }

        # Add pool_(start/stop)_(year/mon) to vlist
        $pool_start_year = sprintf("%3.3d",$pool_start_year);
        $pool_start_mon  = sprintf("%2.2d",$pool_start_mon);
        $pool_stop_year  = sprintf("%3.3d",$pool_stop_year);
        $pool_stop_mon   = sprintf("%2.2d",$pool_stop_mon);
        push @vlist, "pool_start_year=$pool_start_year"
            unless &$In_vlist("pool_start_year");
        push @vlist, "pool_start_month=$pool_start_mon"
            unless &$In_vlist("pool_start_mon");
        push @vlist, "pool_stop_year=$pool_stop_year"
            unless &$In_vlist("pool_stop_year");
        push @vlist, "pool_stop_month=$pool_stop_mon"
            unless &$In_vlist("pool_stop_mon");

        if ($pool_seasons) {
          # pool seasons
          push @jl, ("pool_sea:DMJS$pool_iopts");
        }
        if ($pool_annual) {
          # pool months annually
          # Set start_mon=1 to ensure that annual pooling will always happen
          # at the end of a calendar year
          # Add the start/stop dates for pooling so that pool_ann does not get
          # inserted at the end of the first year unless that year is complete

          # Set job specific values for pool start/stop dates
          $main::job_specific_vars{pool_ann}{pool_start_year}  = $pool_ann_start_year;
          $main::job_specific_vars{pool_ann}{pool_start_month} = $pool_ann_start_mon;
          $main::job_specific_vars{pool_ann}{pool_stop_year}   = $pool_ann_stop_year;
          $main::job_specific_vars{pool_ann}{pool_stop_month}  = $pool_ann_stop_mon;

          push @jl, ("pool_ann:12m$pool_ann_iopts");
        }

        if ($pool_seasons) {
          # Multi year pooling of seasons

          # Create a clone of pool_mul for each season so that each name appears
          # explicitly in stdout. This aids in debugging and user understanding
          # of what is being done.
          &$clone_check("pool_mul_djf");
          $main::job_specific_vars{pool_mul_djf}{pool_mul_djf_id} = "djf";
          &$clone_check("pool_mul_mam");
          $main::job_specific_vars{pool_mul_mam}{pool_mul_mam_id} = "mam";
          &$clone_check("pool_mul_jja");
          $main::job_specific_vars{pool_mul_jja}{pool_mul_jja_id} = "jja";
          &$clone_check("pool_mul_son");
          $main::job_specific_vars{pool_mul_son}{pool_mul_son_id} = "son";
          if ( $pool_ignore_first_year ) {
            my $run_start_year = $user_set_run_start_year;
            $run_start_year = $parin{run_start_year} if exists $parin{run_start_year};
            my $resetval = sprintf("%3.3d:%3.3d", $run_start_year, $run_start_year + 1 );
            $main::job_specific_vars{pool_mul_djf}{pool_mul_djf_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_mam}{pool_mul_mam_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_jja}{pool_mul_jja_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_son}{pool_mul_son_reset_start_year} = $resetval;
          }
          push @jl, ("pool_mul_djf:$pfrq","pool_mul_mam:$pfrq","pool_mul_jja:$pfrq",
                     "pool_mul_son:$pfrq");
        }
        if ($pool_annual) {
          # Multi year pooling of annual pooled files
          # Set the annual pooling frequency such that the multi year
          # annual pooling will always occur on a year boundary
          my ($pannfrq) = $pfrq =~ m/\s*(.*)\s*<.*>\s*$/;
          if ($pannfrq =~ /^\s*\d*m/) {
            # Convert Mm to Nm where N is always a multiple of 12
            my ($freq_in_months, $subfreq) = $pannfrq =~ /^\s*(\d*)m(.*)$/;
            $freq_in_months = 1 unless $freq_in_months;
            my $freq_in_years = $freq_in_months/12;
            $freq_in_months = 12 * $freq_in_years;
            $pannfrq = "${freq_in_months}m$subfreq";
          } elsif ( $pannfrq =~ /^\s*\d*y/ ) {
            # Convert Ny to Mm
            my ($freq_in_years, $subfreq) = $pannfrq =~ /^\s*(\d*)y(.*)$/;
            $freq_in_years = 1 unless $freq_in_years;
            my $freq_in_months = 12 * $freq_in_years;
            $pannfrq = "${freq_in_months}m$subfreq";
          }

          # Create a clone of pool_mul to distinguish multi year annual pooling
          &$clone_check("pool_mul_ann");
          $main::job_specific_vars{pool_mul_ann}{pool_mul_ann_id} = "ann";

          # Set job specific values for pool start/stop dates
          $main::job_specific_vars{pool_mul_ann}{pool_start_year}  = $pool_ann_start_year;
          $main::job_specific_vars{pool_mul_ann}{pool_start_month} = $pool_ann_start_mon;
          $main::job_specific_vars{pool_mul_ann}{pool_stop_year}   = $pool_ann_stop_year;
          $main::job_specific_vars{pool_mul_ann}{pool_stop_month}  = $pool_ann_stop_mon;
          if ( $pool_ignore_first_year ) {
            my $run_start_year = $user_set_run_start_year;
            $run_start_year = $parin{run_start_year} if exists $parin{run_start_year};
            my $resetval = sprintf("%3.3d:%3.3d", $run_start_year, $run_start_year + 1 );
            $main::job_specific_vars{pool_mul_ann}{pool_mul_ann_reset_start_year} = $resetval;
          }
          push @jl, ("pool_mul_ann:$pannfrq$pool_ann_iopts");
        }
        if ($pool_months) {
          # Multi year pooling of individual months

          # Create a clone of pool_mul for each month so that each name appears
          # explicitly in stdout. This aids in debugging and user understanding
          # of what is being done.
          &$clone_check("pool_mul_m01");
          $main::job_specific_vars{pool_mul_m01}{pool_mul_m01_id} = "m01";
          &$clone_check("pool_mul_m02");
          $main::job_specific_vars{pool_mul_m02}{pool_mul_m02_id} = "m02";
          &$clone_check("pool_mul_m03");
          $main::job_specific_vars{pool_mul_m03}{pool_mul_m03_id} = "m03";
          &$clone_check("pool_mul_m04");
          $main::job_specific_vars{pool_mul_m04}{pool_mul_m04_id} = "m04";
          &$clone_check("pool_mul_m05");
          $main::job_specific_vars{pool_mul_m05}{pool_mul_m05_id} = "m05";
          &$clone_check("pool_mul_m06");
          $main::job_specific_vars{pool_mul_m06}{pool_mul_m06_id} = "m06";
          &$clone_check("pool_mul_m07");
          $main::job_specific_vars{pool_mul_m07}{pool_mul_m07_id} = "m07";
          &$clone_check("pool_mul_m08");
          $main::job_specific_vars{pool_mul_m08}{pool_mul_m08_id} = "m08";
          &$clone_check("pool_mul_m09");
          $main::job_specific_vars{pool_mul_m09}{pool_mul_m09_id} = "m09";
          &$clone_check("pool_mul_m10");
          $main::job_specific_vars{pool_mul_m10}{pool_mul_m10_id} = "m10";
          &$clone_check("pool_mul_m11");
          $main::job_specific_vars{pool_mul_m11}{pool_mul_m11_id} = "m11";
          &$clone_check("pool_mul_m12");
          $main::job_specific_vars{pool_mul_m12}{pool_mul_m12_id} = "m12";

          if ( $pool_ignore_first_year ) {
            my $run_start_year = $user_set_run_start_year;
            $run_start_year = $parin{run_start_year} if exists $parin{run_start_year};
            my $resetval = sprintf("%3.3d:%3.3d", $run_start_year, $run_start_year + 1 );
            $main::job_specific_vars{pool_mul_m01}{pool_mul_m01_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m02}{pool_mul_m02_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m03}{pool_mul_m03_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m04}{pool_mul_m04_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m05}{pool_mul_m05_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m06}{pool_mul_m06_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m07}{pool_mul_m07_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m08}{pool_mul_m08_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m09}{pool_mul_m09_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m10}{pool_mul_m10_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m11}{pool_mul_m11_reset_start_year} = $resetval;
            $main::job_specific_vars{pool_mul_m12}{pool_mul_m12_reset_start_year} = $resetval;
          }

          push @jl, ("pool_mul_m01:$pfrq","pool_mul_m02:$pfrq","pool_mul_m03:$pfrq",
                     "pool_mul_m04:$pfrq","pool_mul_m05:$pfrq","pool_mul_m06:$pfrq",
                     "pool_mul_m07:$pfrq","pool_mul_m08:$pfrq","pool_mul_m09:$pfrq",
                     "pool_mul_m10:$pfrq","pool_mul_m11:$pfrq","pool_mul_m12:$pfrq");
        }
      }

      if ($with_tseries) {
        # Create time series of user specified fields in the diagnostic and/or pooled files
        push @jl, ("tseries:$pfrq");
      }

      my $plot_MAM_SON = 1;
      if (exists $parin{plot_MAM_SON}) {
        # Setting plot_MAM_SON on the command line may be used to control
        # inclusion or exclusion of plotting decks
        $plot_MAM_SON = &$ToF($parin{plot_MAM_SON});
        # Remove this variable from vlist/parin
        &$del_vlist("plot_MAM_SON");
      }
      my $plot_ann = 0;
      if (exists $parin{plot_ann}) {
        # Setting plot_ann on the command line may be used to control
        # inclusion or exclusion of plotting decks
        $plot_ann = &$ToF($parin{plot_ann});
        # Remove this variable from vlist/parin
        &$del_vlist("plot_ann");
      }
      my $plot_plotmix = 1;
      if (exists $parin{plot_plotmix}) {
        # Setting plot_plotmix on the command line may be used to control
        # inclusion or exclusion of plotting decks
        $plot_plotmix = &$ToF($parin{plot_plotmix});
        # Remove this variable from vlist/parin
        &$del_vlist("plot_plotmix");
      }
      my $plot_anomaly = 1;
      if (exists $parin{plot_anomaly}) {
        # Setting plot_anomaly on the command line may be used to control
        # inclusion or exclusion of plotting decks
        $plot_anomaly = &$ToF($parin{plot_anomaly});
        # Remove this variable from vlist/parin
        &$del_vlist("plot_anomaly");
      }
      my $plot_heatrans = 0;
      if (exists $parin{plot_heatrans}) {
        # Setting plot_heatrans on the command line may be used to control
        # inclusion or exclusion of plotting decks
        $plot_heatrans = &$ToF($parin{plot_heatrans});
        # Remove this variable from vlist/parin
        &$del_vlist("plot_heatrans");
      }
      my $plot_sst_sss = 0;
      if (exists $parin{plot_sst_sss}) {
        # Setting plot_sst_sss on the command line may be used to control
        # inclusion or exclusion of plotting decks
        $plot_sst_sss = &$ToF($parin{plot_sst_sss});
        # Remove this variable from vlist/parin
        &$del_vlist("plot_sst_sss");
      }
      my $plot_radforce = $radforce;
      if (exists $parin{plot_radforce}) {
        # Setting plot_radforce on the command line may be used to control
        # inclusion or exclusion of plotting decks
        $plot_radforce = &$ToF($parin{plot_radforce});
        # Remove this variable from vlist/parin
        &$del_vlist("plot_radforce");
      }
      if ($with_plot) {
        # Define clones for plotmix and anomaly that will be used to
        # generate seasonal (DJF and JJA) plots every $myp_freq years
        if ($plot_plotmix) {
          &$clone_check("plotmix_DJF");
          &$clone_check("plotmix_JJA");
          # Plot multi year pooled files every $myp_freq years
          push @jl, ("plotmix_DJF:${pfrq}","plotmix_JJA:${pfrq}");
        }
        if ($plot_heatrans) {
          # The heatrans deck must always run at the end of a calendar year
          my $htfrq=$pfrq;
          if ($mypx =~ /^\s*\d+m/) {
            # Convert this to the form Ny...
            my ($freq_in_months, $subfreq) = $mypx =~ /^\s*(\d+)m(.*)$/;
            my $freq_in_years = int($freq_in_months/12);
            $htfrq = "${freq_in_years}y$subfreq$pool_iopts";
          }
          push @jl, ("heatrans:$htfrq");
        }
        if ($plot_anomaly) {
          &$clone_check("anomaly_DJF");
          &$clone_check("anomaly_JJA");
          push @jl, ("anomaly_DJF:${pfrq}","anomaly_JJA:${pfrq}");
        }
        if ($plot_MAM_SON) {
          if ($plot_plotmix) {
            &$clone_check("plotmix_MAM");
            &$clone_check("plotmix_SON");
            push @jl, ("plotmix_MAM:${pfrq}","plotmix_SON:${pfrq}");
          }
          if ($plot_anomaly) {
            &$clone_check("anomaly_MAM");
            &$clone_check("anomaly_SON");
            push @jl, ("anomaly_MAM:${pfrq}","anomaly_SON:${pfrq}");
          }
        }
        if ($plot_ann) {
          if ($plot_plotmix) {
            &$clone_check("plotmix_ann");
            push @jl, ("plotmix_ann:${pfrq}");
          }
          if ($plot_anomaly) {
            &$clone_check("anomaly_ann");
            push @jl, ("anomaly_ann:${pfrq}");
          }
        }
        # Plot time averaged sst and sss anomalies every $myp_freq years
        if ($plot_sst_sss) {
          push @jl, ("sst_sss:${pfrq}");
        }
        # Plot radaitive forcing fields every $myp_freq years
        if ($plot_radforce) {
          push @jl, ("rf_plotmix:${pfrq}");
        }
      }

      if ($dump_impooled) {
        # Dump intermediate pooled files to tape
        # Set defaults for sdump
        unless (exists $parin{sdump_suffix_list}) {
          if ($coupled eq "on" and $with_cp) {
            &$set_vlist(q(sdump_suffix_list="gp xp cp"))
          } else {
            &$set_vlist(q(sdump_suffix_list="gp xp"))
          }
        }
        if ( $pool_ignore_first_year ) {
          my $run_start_year = $user_set_run_start_year;
          $run_start_year = $parin{run_start_year} if exists $parin{run_start_year};
          my $resetval = sprintf("%3.3d:%3.3d", $run_start_year, $run_start_year + 1 );
          &$set_vlist("sdump_reset_start_year=$resetval")
            unless exists $parin{sdump_reset_start_year};
        }
        &$set_vlist("sdump_mon=on") unless exists $parin{sdump_mon};
        &$set_vlist("sdump_ann=on") unless exists $parin{sdump_ann};
        &$set_vlist("sdump_shortermdir=on")
          unless (exists $parin{dump_shortermdir} or exists $parin{sdump_shortermdir});
        &$set_vlist("sdump_masterdir=off")
          unless (exists $parin{dump_masterdir} or exists $parin{sdump_masterdir});
        &$set_vlist("sdump_sv=on")
          unless (exists $parin{dump_sv} or exists $parin{sdump_sv});
        push @jl, ("sdump:$pfrq");
      }
      if ($delete_impooled) {
        # Delete intermediate pooled files
        # Set defaults for sdelete
        unless (exists $parin{sdelete_suffix_list}) {
          if ($coupled eq "on" and $with_cp) {
            &$set_vlist(q(sdelete_suffix_list="gp xp cp"))
          } else {
            &$set_vlist(q(sdelete_suffix_list="gp xp"))
          }
        }
        &$set_vlist("sdelete_mon=on") unless exists $parin{sdelete_mon};
        &$set_vlist("sdelete_ann=on") unless exists $parin{sdelete_mon};
        push @jl, ("sdelete:$pfrq");
      }

      if ($delete_diag) {
        # Delete diagnostic files

        # Define a clone of mdelete to delete history files
        &$clone_check("mdelete_diag");

        # Set defaults for mdelete
        &$set_vlist("mdelete_diag_uxxx=$parin{diag_uxxx}")
          unless (exists $parin{mdelete_diag_uxxx});
        unless (exists $parin{mdelete_diag_suffix_list}) {
          if ($coupled eq "on" and $with_cp) {
            &$set_vlist(q(mdelete_diag_suffix_list="gp xp cp"))
          } else {
            &$set_vlist(q(mdelete_diag_suffix_list="gp xp"))
          }
        }
        # Never delete diag files until the end of a pooling interval
        # They may be needed for monthly multi year pooling
        push @jl, ("mdelete_diag:$pfrq");
      }

      if ($dump_pooled) {
        # dump multi year pooled files to tape
        # Set defaults for psdump
        unless (exists $parin{psdump_suffix_list}) {
          if ($coupled eq "on" and $with_cp) {
            &$set_vlist(q(psdump_suffix_list="gp xp cp"))
          } else {
            &$set_vlist(q(psdump_suffix_list="gp xp"))
          }
        }
        if ( $pool_ignore_first_year ) {
          my $run_start_year = $user_set_run_start_year;
          $run_start_year = $parin{run_start_year} if exists $parin{run_start_year};
          my $resetval = sprintf("%3.3d:%3.3d", $run_start_year, $run_start_year + 1 );
          &$set_vlist("psdump_reset_start_year=$resetval")
            unless exists $parin{psdump_reset_start_year};
        }
        &$set_vlist("psdump_mon=on") unless exists $parin{psdump_mon};
        &$set_vlist("psdump_ann=on") unless exists $parin{psdump_ann};
        &$set_vlist("psdump_shortermdir=on")
          unless (exists $parin{dump_shortermdir} or exists $parin{psdump_shortermdir});
        &$set_vlist("psdump_masterdir=off")
          unless (exists $parin{dump_masterdir} or exists $parin{psdump_masterdir});
        &$set_vlist("psdump_sv=on")
          unless (exists $parin{dump_sv} or exists $parin{psdump_sv});
        push @jl, ("psdump:$pfrq");
      }

      if ($with_pool and $transfer_pooled) {
        # Insert an instance of rtrans to transfer a subset of the pooled files

        my $mod_name = "rtrans_pooled";
        &$clone_check($mod_name);

        # Create a code snippet that will determine, at run time, the
        # correct date_range used as part of the time series file names
        # Define pool_range for use in files names
        my $snippet = q@
  pool_range="${start_year}m${start_mon}_${stop_year}m${stop_mon}"
@;
        if ( $pool_ignore_first_year) {
          # The seasonl pooled files for the first year
          # of the pooling range are not present
          # Overwrite snippet with code that defines this_pool_start_year
          # for the case where the first year of pooled files is missing
          $snippet = q@
  if [ $start_year -eq $run_start_year ]; then
    this_pool_start_year=`echo $start_year|awk '{printf "%3.3d",$1+1}' -`
  else
    this_pool_start_year=`echo $start_year|awk '{printf "%3.3d",$1}' -`
  fi
  pool_range="${this_pool_start_year}m${start_mon}_${stop_year}m${stop_mon}"
@;
        }

        # Append the list of file name defs to snippet
        if ( $lett2 eq "c" ) {
          # Pooled files for "cp", "gp" and "xp" should be present
          $snippet .= q@
  file1=pc_${runid}_${pool_range}_ann_cp
  file2=pc_${runid}_${pool_range}_ann_gp
  file3=pc_${runid}_${pool_range}_ann_xp
  file4=pc_${runid}_${pool_range}_mam_cp
  file5=pc_${runid}_${pool_range}_mam_gp
  file6=pc_${runid}_${pool_range}_mam_xp
  file7=pc_${runid}_${pool_range}_jja_cp
  file8=pc_${runid}_${pool_range}_jja_gp
  file9=pc_${runid}_${pool_range}_jja_xp
  file10=pc_${runid}_${pool_range}_son_cp
  file11=pc_${runid}_${pool_range}_son_gp
  file12=pc_${runid}_${pool_range}_son_xp
  join=12

  # Only include djf if present on disk
  curr_file=pc_${runid}_${pool_range}_djf_gp
  present=`ls -1 $DATAPATH/${curr_file}.[0-9][0-9][0-9] 2>/dev/null | tail -1`
  present=`echo $present|sed 's/\.[0-9][0-9][0-9]$//;s/^.*\///'`
  if [ x"$present" = x"$curr_file" ]; then
    # The file is on disk. Assume all 3 pooled djf files are present
    file13=pc_${runid}_${pool_range}_djf_cp
    file14=pc_${runid}_${pool_range}_djf_gp
    file15=pc_${runid}_${pool_range}_djf_xp
    join=15
  fi
@;
        } else {
          # Pooled files for "gp" and "xp" should be present
          $snippet .= q@
  file1=pa_${runid}_${pool_range}_ann_gp
  file2=pa_${runid}_${pool_range}_ann_xp
  file3=pa_${runid}_${pool_range}_mam_gp
  file4=pa_${runid}_${pool_range}_mam_xp
  file5=pa_${runid}_${pool_range}_jja_gp
  file6=pa_${runid}_${pool_range}_jja_xp
  file7=pa_${runid}_${pool_range}_son_gp
  file8=pa_${runid}_${pool_range}_son_xp
  join=8

  # Only include djf if present on disk
  curr_file=pa_${runid}_${pool_range}_djf_gp
  present=`ls -1 $DATAPATH/${curr_file}.[0-9][0-9][0-9] 2>/dev/null | tail -1`
  present=`echo $present|sed 's/\.[0-9][0-9][0-9]$//;s/^.*\///'`
  if [ x"$present" = x"$curr_file" ]; then
    # The file is on disk. Assume both pooled djf files are present
    file9=pa_${runid}_${pool_range}_djf_gp
    file10=pa_${runid}_${pool_range}_djf_xp
    join=10
  fi
@;
        }

        # Insert this code snippet into the current module
        $JOBDEF{job}{$mod_name} =~ s/^\s*# *<<INSERT_RTRANS_pooled>> *$/$snippet/mi;

        # Set common job specific variables used in rtrans

        my $remusr = $user;
        $remusr = $parin{pool_remusr} if exists $parin{pool_remusr};
        $main::job_specific_vars{$mod_name}{"${mod_name}_remusr"} = $remusr;

        my $remserver = "lxsrv.cccma.ec.gc.ca";
        $remserver = $parin{pool_remserver} if exists $parin{pool_remserver};
        $main::job_specific_vars{$mod_name}{"${mod_name}_remserver"} = $remserver;

        my $remdir = '';
        chomp($remdir = `ssh $remserver 'echo \$RUNPATH' 2\>/dev/null`);
        $remdir = $parin{RMTRUNPATH} if exists $parin{RMTRUNPATH};
        $remdir = $parin{pool_RMTRUNPATH} if exists $parin{pool_RMTRUNPATH};
        $remdir = $parin{pool_remdir} if exists $parin{pool_remdir};
        die "pool: Unable to determine remote dir for file transfer. Stopped"
            unless $remdir;
        # Make sure remdir end with "/pool"
        $remdir .= "/pool" unless $remdir =~ /\/pool\s*$/;
        $main::job_specific_vars{$mod_name}{"${mod_name}_remdir"} = $remdir;

        my $rem_save = "on";
        $rem_save = $parin{pool_rem_save} if exists $parin{pool_rem_save};
        $main::job_specific_vars{$mod_name}{"${mod_name}_rem_save"} = $rem_save;

        # Add this job to the job list
        push @jl, ("${mod_name}:$pfrq");
      }

      if ($delete_pooled) {
        # delete multi year pooled files from disk
        # Set defaults for psdelete
        unless (exists $parin{psdelete_suffix_list}) {
          if ($coupled eq "on" and $with_cp) {
            &$set_vlist(q(psdelete_suffix_list="gp xp cp"))
          } else {
            &$set_vlist(q(psdelete_suffix_list="gp xp"))
          }
        }
        if ( $pool_ignore_first_year ) {
          my $run_start_year = $user_set_run_start_year;
          $run_start_year = $parin{run_start_year} if exists $parin{run_start_year};
          my $resetval = sprintf("%3.3d:%3.3d", $run_start_year, $run_start_year + 1 );
          &$set_vlist("psdelete_reset_start_year=$resetval")
            unless exists $parin{psdelete_reset_start_year};
        }
        &$set_vlist("psdelete_mon=on") unless exists $parin{psdelete_mon};
        &$set_vlist("psdelete_ann=on") unless exists $parin{psdelete_ann};

        # Set job specific values for annual pooling start/stop dates
        $main::job_specific_vars{psdelete}{pool_ann_start_year}  = $pool_ann_start_year;
        $main::job_specific_vars{psdelete}{pool_ann_start_month} = $pool_ann_start_mon;
        $main::job_specific_vars{psdelete}{pool_ann_stop_year}   = $pool_ann_stop_year;
        $main::job_specific_vars{psdelete}{pool_ann_stop_month}  = $pool_ann_stop_mon;

        push @jl, ("psdelete:$pfrq");
      }

      next;
    };
    ############################################################################

    /^[amcrdf]diag2ts$/ and do {
      #############################################################################
      # Convert diagnostic and/or gz files to time series
      #############################################################################

      my $this_job = $_;

      # runid is required to be set on the command line
      die "*** runid must be set on the command line when invoking $_.\n  Stopped"
        unless $preset{changed}{runid};

      # Set a value for crawork
      # The user may overide this with a crawork assignment on the command line
      $crawork="$parin{runid}_diag2ts_$stamp";
      &$set_vlist("crawork=$crawork") unless exists $parin{crawork};

      # The first letter in the alias [amcrd]diag2ts will be the second
      # letter of file names that follow the "official" naming convention
      my ($lett2) = $this_job =~ /^(.)/;
      &$set_vlist("uxxx=m$lett2") unless exists $parin{uxxx};

      # Determine a value for coupled from the first letter of the name
      # under which this alias was invoked
      my $coupled = "off";
      $coupled = "on" if $lett2 eq "c";
      $coupled = "on" if $lett2 eq "f";
      if (exists $parin{diag2ts_coupled}) {
        # Setting diag2ts_coupled on the command line will override the default
        $coupled = $parin{diag2ts_coupled};
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_coupled");
      } elsif (exists $parin{coupled}) {
        # Setting coupled on the command line will override the default
        # unless diag2ts_coupled is also set on the command line
        $coupled = $parin{coupled};
      }
      # Add this definition to vlist if not already there
      push @vlist, "coupled=$coupled" unless &$In_vlist("coupled");

      # chunk_size is the length of individual time series files
      # in units of chunk_unit (default 5 years)
      my $chunk_size = "5";
      my $chunk_unit = "y";
      my $freq = "${chunk_size}y";

      # user_iopts will contain any user supplied definitions enclosed within
      # angle brackets, found appended to $opt
      my $user_iopts = '';

      # Strip any angle bracketed defs from the end of $opt and assign them,
      # as a string without the angle brackets, to $user_iopts
      ($user_iopts) = $opt =~ m/\s*<(.*)>\s*$/ if $opt;
      $opt =~ s/\s*<(.*)>\s*$// if $opt;

      if ($opt) {
        # The user has specified a chunk size on the command line via
        # something like diag2ts:10y. The modifier (10y in this case) must be
        # a positive integer greater than 1, followed by the letters "y" or "m".
        # Any other form of modifier will result in an error.
        ($freq) = $opt =~ /^:(.*)/;
        ($chunk_size) = $freq =~ /^([+-]?\d*)/;
        $chunk_size = 1 unless $chunk_size;
        $freq =~ s/^[+]//;
        ($chunk_unit) = $freq =~ /^[+-]?\d*(.*)/;
        die "$_${opt} --- Invalid chunk size --> $freq <-- (must be in years or months).\n"
          unless ($chunk_unit eq "y" or $chunk_unit eq "m");
        die "$_${opt} --- Chunk size --> $freq <-- must be > 0.\n" if ($chunk_size < 1);
      }

      # Ensure that chunk_size is of the form "\d+m" so that
      # the splitting can start from months other than JAN
      if ( $chunk_unit eq "y" ) {
        # Convert <N>y to <M>m
        my ($freq_in_years, $subfreq) = $freq =~ /^\s*(\d+)y(.*)$/;
        $freq_in_years = 1 unless $freq_in_years;
        my $freq_in_months = 12*$freq_in_years;
        $freq = "${freq_in_months}m$subfreq";
        $chunk_size = $freq_in_months;
        $chunk_unit = "m";
      }

      # A string to hold internal options specifiec to diag2ts
      my $diag2ts_iopts = '';

      # Determine the year and month on which time series will begin
      # The default will be the time set by --start=... on the command line
      # or year=1 mon=1 otherwise
      my ($diag2ts_start_year,$diag2ts_start_mon)  = split(/\s*:\s*/, $Start_time_cmdl);
      $diag2ts_start_year = 1 unless $diag2ts_start_year;
      $diag2ts_start_mon  = 1 unless $diag2ts_start_mon;
      my $user_supplied_diag2ts_start = 0;
      if (exists $parin{diag2ts_start}) {
        # Use the value supplied by the user via the diag2ts_start=... command line def
        $user_supplied_diag2ts_start = 1;
        ($diag2ts_start_year,$diag2ts_start_mon) = split(/\s*:\s*/, $parin{diag2ts_start});
        $diag2ts_start_year = 1 unless $diag2ts_start_year;
        $diag2ts_start_mon  = 1 unless $diag2ts_start_mon;
        # This must be added to diag2ts_iopts if it differs from the default
        $diag2ts_iopts .= ",start_year=$diag2ts_start_year" if $diag2ts_start_year;
	if ($diag2ts_start_mon) {
          if ($diag2ts_start_mon < 1 or $diag2ts_start_mon > 12) {
            my $msg = "Invalid value for --> diag2ts_start=$parin{diag2ts_start} <--";
            $msg .= "set on command line.\n";
            die "$msg";
          }
          # This must be added to diag2ts_iopts if it differs from the default
          $diag2ts_iopts .= ",start_mon=$diag2ts_start_mon";
        }
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_start");
      }

      # Determine the year and month, beyond which time series will not be included in
      # the job string, and add the appropriate stop_year and stop_mon to diag2ts_iopts.
      my ($diag2ts_stop_year,$diag2ts_stop_mon) = split(/\s*:\s*/, $Stop_time_cmdl);
      $diag2ts_stop_year =  1 unless $diag2ts_stop_year;
      $diag2ts_stop_mon  = 12 unless $diag2ts_stop_mon;
      my $user_supplied_diag2ts_stop = 0;
      if (exists $parin{diag2ts_stop}) {
        # Use the value supplied by the user via the diag2ts_stop=... command line def
         $user_supplied_diag2ts_stop = 1;
        ($diag2ts_stop_year,$diag2ts_stop_mon) = split(/\s*:\s*/, $parin{diag2ts_stop});
        $diag2ts_stop_year =  1 unless $diag2ts_stop_year;
        $diag2ts_stop_mon  = 12 unless $diag2ts_stop_mon;
        # This must be added to diag2ts_iopts if it differs from the default
        $diag2ts_iopts .= ",stop_year=$diag2ts_stop_year" if $diag2ts_stop_year;
	if ($diag2ts_stop_mon) {
          if ($diag2ts_stop_mon < 1 or $diag2ts_stop_mon > 12) {
            my $msg = "Invalid value for --> diag2ts_stop=$parin{diag2ts_stop} <--";
            $msg .= "set on command line.\n";
            die "$msg";
          }
          # This must be added to diag2ts_iopts if it differs from the default
          $diag2ts_iopts .= ",stop_mon=$diag2ts_stop_mon";
        }
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_stop");
      } else {
        # Set stop year and mon such that the maximum number of complete time series
        # intervals will fit between diag2ts_start_(year/mon) and the end of the job
        my $total_months = 1 + diff_in_months($diag2ts_start_year,$diag2ts_start_mon,
                                              $diag2ts_stop_year,$diag2ts_stop_mon);
        my $max_diag2ts_intervals = int($total_months/$chunk_size);
        my $max_months = $max_diag2ts_intervals*$chunk_size;
        if ($max_diag2ts_intervals <= 0) {
          my $msg = " *** ERROR *** ";
          $msg .= "You cannot fit any time series intervals between the start";
          $msg .= " of time series (${diag2ts_start_year}:$diag2ts_start_mon) and end";
          $msg .= " of the run (${diag2ts_stop_year}:$diag2ts_stop_mon)";
          die "$msg\n";
        }
        ($diag2ts_stop_mon,$diag2ts_stop_year) =
           new_mon_year($diag2ts_start_mon, $diag2ts_start_year, $max_months - 1);
        $diag2ts_iopts .= ",stop_year=$diag2ts_stop_year";
        $diag2ts_iopts .= ",stop_mon=$diag2ts_stop_mon";
      }

      # Possibly reorder the diag2ts_iopts string
      $diag2ts_iopts = ref2str_iopts( str2ref_iopts($diag2ts_iopts) );

      # Set defaults and modify with user supplied values, if any

      # with_gz is used to indicate that the gz file does not exist even if
      # this happens to be a coupled model run. This will happen when the
      # coupler is used to read specified boundary conditions from a file
      my $with_gz = &$ToF($coupled);
      if (exists $parin{diag2ts_with_gz}) {
        # Setting diag2ts_with_gz on the command line will override the default
        $with_gz = &$ToF($parin{diag2ts_with_gz});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_with_gz");
      } elsif (exists $parin{with_gz}) {
        # Setting with_gz on the command line will override the default
        # unless diag2ts_with_gz is also set on the command line
        $with_gz = &$ToF($parin{with_gz});
      }
      if ($with_gz) {
        $with_gz = "gz";
      } else {
        $with_gz = "";
      }

      # with_cp is used to indicate whether or not the cp file is to be used
      # to create time series, regardless of whether it exists or not
      my $with_cp = &$ToF($coupled);
      if (exists $parin{diag2ts_with_cp}) {
        # Setting diag2ts_with_cp on the command line will override the default
        $with_cp = &$ToF($parin{diag2ts_with_cp});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_with_cp");
      } elsif (exists $parin{with_cp}) {
        # Setting with_cp on the command line will override the default
        # unless diag2ts_with_cp is also set on the command line
        $with_cp = &$ToF($parin{with_cp});
      }
      if ($with_cp) {
        $with_cp = "cp";
      } else {
        $with_cp = "";
      }

      my $load_diag = 1;
      if (exists $parin{diag2ts_load_diag}) {
        # Setting diag2ts_load_diag on the command line will override the default
        $load_diag = &$ToF($parin{diag2ts_load_diag});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_load_diag");
      } elsif (exists $parin{load_diag}) {
        # Setting load_diag on the command line will override the default
        # unless diag2ts_load_diag is also set on the command line
        $load_diag = &$ToF($parin{load_diag});
        # Remove this variable from vlist/parin
        &$del_vlist("load_diag");
      }

      my $delete_diag = $load_diag;
      if (exists $parin{diag2ts_delete_diag}) {
        # Setting diag2ts_delete_diag on the command line will override the default
        $delete_diag = &$ToF($parin{diag2ts_delete_diag});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_delete_diag");
      } elsif (exists $parin{delete_diag}) {
        # Setting delete_diag on the command line will override the default
        # unless diag2ts_delete_diag is also set on the command line
        $delete_diag = &$ToF($parin{delete_diag});
        # Remove this variable from vlist/parin
        &$del_vlist("delete_diag");
      }

      my $with_gztsdiag = 0;
      $with_gztsdiag = 1 if ($lett2 eq "c" and $with_gz);
      $with_gztsdiag = 1 if ($lett2 eq "f" and $with_gz);
      if (exists $parin{diag2ts_with_gztsdiag}) {
        # Setting diag2ts_with_gztsdiag on the command line will override the default
        $with_gztsdiag = &$ToF($parin{diag2ts_with_gztsdiag});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_with_gztsdiag");
      } elsif (exists $parin{with_gztsdiag}) {
        # Setting with_gztsdiag on the command line will override the default
        # unless diag2ts_with_gztsdiag is also set on the command line
        $with_gztsdiag = &$ToF($parin{with_gztsdiag});
        # Remove this variable from vlist/parin
        &$del_vlist("with_gztsdiag");
      }

      my $dump_time_series = 1;
      if (exists $parin{diag2ts_dump_time_series}) {
        # Setting diag2ts_dump_time_series on the command line will override the default
        $dump_time_series = &$ToF($parin{diag2ts_dump_time_series});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_dump_time_series");
      } elsif (exists $parin{dump_time_series}) {
        # Setting dump_time_series on the command line will override the default
        # unless diag2ts_dump_time_series is also set on the command line
        $dump_time_series = &$ToF($parin{dump_time_series});
        # Remove this variable from vlist/parin
        &$del_vlist("dump_time_series");
      }

      my $transfer_time_series = 0;
      if (exists $parin{diag2ts_transfer_time_series}) {
        # Setting diag2ts_transfer_time_series on the command line will override the default
        $transfer_time_series = &$ToF($parin{diag2ts_transfer_time_series});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_transfer_time_series");
      } elsif (exists $parin{transfer_time_series}) {
        # Setting transfer_time_series on the command line will override the default
        # unless diag2ts_transfer_time_series is also set on the command line
        $transfer_time_series = &$ToF($parin{transfer_time_series});
        # Remove this variable from vlist/parin
        &$del_vlist("transfer_time_series");
      }

      # A flag to determine whether or not all dd variables will be transfered
      # if "_dd" files exist and transfer_time_series is true
      my $transfer_all_dd = 0;
      $transfer_all_dd = $parin{diag2ts_transfer_all_dd}
          if exists $parin{diag2ts_transfer_all_dd};
      $transfer_all_dd = $parin{transfer_all_dd}
          if exists $parin{transfer_all_dd};

      my $delete_time_series = $dump_time_series;
      if (exists $parin{diag2ts_delete_time_series}) {
        # Setting diag2ts_delete_time_series on the command line will override the default
        $delete_time_series = &$ToF($parin{diag2ts_delete_time_series});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_delete_time_series");
      } elsif (exists $parin{delete_time_series}) {
        # Setting delete_time_series on the command line will override the default
        # unless diag2ts_delete_time_series is also set on the command line
        $delete_time_series = &$ToF($parin{delete_time_series});
        # Remove this variable from vlist/parin
        &$del_vlist("delete_time_series");
      }

      # load_diag_freq is a positive integer followed by "y" or "m" indicating
      # the frequency at which diagnostic files are loaded
      my $load_diag_freq = $freq;
      if (exists $parin{load_diag_freq}) {
        # load_diag_freq was set on the command line
        my $str1 = "Invalid command line assignment load_diag_freq=$parin{load_diag_freq}";
        my $str2 = "  load_diag_freq must begin with a positive integer";
        my $str3 = " followed by the letter \"y\" or the letter \"m\".";
        die "$str1\n$str2$str3\n  Stopped"
          unless ($parin{load_diag_freq} =~ /^\s*\d+(y|m)(:\d+(y|m))?\s*$/);

        $load_diag_freq = $parin{load_diag_freq};

        # Remove this variable from vlist/parin
        &$del_vlist("load_diag_freq");
      }

      # Ensure that load_diag_freq is of the form "\d+m" so that loading
      # can happpen on intervals other than calendar year boundaries
      if ($load_diag_freq =~ /^\s*\d+y/) {
        # Convert Ny to Mm
        my ($freq_in_years, $subfreq) = $load_diag_freq =~ /^\s*(\d+)y(.*)$/;
        my $freq_in_months = 12*$freq_in_years;
        $load_diag_freq = "${freq_in_months}m$subfreq";
      }

      # dump_ts_freq is a positive integer followed by "y" or "m" indicating
      # the frequency at which time series files are dumped to cfs
      my $dump_ts_freq = $freq;
      if (exists $parin{dump_ts_freq}) {
        # dump_ts_freq was set on the command line
        my $str1 = "Invalid command line assignment dump_ts_freq=$parin{dump_ts_freq}";
        my $str2 = "  dump_ts_freq must begin with a positive integer";
        my $str3 = " followed by the letter \"y\" or the letter \"m\".";
        die "$str1\n$str2$str3\n  Stopped"
          unless ($parin{dump_ts_freq} =~ /^\s*\d+(y|m)(:\d+(y|m))?\s*$/);

        $dump_ts_freq = $parin{dump_ts_freq};

        # Remove this variable from vlist/parin
        &$del_vlist("dump_ts_freq");
      }

      # Ensure that dump_ts_freq is of the form "\d+m" so that dumping
      # can happpen on intervals other than calendar year boundaries
      if ($dump_ts_freq =~ /^\s*\d+y/) {
        # Convert Ny to Mm
        my ($freq_in_years, $subfreq) = $dump_ts_freq =~ /^\s*(\d+)y(.*)$/;
        my $freq_in_months = 12*$freq_in_years;
        $dump_ts_freq = "${freq_in_months}m$subfreq";
      }

      # delete_ts_freq is a positive integer followed by "y" or "m" indicating
      # the frequency at which time series files are dumped to cfs
      my $delete_ts_freq = $freq;
      if (exists $parin{delete_ts_freq}) {
        # delete_ts_freq was set on the command line
        my $str1 = "Invalid command line assignment delete_ts_freq=$parin{delete_ts_freq}";
        my $str2 = "  delete_ts_freq must begin with a positive integer";
        my $str3 = " followed by the letter \"y\" or the letter \"m\".";
        die "$str1\n$str2$str3\n  Stopped"
          unless ($parin{delete_ts_freq} =~ /^\s*\d+(y|m)(:\d+(y|m))?\s*$/);

        $delete_ts_freq = $parin{delete_ts_freq};

        # Remove this variable from vlist/parin
        &$del_vlist("delete_ts_freq");
      }

      # Ensure that delete_ts_freq is of the form "\d+m" so that dumping
      # can happpen on intervals other than calendar year boundaries
      if ($delete_ts_freq =~ /^\s*\d+y/) {
        # Convert Ny to Mm
        my ($freq_in_years, $subfreq) = $delete_ts_freq =~ /^\s*(\d+)y(.*)$/;
        my $freq_in_months = 12*$freq_in_years;
        $delete_ts_freq = "${freq_in_months}m$subfreq";
      }

      # daily_cmip5_tiers is a space separated list of integers indicating which
      # data types are created by the diagnostic deck daily_cmip5.dk
      # if daily_cmip5_tiers contains "1" then "*_dd" files are created
      # if daily_cmip5_tiers contains "2" then "*_dp" files are created
      my $daily_cmip5_tiers = '1 2';
      if (exists $parin{diag2ts_daily_cmip5_tiers}) {
        # Setting diag2ts_daily_cmip5_tiers on the command line will override the default
        $daily_cmip5_tiers = $parin{diag2ts_daily_cmip5_tiers};
      } elsif (exists $parin{daily_cmip5_tiers}) {
        # Setting daily_cmip5_tiers on the command line will override the default
        $daily_cmip5_tiers = $parin{daily_cmip5_tiers};
      }

      my $with_dd = '';
      my $with_dp = '';
      foreach (split / /,$daily_cmip5_tiers) {
        $with_dd = "dd" if /^1$/;
        $with_dp = "dp" if /^2$/;
      }
      if (exists $parin{diag2ts_with_dd}) {
        # Setting diag2ts_with_dd on the command line will override the default
        $with_dd = &$ToF($parin{diag2ts_with_dd});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_with_dd");
      } elsif (exists $parin{with_dd}) {
        # Setting with_dd on the command line will override the default
        # unless diag2ts_with_dd is also set on the command line
        $with_dd = &$ToF($parin{with_dd});
      }
      if ($with_dd) {
        $with_dd = "dd";
      } else {
        $with_dd = "";
      }
      if (exists $parin{diag2ts_with_dp}) {
        # Setting diag2ts_with_dp on the command line will override the default
        $with_dp = &$ToF($parin{diag2ts_with_dp});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_with_dp");
      } elsif (exists $parin{with_dp}) {
        # Setting with_dp on the command line will override the default
        # unless diag2ts_with_dp is also set on the command line
        $with_dp = &$ToF($parin{with_dp});
      }
      if ($with_dp) {
        $with_dp = "dp";
      } else {
        $with_dp = "";
      }

      # gssave is a boolean flag indicating whether or not the diagnostic
      # deck daily_cmip5.dk will create "*_ds" files, in addition to whatever
      # is specified by daily_cmip5_tiers
      my $gssave = 'off';
      if (exists $parin{diag2ts_gssave}) {
        # Setting diag2ts_gssave on the command line will override the default
        $gssave = $parin{diag2ts_gssave};
      } elsif (exists $parin{gssave}) {
        # Setting gssave on the command line will override the default
        $gssave = $parin{gssave};
      }

      # Typically we do not want to create time series for "_ds" files
      # nor to save "_ds" time series to cfs so $with_ds is null by default
      # The user may override this by explicitly setting diag2ts_with_ds
      # true on the cccjob command line.
      my $with_ds = "";
      if (exists $parin{diag2ts_with_ds}) {
        # Setting diag2ts_with_ds on the command line will override the default
        $with_ds = &$ToF($parin{diag2ts_with_ds});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_with_ds");
      }
      if ($with_ds) {
        $with_ds = "ds";
      } else {
        $with_ds = "";
      }

      my $with_dsd = "";
      if (exists $parin{diag2ts_with_dsd}) {
        # Setting diag2ts_with_dsd on the command line will override the default
        $with_dsd = &$ToF($parin{diag2ts_with_dsd});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_with_dsd");
      } elsif (exists $parin{with_dsd}) {
        # Setting with_dsd on the command line will override the default
        # unless diag2ts_with_dsd is also set on the command line
        $with_dsd = &$ToF($parin{with_dsd});
      }
      if ($with_dsd) {
        $with_dsd = "dsd";
      } else {
        $with_dsd = "";
      }

      my $with_dcosp = "";
      if (exists $parin{diag2ts_with_dcosp}) {
        # Setting diag2ts_with_dcosp on the command line will override the default
        $with_dcosp = &$ToF($parin{diag2ts_with_dcosp});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_with_dcosp");
      } elsif (exists $parin{with_dcosp}) {
        # Setting with_dcosp on the command line will override the default
        # unless diag2ts_with_dcosp is also set on the command line
        $with_dcosp = &$ToF($parin{with_dcosp});
      }
      if ($with_dcosp) {
        $with_dcosp = "dcosp";
      } else {
        $with_dcosp = "";
      }

      # If one_job_per_suffix is true then define the prefix/suffix lists
      # such that each suffix will be converted by splitdiag in a separate
      # job. This is an attempt to mitigate time outs that were happening
      # circa July, 2014.
      my $one_job_per_suffix = 0;
      if (exists $parin{diag2ts_one_job_per_suffix}) {
        # Setting diag2ts_one_job_per_suffix on the command line will override the default
        $one_job_per_suffix = &$ToF($parin{diag2ts_one_job_per_suffix});
        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_one_job_per_suffix");
      } elsif (exists $parin{one_job_per_suffix}) {
        # Setting one_job_per_suffix on the command line will override the default
        # unless diag2ts_one_job_per_suffix is also set on the command line
        $one_job_per_suffix = &$ToF($parin{one_job_per_suffix});
      }

      # These variables are used to define the prefix and suffix lists used below
      # when one_job_per_suffix is true
      my $pfx_cp    = $with_cp    ? ":d$lett2" : "";
      my $pfx_dd    = $with_dd    ? ":d$lett2" : "";
      my $pfx_dp    = $with_dp    ? ":d$lett2" : "";
      my $pfx_ds    = $with_ds    ? ":d$lett2" : "";
      my $pfx_dsd   = $with_dsd   ? ":d$lett2" : "";
      my $pfx_dcosp = $with_dcosp ? ":d$lett2" : "";
      my $pfx_gz    = $with_gz    ? ":m$lett2" : "";

      my $sfx_cp    = $with_cp    ? ":$with_cp"    : "";
      my $sfx_dd    = $with_dd    ? ":$with_dd"    : "";
      my $sfx_dp    = $with_dp    ? ":$with_dp"    : "";
      my $sfx_ds    = $with_ds    ? ":$with_ds"    : "";
      my $sfx_dsd   = $with_dsd   ? ":$with_dsd"   : "";
      my $sfx_dcosp = $with_dcosp ? ":$with_dcosp" : "";
      my $sfx_gz    = $with_gz    ? ":$with_gz"    : "";

      # Define prefix and suffix lists to be used below
      # Each list will appear in a list variable (e.g. @prefix_list)
      # as well as in a string containing a colon separated list

      # spltdiag requires the following information
      #   1) an input file prefix      (e.g. dc)
      #   2) an input file suffix list (e.g. gp cp)
      #   3) an output file prefix     (e.g. sc)

      my $diag2ts_prefix_list = "d$lett2";
      my $diag2ts_suffix_list = "gp $with_dd $with_dp $with_ds $with_dsd $with_dcosp";
      my $diag2ts_prefix_out  = "s$lett2";
      if ( $one_job_per_suffix ) {
        # Define prefix/suffix lists with an equal number of colon separated entries
        $diag2ts_prefix_list = "d$lett2$pfx_cp$pfx_dd$pfx_dp$pfx_ds$pfx_dsd$pfx_dcosp$pfx_gz";
        $diag2ts_suffix_list = "gp$sfx_cp$sfx_dd$sfx_dp$sfx_ds$sfx_dsd$sfx_dcosp$sfx_gz";
      }
      if ( $lett2 eq "c" ) {
        if ( $with_gz ) {
          $diag2ts_prefix_list = "dc:mc";
          $diag2ts_suffix_list = "gp $with_cp $with_dd $with_dp $with_ds $with_dsd $with_dcosp :gz";
        } else {
          $diag2ts_prefix_list = "dc";
          $diag2ts_suffix_list = "gp $with_cp $with_dd $with_dp $with_ds $with_dsd $with_dcosp";
        }
        $diag2ts_prefix_out  = "sc";
        if ( $one_job_per_suffix ) {
          # Define prefix/suffix lists with an equal number of colon separated entries
          $diag2ts_prefix_list = "dc$pfx_cp$pfx_dd$pfx_dp$pfx_ds$pfx_dsd$pfx_dcosp$pfx_gz";
          $diag2ts_suffix_list = "gp$sfx_cp$sfx_dd$sfx_dp$sfx_ds$sfx_dsd$sfx_dcosp$sfx_gz";
        }
      }
      if ( $lett2 eq "f" and $with_gz ) {
        if ( $with_gz ) {
          $diag2ts_prefix_list = "df:mf";
          $diag2ts_suffix_list = "gp $with_cp $with_dd $with_dp $with_ds $with_dsd $with_dcosp :gz";
        } else {
          $diag2ts_prefix_list = "df";
          $diag2ts_suffix_list = "gp $with_cp $with_dd $with_dp $with_ds $with_dsd $with_dcosp";
        }
        $diag2ts_prefix_out  = "sf";
        if ( $one_job_per_suffix ) {
          # Define prefix/suffix lists with an equal number of colon separated entries
          $diag2ts_prefix_list = "df$pfx_cp$pfx_dd$pfx_dp$pfx_ds$pfx_dsd$pfx_dcosp$pfx_gz";
          $diag2ts_suffix_list = "gp$sfx_cp$sfx_dd$sfx_dp$sfx_ds$sfx_dsd$sfx_dcosp$sfx_gz";
        }
      }
      if ( exists $parin{diag2ts_prefix_list} ) {
        # Override with a user supplied value
        $diag2ts_prefix_list = $parin{diag2ts_prefix_list};

        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_prefix_list");
      }
      if ( exists $parin{diag2ts_suffix_list} ) {
        # Override with a user supplied value
        $diag2ts_suffix_list = $parin{diag2ts_suffix_list};

        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_suffix_list");
      }
      if ( exists $parin{diag2ts_prefix_out} ) {
        # Override with a user supplied value
        $diag2ts_prefix_out = $parin{diag2ts_prefix_out};

        # Remove this variable from vlist/parin
        &$del_vlist("diag2ts_prefix_out");
      }

      # Replace multiple spaces with a single space in diag2ts_suffix_list
      # and remove any trailing whitespace
      $diag2ts_suffix_list =~ s/\s*$//;
      $diag2ts_suffix_list =~ s/\s+/ /;

      my @prefix_list = split(/\s*:\s*/,$diag2ts_prefix_list);
      my @suffix_list = split(/\s*:\s*/,$diag2ts_suffix_list);

      if ( scalar(@prefix_list) != scalar(@suffix_list) ) {
        # diag2ts_suffix_list and diag2ts_prefix_list must have
        # the same number of colon separated fields
        print "${this_job}${opt} --- diag2ts_prefix_list --> $diag2ts_prefix_list <--";
        print " and diag2ts_suffix_list --> $diag2ts_suffix_list <-- are inconsistent.\n";
        die "\n    Stopped";
      }
      die "${this_job}${opt} --- Empty prefix/suffix lists\n    Stopped"
        unless scalar(@prefix_list);

      my @prefix_out  = ();
      # Strip whitespace ...unlike diag2ts_suffix_list, diag2ts_prefix_out
      # must contain only 1 prefix in each colon separated element
      $diag2ts_prefix_out =~ s/\s*//g;
      if ( $diag2ts_prefix_out =~ /:/ ) {
        # Assume the user has supplied a colon separated list of output prefixes
        @prefix_out  = split(/\s*:\s*/,$diag2ts_prefix_out);
        if ( scalar(@prefix_out) != scalar(@suffix_list) ) {
          die "${this_job}${opt} --- Invalid output prefix list -->  <--\n";
        }
      } else {
        # Assign a list with the same length as suffix_list where each
        # element is the same, namely $diag2ts_prefix_out
        foreach (@suffix_list) {
          push @prefix_out,$diag2ts_prefix_out;
        }
      }

      if ($Verbose > 10) {
        for ( my $idx = 0; $idx < scalar(@prefix_list); $idx++ ) {
          print "pfx=$prefix_list[$idx]   sfx=$suffix_list[$idx]   pfx_out=$prefix_out[$idx]\n";
        }
      }

      #=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
      #=#=#=#=#=#   Build the job list  #=#=#=#=#=#
      #=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#

      if ($load_diag) {
        # Load diagnostic files

        # Define a clone of mload for loading diagnostic files
        &$clone_check("mload_diag2ts");

        # Set defaults for mload_diag2ts

        if ( exists $parin{diag2ts_RUNPATH} and defined $parin{diag2ts_RUNPATH} ) {
          # Reset RUNPATH for this load
          &$set_vlist("mload_diag2ts_RUNPATH=$parin{diag2ts_RUNPATH}")
        }
        if ( exists $parin{diag2ts_CCRNTMP} and defined $parin{diag2ts_CCRNTMP} ) {
          # Reset CCRNTMP for this load
          &$set_vlist("mload_diag2ts_CCRNTMP=$parin{diag2ts_CCRNTMP}")
        }

        unless (exists $parin{mload_diag2ts_prefix_list}) {
          &$set_vlist("mload_diag2ts_prefix_list=$diag2ts_prefix_list")
        }
        unless (exists $parin{mload_diag2ts_suffix_list}) {
          &$set_vlist("mload_diag2ts_suffix_list=$diag2ts_suffix_list")
        }
        unless (exists $parin{load_shortermdir} or
                exists $parin{mload_diag2ts_shortermdir}) {
          &$set_vlist("mload_diag2ts_shortermdir=off");
        }
        unless (exists $parin{load_masterdir} or
                exists $parin{mload_diag2ts_masterdir}) {
          &$set_vlist("mload_diag2ts_masterdir=off");
        }
        unless (exists $parin{load_sv} or
                exists $parin{mload_diag2ts_sv}) {
          &$set_vlist("mload_diag2ts_sv=on");
        }
        push @jl, ("mload_diag2ts:-$load_diag_freq");
      }

      # Insert 1 or more instances of spltdiag to create the time series

      for ( my $idx = 0; $idx < scalar(@prefix_list); $idx++ ) {
        # Clone spltdiag
        my $mod_name = "$prefix_list[$idx]_$suffix_list[$idx]";
        $mod_name =~ s/\s*$//;
        $mod_name =~ s/\s+/_/g;
        substr($mod_name,0,0) = "spltdiag_";
        &$clone_check($mod_name);

        # Set the job specific input prefix
        my $var = "${mod_name}_uxxx";
        $main::job_specific_vars{$mod_name}{$var} = $prefix_list[$idx];

        # Set the job specific output prefix
        $var = "${mod_name}_mts_uxxx";
        $main::job_specific_vars{$mod_name}{$var} = $prefix_out[$idx];

        # Set the job specific suffix list
        $var = "${mod_name}_suffix_list";
        $main::job_specific_vars{$mod_name}{$var} = $suffix_list[$idx];

        if ( exists $parin{spltdiag_vars} and defined $parin{spltdiag_vars} ) {
          # Set a job specific spltdiag_vars
          my $var = "${mod_name}_vars";
          $main::job_specific_vars{$mod_name}{$var} = $parin{spltdiag_vars};
        }

        if ( exists $parin{diag2ts_RUNPATH} and defined $parin{diag2ts_RUNPATH} ) {
          # Set a job specific RUNPATH
          my $var = "${mod_name}_RUNPATH";
          $main::job_specific_vars{$mod_name}{$var} = $parin{diag2ts_RUNPATH};
        }

        if ( exists $parin{diag2ts_CCRNTMP} and defined $parin{diag2ts_CCRNTMP} ) {
          # Set a job specific CCRNTMP
          my $var = "${mod_name}_CCRNTMP";
          $main::job_specific_vars{$mod_name}{$var} = $parin{diag2ts_CCRNTMP};
        }

        # Add this job to the job list
        push @jl, ("${mod_name}:$freq");
      }

      # with_gztsdiag4IPCC is used below to indicate whether or not an additional
      # file list with suffix "gz_ipcc_file_list" is to be used
      my $with_gztsdiag4IPCC = 0;
      if ($with_gztsdiag) {
        # Include decks to generate additional ocean diagnostics from
        # the time series files created by spltdiag

        # Clone gztsdiag for deck 1
        my $gztsdiag_deck1 = "gztsdiag5";
        if (exists $parin{diag2ts_gztsdiag_deck1}) {
          # Setting gztsdiag_deck1 on the command line will override the default
          $gztsdiag_deck1 = $parin{diag2ts_gztsdiag_deck1};
          # Remove this variable from vlist/parin
          &$del_vlist("diag2ts_gztsdiag_deck1");
        } elsif (exists $parin{gztsdiag_deck1}) {
          # Setting gztsdiag_deck1 on the command line will override the default
          $gztsdiag_deck1 = $parin{gztsdiag_deck1};
          # Remove this variable from vlist/parin
          &$del_vlist("gztsdiag_deck1");
        }
        # Strip any ".dk" from the end of gztsdiag_deck1
        $gztsdiag_deck1 =~ s/\.dk\s*$//;

        my $clone_name = "$gztsdiag_deck1";
        my $var_name   = "${gztsdiag_deck1}_deck";
        my $deck_name  = "${gztsdiag_deck1}.dk";

        # If the deck name is "no" or "off" then do not include this job
        unless ( $gztsdiag_deck1 =~ m/^\s*(no|off)\s*$/i ) {
          # Create the clone and add the deck name to the cloned job only
          &$clone_check($clone_name);
          $main::job_specific_vars{$clone_name}{$var_name} = $deck_name;

          if ( exists $parin{diag2ts_RUNPATH} and defined $parin{diag2ts_RUNPATH} ) {
            # Set a job specific RUNPATH
            my $var = "${clone_name}_RUNPATH";
            $main::job_specific_vars{$clone_name}{$var} = $parin{diag2ts_RUNPATH};
          }

          if ( exists $parin{diag2ts_CCRNTMP} and defined $parin{diag2ts_CCRNTMP} ) {
            # Set a job specific CCRNTMP
            my $var = "${clone_name}_CCRNTMP";
            $main::job_specific_vars{$clone_name}{$var} = $parin{diag2ts_CCRNTMP};
          }

          # Add this job to the job list
          push @jl, ("${clone_name}:$freq");
        }

        # Clone gztsdiag for deck 2
        my $gztsdiag_deck2 = "gztsdiag4IPCC_v3";
        if (exists $parin{diag2ts_gztsdiag_deck2}) {
          # Setting gztsdiag_deck2 on the command line will override the default
          $gztsdiag_deck2 = $parin{diag2ts_gztsdiag_deck2};
          # Remove this variable from vlist/parin
          &$del_vlist("diag2ts_gztsdiag_deck2");
        } elsif (exists $parin{gztsdiag_deck2}) {
          # Setting gztsdiag_deck2 on the command line will override the default
          $gztsdiag_deck2 = $parin{gztsdiag_deck2};
          # Remove this variable from vlist/parin
          &$del_vlist("gztsdiag_deck2");
        }
        # Strip any ".dk" from the end of gztsdiag_deck2
        $gztsdiag_deck2 =~ s/\.dk\s*$//;

        $clone_name = "$gztsdiag_deck2";
        $var_name   = "${gztsdiag_deck2}_deck";
        $deck_name  = "${gztsdiag_deck2}.dk";

        # If the deck name is "no" or "off" then do not include this job
        unless ( $gztsdiag_deck2 =~ m/^\s*(no|off)\s*$/i ) {
          # Create the clone and add the deck name to the cloned job only
          &$clone_check($clone_name);
          $main::job_specific_vars{$clone_name}{$var_name} = $deck_name;

          if ( exists $parin{diag2ts_RUNPATH} and defined $parin{diag2ts_RUNPATH} ) {
            # Set a job specific RUNPATH
            my $var = "${clone_name}_RUNPATH";
            $main::job_specific_vars{$clone_name}{$var} = $parin{diag2ts_RUNPATH};
          }

          if ( exists $parin{diag2ts_CCRNTMP} and defined $parin{diag2ts_CCRNTMP} ) {
            # Set a job specific CCRNTMP
            my $var = "${clone_name}_CCRNTMP";
            $main::job_specific_vars{$clone_name}{$var} = $parin{diag2ts_CCRNTMP};
          }

          # Add this job to the job list
          push @jl, ("${clone_name}:$freq");

          # Set a variable to indicate whether or not
          # a deck named gztsdiag4IPCC* is used
          $with_gztsdiag4IPCC = 1 if $gztsdiag_deck2 =~ /^\s*gztsdiag4IPCC/;
        }
      }

      if ($delete_diag) {
        # Delete diagnostic files

        # Define a clone of mdelete to delete diagnostic files
        &$clone_check("mdelete_diag2ts");

        # Set defaults for mdelete
        unless (exists $parin{mdelete_diag2ts_prefix_list}) {
          &$set_vlist("mdelete_diag2ts_prefix_list=$diag2ts_prefix_list")
        }
        unless (exists $parin{mdelete_diag2ts_suffix_list}) {
          &$set_vlist("mdelete_diag2ts_suffix_list=$diag2ts_suffix_list")
        }

        unless (exists $parin{mdelete_diag2ts_leave_last_mon}) {
          &$set_vlist("mdelete_diag2ts_leave_last_mon=off")
        }

        if ( exists $parin{diag2ts_RUNPATH} and defined $parin{diag2ts_RUNPATH} ) {
          # Set a job specific RUNPATH
          my $var = "mdelete_diag2ts_RUNPATH";
          $main::job_specific_vars{mdelete_diag2ts}{$var} = $parin{diag2ts_RUNPATH};
        }

        if ( exists $parin{diag2ts_CCRNTMP} and defined $parin{diag2ts_CCRNTMP} ) {
          # Set a job specific CCRNTMP
          my $var = "mdelete_diag2ts_CCRNTMP";
          $main::job_specific_vars{mdelete_diag2ts}{$var} = $parin{diag2ts_CCRNTMP};
        }

        # Never delete diag files until the end of a time series chunk
        push @jl, ("mdelete_diag2ts:$freq");
      }

      if ($dump_time_series) {
        # Dump the time series files created by spltdiag to cfs

        # Time series file names are defined in spltdiag with the format
        #     ${prefix}_${runid}_${date_range}_${suffix}_${var}
        #   prefix and suffix are elements of @prefix_out and @suffix_list
        #   var is a variable name determined in spltdiag
        #   date_range is either
        #     YYYY_YYYY when first and last years are complete (start_month=1 stop_month=12)
        #     YYYYMM_YYYYMM when either the first or last year is incomplete
        #
        # A file containing a list of all time series file names is created by spltdiag
        # and saved with a similar file name but the variable name is set to "file_list"
        #     ${prefix}_${runid}_${date_range}_${suffix}_file_list

        # Insert instances of dump_sublist to dump the time series

        for ( my $idx = 0; $idx < scalar(@suffix_list); $idx++ ) {
          my $pfx = $prefix_out[$idx];
          my $sfxlist = $suffix_list[$idx];
          $sfxlist =~ s/\s*$//;
          $sfxlist =~ s/\s+/_/g;
          # Clone dump_sublist
          my $mod_id = "${pfx}_$sfxlist";
          my $mod_name = $mod_id;
          substr($mod_name,0,0) = "dump_sublist_";
          &$clone_check($mod_name);

          # Create a code snippet that will determine, at run time, the
          # correct date_range used as part of the time series file names
          my $snippet = q@
  # Determine a date range to be used as part of the file name
  # containing the list of time series files to be dumped
  if [ $start_mon -ne 1 -o $stop_mon -ne 12 ]; then
    date_range="$start_year${start_mon}_$stop_year$stop_mon"
  else
    date_range="${start_year}_$stop_year"
  fi
@;
          # Insert this code snippet into the current module
          $JOBDEF{job}{$mod_name} =~ s/^\s*# *<<INSERT_DUMP_SUBLIST_$mod_id>> *$/$snippet/mi;

          # Determine names of the files containg lists of time series file names
          # Note that the variable ${date_range} will not be expanded at job creation
          # time but will be expanded at run time and will contain the value
          # determined by the code snippet above
          my $name_list = '';
          foreach my $sfx ( split(/\s+/,$suffix_list[$idx]) ) {
            $name_list .= " ${pfx}_${runid}_\${date_range}_${sfx}_file_list";
            if ( $sfx eq "gz" and $with_gztsdiag4IPCC ) {
              # An additional file list with the suffix "gz_ipcc_file_list" is created
              # by the deck gztsdiag4IPCC* and should be included with the rest of the
              # ocean (gz) files when that deck is run as part of this job string
              $name_list .= " ${pfx}_${runid}_\${date_range}_gz_ipcc_file_list";
            }
          }
          $name_list =~ s/^\s*//;
          die "${this_job}${opt} --- Unable to detemine name_list\n  Stopped"
              unless $name_list;

          # Set the job specific file name list
          $main::job_specific_vars{$mod_name}{dump_file_list} = $name_list;

          # Set the job specific arclabel
          # Note that the variable ym_range is defined, at run time, in dump_sublist
          my $var = "${mod_name}_arclabel";
          $main::job_specific_vars{$mod_name}{$var} =
              "${pfx}_${runid}_\${ym_range}_${sfxlist}";

          # Set a job specific parameter to limit the number of files per dump
          my $max_files_per_arc = 400;
          $max_files_per_arc = $parin{max_files_per_arc}
                     if exists $parin{max_files_per_arc};
          $max_files_per_arc = $parin{diag2ts_max_files_per_arc}
                     if exists $parin{diag2ts_max_files_per_arc};
          $main::job_specific_vars{$mod_name}{max_files_per_arc} = $max_files_per_arc;

          # Set a job specific parameter to limit the size of each arc file
          my $max_arcsize = "12GB";
          $max_arcsize = $parin{max_arcsize}
              if exists $parin{max_arcsize};
          $max_arcsize = $parin{diag2ts_max_arcsize}
              if exists $parin{diag2ts_max_arcsize};
          $main::job_specific_vars{$mod_name}{max_arcsize} = $max_arcsize;

          if ( exists $parin{diag2ts_RUNPATH} and defined $parin{diag2ts_RUNPATH} ) {
            # Set a job specific RUNPATH
            my $var = "${mod_name}_RUNPATH";
            $main::job_specific_vars{$mod_name}{$var} = $parin{diag2ts_RUNPATH};
          }

          if ( exists $parin{diag2ts_CCRNTMP} and defined $parin{diag2ts_CCRNTMP} ) {
            # Set a job specific CCRNTMP
            my $var = "${mod_name}_CCRNTMP";
            $main::job_specific_vars{$mod_name}{$var} = $parin{diag2ts_CCRNTMP};
          }

          # For each of the job specific variable assignments for shortermdir,
          # masterdir and sv, let the diag2ts_* form of any user supplied
          # definition take precedence over the * form (*=variable name)
          my $shortermdir = "on";
          $shortermdir = $parin{shortermdir}         if exists $parin{shortermdir};
          $shortermdir = $parin{diag2ts_shortermdir} if exists $parin{diag2ts_shortermdir};
          $main::job_specific_vars{$mod_name}{shortermdir} = $shortermdir;

          my $masterdir = "off";
          $masterdir = $parin{masterdir}         if exists $parin{masterdir};
          $masterdir = $parin{diag2ts_masterdir} if exists $parin{diag2ts_masterdir};
          $main::job_specific_vars{$mod_name}{masterdir} = $masterdir;

          my $sv = "on";
          $sv = $parin{sv}         if exists $parin{sv};
          $sv = $parin{diag2ts_sv} if exists $parin{diag2ts_sv};
          $main::job_specific_vars{$mod_name}{sv} = $sv;

          my $besc = "off";
          $besc = $parin{besc}         if exists $parin{besc};
          $besc = $parin{diag2ts_besc} if exists $parin{diag2ts_besc};
          $main::job_specific_vars{$mod_name}{besc} = $besc;

          my $rmdskcpy = "on";
          $rmdskcpy = $parin{rmdskcpy}         if exists $parin{rmdskcpy};
          $rmdskcpy = $parin{diag2ts_rmdskcpy} if exists $parin{diag2ts_rmdskcpy};
          $main::job_specific_vars{$mod_name}{rmdskcpy} = $rmdskcpy;

          my $with_lsarc = "on";
          $with_lsarc = $parin{with_lsarc}         if exists $parin{with_lsarc};
          $with_lsarc = $parin{diag2ts_with_lsarc} if exists $parin{diag2ts_with_lsarc};
          $main::job_specific_vars{$mod_name}{with_lsarc} = $with_lsarc;

          # Add this job to the job list
          push @jl, ("${mod_name}:$freq");
        }
      }

      if ($transfer_time_series) {
        # Insert instances of rtrans to transfer the time series files

        for ( my $idx = 0; $idx < scalar(@suffix_list); $idx++ ) {
          my $pfx = $prefix_out[$idx];
          foreach my $sfx ( split(/\s+/,$suffix_list[$idx]) ) {
            # Clone rtrans
            my $mod_name = "${pfx}_$sfx";
            $mod_name =~ s/\s*$//;
            $mod_name =~ s/\s+//g;
            my $mod_id = $mod_name;
            substr($mod_name,0,0) = "rtrans_";
            &$clone_check($mod_name);

            # Create a code snippet that will determine, at run time, the
            # correct date_range used as part of the time series file names
            my $snippet = q@
  # Determine a date range to be used as part of the file name
  # containing the list of time series files to be transferd
  if [ $start_mon -ne 1 -o $stop_mon -ne 12 ]; then
    date_range="$start_year${start_mon}_$stop_year$stop_mon"
  else
    date_range="${start_year}_$stop_year"
  fi
@;

            if ( $sfx =~ /^\s*gz\s*$/ ) {
              # For the "gz" suffix we transfer only a subset of files
              # This subset will be determined at run time by the following code
              $snippet .= q@
  gz_list="data_description opld psib salt temp sss sst u v"
  release hblm
  access hblm OPFX_${date_range}_gz_hblm na
  if [ -s "hblm" ] ; then
    gz_list="$gz_list hblm"
    release hblm
  fi

  if [ "x$CarbO" = "xon" ] ; then
    CarO_lst="dici fcoo odpc ofal ofic ofnf ofni oppo ofxi ofxo opco oph
              salk schl snit sdet sdic spon sphy szoo ofso ofsi ovic oval
              annual_ochl annual_odet annual_onit annual_ophy annual_ozoo
              annual_odic annual_oph annual_pon"

    release annual_alk
    access annual_alk OPFX_${date_range}_gz_annual_alk na
    if [ -s "annual_alk" ] ; then
      CarO_lst="$CarO_lst annual_alk"
      release annual_alk
    else
      CarO_lst="$CarO_lst annual_oalk"
    fi

    release ovni
    access ovni OPFX_${date_range}_gz_ovni na
    if [ -s "ovni" ] ; then
      CarO_lst="$CarO_lst ovni"
      release ovni
    fi
    gz_list="$gz_list $CarO_lst"
  fi

  n=0
  for sfx in $gz_list ; do
    n=`expr $n + 1`
    eval file${n}=OPFX_${runid}_${date_range}_gz_$sfx
  done
  join=$n
@;
            }

            if ( $sfx =~ /^\s*dd\s*$/ and not $transfer_all_dd ) {
              # For the "dd" suffix we normally transfer only a subset of files
              # If transfer_all_dd is true then all files in the dd_file_list
              # file will be transferred
              # This subset is determined at run time by the following code
              $snippet .= q@
  dd_list="cldt fdl fla flg fn fsg fslo fss gt hfl hfs pcp pcpc pcpn pmsl rof
           sic sicn sno sq srh srhn srhx st stmn stmx su sv swa swx wgfl"

  release dd_sst
  access dd_sst OPFX_${runid}_${date_range}_dd_sst na
  if [ -f "dd_sst" ] ; then
    dd_list="$dd_list sst sst2 hblm"
    release dd_sst
  fi

  release dd_uice
  access dd_uice OPFX_${runid}_${date_range}_dd_uice na
  if [ -f "dd_uice" ] ; then
    dd_list="$dd_list uice vice"
    release dd_uice
  fi

  n=0
  for sfx in $dd_list ; do
    n=`expr $n + 1`
    eval file${n}=OPFX_${runid}_${date_range}_dd_$sfx
  done
  join=$n
@;
            }

            # Replace OPFX with the correct output prefix in snippet
            $snippet =~ s/OPFX/s$lett2/g;

            # Insert this code snippet into the current module
            $JOBDEF{job}{$mod_name} =~ s/^\s*# *<<INSERT_RTRANS_$mod_id>> *$/$snippet/mi;

            # Determine the name of the file containg the list of time series file names
            # Note that the variable ${date_range} will not be expanded at job creation
            # time but will be expanded at run time and will contain the value
            # determined by the code snippet above
            my $name_list = "${pfx}_${runid}_\${date_range}_${sfx}_file_list";

            if ( $sfx =~ /^\s*gz\s*$/ and $with_gztsdiag4IPCC ) {
              # An additional file list with the suffix "gz_ipcc_file_list" is created
              # by the deck gztsdiag4IPCC* and should be included when that deck
              # is run as part of this job string.
              # For transfers, this file list will replace that list in *_gz_file_list
              $name_list = "${pfx}_${runid}_\${date_range}_gz_ipcc_file_list";
            }

            if ( $sfx =~ /^\s*dd\s*$/ and not $transfer_all_dd ) {
              # For the "dd" suffix we normally transfer only a subset of files
              # If transfer_all_dd is true then all files in the dd_file_list
              # file will be transfered.
              # When just a subset of files is transfered, do not set the
              # rtrans*_file_list variable in this job.
              $name_list = "";
            }

            if ( $name_list ) {
              # Set the job specific file name list
              $main::job_specific_vars{$mod_name}{"${mod_name}_file_list"} = $name_list;
            }

            # Set common job specific variables used in rtrans

            my $remusr = $user;
            $remusr = $parin{diag2ts_remusr} if exists $parin{diag2ts_remusr};
            $main::job_specific_vars{$mod_name}{"${mod_name}_remusr"} = $remusr;

            my $remserver = "lxsrv.cccma.ec.gc.ca";
            $remserver = $parin{diag2ts_remserver} if exists $parin{diag2ts_remserver};
            $main::job_specific_vars{$mod_name}{"${mod_name}_remserver"} = $remserver;

            my $remdir = '';
            chomp($remdir = `ssh $remserver 'echo \$RUNPATH' 2\>/dev/null`);
            $remdir = $parin{RMTRUNPATH} if exists $parin{RMTRUNPATH};
            $remdir = $parin{diag2ts_RMTRUNPATH} if exists $parin{diag2ts_RMTRUNPATH};
            $remdir = $parin{diag2ts_remdir} if exists $parin{diag2ts_remdir};
            die "diag2ts: Unable to determine remote dir for file transfer. Stopped"
                unless $remdir;
            # Make sure remdir end with "/time"
            $remdir .= "/time" unless $remdir =~ /\/time\s*$/;
            $main::job_specific_vars{$mod_name}{"${mod_name}_remdir"} = $remdir;

            my $rem_save = "on";
            $rem_save = $parin{diag2ts_rem_save} if exists $parin{diag2ts_rem_save};
            $main::job_specific_vars{$mod_name}{"${mod_name}_rem_save"} = $rem_save;

            if ( exists $parin{diag2ts_RUNPATH} and defined $parin{diag2ts_RUNPATH} ) {
              # Set a job specific RUNPATH
              my $var = "${mod_name}_RUNPATH";
              $main::job_specific_vars{$mod_name}{$var} = $parin{diag2ts_RUNPATH};
            }

            if ( exists $parin{diag2ts_CCRNTMP} and defined $parin{diag2ts_CCRNTMP} ) {
              # Set a job specific CCRNTMP
              my $var = "${mod_name}_CCRNTMP";
              $main::job_specific_vars{$mod_name}{$var} = $parin{diag2ts_CCRNTMP};
            }

            # Add this job to the job list
            push @jl, ("${mod_name}:$freq");
          }
        }
      }

      if ($delete_time_series) {
        # Insert instances of del_list to delete the time series files

        if ( $with_gztsdiag4IPCC ) {
          # An additional file list with the suffix "gz_ipcc_file_list" is created
          # by the deck gztsdiag4IPCC* and should be included when that deck
          # is run as part of this job string
          push @prefix_out, $diag2ts_prefix_out;
          push @suffix_list, "gz_ipcc";
        }

        for ( my $idx = 0; $idx < scalar(@suffix_list); $idx++ ) {
          my $pfx = $prefix_out[$idx];
          foreach my $sfx ( split(/\s+/,$suffix_list[$idx]) ) {
            # Clone del_list
            my $mod_name = "${pfx}_$sfx";
            $mod_name =~ s/\s*$//;
            $mod_name =~ s/\s+//g;
            my $mod_id = $mod_name;
            substr($mod_name,0,0) = "del_list_";
            &$clone_check($mod_name);

            # Create a code snippet that will determine, at run time, the
            # correct date_range used as part of the time series file names
            my $snippet = q@
  # Determine a date range to be used as part of the file name
  # containing the list of time series files to be deleted
  if [ $start_mon -ne 1 -o $stop_mon -ne 12 ]; then
    date_range="$start_year${start_mon}_$stop_year$stop_mon"
  else
    date_range="${start_year}_$stop_year"
  fi
@;
            # Insert this code snippet into the current module
            $JOBDEF{job}{$mod_name} =~ s/^\s*# *<<INSERT_DEL_LIST_$mod_id>> *$/$snippet/mi;

            # Determine the name of the file containg the list of time series file names
            # Note that the variable ${date_range} will not be expanded at job creation
            # time but will be expanded at run time and will contain the value
            # determined by the code snippet above
            my $name_list = "${pfx}_${runid}_\${date_range}_${sfx}_file_list";

            # Set the job specific file name list
            $main::job_specific_vars{$mod_name}{delete_file_list} = $name_list;

            if ( exists $parin{diag2ts_RUNPATH} and defined $parin{diag2ts_RUNPATH} ) {
              # Set a job specific RUNPATH
              my $var = "${mod_name}_RUNPATH";
              $main::job_specific_vars{$mod_name}{$var} = $parin{diag2ts_RUNPATH};
            }

            if ( exists $parin{diag2ts_CCRNTMP} and defined $parin{diag2ts_CCRNTMP} ) {
              # Set a job specific CCRNTMP
              my $var = "${mod_name}_CCRNTMP";
              $main::job_specific_vars{$mod_name}{$var} = $parin{diag2ts_CCRNTMP};
            }

            # Add this job to the job list
            push @jl, ("${mod_name}:$freq");
          }
        }
      }

      next;
    };
    ############################################################################

    /^pdiag$/ and do {
      #############################################################################
      # Parallel diagnostics
      # Run diagnostics/tdumper/pooling/time series/transfer in parallel
      # with the model
      #############################################################################

      my $this_job = $_;

      # Initialize start and stop dates from user input values
      my ($pdiag_start_year,$pdiag_start_mon)  = split(/\s*:\s*/, $Start_time_cmdl);
      $pdiag_start_year = 1 unless $pdiag_start_year;
      $pdiag_start_mon  = 1 unless $pdiag_start_mon;

      my ($pdiag_stop_year,$pdiag_stop_mon) = split(/\s*:\s*/, $Stop_time_cmdl);
      $pdiag_stop_year =  1 unless $pdiag_stop_year;
      $pdiag_stop_mon  = 12 unless $pdiag_stop_mon;

      # A string to hold internal options specific to pdiag
      my $pdiag_iopts = '';

      # Strip any angle bracketed defs from the end of $opt and assign them,
      # as a string without the angle brackets, to $pdiag_iopts
      ($pdiag_iopts) = $opt =~ m/\s*<(.*)>\s*$/ if $opt;
      $opt =~ s/\s*<(.*)>\s*$// if $opt;

      # Ensure that opt has a non null value
      $opt = ':y' unless $opt;

      # Possibly reorder the pdiag_iopts string
      $pdiag_iopts = ref2str_iopts( str2ref_iopts($pdiag_iopts) );

      # Determine the insertion frequency in units of months
      my $pdiag_freq = 1;
      if ($opt) {
        # The user has specified an insertion frequency on the command line via
        # something like pdiag=xyz:y. The modifier (y in this case) must be
        # of the form N[ym] where N is an optional integer.
        my ($freq, $unit) = $opt =~ /^:([+-]?\d*)(.*)/;
        unless ($freq) {
          if ( $unit =~ /^m\s*$/ ) {
            if (exists $parin{months}) {
              $freq = $parin{months};
            } else {
              $freq = 1;
            }
          } else {
            $freq = 1;
          }
        }
        $freq = abs($freq);
        if ( $unit =~ /^m\s*$/ ) {
          $pdiag_freq = $freq;
        } elsif ( $unit =~ /^y\s*$/ ) {
          $pdiag_freq = 12*$freq;
        } else {
          die "$_$opt :: Invalid insertion frequency --> $freq$unit <--\n  Stopped";
        }
      }

      # Ensure that the pdiag insertion frequency is a multiple of "months"
      my $months = 1;
      $months = $parin{months} if exists $parin{months};
      my $msg = "$_$opt :: pdiag insertion frequency --> ${pdiag_freq}m <--- ";
      $msg .= "must be a multiple of months = $months ";
      die "$msg\n  Stopped" unless ( $pdiag_freq % $months == 0 );

      my $diag_job = '';
      # Determine the name of the file containing the diagnostic string
      if ($curr_fnames[0]) {
        # Take the name of the diagnostic job from the pdiag=fname element
        # of the job description found on the cccjob command line
        $diag_job = $curr_fnames[0];
        if ( $diag_job =~ /^\s*nodiag\s*$/i ) {
          # When diag_job is provided with the special string "nodiag" then
          # unset diag_job so that no value is inserted into the string and
          # therefore diagnostics are not done by the string
          $diag_job = '';
        }
      } else {
        # This diag job name must be present when diagnostics are to be run
        # this is not an error but issue a warning in case the user
        warn "$_$opt :: $this_job was specified without a diagnostic job name.\n";
        warn "$_$opt :: Diagnostics will not run but history files will be dumped to cfs.\n";
      }

      if ( $diag_job ) {
        # Set a job specific value for diag_job
        # This is the name of a diagnostic job that must be available at run time
        $main::job_specific_vars{pdiag}{diag_job} = $diag_job;
      }

      # runid is required to be set on the command line
      die "*** runid must be set when invoking $_.\n  Stopped"
        unless $preset{changed}{runid};

      # The second letter in either pdiag_uxxx, mdump_uxxx or uxxx is required
      # by pdiag to determine file names at run time
      my $lett2 = '';
      my $pfx = '';
      if (exists $parin{pdiag_uxxx}) {
        ($lett2) = $parin{pdiag_uxxx} =~ /^\s*.(.)/;
        $pfx = $parin{pdiag_uxxx};
      } elsif (exists $parin{uxxx}) {
        ($lett2) = $parin{uxxx} =~ /^\s*.(.)/;
        $pfx = $parin{uxxx};
      } elsif (exists $parin{mdump_uxxx}) {
        ($lett2) = $parin{mdump_uxxx} =~ /^\s*.(.)/;
        $pfx = $parin{mdump_uxxx};
      } else {
        die "*** Either uxxx, pdiag_uxxx or mdump_uxxx must be set when invoking $_.\n  Stopped";
      }

      unless ( $lett2 =~ /^a|m|c|r|d|f$/ ) {
        die "$_ :: Found invalid prefix --> $pfx <--\n  Stopped";
      }

      # Set a job specific value for pdiag_uxxx
      $main::job_specific_vars{pdiag}{pdiag_uxxx} = "m$lett2";

      # Set defaults and do basic sanity checks on mdump_months and pdiag_chunk_size
      my $mdump_months = 1;
      $mdump_months = $parin{mdump_months} if exists $parin{mdump_months};

      if ( $mdump_months <= $pdiag_freq ) {
        my $msg = "$_$opt :: pdiag insertion frequency --> ${pdiag_freq}m <--- ";
        $msg .= "must be a multiple of mdump_months = $mdump_months ";
        die "$msg\n  Stopped" unless ( $pdiag_freq % $mdump_months == 0 );
      } else {
        my $msg = "$_$opt :: pdiag insertion frequency --> ${pdiag_freq}m <--- ";
        $msg .= "must be greater than or equal to mdump_months --> $mdump_months <--";
        die "$msg\n  Stopped";
      }

      my $chunk_size = 120;
      $chunk_size = $parin{pdiag_chunk_size} if exists $parin{pdiag_chunk_size};

      my $first_chunk_size = $chunk_size;
      if ( exists $parin{pdiag_chunk_size} ) {
        $first_chunk_size = $chunk_size;
      } else {
        $first_chunk_size = $chunk_size + 12;
      }
      if ( exists $parin{pdiag_first_chunk_size} ) {
        $first_chunk_size = $parin{pdiag_first_chunk_size};
      }

      if ( $mdump_months <= $chunk_size ) {
        my $msg = "$_$opt :: pdiag chunk size = $chunk_size ";
        $msg .= "must be a multiple of mdump_months = $mdump_months ";
        die "$msg\n  Stopped" unless ( $chunk_size % $mdump_months == 0 );
      } else {
        my $msg = "$_$opt :: pdiag chunk size = $chunk_size ";
        $msg .= "must be greater than or equal to mdump_months = $mdump_months";
        die "$msg\n  Stopped";
      }

      # Set a job specific value for mdump_months and pdiag_chunk_size
      $main::job_specific_vars{pdiag}{mdump_months} = $mdump_months;
      $main::job_specific_vars{pdiag}{pdiag_chunk_size} = $chunk_size;

      # When run_stand_alone is true this alias will create a job string
      # that will create a single diagnostic string to be run after creation.
      # If run_stand_alone is true then pdiag should be the only job in the
      # job description provided by the user to cccjob.
      my $run_stand_alone = 0;
      my $sub_diag = "";
      if (exists $parin{pdiag_run_stand_alone}) {
        # Setting pdiag_run_stand_alone on the command line will override the default
        $run_stand_alone = &$ToF($parin{pdiag_run_stand_alone});
        # Remove this variable from vlist/parin
        &$del_vlist("pdiag_run_stand_alone");
      } elsif (exists $parin{run_stand_alone}) {
        $run_stand_alone = &$ToF($parin{run_stand_alone});
        # Remove this variable from vlist/parin
        &$del_vlist("run_stand_alone");
      }

      # When submit_stand_alone is true and run_stand_alone is true the diag
      # string will be created in the users crawork dir but not submitted
      my $submit_stand_alone = 1;
      if (exists $parin{pdiag_submit_stand_alone}) {
        # Setting pdiag_submit_stand_alone on the command line will override the default
        $submit_stand_alone = &$ToF($parin{pdiag_submit_stand_alone});
        # Remove this variable from vlist/parin
        &$del_vlist("pdiag_submit_stand_alone");
      } elsif (exists $parin{submit_stand_alone}) {
        $submit_stand_alone = &$ToF($parin{submit_stand_alone});
        # Remove this variable from vlist/parin
        &$del_vlist("submit_stand_alone");
      }

      if ($run_stand_alone) {
        # Set crawork for this job
        my $craworkval = "$parin{runid}_create_diag_$stamp";
        &$set_vlist("crawork=$craworkval") unless exists $parin{crawork};

        # Create a jobdef for a job that will submit the serial diagnostic
        # string created when $run_stand_alone is true
        $sub_diag = "sub_diag";
        my $sub_diag_job =
q@#!/bin/sh
#=======================================================================
#                                --- submit serial diagnostic string ---
#=======================================================================
 set -a
 . betapath2

#  * ........................... Parmsub Parameters ............................

 username="acrnxxx"; user="XXX";
 runid=job000; uxxx='uxxx'; crawork="${runid}_job"

 nextjob=on
 noprint=on

 jobname=sub_diag; time="600" ; memory="900mb" ;

 . comjcl.cdk

cat > Execute_Script <<'end_of_script'

  cp $HOME/.queue/.crawork/SUB_DIAG_JOB sub_diag_job
  ls -l
  rsub mdest=pollux sub_diag_job

end_of_script

 . endjcl.cdk

#end_of_job
@;

        # Define stand_alone_craworkval in the output job
        $craworkval = "$parin{runid}_diag_$stamp";
        $main::job_specific_vars{pdiag}{stand_alone_craworkval} = $craworkval;

        # Replace the string "SUB_DIAG_JOB" with the name of this crawork string
        $sub_diag_job =~ s/SUB_DIAG_JOB/${craworkval}_string/;

        $JOBDEF{clone}{$sub_diag} = 0;
        $JOBDEF{description}{$sub_diag} = "submit serial diagostic string";
        $JOBDEF{hide}{$sub_diag} = '';
        $JOBDEF{job}{$sub_diag} = "$sub_diag_job";
        $JOBDEF{path}{$sub_diag} = "internal: pdiag with run_stand_alone true";
        $JOBDEF{user_supplied}{$sub_diag} = 1;

        my $serial_iopts = "";
        if ( $first_chunk_size != $chunk_size ) {
          # my $mon_offset = 0;
          # my $insert_at_start = 0;
          # if ( $first_chunk_size > $chunk_size ) {
          #   # The first chunk is longer than all the rest
          #   $mon_offset = $chunk_size - $first_chunk_size;
          #   $insert_at_start = 1;
          # } else {
          #   # The first chunk is shorter than all the rest
          #   $mon_offset = $chunk_size - $first_chunk_size;
          #   $insert_at_start = 0;
          # }

          # Define a job specific mon_offset
          my $mon_offset = $chunk_size - $first_chunk_size;
          $serial_iopts = set_iopts($serial_iopts, {mon_offset => $mon_offset} );

          # # Define a job specific insert_at_start flag
          # $serial_iopts = set_iopts($serial_iopts, {insert_at_start => $insert_at_start} );
        }

        # Possibly reorder the serial_iopts string
        $serial_iopts = ref2str_iopts( str2ref_iopts($serial_iopts) ) if $serial_iopts;

        if ($Verbose > 10) {
          print "serial_iopts= ",$serial_iopts ? $serial_iopts : '',"\n";
	}

        # This is a serial diagnostic job
        push @jl, "pdiag:${chunk_size}m$serial_iopts";
        if ( $submit_stand_alone ) {
          push @jl, "${sub_diag}:s";
        }

      } else {

        # This is a parallel diagnostic job
        push @jl, "pdiag:${pdiag_freq}m";

      }

      next;
    };
    ############################################################################

    /^pool_all$/ and do {
      #----------- insert a multi year pool of all months and all seasons
      $opt = ':s' unless $opt;
      push @jl, ("pool_mul=djf$opt","pool_mul=mam$opt","pool_mul=jja$opt",
                 "pool_mul=son$opt","pool_mul=m01$opt","pool_mul=m02$opt",
                 "pool_mul=m03$opt","pool_mul=m04$opt","pool_mul=m05$opt",
                 "pool_mul=m06$opt","pool_mul=m07$opt","pool_mul=m08$opt",
                 "pool_mul=m09$opt","pool_mul=m10$opt","pool_mul=m11$opt",
                 "pool_mul=m12$opt");
      next
    };
    ############################################################################

    /^pool_all_mon$/ and do {
      #----------- insert a multi year pool of all months
      $opt = ':s' unless $opt;
      push @jl, ("pool_mul=m01$opt","pool_mul=m02$opt","pool_mul=m03$opt",
                 "pool_mul=m04$opt","pool_mul=m05$opt","pool_mul=m06$opt",
                 "pool_mul=m07$opt","pool_mul=m08$opt","pool_mul=m09$opt",
                 "pool_mul=m10$opt","pool_mul=m11$opt","pool_mul=m12$opt");
      next
    };
    ############################################################################

    /^pool_all_sea$/ and do {
      #----------- insert a multi year pool of all seasons
      $opt = ':s' unless $opt;
      push @jl, ("pool_mul=djf$opt","pool_mul=mam$opt","pool_mul=jja$opt",
                 "pool_mul=son$opt");
      next
    };
    ############################################################################

    /^(custom\w*|template)$/ and do {
      if ( /^template$/ ) {
        $opt = ':s' unless $opt;
        push @jl, ("custom".$opt);
      } else {
        $opt = ':m' unless $opt;
        my ($id) = /^custom(\w*)$/;
        # assume the file name is the first element of curr_fnames
        my $vname = "CUSTOM${id}_fname";
        $$vname = $curr_fnames[0];
        unless ($$vname) {
          die "Filename for custom module was not provided.\n Stopped";
        }
       unless (-r $$vname) {
          die "Unable to read file $$vname for the custom module\n Stopped";
        }
        # add this job to the job string
        push @jl, ($_.$opt);
      }
      next
    };
    ############################################################################

    /^cloop\w*$/ and do {
      $opt = ':m' unless $opt;
      my ($id) = /^cloop(\w*)$/;
      my $vname = '';
      if ($fspec_contains_percent) {
        $vname = "CLOOP${id}_HEAD_fname";
        if ($curr_fnames[0]) {
          $$vname = $curr_fnames[0];
          die "Unable to read cloop file $$vname\n"
            unless (-r $$vname);
        } else {
          $$vname = '';
        };
        $vname = "CLOOP${id}_fname";
        if ($curr_fnames[1]) {
          $$vname = $curr_fnames[1];
          die "Unable to read cloop file $$vname\n"
            unless (-r $$vname);
        } else {
          $$vname = '';
        };
        $vname = "CLOOP${id}_TAIL_fname";
        if ($curr_fnames[2]) {
          $$vname = $curr_fnames[2];
          die "Unable to read cloop file $$vname\n"
            unless (-r $$vname);
        } else {
          $$vname = '';
        };
      } else {
        # The filename specification does not contain a "%"
        # Assign the only filename provided to CLOOP_fname
        $vname = "CLOOP${id}_HEAD_fname";
        $$vname = '';
        $vname = "CLOOP${id}_TAIL_fname";
        $$vname = '';
        $vname = "CLOOP${id}_fname";
        $$vname = $curr_fnames[0];
        die "Filename for cloop module was not provided.\n Stopped"
          unless $$vname;
        die "Unable to read cloop file $$vname\n"
          unless (-r $$vname);
      };
      my $aa = "CLOOP${id}_HEAD_fname";
      my $bb = "CLOOP${id}_fname";
      my $cc = "CLOOP${id}_TAIL_fname";
      unless ($$aa or $$bb or $$cc) {
        die "No file names for cloop$id module were specified\n";
      };
      # add this job to the job string
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^ccc2nc\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^ccc2nc(\w*)$/;
      # assume the file name is the first element of curr_fnames
      my $vname = "CCC2NC${id}_fname";
      $$vname = $curr_fnames[0];
      unless ($$vname) {
        # There was no file name provided on the command line
        # Check for variables of the form fileN and if any exist
        # then create a temporary file containing a list of file names,
        # one name per line, that are specified by these variables.
        my $vn = "file1";
        my $nn = 1;
        my @fl = ();
        while (my $val = &$In_vlist("$vn")) {
          # Add this file name to the list and delete the definition from vlist
          push @fl, $val;
          &$del_vlist("$vn");
          # Determine the next fileN in the sequence
          $vn=sprintf("file%d",++$nn);
        }
        if (scalar(@fl)) {
          $$vname = "ccc2nc${id}_file_list_$stamp";
          open(FILE_LIST, ">$$vname") or die "Unable to open $$vname\n  Stopped";
          for (@fl) { print FILE_LIST "$_\n" }
          close(FILE_LIST);
          # Ensure this file gets removed at the end
          push @TMPflist, $$vname;
        } else {
          print "Use either --job=\"... ccc2nc=file_containing_names ...\"";
          print " or \"file1=fname1 file2=fname2 ....\"";
          print " to specify files on the command line\n";
          die "Stopped";
        }
      }
      unless (-r $$vname) {
        die "Unable to read ccc2nc file $$vname\n Stopped";
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^load_list\w*$/ and do {
      $opt = ':a' unless $opt;
      my ($id) = /^load_list(\w*)$/;
      # assume the file name is the first element of curr_fnames
      my $vname = "LOAD_list${id}_fname";
      $$vname = $curr_fnames[0];
      unless ($$vname) {
        die "Filename for load_list was not provided.\n Stopped";
      };
      unless (-r $$vname) {
        die "Unable to read load_list file $$vname\n Stopped";
      };
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^load_sublist\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^load_sublist(\w*)$/;
      my $have_files = 0;
      my $locname = shift @curr_fnames;
      if ($locname) {
        # A local file name was supplied with load_sublist
        $have_files = 1;
        my $vname = "LOAD_sublist${id}_fname";
        $$vname = $locname;
        die "Unable to read load_sublist$id file $$vname\n Stopped" unless (-r $$vname);
      }
      my $remname = shift @curr_fnames;
      if ($remname) {
        # A file name to be "accesss"ed at runtime was supplied
        $have_files = 1;
        my $key = "load_sublist$id";
        $main::job_specific_vars{$key}{load_file_list} = $remname;
      }
      # Check for file name patterns that were supplied in @curr_fnames
      my $fnpatt = '';
      foreach (@curr_fnames) {
        # If there are any remaining elements in curr_fnames then
        # they are assumed to be file bname patterns
        unless ($fnpatt) {
          $fnpatt = "$_";
        } else {
          $fnpatt .= ":$_";
        }
      }
      if ($fnpatt) {
        # File name pattern(s) were supplied
        $have_files = 1;
        my $key = "load_sublist$id";
        $main::job_specific_vars{$key}{"${key}_pattern"} = $fnpatt;
      }
      unless ($have_files) {
        print "load_sublist$id requires the name of a file containing a file list.\n";
        print "  Use --job=\'... load_sublist${id}=local_file%run_time_file%pattern...\'";
        print " on the cccjob command line.\n";
        print "  At least one of local_file, run_time_file and/or pattern must be present.\n";
        print "    local_file must be in your cwd at the time this string is created.\n";
        print "    run_time_file must be available for \"access\" when the job runs.\n";
        print "    pattern is used at run time to find file names on disk.\n";
        die "Stopped";
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^nccrip\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^nccrip(\w*)$/;
      my $locname = $curr_fnames[0];
      if ($locname) {
        # A file name was supplied on the command line
        my $vname = "NCCRIP${id}_fname";
        $$vname = $locname;
        die "Filename for nccrip was not provided.\n Stopped" unless ($$vname);
        die "Unable to read nccrip file $$vname\n Stopped" unless (-r $$vname);
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^dump_list\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^dump_list(\w*)$/;
      my $modname = "dump_list$id";

      if ( $id ) {
        # This is a clone of dump_list
        my $curr_arclabel = "arclabel$id";
        if ( exists $parin{$curr_arclabel} ) {
          # A a job specific arclabel was supplied by the user
          # Define arclabel for this job only
          my $val  = $parin{$curr_arclabel};
          $job_specific_vars{$modname}{arclabel} = $val;
        }
      }

      # assume the file name is the first element of curr_fnames
      my $vname = "DUMP_list${id}_fname";
      $$vname = $curr_fnames[0];
      die "Filename for dump_list was not provided.\n Stopped" unless ($$vname);
      die "Unable to read dump_list file $$vname\n Stopped" unless (-r $$vname);
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^dump_sublist\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^dump_sublist(\w*)$/;
      my $modname = "dump_sublist$id";

      if ( $id ) {
        # This is a clone of dump_sublist
        my $curr_arclabel = "arclabel$id";
        if ( exists $parin{$curr_arclabel} ) {
          # A a job specific arclabel was supplied by the user
          # Define arclabel for this job only
          my $val  = $parin{$curr_arclabel};
          $job_specific_vars{$modname}{arclabel} = $val;
        }
      }

      my $have_files = 0;
      my $locname = shift @curr_fnames;
      if ($locname) {
        # A local file name was supplied with dump_sublist
        $have_files = 1;
        my $vname = "DUMP_sublist${id}_fname";
        $$vname = $locname;
        die "Unable to read dump_sublist$id file $$vname\n Stopped" unless (-r $$vname);
      }
      my $remname = shift @curr_fnames;
      if ($remname) {
        # A file name to be "accesss"ed at runtime was supplied
        $have_files = 1;
        my $key = "dump_sublist$id";
        $main::job_specific_vars{$key}{dump_file_list} = $remname;
      }
      # Check for file name patterns that were supplied in @curr_fnames
      my $fnpatt = '';
      foreach (@curr_fnames) {
        # If there are any remaining elements in curr_fnames then
        # they are assumed to be file bname patterns
        unless ($fnpatt) {
          $fnpatt = "$_";
        } else {
          $fnpatt .= ":$_";
        }
      }
      if ($fnpatt) {
        # File name pattern(s) were supplied
        $have_files = 1;
        my $key = "dump_sublist$id";
        $main::job_specific_vars{$key}{"${key}_pattern"} = $fnpatt;
      }
      unless ($have_files) {
        print "dump_sublist$id requires the name of a file containing a file list.\n";
        print "  Use --job=\'... dump_sublist${id}=local_file%run_time_file%pattern...\'";
        print " on the cccjob command line.\n";
        print "  At least one of local_file, run_time_file and/or pattern must be present.\n";
        print "    local_file must be in your cwd at the time this string is created.\n";
        print "    run_time_file must be available for \"access\" when the job runs.\n";
        print "    pattern is used at run time to find file names on disk.\n";
        die "Stopped";
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^del_list\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^del_list(\w*)$/;
      my $have_files = 0;
      if ($curr_fnames[0]) {
        # A local file name was supplied with del_list
        $have_files = 1;
        my $vname = "DEL_list${id}_fname";
        $$vname = $curr_fnames[0];
        die "Unable to read del_list file $$vname\n Stopped"
          unless (-r $$vname);
      }
      if ($curr_fnames[1]) {
        # A file name to be "accesss"ed at runtime was supplied
        $have_files = 1;
        my $key = "del_list$id";
        $main::job_specific_vars{$key}{delete_file_list} = $curr_fnames[1];
      }
      unless ($have_files) {
        print "del_list$id requires the name of a file containing a file list.\n";
        print "  Use --job=\'... del_list${id}=local_file%run_time_file ...\'";
        print " on the cccjob command line.\n";
        print "  At least one of local_file and/or run_time_file must be present.\n";
        print "    local_file must be in your cwd at the time this string is created.\n";
        print "    run_time_file must be available for \"access\" when the job runs.\n";
        die "Stopped";
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^rtrans\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^rtrans(\w*)$/;
      my $have_files = 0;
      if ($curr_fnames[0]) {
        # A file name was supplied with rtrans
        $have_files = 1;
        my $vname = "RTRANS${id}_fname";
        $$vname = $curr_fnames[0];
        if (not -s "$$vname") {
          # Flag an error if a file name was provided but
          # that file is missing or empty
          die "A missing or empty file name was provided for rtrans${id}.\n  Stopped";
        }
      }
      if ($curr_fnames[1]) {
        # A file name to be "accesss"ed at runtime was supplied
        $have_files = 1;
        my $key = "rtrans$id";
        my $flvname = "rtrans${id}_file_list";
        $main::job_specific_vars{$key}{$flvname} = $curr_fnames[1];
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^lsarc\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^lsarc(\w*)$/;
      if ($curr_fnames[0]) {
        # A file name was supplied with lsarc
        my $vname = "LSARC${id}_fname";
        $$vname = $curr_fnames[0];
        if (not -s "$$vname") {
          # Flag an error if a file name was provided but
          # that file is missing or empty
          die "A missing or empty file name was provided for lsarc${id}.\n  Stopped";
        }
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^othr2fe$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^othr2fe(\w*)$/;
      if ($curr_fnames[0]) {
        # A file name was supplied with othr2fe
        my ($id) = /^othr2fe(\w*)$/;
        my $vname = "OTHR2FE${id}_fname";
        $$vname = $curr_fnames[0];
        if (not -s "$$vname") {
          # Flag an error if a file name was provided but
          # that file is missing or empty
          die "A missing or empty file name was provided for othr2fe${id}.\n  Stopped";
        }
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^pxdump$/ and do {
      $opt = ':s' unless $opt;
      if ($curr_fnames[0]) {
        # A file name was supplied with pxdump
        my $vname = "PXDUMP_fname";
        $$vname = $curr_fnames[0];
        if (not -s "$$vname") {
          # Flag an error if a file name was provided but
          # that file is missing or empty
          die "A missing or empty file name was provided for pxdump.\n  Stopped";
        }
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^pxdiag$/ and do {
      $opt = ':s' unless $opt;
      if ($curr_fnames[0]) {
        # A file name was supplied with pxdiag
        my $vname = "PXDIAG_fname";
        $$vname = $curr_fnames[0];
        if (not -s "$$vname") {
          # Flag an error if a file name was provided but
          # that file is missing or empty
          die "A missing or empty file name was provided for pxdiag.\n  Stopped";
        }
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^pxdel$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^pxdel(\w*)$/;
      # assume the file name is the first element of curr_fnames
      my $vname = "PXDEL${id}_fname";
      $$vname = $curr_fnames[0];
      die "Filename for pxdel was not provided.\n Stopped" unless ($$vname);
      die "Unable to read pxdel file $$vname\n Stopped" unless (-r $$vname);
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^gztsdiag\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($in_id) = /^gztsdiag(\w*)$/;
      my $clone_name = "gztsdiag$in_id";
      my $var_name   = "gztsdiag${in_id}_deck";
      my $deck_name  = "gztsdiag${in_id}.dk";
      if ($curr_fnames[0]) {
        # An input deck name was supplied
        my ($id) = $curr_fnames[0] =~ /^(?:gztsdiag)?(.*?)(?:\.dk|\.cdk)?\s*$/;
        $id =~ s/[\s.]*//g;
        $clone_name = "gztsdiag$id";
        $var_name   = "gztsdiag${id}_deck";
        $deck_name  = $curr_fnames[0];
        unless ( $deck_name =~ /dk\s*$/ ) {
          # Append ".dk" if the deck name does not already end in "dk"
          $deck_name .= ".dk"
        }
        # Eliminate any spaces in the deck name
        $deck_name =~ s/\s*//g;
      } else {
        unless ($in_id) {
          # Require gztsdiag to have a file name supplied
          warn "*** gztsdiag usage: gztsdiag=deck_name$opt.\n";
          die "*** gztsdiag requires the name of a deck to be run.\n  Stopped";
        }
      }

      # Add the deck name to the current job only
      $main::job_specific_vars{$clone_name}{$var_name} = $deck_name;

      # Push this cloned job onto the job list
      push @jl, (${clone_name}.$opt);
      next
    };
    ############################################################################

    /^spltdiag\w*$/ and do {
      $opt = ':s' unless $opt;
      my ($id) = /^spltdiag(\w*)$/;
      if ($curr_fnames[0]) {
        # An input file name prefix was supplied
        my $key = "spltdiag$id";
        my $var = "spltdiag${id}_uxxx";
        $main::job_specific_vars{$key}{$var} = $curr_fnames[0];
      } else {
        # Set a default value for the input file name prefix
        my $key = "spltdiag$id";
        my $var = "spltdiag${id}_uxxx";
        my $val = &$In_vlist($var);
        if ($val) {
          $main::job_specific_vars{$key}{$var} = $val;
        } else {
          $main::job_specific_vars{$key}{$var} = "da";
        }
      }
      if ($curr_fnames[1]) {
        # An output file name prefix was supplied
        my $key = "spltdiag$id";
        my $var = "spltdiag${id}_mts_uxxx";
        $main::job_specific_vars{$key}{$var} = $curr_fnames[1];
      } else {
        # Set a default value for the output file name prefix
        my $key = "spltdiag$id";
        my $var = "spltdiag${id}_mts_uxxx";
        my $val = &$In_vlist($var);
        if ($val) {
          $main::job_specific_vars{$key}{$var} = $val;
        } else {
          $main::job_specific_vars{$key}{$var} = "sa";
        }
      }
      if ($curr_fnames[2]) {
        # An input file name suffix list was supplied
        my $key = "spltdiag$id";
        my $var = "spltdiag${id}_suffix_list";
        my $val = join(" ", split(/\s*,\s*/,$curr_fnames[2]));
        $main::job_specific_vars{$key}{$var} = $val;
      } else {
        # Set a default value for the input file name suffix list
        my $key = "spltdiag$id";
        my $var = "spltdiag${id}_suffix_list";
        my $val = &$In_vlist($var);
        if ($val) {
          $main::job_specific_vars{$key}{$var} = $val;
        } else {
          $main::job_specific_vars{$key}{$var} = "gp";
        }
      }
      if ($Verbose > 10) {
        my $key = "spltdiag$id";
        my $var = "spltdiag${id}_uxxx";
        print "$key :: $var = $main::job_specific_vars{$key}{$var}\n";
        $var = "spltdiag${id}_mts_uxxx";
        print "$key :: $var = $main::job_specific_vars{$key}{$var}\n";
        $var = "spltdiag${id}_suffix_list";
        print "$key :: $var = $main::job_specific_vars{$key}{$var}\n";
      }
      push @jl, ($_.$opt);
      next
    };
    ############################################################################

    /^block\w*$/ and do {
      # Insert a job that will, in turn, create another job string using cccjob
      # together with user supplied parameter definitions and a job description
      # input via auxfile, then submit this new job string at run time.
      $opt = ':y' unless $opt;
      my ($id) = /^block(\w*)$/;
      my $modname = "block$id";

      # Strip any angle bracketed defs from the end of $opt and assign them,
      # as a string without the angle brackets, to $user_iopts
      my $user_iopts = '';
      ($user_iopts) = $opt =~ m/\s*<(.*)>\s*$/ if $opt;
      $opt =~ s/\s*<(.*)>\s*$// if $opt;

      # Determine the insertion frequency
      # This must be of the form "Ny" or "Nm" where N is an integer
      # If N==1 then the frequency may be "y" or "m"
      # Note that opt has already had any angle bracketed defs removed
      # above so there is no need to check for them here
      my ($freq) = $opt =~ /^:(.*)$/ if $opt;

      # Determine units for this frequency and convert "y" to "m" if required
      my ($fval, $funit) = (0, '');
      if ( $freq eq "y" ) {
        # No numeric prefix, assume 1
        ($fval, $funit) = (1, "y");
      } elsif ( $freq eq "m" ) {
        # No numeric prefix, assume 1
        ($fval, $funit) = (1, "m");
      } else {
        # Otherwise split into numeric prefix (if any) and the rest
        ($fval, $funit) = $freq =~ /^(\d+)([^:<]*)/;
      }
      if ( $funit eq "y" ) {
        # Convert to months
        $fval = 12*$fval;
        $funit = "m";
      } elsif ( $funit ne "m" ) {
        # The only other valid option is months
        die "Invalid frequency supplied for $modname --> $freq <--\n";
      }
      # Reassign freq to ensure the frequency is in months
      if ( $fval == 1 ) {
        # Use just the units when there is only 1
        $freq = "$funit";
      } else {
        $freq = "$fval$funit";
      }

      # Define chunk_size as the frequency in months
      my $chunk_size = $fval;
      my $first_chunk_size = $chunk_size;

      # Determine the start year/month as specified via --start on the command line
      my ($run_start_year, $run_start_month) = split /\s*:\s*/,$Start_time_cmdl;
      $run_start_year  = $parin{run_start_year}  if exists $parin{run_start_year};
      $run_start_year  = 1 unless $run_start_year;
      $run_start_month = 1 unless $run_start_month;
      $run_start_month = $parin{run_start_month} if exists $parin{run_start_month};
      die "block: run_start_month=$run_start_month is out of range.\n"
          if ($run_start_month < 1 or $run_start_month > 12);
      $run_start_year  = sprintf("%3.3d", $run_start_year);
      $run_start_month = sprintf("%2.2d", $run_start_month);

      # Determine the stop year/month as specified via --stop on the command line
      my ($run_stop_year, $run_stop_month) = split /\s*:\s*/,$Stop_time_cmdl;
      $run_stop_year  = $run_start_year unless $run_stop_year;
      $run_stop_month = 12 unless $run_stop_month;
      $run_stop_year  = $parin{run_stop_year}  if exists $parin{run_stop_year};
      $run_stop_month = $parin{run_stop_month} if exists $parin{run_stop_month};
      die "block: run_stop_month=$run_stop_month is out of range.\n"
          if ($run_stop_month < 1 or $run_stop_month > 12);
      $run_stop_year  = sprintf("%3.3d", $run_stop_year);
      $run_stop_month = sprintf("%2.2d", $run_stop_month);

      # assume an auxillary file name is the first element of curr_fnames
      my $auxfile = $curr_fnames[0];
      die "Filename for block was not provided.\n Stopped" unless ($auxfile);
      die "Unable to read block file $auxfile\n Stopped" unless (-r $auxfile);

      # Read this auxillary file into memory
      open(AUXFILE, "<$auxfile") or die $!;
      chomp( my @lines = <AUXFILE> );
      close(AUXFILE);

      # Initialize the modname element of the auxcode hash
      $auxcode{$modname} = "  ${modname}_jobdefs=block_jobdefs_$stamp\n";
      $auxcode{$modname} .= '  cat >$'."${modname}_jobdefs <<end_block_jobdefs\n";

      # Add definitions for run start/stop dates
      $auxcode{$modname} .= "    run_start_year=$run_start_year\n";
      $auxcode{$modname} .= "    run_start_month=$run_start_month\n";
      $auxcode{$modname} .= "    run_stop_year=$run_stop_year\n";
      $auxcode{$modname} .= "    run_stop_month=$run_stop_month\n";

      # Optionally add an option to always add updates (if they exist) to every job in
      # the job strings that are created by the blocks children
      if ( $Always_add_updates ) {
        $auxcode{$modname} .= "    cccjob_opt_always_add_updates=1\n";
      }

      # Process the auxillary file to produce a code snippet to insert in the job
      # The suplementary code is stored in the auxcode hash and inserted into the
      # job string after it has been created and all variable substitution has
      # been done. This means that the code in auxcode is not affected by normal
      # cccjob variable or regex subtitution
      my $block_JOBDESC = '';
      my $model_base = '';
      my $diag_base = '';
      my $restart = '';
      # Extract the value of these variables from the user supplied auxfile
      my $pdiag_first_year = '';
      my $pdiag_chunk_size = '';
      my $pdiag_first_chunk_size = '';
      my $block_chunk_size = '';
      my $block_first_chunk_size = '';
      my $autorestart = '';
      my $logall = '';
      my $use_jhome_run_dir = '';
      my @var_val_list = ('pdiag_chunk_size', 'pdiag_first_chunk_size', 'pdiag_first_year',
         'block_chunk_size', 'block_first_chunk_size', 'autorestart', 'logall',
         'use_jhome_run_dir');
      my $vals_href = {};
      foreach my $line (@lines) {
        # Ignore blank lines
	next if $line =~ /^\s*$/;

        # Look for specific variables and extract their values
        my $line_vals_href = line_var_vals($line, @var_val_list);
        foreach ( keys %{$line_vals_href} ) {
          # Add any variable defs found on this line to the vals_href hash
          # Use only the first non-null value found
          $vals_href->{$_} = $line_vals_href->{$_} unless $vals_href->{$_};
        }

        # Extract certain parameter values then discard the line but only
        # if one of these parameter definitions is found on the line
        # These parameters must appear alone on a single line
        if ( $line =~ /^\s*block_JOBDESC\s*=/ ) {
          # The line containing block_JOBDESC will be discarded after it is read
          ($block_JOBDESC) = $line =~ m/\bblock_JOBDESC\s*=\s*("[^"]*"|'[^']*'|[^#]*)/;
          # Remove any single or double quotes around this value
          $block_JOBDESC =~ s/^\s*'([^']*)'\s*$/$1/;
          $block_JOBDESC =~ s/^\s*"([^"]*)"\s*$/$1/;
        } elsif ( $line =~ /^\s*model_base\s*=/ ) {
          # The line containing model_base will be discarded after it is read
          # Note: The value of model_base is only read up to the first space or # char
          ($model_base) = $line =~ m/\bmodel_base\s*=\s*("[^"]*"|'[^']*'|[^\s#]*)/;
          # Remove any single or double quotes around this value
          $model_base =~ s/^\s*'([^']*)'\s*$/$1/;
          $model_base =~ s/^\s*"([^"]*)"\s*$/$1/;
        } elsif ( $line =~ /^\s*diag_base\s*=/ ) {
          # The line containing diag_base will be discarded after it is read
          # Note: The value of diag_base is only read up to the first space or # char
          ($diag_base) = $line =~ m/\bdiag_base\s*=\s*("[^"]*"|'[^']*'|[^\s#]*)/;
          # Remove any single or double quotes around this value
          $diag_base =~ s/^\s*'([^']*)'\s*$/$1/;
          $diag_base =~ s/^\s*"([^"]*)"\s*$/$1/;
        } elsif ( $line =~ /^\s*restart\s*=/ ) {
          # The line containing restart will be discarded after it is read
          # Note: The value of restart is only read up to the first space or # char
          ($restart) = $line =~ m/\brestart\s*=\s*("[^"]*"|'[^']*'|[^\s#]*)/;
          # Remove any single or double quotes around this value
          $restart =~ s/^\s*'([^']*)'\s*$/$1/;
          $restart =~ s/^\s*"([^"]*)"\s*$/$1/;
        } else {
          # Otherwise append the line as is to auxcode unless the first
          # non-whitespace characters are "#-#"
          # First strip any leading white space
	  my ($fmt_line) = $line =~ /^\s*(.*)$/;
          $auxcode{$modname} .= "    $fmt_line\n" unless $line =~ /^\s*#-#/;
        }
      }
      $auxcode{$modname} .= "end_block_jobdefs\n\n";

      # Determine values for certain variables that may have been defined
      # in the user supplied auxillary file
      $pdiag_first_year = $vals_href->{pdiag_first_year};
      $pdiag_chunk_size = $vals_href->{pdiag_chunk_size};
      $pdiag_first_chunk_size = $vals_href->{pdiag_first_chunk_size};
      $block_chunk_size = $vals_href->{block_chunk_size};
      $block_first_chunk_size = $vals_href->{block_first_chunk_size};

      $auxcode{$modname} .= "  # Parameters used with the child job description\n";
      # Add definitions for model_base and diag_base to the code snippet
      if ( $model_base ) {
        # Insert the user supplied value
        # Use double quotes around the value assigned to model_base so
        # that any embedded variables will be expanded at run time
        $auxcode{$modname} .= "  model_base=\"$model_base\"\n";
      } else {
        # Insert a null definition
        $auxcode{$modname} .= "  model_base=\'\'\n";
      }
      if ( $diag_base ) {
        # Insert the user supplied value
        # Use double quotes around the value assigned to diag_base so
        # that any embedded variables will be expanded at run time
        $auxcode{$modname} .= "  diag_base=\"$diag_base\"\n";
      } else {
        # Insert a null definition
        $auxcode{$modname} .= "  diag_base=\'\'\n";
      }

      # Add a definition for block_JOBDESC to the code snippet
      die "A definition for block_JOBDESC is required in $auxfile used with block.\n"
        unless $block_JOBDESC;
      # Add double quotes around the value of block_JOBDESC so that variable
      # substitution can happen in the job string at execution time
      $auxcode{$modname} .= "  ${modname}_JOBDESC=\"$block_JOBDESC\"\n";

      # Add a definition for restart to the code snippet, if defined
      $auxcode{$modname} .= "  restart=\'$restart\'\n" if $restart;

      # Add the name of the file containing this job string at creation time
      `touch $Outfile`;
      my $ctime_file_name = abs_path($Outfile);
      unlink $Outfile;
      $ctime_file_mach = hostname;
      if ( $ctime_file_mach =~ /^c1/ )    { $ctime_file_mach = "spica"  }
      if ( $ctime_file_mach =~ /^c2/ )    { $ctime_file_mach = "hadar"  }
      if ( $ctime_file_mach =~ /^joule/ ) { $ctime_file_mach = "joule"  }
      if ( $ctime_file_mach =~ /^ib3/ )   { $ctime_file_mach = "pollux" }
      $job_specific_vars{$modname}{ctime_file_name} = $ctime_file_name;
      $job_specific_vars{$modname}{ctime_file_mach} = $ctime_file_mach;

      # Add a value for autorestart if supplied by the user
      if ( $vals_href->{autorestart} ) {
        $job_specific_vars{$modname}{autorestart} = $vals_href->{autorestart};
      }

      # Add a value for logall if supplied by the user
      if ( $vals_href->{logall} ) {
        $job_specific_vars{$modname}{logall} = $vals_href->{logall};
      }

      # Add a value for use_jhome_run_dir if supplied by the user
      if ( $vals_href->{use_jhome_run_dir} ) {
        $job_specific_vars{$modname}{use_jhome_run_dir} = $vals_href->{use_jhome_run_dir};
      }

      # Append defs required by next_block if a second curr_fnames is present
      if ( $curr_fnames[1] ) {
        # The following info will be passed to next_block at run time
        # These definitions will be used to replace the default post processing that
        # is done at the end of each time chunk by a user supplied set of jobs

        # assume an auxillary file name is the second element of curr_fnames
        my $next_auxfile = $curr_fnames[1];
        die "Second filename for block was not provided.\n Stopped" unless ($next_auxfile);
        die "Unable to read second block file $next_auxfile\n Stopped" unless (-r $next_auxfile);

        # Read this auxillary file into memory
        open(NEXT_AUXFILE, "<$next_auxfile") or die $!;
        chomp( my @lines = <NEXT_AUXFILE> );
        close(NEXT_AUXFILE);

        # Initialize the modname element of the auxcode hash
        $auxcode{$modname} .= "\n  next_block_jobdefs=next_block_jobdefs_$stamp\n";
        $auxcode{$modname} .= '  cat >$'."next_block_jobdefs <<end_next_block_jobdefs\n";

        # Add definitions for run start/stop dates
        $auxcode{$modname} .= "    run_start_year=$run_start_year\n";
        $auxcode{$modname} .= "    run_start_month=$run_start_month\n";
        $auxcode{$modname} .= "    run_stop_year=$run_stop_year\n";
        $auxcode{$modname} .= "    run_stop_month=$run_stop_month\n";

        # Process the auxillary file to produce a code snippet to insert in the job
        # The suplementary code is stored in the auxcode hash and inserted into the
        # job string after it has been created and all variable substitution has
        # been done. This means that the code in auxcode is not affected by normal
        # cccjob variable or regex subtitution
        my $vals_href = {};
        foreach my $line (@lines) {
          # Ignore blank lines
          next if $line =~ /^\s*$/;

          # Otherwise append the line as is to auxcode unless the first
          # non-whitespace characters are "#-#"
          # First strip any leading white space
          my ($fmt_line) = $line =~ /^\s*(.*)$/;
          $auxcode{$modname} .= "    $fmt_line\n" unless $line =~ /^\s*#-#/;
        }
        $auxcode{$modname} .= "end_next_block_jobdefs\n\n";
      }

      if ( $pdiag_chunk_size ) {
        die "pdiag_chunk_size=$pdiag_chunk_size is different than frequency=$freq\n"
            unless $pdiag_chunk_size == $chunk_size;
      }
      if ( $block_chunk_size ) {
        die "block_chunk_size=$block_chunk_size is different than frequency=$freq\n"
            unless $block_chunk_size == $chunk_size;
      }

      # Reset first_chunk_size from the user supplied pdiag_first_chunk_size
      $first_chunk_size = $pdiag_first_chunk_size if $pdiag_first_chunk_size;

      # Reset first_chunk_size from the user supplied block_first_chunk_size
      $first_chunk_size = $block_first_chunk_size if $block_first_chunk_size;

      # Always set resize_last_chunk when using this block module
      $user_iopts = set_iopts($user_iopts, {resize_last_chunk => 1} );

      if ( $pdiag_first_year ) {
        # When pdiag_first_year is defined then only add the month offset
        # if pdiag_first_year is the same as run_start_year, otherwise
        # the chunks will get shifted when the first job string is continued
        # by creating another string starting after the first string ends.
        if ( $pdiag_first_year == $run_start_year ) {
          if ( $first_chunk_size and $first_chunk_size != $chunk_size ) {
            # Define a job specific mon_offset
            my $mon_offset = $chunk_size - $first_chunk_size;
            $user_iopts = set_iopts($user_iopts, {mon_offset => $mon_offset} );
          }
        }
      } else {
        # Simply check for a different first_chunk_size
        if ( $first_chunk_size and $first_chunk_size != $chunk_size ) {
          # Define a job specific mon_offset
          my $mon_offset = $chunk_size - $first_chunk_size;
          $user_iopts = set_iopts($user_iopts, {mon_offset => $mon_offset} );
        }
      }

      # Possibly reorder the user_iopts string
      $user_iopts = ref2str_iopts( str2ref_iopts($user_iopts) ) if $user_iopts;

      # Add this job to the job string
      my $blockjob = "$modname:$freq";
      $blockjob .= $user_iopts if $user_iopts;
      push @jl, ("$blockjob");
      next
    };
    ############################################################################

    /^next_block\w*$/ and do {
      # Insert a job that will create a post processing job string to be run after
      # each block that was inserted via the block module.
      # This is intended to be used within the block module and not called directly
      # by the user
      $opt = ':s' unless $opt;
      my ($id) = /^next_block(\w*)$/;
      my $modname = "next_block$id";

      # Strip any angle bracketed defs from the end of $opt and assign them,
      # as a string without the angle brackets, to $user_iopts
      my $user_iopts = '';
      ($user_iopts) = $opt =~ m/\s*<(.*)>\s*$/ if $opt;
      $opt =~ s/\s*<(.*)>\s*$// if $opt;

      # Determine the insertion frequency
      # This will normally be of the form "s" (ie insert at end of string)
      # Note that opt has already had any angle bracketed defs removed
      # above so there is no need to check for them here
      my ($freq) = $opt =~ /^:(.*)$/ if $opt;

      # Determine the start year/month as specified via --start on the command line
      my ($run_start_year, $run_start_month) = split /\s*:\s*/,$Start_time_cmdl;
      $run_start_year  = $parin{run_start_year}  if exists $parin{run_start_year};
      $run_start_year  = 1 unless $run_start_year;
      $run_start_month = 1 unless $run_start_month;
      $run_start_month = $parin{run_start_month} if exists $parin{run_start_month};
      die "next_block: run_start_month=$run_start_month is out of range.\n"
          if ($run_start_month < 1 or $run_start_month > 12);
      $run_start_year  = sprintf("%3.3d", $run_start_year);
      $run_start_month = sprintf("%2.2d", $run_start_month);

      # Determine the stop year/month as specified via --stop on the command line
      my ($run_stop_year, $run_stop_month) = split /\s*:\s*/,$Stop_time_cmdl;
      $run_stop_year  = $run_start_year unless $run_stop_year;
      $run_stop_month = 12 unless $run_stop_month;
      $run_stop_year  = $parin{run_stop_year}  if exists $parin{run_stop_year};
      $run_stop_month = $parin{run_stop_month} if exists $parin{run_stop_month};
      die "next_block: run_stop_month=$run_stop_month is out of range.\n"
          if ($run_stop_month < 1 or $run_stop_month > 12);
      $run_stop_year  = sprintf("%3.3d", $run_stop_year);
      $run_stop_month = sprintf("%2.2d", $run_stop_month);

      # assume an auxillary file name is the first element of curr_fnames
      # This auxillary file is not mandatory. It is required when the user wants to override
      # the default post processing that is done after each block executes successfully.
      my $auxfile = $curr_fnames[0];
      if ( $auxfile ) {
        # The user has supplied an auxillary file

########################
### TESTING REQUIRED ###
########################

        # Read this auxillary file into memory
        open(AUXFILE, "<$auxfile") or die $!;
        chomp( my @lines = <AUXFILE> );
        close(AUXFILE);

        # Initialize the modname element of the auxcode hash
        $auxcode{$modname} = "  post_jobdefs=post_jobdefs_$stamp\n";
        $auxcode{$modname} .= '  cat >$'."post_jobdefs <<end_post_jobdefs\n";

        # Add definitions for run start/stop dates
        $auxcode{$modname} .= "    run_start_year=$run_start_year\n";
        $auxcode{$modname} .= "    run_start_month=$run_start_month\n";
        $auxcode{$modname} .= "    run_stop_year=$run_stop_year\n";
        $auxcode{$modname} .= "    run_stop_month=$run_stop_month\n";

        # Process the auxillary file to produce a code snippet to insert in the job
        # The suplementary code is stored in the auxcode hash and inserted into the
        # job string after it has been created and all variable substitution has
        # been done. This means that the code in auxcode is not affected by normal
        # cccjob variable or regex subtitution
        my $post_JOBDESC = '';
        foreach my $line (@lines) {
          # Ignore blank lines
          next if $line =~ /^\s*$/;

          # Extract certain parameter values then discard the line but only
          # if one of these parameter definitions is found on the line
          if ( $line =~ /^\s*post_JOBDESC\s*=/ ) {
            # post_JOBDESC=... must appear alone on a single line
            # The line containing post_JOBDESC will be discarded after it is read
            ($post_JOBDESC) = $line =~ m/\bpost_JOBDESC\s*=\s*("[^"]*"|'[^']*'|.*)/;
            # Remove any single or double quotes around this value
            $post_JOBDESC =~ s/^\s*'([^']*)'\s*$/$1/;
            $post_JOBDESC =~ s/^\s*"([^"]*)"\s*$/$1/;
          } else {
            # Otherwise append the line as is to auxcode unless the first
            # non-whitespace characters are "#-#"
            # First strip any leading white space
            my ($fmt_line) = $line =~ /^\s*(.*)$/;
            $auxcode{$modname} .= "    $fmt_line\n" unless $line =~ /^\s*#-#/;
          }
        }
        $auxcode{$modname} .= "end_post_jobdefs\n\n";

        # Add a definition for post_JOBDESC to the code snippet
        die "A definition for post_JOBDESC is required in $auxfile used with next_block.\n"
          unless $post_JOBDESC;
        # Add double quotes around the value of post_JOBDESC so that variable
        # substitution can happen in the job string at execution time
        $auxcode{$modname} .= "  # Post processing job description\n";
        $auxcode{$modname} .= "  post_JOBDESC=\"$post_JOBDESC\"\n";
      }

      # Add this job to the job string
      my $next_blockjob = "$modname:$freq";
      $next_blockjob .= $user_iopts if $user_iopts;
      push @jl, ("$next_blockjob");
      next
    };
    ############################################################################

    /^resavejhome\w*$/ and do {
      # Insert a job that will "save" a user supplied list of files on the
      # current RUNPATH/DATAPATH
      # The input files must be full pathnames and one filename per line
      $opt = ':s' unless $opt;
      my ($id) = /^resavejhome(\w*)$/;
      my $modname = "resavejhome$id";

      # Strip any angle bracketed defs from the end of $opt and assign them,
      # as a string without the angle brackets, to $user_iopts
      my $user_iopts = '';
      ($user_iopts) = $opt =~ m/\s*<(.*)>\s*$/ if $opt;
      $opt =~ s/\s*<(.*)>\s*$// if $opt;

      # Determine the insertion frequency
      # Note that opt has already had any angle bracketed defs removed
      # above so there is no need to check for them here
      my ($freq) = $opt =~ /^:(.*)$/ if $opt;

      # assume an auxillary file name is the first element of curr_fnames
      my $auxfile = $curr_fnames[0];
      die "Filename for resavejhome was not provided.\n Stopped" unless ($auxfile);
      die "Unable to read resavejhome file $auxfile\n Stopped" unless (-r $auxfile);

      # Read this auxillary file into memory
      open(AUXFILE, "<$auxfile") or die $!;
      chomp( my @lines = <AUXFILE> );
      close(AUXFILE);

      # Initialize the modname element of the auxcode hash
      $auxcode{$modname} = "  ${modname}_files=resavejhome_files_$stamp\n";
      $auxcode{$modname} .= '  cat >$'."${modname}_files <<end_resavejhome_files\n";

      # Process the auxillary file to produce a code snippet to insert in the job
      # The suplementary code is stored in the auxcode hash and inserted into the
      # job string after it has been created and all variable substitution has
      # been done. This means that the code in auxcode is not affected by normal
      # cccjob variable or regex subtitution
      foreach my $line (@lines) {
        # Ignore blank lines
        next if $line =~ /^\s*$/;

        # Otherwise append the line as is to auxcode unless the first
        # non-whitespace characters are "#-#"
        # First strip any leading white space
        my ($fmt_line) = $line =~ /^\s*(.*)$/;
        $auxcode{$modname} .= "    $fmt_line\n" unless $line =~ /^\s*#-#/;
      }
      $auxcode{$modname} .= "end_resavejhome_files\n\n";

      # Add this job to the job string
      my $resavejhomejob = "$modname:$freq";
      $resavejhomejob .= $user_iopts if $user_iopts;
      push @jl, ("$resavejhomejob");
      next
    };
    ############################################################################

    /^rcm_data$/ and do {
      # default to run for 6 years
      # These may be changed via --start_time and --stop_time
      $Start_time_cmdl = '0:12' unless $Start_time_cmdl;
      $Stop_time_cmdl = '6:11' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # Run in 1 month chunks unless months is set on the command line
      push @vlist, "months=1" unless &$In_vlist("months");
      # ensure we can start from some time other than year 1 month 1
      $Initial_conditions = 0;
      $Restart = 1;
      # make sure files are loaded onto pollux
      push @vlist, "sv=on"
        unless &$In_vlist("sv");
      # define mload_mon_offset so that files loaded are shifted by 1 month
      push @vlist, "mload_mon_offset=1"
        unless &$In_vlist("mload_mon_offset");
      # define mload_suffix_list unless it is set on the command line
      push @vlist, "mload_suffix_list=gs ss"
        unless &$In_vlist("mload_suffix_list");
      # define mdelete_mon_offset
      push @vlist, "mdelete_mon_offset=1"
        unless &$In_vlist("mdelete_mon_offset");
      # define mdelete_suffix_list unless it is set on the command line
      push @vlist, "mdelete_suffix_list=ss gs"
        unless &$In_vlist("mdelete_suffix_list");
      # define dump_list_uxxx unless it is set on the command line
      push @vlist, "dump_list_uxxx=mr"
        unless &$In_vlist("dump_list_uxxx");
      # define del_list_uxxx unless it is set on the command line
      push @vlist, "del_list_uxxx=mr"
        unless &$In_vlist("del_list_uxxx");
      # NOTE: mload_prefix is set below after the RCM parameters have been defined

      $job_specific_vars{dump_list}{shortermdir} = "on";
      $job_specific_vars{dump_list}{cfsuser} = $user;

      # write a snippet of script to a temporary file to be used to determine
      # which files will be dumped by the job dump_list
      $DUMP_list_fname = "dump_list_$stamp";
      push @TMPflist, $DUMP_list_fname;
      if (open(DUMP_LIST, ">$DUMP_list_fname")) {
        print DUMP_LIST q@ yyy=`echo $previous_year|awk '{y=$1-1;printf "%3.3d",y}' -`@."\n";
        print DUMP_LIST q@ join=0@."\n";
        print DUMP_LIST q@ while [ $yyy -lt $current_year ]; do@."\n";
        print DUMP_LIST q@   yyy=`echo $yyy|awk '{y=1+$1;printf "%3.3d",y}' -`@."\n";
        print DUMP_LIST q@   if [ $yyy -le 0 ]; then@."\n";
        print DUMP_LIST q@     continue@."\n";
        print DUMP_LIST q@   fi@."\n";
        print DUMP_LIST q@   if [ $yyy -eq $previous_year ]; then@."\n";
        print DUMP_LIST q@     mm=`echo $previous_month|awk '{m=$1-1;printf "%2.2d",m}' -`@."\n";
        print DUMP_LIST q@   else@."\n";
        print DUMP_LIST q@     mm=0@."\n";
        print DUMP_LIST q@   fi@."\n";
        print DUMP_LIST q@   if [ $yyy -eq $current_year ]; then@."\n";
        print DUMP_LIST q@     mm_end=$current_month@."\n";
        print DUMP_LIST q@   else@."\n";
        print DUMP_LIST q@     mm_end=12@."\n";
        print DUMP_LIST q@   fi@."\n";
        print DUMP_LIST q@   while [ $mm -lt $mm_end ]; do@."\n";
        print DUMP_LIST q@     mm=`echo $mm|awk '{m=1+$1;printf "%2.2d",m}' -`@."\n";
        print DUMP_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DUMP_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DUMP_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_inmrc@."\n";
        print DUMP_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DUMP_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DUMP_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_an@."\n";
        print DUMP_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DUMP_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DUMP_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_settings@."\n";
        print DUMP_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DUMP_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DUMP_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_sst@."\n";
        print DUMP_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DUMP_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DUMP_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_sic@."\n";
        print DUMP_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DUMP_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DUMP_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_sicn@."\n";
        print DUMP_LIST q@   done@."\n";
        print DUMP_LIST q@ done@."\n";
        close(DUMP_LIST);
      } else {
        die "Unable to create temporay file $DUMP_list_fname\n Stopped";
      };

      # write a snippet of script to a temporary file to be used to determine
      # which files will be deleted by the job del_list
      $DEL_list_fname = "del_list_$stamp";
      push @TMPflist, $DEL_list_fname;
      if (open(DEL_LIST, ">$DEL_list_fname")) {
        print DEL_LIST q@ yyy=`echo $previous_year|awk '{y=$1-1;printf "%3.3d",y}' -`@."\n";
        print DEL_LIST q@ join=0@."\n";
        print DEL_LIST q@ while [ $yyy -lt $current_year ]; do@."\n";
        print DEL_LIST q@   yyy=`echo $yyy|awk '{y=1+$1;printf "%3.3d",y}' -`@."\n";
        print DEL_LIST q@   if [ $yyy -le 0 ]; then@."\n";
        print DEL_LIST q@     continue@."\n";
        print DEL_LIST q@   fi@."\n";
        print DEL_LIST q@   if [ $yyy -eq $previous_year ]; then@."\n";
        print DEL_LIST q@     mm=`echo $previous_month|awk '{m=$1-1;printf "%2.2d",m}' -`@."\n";
        print DEL_LIST q@   else@."\n";
        print DEL_LIST q@     mm=0@."\n";
        print DEL_LIST q@   fi@."\n";
        print DEL_LIST q@   if [ $yyy -eq $current_year ]; then@."\n";
        print DEL_LIST q@     mm_end=$current_month@."\n";
        print DEL_LIST q@   else@."\n";
        print DEL_LIST q@     mm_end=12@."\n";
        print DEL_LIST q@   fi@."\n";
        print DEL_LIST q@   while [ $mm -lt $mm_end ]; do@."\n";
        print DEL_LIST q@     mm=`echo $mm|awk '{m=1+$1;printf "%2.2d",m}' -`@."\n";
        print DEL_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DEL_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DEL_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_inmrc@."\n";
        print DEL_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DEL_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DEL_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_an@."\n";
        print DEL_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DEL_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DEL_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_settings@."\n";
        print DEL_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DEL_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DEL_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_sst@."\n";
        print DEL_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DEL_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DEL_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_sic@."\n";
        print DEL_LIST q@     join=`echo $join|awk '{j=1+$1;printf "%d",j}' -`@."\n";
        print DEL_LIST q@     join=`echo $join|sed -e 's/^ *//' -e 's/^0*//'`@."\n";
        print DEL_LIST q@     eval file${join}=${prefix}${yyy}_m${mm}_sicn@."\n";
        print DEL_LIST q@   done@."\n";
        print DEL_LIST q@ done@."\n";
        close(DEL_LIST);
      } else {
        die "Unable to create temporay file $DEL_list_fname\n Stopped";
      };

      # add rcm_data jobs to the job list
      push @jl, ("mload:-12m","rcmdata:m","dump_list:3m","del_list:3m","mdelete:12m");
      next;
    };
    ############################################################################

    /^rcm_run$/ and do {
      # default to run for 6 years
      # These may be changed via --start_time and --stop_time
      $Start_time_cmdl = '1:1' unless $Start_time_cmdl;
      $Stop_time_cmdl = '1:12' unless $Stop_time_cmdl;
      $optset{Start_time_cmdl} = 1;
      $optset{Stop_time_cmdl}  = 1;
      # Run in 1 month chunks unless months is set on the command line
      push @vlist, "months=1" unless &$In_vlist("months");
      # ensure we can start from some time other than year 1 month 1
      $Initial_conditions = 0;
      $Restart = 1;
      # define some rcm specific quantities
      unless ($rcm_fdata_uxxx = &$In_vlist("rcm_fdata_uxxx")) {
        # uxxx for rcm forcing data found on cfs
        $rcm_fdata_uxxx = "mr";
      };
      unless ($rcm_fdata_runid = &$In_vlist("rcm_fdata_runid")) {
        # runid for rcm forcing data found on cfs
        $rcm_fdata_runid = $runid;
      };
      $job_specific_vars{mload}{runid} = $rcm_fdata_runid;
      $job_specific_vars{load_list}{runid} = $rcm_fdata_runid;
      #$job_specific_vars{mdelete}{runid} = $rcm_fdata_runid;
      $rcm_compile = &$In_vlist("rcm_compile");
      if ($rcm_compile) {
        # rcm_compile flags the addition of an initial compile job
        $rcm_compile = 0 if $rcm_compile =~ /^\s*off\s*$/i;
        $rcm_compile = 0 if $rcm_compile =~ /^\s*no\s*$/i;
      } else {
        $rcm_compile = 1;
      };
      # define mload_suffix_list unless it is set on the command line
      push @vlist, "mload_suffix_list=an inmrc settings sst sic sicn"
        unless &$In_vlist("mload_suffix_list");
      push @vlist, "mdump_suffix_list=gs rs dyrs"
        unless &$In_vlist("mdump_suffix_list");
      push @vlist, "mdelete_suffix_list=an inmrc settings sst sic sicn"
        unless &$In_vlist("mdelete_suffix_list");
      # define mload_uxxx unless it is set on the command line or
      # rcm_fdata_uxxx is set on the command line
      push @vlist, "mload_uxxx=$rcm_fdata_uxxx"
        unless &$In_vlist("mload_uxxx");
      push @vlist, "load_list_uxxx=$rcm_fdata_uxxx"
        unless &$In_vlist("load_list_uxxx");
      push @vlist, "mload_prefix=${rcm_fdata_uxxx}_${rcm_fdata_runid}"
        unless &$In_vlist("mload_prefix");
      push @vlist, "load_list_prefix=${rcm_fdata_uxxx}_${rcm_fdata_runid}"
        unless &$In_vlist("load_list_prefix");
      # define mdump_uxxx unless it is set on the command line
      push @vlist, "mdump_uxxx=mr"
        unless &$In_vlist("mdump_uxxx");
      # define rcmrun_uxxx unless it is set on the command line
      push @vlist, "rcmrun_uxxx=mr"
        unless &$In_vlist("rcmrun_uxxx");
      # define mdelete_uxxx unless it is set on the command line
      push @vlist, "mdelete_uxxx=mr"
        unless &$In_vlist("mdelete_uxxx");
      push @vlist, "mload_mon_offset=1"
        unless &$In_vlist("mload_mon_offset");
      # These job specific substitutions are temporary. They should be
      # set as shortermdir=off for production runs.
      $job_specific_vars{mdump}{shortermdir} = "on";
      $job_specific_vars{mload}{shortermdir} = "on";
      $job_specific_vars{load_list}{shortermdir} = "on";

      # write a snippet of script to a temporary file to be used to determine
      # which files will be loaded by the job load_list
      $LOAD_list_fname = "load_list_$stamp";
      push @TMPflist, $LOAD_list_fname;
      if (open(LOAD_LIST, ">$LOAD_list_fname")) {
        my ($yyy, $mm) = split /\s*:\s*|\s+/,$Start_time_cmdl;
        unless ($yyy) {
          warn "Start year is not set, it will default to 1.\n  ";
          push @warnings, "Start year is not set, it will default to 1";
          $yyy=1;
        };
        unless ($mm) {
          warn "Start month is not set, it will default to 1.\n  ";
          push @warnings, "Start month is not set, it will default to 1";
          $mm=1;
        };
        $yyy = sprintf("%3.3d",$yyy);
        $mm  = sprintf("%2.2d",$mm);
        print LOAD_LIST " yyy=$yyy; mm=$mm"."\n";
        print LOAD_LIST q@ file1=${prefix}${yyy}_m${mm}_inmrc@."\n";
        print LOAD_LIST q@ file2=${prefix}${yyy}_m${mm}_an@."\n";
        print LOAD_LIST q@ file3=${prefix}${yyy}_m${mm}_settings@."\n";
        print LOAD_LIST q@ file4=${prefix}${yyy}_m${mm}_sst@."\n";
        print LOAD_LIST q@ file5=${prefix}${yyy}_m${mm}_sic@."\n";
        print LOAD_LIST q@ file6=${prefix}${yyy}_m${mm}_sicn@."\n";
        print LOAD_LIST q@ join=6@."\n";
        close(LOAD_LIST);
        if ($Verbose > 10) {system('cat',"$LOAD_list_fname")};
      } else {
        die "Unable to create temporay file $LOAD_list_fname\n Stopped";
      };

      # write a snippet of script to a temporary file to be used by the
      # custom module to rename rcm forcing data after reading it from cfs
      my $uxxxb = &$In_vlist("mdump_uxxx");
      my $prea = "${rcm_fdata_uxxx}_${rcm_fdata_runid}_";
      my $preb = "${uxxxb}_${runid}_";
      my $rename_fdata = 0;
      unless ($prea eq $preb) {
        $rename_fdata = 1;
        $CUSTOM_fname = "custom_$stamp";
        push @TMPflist, $CUSTOM_fname;
        if (open(CUSTOM, ">$CUSTOM_fname")) {
          print CUSTOM " prea=$prea"."\n";
          print CUSTOM " preb=$preb"."\n";
          print CUSTOM "\n";
          print CUSTOM q@ fa=${prea}${current_year}_m${current_month}_inmrc@."\n";
          print CUSTOM q@ fb=${preb}${current_year}_m${current_month}_inmrc@."\n";
          print CUSTOM q@ access $fa $fa@."\n";
          print CUSTOM q@ cp $fa $fb@."\n";
          print CUSTOM q@ delete $fa@."\n";
          print CUSTOM q@ release $fa@."\n";
          print CUSTOM q@ save $fb $fb@."\n";
          print CUSTOM q@ release $fb@."\n";
          print CUSTOM "\n";
          print CUSTOM q@ fa=${prea}${current_year}_m${current_month}_an@."\n";
          print CUSTOM q@ fb=${preb}${current_year}_m${current_month}_an@."\n";
          print CUSTOM q@ access $fa $fa@."\n";
          print CUSTOM q@ cp $fa $fb@."\n";
          print CUSTOM q@ delete $fa@."\n";
          print CUSTOM q@ release $fa@."\n";
          print CUSTOM q@ save $fb $fb@."\n";
          print CUSTOM q@ release $fb@."\n";
          print CUSTOM "\n";
          print CUSTOM q@ fa=${prea}${current_year}_m${current_month}_settings@."\n";
          print CUSTOM q@ fb=${preb}${current_year}_m${current_month}_settings@."\n";
          print CUSTOM q@ access $fa $fa@."\n";
          print CUSTOM q@ cp $fa $fb@."\n";
          print CUSTOM q@ delete $fa@."\n";
          print CUSTOM q@ release $fa@."\n";
          print CUSTOM q@ save $fb $fb@."\n";
          print CUSTOM q@ release $fb@."\n";
          print CUSTOM "\n";
          print CUSTOM q@ fa=${prea}${current_year}_m${current_month}_sst@."\n";
          print CUSTOM q@ fb=${preb}${current_year}_m${current_month}_sst@."\n";
          print CUSTOM q@ access $fa $fa@."\n";
          print CUSTOM q@ cp $fa $fb@."\n";
          print CUSTOM q@ delete $fa@."\n";
          print CUSTOM q@ release $fa@."\n";
          print CUSTOM q@ save $fb $fb@."\n";
          print CUSTOM q@ release $fb@."\n";
          print CUSTOM "\n";
          print CUSTOM q@ fa=${prea}${current_year}_m${current_month}_sic@."\n";
          print CUSTOM q@ fb=${preb}${current_year}_m${current_month}_sic@."\n";
          print CUSTOM q@ access $fa $fa@."\n";
          print CUSTOM q@ cp $fa $fb@."\n";
          print CUSTOM q@ delete $fa@."\n";
          print CUSTOM q@ release $fa@."\n";
          print CUSTOM q@ save $fb $fb@."\n";
          print CUSTOM q@ release $fb@."\n";
          print CUSTOM "\n";
          print CUSTOM q@ fa=${prea}${current_year}_m${current_month}_sicn@."\n";
          print CUSTOM q@ fb=${preb}${current_year}_m${current_month}_sicn@."\n";
          print CUSTOM q@ access $fa $fa@."\n";
          print CUSTOM q@ cp $fa $fb@."\n";
          print CUSTOM q@ delete $fa@."\n";
          print CUSTOM q@ release $fa@."\n";
          print CUSTOM q@ save $fb $fb@."\n";
          print CUSTOM q@ release $fb@."\n";
          close(CUSTOM);
          if ($Verbose > 10) {system('cat',"$CUSTOM_fname")};
        } else {
          die "Unable to create temporay file $CUSTOM_fname\n Stopped";
        };
      };

      # add the RCM jobs to the job list
      push @jl, "rcm_comp:n" if $rcm_compile;
      if ($rename_fdata) {
        push @jl, ("load_list:n","mload:m","custom:m","rcmrun:m","mdump:m","mdelete:m");
      } else {
        push @jl, ("load_list:n","mload:m","rcmrun:m","mdump:m","mdelete:m");
      };
      next;
    };
    ############################################################################

    # if no special values are found simply push the current value
    if ($_) {push @jl, $_.$keymod.$opt};  # ignore null elements
  };
  @Joblist = @jl;
}

if ($Verbose > 2) {
  print "Joblist after alias expantion:\n",join("\n",@Joblist),"\n";
}

# MK_RCM_DATA will be used to determine if RCM forcing data is to be generated
$MK_RCM_DATA = grep /^rcmdata/, @Joblist;

if ($Show_jobdef) {
  show_JOBDEF("The current list of keywords recognized by the --joblist option are:",
              {FULL_HELP=>1});
  exit;
}

# Determine start and stop times for the job string

# Set $Initial_conditions and $Restart so that we start from initial
# conditions if the starting from year 1 day 1 month 1
# The default is to start from a restart
if ($optset{Start_time_cmdl}) {
  my ($inity,$initm,$initd) = split /\s*:\s*|\s+/,$Start_time_cmdl;
  $inity = 1 unless (defined $inity and $inity =~ /[\d\s]+/);
  $initm = 1 unless (defined $initm and $initm =~ /[\d\s]+/);
  $initd = 1 unless (defined $initd and $initd =~ /[\d\s]+/);
  if ($inity == 1 and $initm == 1 and $initd == 1) {
    # always start from initial conditions on year 1 month 1 day 1
    $Initial_conditions = 1 unless $optset{Initial_conditions};
    $Restart = 0 unless $optset{Restart};
  };
} else {
  # If start time was not set on the command line, or above, then it will
  # default to year 1 day 1 month 1 and we want to start from ic
  $Initial_conditions = 1 unless $optset{Initial_conditions};
  $Restart = 0 unless $optset{Restart};
};

# Set defaults
$start_time{year}{value}   = 1;
$start_time{month}{value}  = 1;
$start_time{day}{value}    = 1;  # day of the month
$start_time{hour}{value}   = 1;
$start_time{minute}{value} = 1;
$start_time{second}{value} = 1;
$start_time{year}{set_by_user}   = 0;
$start_time{month}{set_by_user}  = 0;
$start_time{day}{set_by_user}    = 0;
$start_time{hour}{set_by_user}   = 0;
$start_time{minute}{set_by_user} = 0;
$start_time{second}{set_by_user} = 0;

$stop_time{year}{value}   = 1;
$stop_time{month}{value}  = 12;
$stop_time{day}{value}    = -1; # -1 means the last day of the last month
$stop_time{hour}{value}   = 24;
$stop_time{minute}{value} = 60;
$stop_time{second}{value} = 60;
$stop_time{year}{set_by_user}   = 0;
$stop_time{month}{set_by_user}  = 0;
$stop_time{day}{set_by_user}    = 0;
$stop_time{hour}{set_by_user}   = 0;
$stop_time{minute}{set_by_user} = 0;
$stop_time{second}{set_by_user} = 0;

# First process Start_time_cmdl and Stop_time_cmdl
# if they were set on the command line or above
$Start_time_cmdl =~ s/^\s+//;  # strip leading space
$Start_time_cmdl =~ s/\s+$//;  # strip trailing space
if (defined $Start_time_cmdl and $Start_time_cmdl ne '') {
  # split on ":" or white space
  (my $y,my $mo,my $d,my $h,my $mi,my $s) = split /\s*:\s*|\s+/,$Start_time_cmdl;
  # Assign only when non-null and integer
  if (defined $y and $y ne '')  {
    $start_time{year}{value}       = 1*$y;
      # or die "Invalid run start year value ($y). Stopped";
    $start_time{year}{set_by_user} = 1;
  };
  if (defined $mo and $mo ne '') {
    $start_time{month}{value}       = 1*$mo
      or die "Invalid run start month value ($mo). Stopped";
    $start_time{month}{set_by_user} = 1;
  };
  if (defined $d and $d ne '')  {
    $start_time{day}{value}       = 1*$d
      or die "Invalid run start day value ($d). Stopped";
    $start_time{day}{set_by_user} = 1;
  };
  if (defined $h and $h ne '')  {
    $start_time{hour}{value}       = 1*$h
      or die "Invalid run start hour value ($h). Stopped";
    $start_time{hour}{set_by_user} = 1;
  };
  if (defined $mi and $mi ne '') {
    $start_time{minute}{value}       = 1*$mi
      or die "Invalid run start minute value ($mi). Stopped";
    $start_time{minute}{set_by_user} = 1;
  };
  if (defined $s and $s ne '')  {
    $start_time{second}{value}       = 1*$s
      or die "Invalid run start second value ($s). Stopped";
    $start_time{second}{set_by_user} = 1;
  };
};
if ($Verbose > 10) {
  print "After processing --start_time option:\n";
  print "Run start year   = $start_time{year}{value}\n";
  print "Run start month  = $start_time{month}{value}\n";
  print "Run start dom    = $start_time{day}{value}\n";
  print "Run start hour   = $start_time{hour}{value}\n";
  print "Run start minute = $start_time{minute}{value}\n";
  print "Run start second = $start_time{second}{value}\n";
};

$Stop_time_cmdl =~ s/^\s+//;  # strip leading space
$Stop_time_cmdl =~ s/\s+$//;  # strip trailing space
if (defined $Stop_time_cmdl and $Stop_time_cmdl ne '') {
  # split on ":" or white space
  (my $y,my $mo,my $d,my $h,my $mi,my $s) = split /\s*:\s*|\s+/,$Stop_time_cmdl;
  # Assign only when non-null and integer
  if (defined $y and $y ne '')  {
    $stop_time{year}{value}       = 1*$y
      or die "Invalid run stop year value ($y). Stopped";
    $stop_time{year}{set_by_user} = 1;
  };
  if (defined $mo and $mo ne '') {
    $stop_time{month}{value}       = 1*$mo
      or die "Invalid run stop month value ($mo). Stopped";
    $stop_time{month}{set_by_user} = 1;
  };
  if (defined $d and $d ne '')  {
    $stop_time{day}{value}       = 1*$d
      or die "Invalid run stop day value ($d). Stopped";
    $stop_time{day}{set_by_user} = 1;
  };
  if (defined $h and $h ne '')  {
    $stop_time{hour}{value}       = 1*$h
      or die "Invalid run stop hour value ($h). Stopped";
    $stop_time{hour}{set_by_user} = 1;
  };
  if (defined $mi and $mi ne '') {
    $stop_time{minute}{value}       = 1*$mi
      or die "Invalid run stop minute value ($mi). Stopped";
    $stop_time{minute}{set_by_user} = 1;
  };
  if (defined $s and $s ne '')  {
    $stop_time{second}{value}       = 1*$s
      or die "Invalid run stop second value ($s). Stopped";
    $stop_time{second}{set_by_user} = 1;
  };
};
if ($Verbose > 10) {
  print "After processing --stop_time option:\n";
  print "Run stop year = $stop_time{year}{value}\n";
  print "Run stop month = $stop_time{month}{value}\n";
  print "Run stop dom = $stop_time{day}{value}\n";
  print "Run stop hour = $stop_time{hour}{value}\n";
  print "Run stop minute = $stop_time{minute}{value}\n";
  print "Run stop second = $stop_time{second}{value}\n";
};

# Process --year_range command line option  *** --year_range is depreciated ***
$Year_range =~ s/^\s+//;  # strip leading space
$Year_range =~ s/\s+$//;  # strip trailing space
if (defined $Year_range and $Year_range ne '') {
  # split on "," or white space
  (my $y1,my $y2) = split /\s*,\s*|\s+/,$Year_range;
  # Assign only when non-null and integer and not previously set
  unless ($start_time{year}{set_by_user}) {
    if (defined $y1 and $y1 ne '')  {
      $start_time{year}{value}       = 1*$y1
        or die "Invalid run start year value ($y1). Stopped";
      $start_time{year}{set_by_user} = 1;
    };
  };
  unless ($stop_time{year}{set_by_user}) {
    if (defined $y2 and $y2 ne '')  {
      $stop_time{year}{value}       = 1*$y2
        or die "Invalid run stop year value ($y2). Stopped";
      $stop_time{year}{set_by_user} = 1;
    };
  };
};
if ($Verbose > 10) {
  print "After processing --year_range option:\n";
  print "Run start year = $start_time{year}{value}\n";
  print "Run stop year  = $stop_time{year}{value}\n";
};

# Process --month_range command line option  *** --month_range is depreciated ***
$Month_range =~ s/^\s+//;  # strip leading space
$Month_range =~ s/\s+$//;  # strip trailing space
if (defined $Month_range and $Month_range ne '') {
  # split on "," or white space
  (my $m1,my $m2) = split /\s*,\s*|\s+/,$Month_range;
  # Assign only when non-null and integer and not previously set
  unless ($start_time{month}{set_by_user}) {
    if (defined $m1 and $m1 ne '')  {
      $start_time{month}{value}       = 1*$m1
        or die "Invalid run start month value ($m1). Stopped";
      $start_time{month}{set_by_user} = 1;
    };
  };
  unless ($stop_time{month}{set_by_user}) {
    if (defined $m2 and $m2 ne '')  {
      $stop_time{month}{value}       = 1*$m2
        or die "Invalid run stop month value ($m2). Stopped";
      $stop_time{month}{set_by_user} = 1;
    };
  };
};
if ($Verbose > 10) {
  print "After processing --month_range option:\n";
  print "Run start month = $start_time{month}{value}\n";
  print "Run stop month  = $stop_time{month}{value}\n";
};

# Process --day_range command line option  *** --day_range is depreciated ***
$Day_range =~ s/^\s+//;  # strip leading space
$Day_range =~ s/\s+$//;  # strip trailing space
if (defined $Day_range and $Day_range ne '') {
  # split on "," or white space
  (my $d1,my $d2) = split /\s*,\s*|\s+/,$Day_range;
  # Assign only when non-null and integer and not previously set
  unless ($start_time{day}{set_by_user}) {
    if (defined $d1 and $d1 ne '')  {
      $start_time{day}{value}       = 1*$d1
        or die "Invalid run start day value ($d1). Stopped";
      $start_time{day}{set_by_user} = 1;
    };
  };
  unless ($stop_time{day}{set_by_user}) {
    if (defined $d2 and $d2 ne '')  {
      $stop_time{day}{value}       = 1*$d2
        or die "Invalid run stop day value ($d2). Stopped";
      $stop_time{day}{set_by_user} = 1;
    };
  };
};
if ($Verbose > 10) {
  print "After processing --day_range option:\n";
  print "Run start day = $start_time{day}{value}\n";
  print "Run stop day  = $stop_time{day}{value}\n";
};

# Initial consistency check of start/stop time values

if ($start_time{year}{value} < 0) {
  die "Run start year = $start_time{year}{value} is out of range. Stopped";
}
if ($stop_time{year}{value} < 0) {
  die "Run end year = $stop_time{year}{value} is out of range. Stopped";
}
if ($start_time{year}{value} > $stop_time{year}{value}) {
  if ($stop_time{year}{set_by_user}) {
    print "Run start year=$start_time{year}{value} is greater than ";
    print "Run end year=$stop_time{year}{value}\n";
    die "  Stopped";
  } else {
    $stop_time{year}{value} = $start_time{year}{value};
    $stop_time{year}{set_by_user} = 1;
  };
}

if ($start_time{month}{value} < 1 or $start_time{month}{value} > 12) {
  die "Run start month = $start_time{month}{value} is out of range. Stopped";
}
if ($stop_time{month}{value} < 1 or $stop_time{month}{value} > 12) {
  die "Run end month = $stop_time{month}{value} is out of range. Stopped";
}

if ($start_time{day}{value} < 1 or $start_time{day}{value} > 31) {
  die "Run start day = $start_time{day}{value} is out of range. Stopped";
}
if ($stop_time{day}{value} != -1 and
   ($stop_time{day}{value} < 1 or $stop_time{day}{value} > 31)) {
  die "Run end day = $stop_time{day}{value} is out of range. Stopped";
}

if ($start_time{hour}{value} < 1 or $start_time{hour}{value} > 24) {
  die "Run start hour = $start_time{hour}{value} is out of range. Stopped";
}
if ($stop_time{hour}{value} < 1 or $stop_time{hour}{value} > 24) {
  die "Run end hour = $stop_time{hour}{value} is out of range. Stopped";
}

if ($start_time{minute}{value} < 1 or $start_time{minute}{value} > 60) {
  die "Run start minute = $start_time{minute}{value} is out of range. Stopped";
}
if ($stop_time{minute}{value} < 1 or $stop_time{minute}{value} > 60) {
  die "Run end minute = $stop_time{minute}{value} is out of range. Stopped";
}

if ($start_time{second}{value} < 1 or $start_time{second}{value} > 60) {
  die "Run start second = $start_time{second}{value} is out of range. Stopped";
}
if ($stop_time{second}{value} < 1 or $stop_time{second}{value} > 60) {
  die "Run end second = $stop_time{second}{value} is out of range. Stopped";
}

# Add run_[start|stop]_[year|month] values to vlist
push @vlist, "run_start_year=".sprintf("%3.3d",$start_time{year}{value})
   unless &$In_vlist("run_start_year");
push @vlist, "run_start_month=".sprintf("%2.2d",$start_time{month}{value})
   unless &$In_vlist("run_start_month");
push @vlist, "run_stop_year=".sprintf("%3.3d",$stop_time{year}{value})
   unless &$In_vlist("run_stop_year");
push @vlist, "run_stop_month=".sprintf("%2.2d",$stop_time{month}{value})
   unless &$In_vlist("run_stop_month");

# Process Updates_list read from the command line

$parc_tmpfile = "parc_updates_" . $stamp;
unless ($Parc_check) {
  print "*** WARNING *** --noparc is no longer supported.\n";
  print "*** WARNING *** Use the condef parameter parc_check=off instead.\n";
}
#  # This is a kludge to turn off critical parameter checking in gcmjcl
#  # It should be replaced by permanently adding the following updates
#  # (without the parc_check=off line) to gcmjcl
#    my $parc_updates =
#q@### update script ###
#%d gcmjcl.383
#  parc_check=off
#  if [ -n "$parc_check" -a "$parc_check" = "off" ]; then
#    if [ "$float1" = 'on' ] ; then
#      FLTEXTNSN='_float1'
#    else
#      FLTEXTNSN=' '
#    fi
#    separmc${FLTEXTNSN} ST_nmf X_ST XPARM XPARC
#    if [ -s PARM -a -s PARC ] ; then
#      joinup${FLTEXTNSN} new_ST_nmf X_ST PARC PARM
#    elif [ -s PARM ] ; then
#      joinup${FLTEXTNSN} new_ST_nmf X_ST PARM
#    else
#      mv X_ST new_ST_nmf
#    fi
#    rm -f X_ST XPARM XPARC
#  else
#    . check_st_nmf_parc.cdk
#  fi
#@;
#  if (open(PARC_UPDATES, ">$parc_tmpfile")) {
#    print PARC_UPDATES "$parc_updates";
#    close PARC_UPDATES;
#    push @Updates_list, $parc_tmpfile;
#    push @TMPflist, $parc_tmpfile;
#  } else {
#    die "*** ERROR *** attempting to open temporary file $parc_tmpfile. Stopped";
#  };
#};
# Updates_fname is used below to add updates_fname=$Updates_fname to vlist
# when updates are added via files read here
$Updates_fname = '';
# If Updates_list is non-empty then assume that it contains names of
# files containing updates and read the contents of these files
foreach (@Updates_list) {
  # Each element of Updates_list may be a comma or whitespace separated
  # list of file names, each of which will contain updates
  foreach $fname (split /\s*,\s*|\s+/,$_) {
    # read updates from a file
    unless ($fname eq $parc_tmpfile) {
      # do not add $parc_tmpfile to the $Updates_fname string
      if ($Updates_fname) {$Updates_fname .= " $fname"}
      else {$Updates_fname = "$fname"};
    };
    $Updates = "";

    # look for options appended to the file name
    ($opt) = $fname =~ /(:\w*)$/;
    # strip any options from the end of the file name
    $fname =~ s/:\w*$//;

    die "Invalid update specification ${fname}$opt\n  stopped" unless ($fname);

    # Update_ignore_error will override the default behaviour in the subroutine
    # insert_updates, which is to abort if updates are supplied but not used.
    # This flag is passed via the UPDATES hash.
    $Update_ignore_error = 0;
    if ($opt) {
      # do the right thing for each option
      foreach (split '',$opt) {
        /:/ and do {next};
        /f/ and do {$Always_add_updates = 1; next};
        /i/ and do {$Update_ignore_error = 1; next};
        die "Unknown option $_ on updates file name\n Stopped";
      }
    };

    unless (-s $fname) {
      # if $fname itself is not a regular file then look for a file by that
      # same name on CCCJOB_PATH or in the user or default updates directory
      my ($f) = on_cccjob_path($fname);
      unless ($f) {
        $f = $fname;
        #if (defined $USR_LIB) {
        #  $f = "$USR_LIB/updates/$fname";
        #  unless (-s $f) {
        #    $f = "$USR_LIB/updates/${fname}_update";
        #  };
        #};
        unless (-s $f) {
          $f = "$CCCJOB_LIB/updates/$fname";
          unless (-s $f) {
            $f = "$CCCJOB_LIB/updates/${fname}_update";
          };
        };
      };
      $fname = $f;
    }

    # read the file line by line
    if (open(UPDATES, "<$fname")) {
      # abort if the updates file is empty
      if (-z UPDATES) {die "Updates file $fname is empty. Stopped"};
      while (<UPDATES>) {$Updates .= $_};
      close(UPDATES);
    } else {
      die "\n*** ERROR *** Cannot open updates file $fname  ...stopped"
        unless $Update_ignore_error;
    }

    # append the updates file name to the list of files dumped to the log dir
    push @files_to_log,$fname;

    # Break the updates into those for script, model, sub or ocean
    $section = '';
    $default_updates = '';
    # The value of the string $default_updates will be the default section
    # name for lines found prior to any /^ *###.*update/ lines in $Updates
    # or when no /^ *###.*update/ lines are found. Setting $default_updates
    # to the NULL value implies these lines are ignored.
    $section_name = $default_updates;
    foreach (split '\n',$Updates) {
      if (($sname) = /^ *###.*update.*(script)/ or
          ($sname) = /^ *###.*update.*(model)/ or
          ($sname) = /^ *###.*update.*(ocean)/ or
          ($sname) = /^ *###.*update.*(sub)/) {
        # remove leading white space
        # $section =~ s/^\s+//s;
        if ($section) {
          if ($section_name) {
            $UPDATE{$section_name} .= $section;
            $UPDATE{$section_name}{ignore_error} = $Update_ignore_error;
          }
        }
        $section_name = $sname;
        $section = '';
      } else {
        # terminate at the end of the first "Model_Input" here document
        last if /^\s*end_of_data/;
        # only read up to the end of the first job in an existing job string
        last if /^\s*#end_of_job/;
        $section .= "$_\n";
      }
    }
    if ($section) {
      if ($section_name) {
        $UPDATE{$section_name} .= $section;
        $UPDATE{$section_name}{ignore_error} = $Update_ignore_error;
      }
    }
    if ($Verbose > 10) {
      print "### update script\n$UPDATE{script}" if defined $UPDATE{script};
      print "### update model\n$UPDATE{model}" if defined $UPDATE{model};
      print "### update sub\n$UPDATE{sub}" if defined $UPDATE{sub};
      print "### update ocean\n$UPDATE{ocean}" if defined $UPDATE{ocean};
    }
  }
}

# Remove the parc updates file if it exists
# if (-f "$parc_tmpfile") {unlink $parc_tmpfile};

if (@Joblist or $Extended_usage) {
  # assign the job string internally
  @job_string = define_job(@Joblist);
  # Strip any internal option from each entry in Joblist
  # after they have been processed by define_job
  foreach (@Joblist) {s/(<.*>)$//}
} else {
  my $top_msg = "The --joblist=jlist command line option is mandatory.";
  $top_msg .= "\nValid --joblist arguments are:";
  show_usage($top_msg, {SHOW_JOBDEF_TOP => 1});
}

if ($Verbose > 5) {
  print "Number of jobs in job string ",scalar(@job_string),"\n"
};

# Extract the value of certain variables from the input job string
$In_job_string = sub { my ($vin)=@_;
  # Loop over the job string line by line to find variable definitions
  # Single or double quotes will be stripped from definitions returned
  my $order;
  my $init_val='';
  my $Warn = 0 unless ($Verbose > 2);
  # Normally the job string is searched from beginning to end, returning the first
  # value found. If a second argument "last" is passed in, the search order is
  # reversed and the last value defined in the job string is returned.
  if ($_[1]) {$order = $_[1]}
  else {$order = "first"};
  foreach ($order eq "last" ? reverse @job_string : @job_string) {
    @vals = var_find($_,$vin);
    if (scalar(@vals)) {
      if ($order eq "last") {$curr_val = $vals[$#vals]}
      else {$curr_val = $vals[0]}
    } else {$curr_val = ''};
    unless ($init_val) {$init_val = $curr_val};
    if ($Warn and $curr_val ne $init_val) {
      print "### Inconsistent variable definition ### ";
      print "$vin=$init_val ...current value is $curr_val\n";
    };
  };
  # If the variable is defined in the job string return its value
  # otherwise return false
  if ($init_val) {return $init_val}
  else           {return 0};
};

$Init_runid = &$In_job_string("runid");
$Init_year = &$In_job_string("year");
$Last_year = &$In_job_string("year","last");
$Init_mon = &$In_job_string("mon");
$Last_mon = &$In_job_string("mon","last");
$Init_year_restart = &$In_job_string("year_restart");
$Init_mon_restart = &$In_job_string("mon_restart");
$Init_kfinal = &$In_job_string("kfinal");
$Last_kfinal = &$In_job_string("kfinal","last");
$Init_months = &$In_job_string("months");
$Init_delt = &$In_job_string("delt");
$Init_prefix = &$In_job_string("prefix");
$Init_uxxx = &$In_job_string("uxxx");
if ($Verbose > 10) {
  if ($Init_runid) {print "Init_runid=$Init_runid\n"};
  if ($Init_year) {print "Init_year=$Init_year\n"};
  if ($Last_year) {print "Last_year=$Last_year\n"};
  if ($Init_mon) {print "Init_mon=$Init_mon\n"};
  if ($Last_mon) {print "Last_mon=$Last_mon\n"};
  if ($Init_year_restart) {print "Init_year_restart=$Init_year_restart\n"};
  if ($Init_mon_restart) {print "Init_mon_restart=$Init_mon_restart\n"};
  if ($Init_kfinal) {print "Init_kfinal=$Init_kfinal\n"};
  if ($Last_kfinal) {print "Last_kfinal=$Last_kfinal\n"};
  if ($Init_months) {print "Init_months=$Init_months\n"};
  if ($Init_delt) {print "Init_delt=$Init_delt\n"};
  if ($Init_prefix) {print "Init_prefix=$Init_prefix\n"};
  if ($Init_uxxx) {print "Init_uxxx=$Init_uxxx\n"};
};

# if Initial_conditions is set and initsp is not in vlist then add it
if ($Initial_conditions) {
  # This will tell the subroutine expand_template to create
  # a job string that starts from initial conditions
  if (not &$In_vlist("initsp")) {push @vlist, "initsp=on"};
  if (not &$In_vlist("samerun")) {push @vlist, "samerun=off"};
} else {
  # This will tell the subroutine expand_template to create
  # a job string that starts from a restart
  if (not &$In_vlist("initsp")) {push @vlist, "initsp=off"};
  if (not &$In_vlist("samerun")) {push @vlist, "samerun=on"};
  # Add restart_fname to vlist if it is not already there
  if ($Set_vlist_def and not &$In_vlist("restart_fname")) {
    if ($Restart_fname) {push @vlist, "restart_fname=\"$Restart_fname\""};
  };
};

if ($Set_vlist_def and not &$In_vlist("crawork")) {
  # crawork must be the same for every job in the job string
  # make sure that it is defined in vlist if not there already
  push @vlist, "crawork=${runid}_$stamp";
};

# if RUNID is not in vlist and runid is set then add RUNID to vlist
if ($Set_vlist_def and not &$In_vlist("RUNID")) {
  # set RUNID to the upper case of runid if runid is set
  if ($runid) {($RUNID = $runid) =~ tr/a-z/A-Z/};
  if ($RUNID) {push @vlist, "RUNID=$RUNID"};
};

# Add updates_fname to vlist if there are updates and it is not already there
if ($Set_vlist_def and not &$In_vlist("updates_fname")) {
  if ($Updates_fname) {push @vlist, "updates_fname=\"$Updates_fname\""};
};

# if prefix is not in vlist then add prefix to vlist
# if ($Set_vlist_def and not &$In_vlist("prefix")) {
#   # remove whitespace from runid before assigning prefix
#   ($runid = &$In_vlist("runid")) =~ s/\s*//;
#   if ($uxxx) {push @vlist, "prefix=${uxxx}_$runid"}
#   else {push @vlist, "prefix=ma_$runid"};
# };

# When $Runid_substitute is set globally replace
# the old runid value with the new runid value
if ($Runid_substitute) {
  # get the value of runid found in the input job string
  $Init_runid = &$In_job_string("runid");
  # get the value of runid found in vlist
  if ($Init_runid and $new_runid = &$In_vlist("runid")) {
    # add a substitution "new" runid value for "old" runid value to SUBS
    push @SUBS, "/$Init_runid/$new_runid/";
  };
};

# if rcm_param is in vlist then use its' value as the name of the
# rcm_PARAMETERS file and replace the default set of parameters in JOBDEF
if (&$In_vlist("rcm_param")) {
  foreach (@vlist) {
    my ($vn,$vv) = split '=',$_,2;
    if ("$vn" eq "rcm_param") {
      if (-s "$vv") {
        if (open(PARAMETERS, "<$vv")) {
          # abort if the job file file is empty
          if (-z PARAMETERS) {
            die "\n*** ERROR *** RCM parameters file $vv is empty.\n";
          };
          # replace any existing entry with the data from this file
          $RCM_PARAMS = '';
          while (<PARAMETERS>) {$RCM_PARAMS .= $_;print $_};
          close(PARAMETERS);
        } else {
          die "\n*** ERROR *** Cannot open RCM parameters file $vv.\n";
        };
      } else {
        die "\n*** ERROR *** RCM parameters file $vv is missing or empty.\n";
      }
    };
  };
};
if ($Verbose > 10) {print "RCM_PARAMS ::\n$RCM_PARAMS\n\n"};

if ($MK_RCM_DATA) {
  # define mload_prefix unless it is set on the command line
  # this must be the same as the RCM parameter MLX
  my $rcm_MLX;
  my $rcm_MLX_no_;
  if (&$In_vlist("MLX")) {
    # a command line definition will override a value in the RCM parameters file
    foreach (@vlist) {
      my ($vn,$vv) = split '=',$_,2;
      if ("$vn" eq "MLX") {$rcm_MLX = $vv; last};
    };
  } else {
    ($rcm_MLX) = var_find($RCM_PARAMS,"MLX");
  };
  if (&$In_vlist("mload_prefix")) {
    # if mload_prefix is defined on the command line then use this value
    # to replace any value defined in the RCM parameters file or to override
    # any MLX= definition provided on the command line
    foreach (@vlist) {
      my ($vn,$vv) = split '=',$_,2;
      if ("$vn" eq "mload_prefix") {
        $rcm_MLX = $vv;
        ($RCM_PARAMS) = var_replace($RCM_PARAMS,"MLX",$rcm_MLX);
        last;
      };
    };
  };
  unless ($rcm_MLX) {die "Unable to determine RCM parameter MLX.\n  Stopped"};
  # rcm_MLX_no_ will be rcm_MLX with any trailing underscore removed
  ($rcm_MLX_no_ = $rcm_MLX) =~ s/_$//;
  push @vlist, "mload_prefix=$rcm_MLX_no_" unless &$In_vlist("mload_prefix");

  # add mload_prefix_ to vlist so that mload will look for files of the form
  # $MLX${yyy}_m${mm}_* rather than of the form ${MLX}_${yyy}_m${mm}_*
  push @vlist, "mload_prefix_=$rcm_MLX" unless &$In_vlist("mload_prefix_");

  # mdelete_prefix must be the same as mload_prefix
  push @vlist, "mdelete_prefix=$rcm_MLX_no_";
};

# Make sure any variables that need to be, are formatted properly
@vlist = &format_vlist(@vlist);

# If requested, print extended usage info and quit at this point
# show_usage if $Extended_usage;
show_usage('', {FULL_HELP => 1}) if $Extended_usage;

# set up the variable info hash table VARS
$rank = 0;
foreach $pair (@vlist) {
  ($vname,$vval) = split '=', $pair,2;
  $usrset{$vname} = 0 unless (defined $usrset{$vname});
  $VARS{$vname}{value}  = $vval;
  $VARS{$vname}{usrset} = $usrset{$vname};
  $VARS{$vname}{add}    = $Addvar unless defined $VARS{$vname}{add};
  $VARS{$vname}{remove} = $Remvar unless defined $VARS{$vname}{remove};
  $VARS{$vname}{rank}   = ++$rank;
};

# Print vlist if requested
if ($Verbose > 0) {
  print "Variable definitions used: ";
  if (@vlist) {print "\n"} else {print "...none\n"};
  foreach (@vlist) {
    ($vname,$vval) = split '=',$_,2;
    # quote blanks in the value when printing
    if ($vval =~ / / and $vval !~ /"/) {$vval = '"' . "$vval" . '"'};
    # check if vname is present in the job string
    $found = 0;
    foreach (@job_string) {/\b$vname=/ and $found = 1};
    if ($vname eq "mload_mon_offset"     or
        $vname eq "mdump_mon_offset"     or
        $vname eq "mdelete_mon_offset"   or
        $vname eq "load_list_mon_offset" or
        $vname eq "dump_list_mon_offset" or
        $vname eq "custom_mon_offset"    or
        $vname eq "cloop_mon_offset"    or
        $vname eq "del_list_mon_offset"     ) {
      # these variables are typically not present in the job string but
      # are used in the subroutine expand_template. They are passed
      # to expand_template via the VARS hash.
      $found = 1;
    }
    if ($found) {
      printf "%30s = %-30s\n",$vname,$vval;
    } else {
      if ($VARS{$vname}{add}) {
        printf "%30s = %-30s   ...to be added\n",$vname,$vval;
      } else {
        printf "%30s = %-30s   ...%s not found in job string\n",$vname,$vval,$vname;
        $die_without_p5lib = 0;
        if ($die_without_p5lib and $vname = "p5lib") {
          die "$vname must be present in your base string.\n";
        }
      }
    }
  }

  print "\nSubstitutions requested: ";
  if (@SUBS) {print "\n"} else {print "...none\n"};
  foreach (@SUBS) {print "  $_\n"};
  print "\nJob list: ";
  if ($#Joblist > -1) {print "\n"} else {print "...none\n"};
  foreach (@Joblist) {
    $Jobname = $_; # avoid changing the Joblist array
    printf "%20s  ::  ",$Jobname;
    $Jobname =~ s/:.*$//; # strip options for use as a key string in the JOBDEF hash
    ($Jobname) = split '=',$Jobname; # strip any key modifiers
    if (defined $JOBDEF{description}{$Jobname}) {
      print "$JOBDEF{description}{$Jobname}\n";
    } elsif (-r $Jobname or on_cccjob_path($Jobname)) {
      print "file\n";
    } else {
      if (defined $JOBDEF{job}{$Jobname}) {print "\n"}
      else {print "Invalid\n"};
    };
  };
};

# subdivide the job string into subsections
# parse_job will return an array of hashes, one for each subsection
@JOBS = parse_job(@job_string);

# add jobnames to each hash in @JOBS
foreach my $href (@JOBS) {
  ($href->{jobname}) = read_jobtag($href->{job},{NAME_TAG=>1});
}
foreach my $href (@JOBS) {
  # remove any tags from the job and section elements
  read_jobtag($href->{job},{REMOVE_TAG=>1});
  read_jobtag($href->{section},{REMOVE_TAG=>1});
}

# print the JOBS array of hashes
if ($Verbose > 2) {
  # define an order to print each hash
  $i=0; foreach ( qw(jobname sequence id subid section job) ) {$rankJOBS{$_} = ++$i};
  $i=0;
  printf "%s  BEGINNING OF JOB STRING  %s\n","/" x 30,"/" x 30;
  foreach $href (@JOBS) {
    printf "%s  section %4i  %s\n","+" x 35,++$i,"+" x 35;
    foreach $k (sort {$rankJOBS{$a} <=> $rankJOBS{$b}} keys %$href) {
      if ($Verbose < 10 and $k eq "job") {next};
      if ($k eq "section") {print "section:\n",$href->{$k},"\n"}
      elsif ($k eq "job") {print "job:\n",$href->{$k},"\n"}
      else {printf "%s %8s = %s\n", '.' x 20, $k, $href->{$k}};
    };
  };
  printf "%s  END OF JOB STRING  %s\n","/" x 33,"/" x 33;
};

# print the variable info hash table VARS
if ($Verbose > 1) {
  # define an order to print each set of attributes
  $i=0; foreach ( qw(remove add rank value) ) {$rankATTS{$_} = ++$i};

  # print variables sorted on the value of VARS{$vname}{rank}
  print "\nVARS hash table:";
  if (%VARS) {print "\n"} else {print "...none\n"};
  foreach $vname (sort {$VARS{$a}{rank} <=> $VARS{$b}{rank}} keys %VARS) {
    printf "%15s: ",$vname;
    # print attributes sorted according to $rankATTS defined above
    foreach $att (sort {$rankATTS{$a} <=> $rankATTS{$b}} keys %{ $VARS{$vname} }) {
      print "$att=$VARS{$vname}{$att} ";
    };
    print "\n";
  };
  print "\n";
};

# Ensure that the run_start_month values in the VARS hash and in vlist
# are consistent with the value determined in the parse_job routine unless
# run_start_month has been set on the command line
unless (defined $usrset{run_start_month}) {
  $VARS{run_start_month}{value} = sprintf("%2.2d",$start_time{month}{value});
  &$set_vlist("run_start_month=$VARS{run_start_month}{value}");
};

# Modify the job string with substitutions found in SUBS
@JOUT = substitute_job(\@JOBS, \@SUBS);

# Modify the job string by setting variable names according to values in VARS
@JOUT = change_var_vals(\@JOBS, \%VARS);

# add job specific variables to the processed job list
# Note: this changes the JOBS array not the JOUT array, therefore insert_updates or
#       another subroutine that will return JOUT must be called after this loop
foreach my $href (@JOBS) {
  foreach my $jname (keys %job_specific_vars) {
    if ($Verbose > 10) {print "jobname=$href->{jobname}   jname=$jname\n"};
    next unless defined $href->{jobname};
    next unless ($jname eq $href->{jobname});
    if ($Verbose > 1) {
      print " job specific variable replacement in $jname :: sequence=$href->{sequence}\n";
    }
    while (my ($var,$val) = each %{$job_specific_vars{$jname}}) {
      if ($Verbose > 1) {print "  $var = $val"};
      my $str = $href->{section};
      ($str) = var_replace($str, $var, $val);
      $href->{section} = $str;
      $str = $href->{job};
      ($str) = var_replace($str, $var, $val);
      $href->{job} = $str;
    }
    if ($Verbose > 1) {print "\n"};
  }
}

# insert updates, if any, into the job string
@JOUT = insert_updates(\@JOBS, \%UPDATE);

unless ($Always_add_updates) {
  # filter out unessecary updates
  @JOUT = filter_updates(\@JOBS);
}

my $tmp_list;

# define a list of shell reserved words
my @sh_reserved = ('case','do','done','elif','else','esac','fi','for',
  'function','if','in','select','then','until','while','time');
my @sh_reserved2 = ('{','}','!','[[',']]');
# define a list of shell builtin commands
my @sh_command = ('continue','exit','return','trap','eval','export','set','unset',
  'break','exec','readonly','shift','builtin','times','typeset','alias','command',
  'fg','kill','umask','bg','false','getopts','read','unalias','cd','fc','jobs',
  'true','wait','let','pwd','ulimit','echo','print','test','whence');
my @sh_command2 = ('.',':','[');

# compose a list of words (sh reserved words and builtins) to look for in
# lines of text that would indicate that the line is a shell command
my @sh_words;
push @sh_words, @sh_reserved, @sh_command;

# Insert the contents of user supplied files into the custom and cloop modules
# File names are available in the variables $CUSTOM_fname, $CLOOP_HEAD_fname,
# $CLOOP_fname and $CLOOP_TAIL_fname, if defined.
# These files are inserted verbatim at predetermine locations.
$insert_file = sub {
  my ($MODULE) = shift;  # input a module name
  my $vname = "${MODULE}_fname";
  my $insert_at_string = "INSERT_${MODULE}_COMMANDS_HERE";
  # ensure upper case for the insert tag
  $insert_at_string =~ tr/a-z/A-Z/;
  if ($Verbose > 10) {
    print "insert_file:  vname=$vname   insert_at_string=$insert_at_string\n";
  };
  if ($$vname) {
    if (open(USR_FILE, "<$$vname")) {
      # abort if the file is empty and is not /dev/null
      unless ($$vname eq "/dev/null" ) {
        die "User supplied file $$vname is empty\n Stopped"
          if (-z USR_FILE);
      };
      my $usr_file = "\n";
      while (my $line = <USR_FILE>) {$usr_file .= $line};
      close(USR_FILE);
      # add this file name to the list of files to be dumped to the log dir
      push @files_to_log,$$vname;
      foreach (@JOUT) {
        s/^\s*# *<<$insert_at_string>> *$/$usr_file/m;
      };
    } else {
      die "\n*** ERROR *** Cannot open user supplied file $$vname\n Stopped";
    };
  } else {
    # If no filename was specified simply remove the insertion locator
     foreach (@JOUT) {
       # do a case insensitive substitution
       s/^\s*# *<<$insert_at_string>> *$//mi;
     };
  };
};
foreach (("CUSTOM","CLOOP_HEAD","CLOOP","CLOOP_TAIL")) {
  &$insert_file($_);
};
# DO likewise for each clone of the custom or cloop modules
foreach (@{$clone{custom}}) {
  my ($id) = /^custom(\w+)$/;
  &$insert_file("CUSTOM$id");
};
foreach (@{$clone{cloop}}) {
  my ($id) = /^cloop(\w+)$/;
  &$insert_file("CLOOP${id}_HEAD");
  &$insert_file("CLOOP$id");
  &$insert_file("CLOOP${id}_TAIL");
};

# Insert user supplied auxillary code snippets into specific jobs in the current string
# modules that currently support this are:
#     block next_block resavejhome
foreach my $modname ( keys %auxcode ) {
  # Insert this code snippet into the current module, replacing any line
  # containing the insert_tag defined here
  my $insert_tag = uc("INSERT_$modname");
  foreach (@JOUT) {
    # do a case insensitive substitution
    s/^\s*# *<<\s*$insert_tag\s*>> *$/$auxcode{$modname}/mi;
  }
}


# Insert the contents of user supplied files into dump_list, dump_sublist,
# load_list, load_sublist, del_list, rtrans, ccc2nc, pxdel, nccrip
# File names are available in the variables $DUMP_list_fname, $DUMP_sublist_fname,
# $LOAD_list_fname, $LOAD_sublist_fname, $DEL_list_fname, $RTRANS_fname, $CCC2NC_fname,
# $PXDEL_fname, $NCCRIP_fname if defined.
# Each line of these files is scanned for valid shell reserved words.
# If no shell reserved words are found on a line and that line is not a
# comment line or a blank line and that line contains only 1 word made up
# of alphanumeric characters or "$" then it is assumed to be a file name.
# File names found will be replaced with the assignment filen=word_on_line
# where the n in filen is incremented for each file name found.
# All other lines are inserted verbatim.
$insert_list = sub {
  my ($MODULE) = shift;  # input a module name
  my $vname = "${MODULE}_fname";
  my $insert_at_string = "INSERT_${MODULE}";
  # ensure upper case for the insert tag
  $insert_at_string =~ tr/a-z/A-Z/;
  if ($Verbose > 10) {
    print "insert_list:  vname=$vname   insert_at_string=$insert_at_string\n";
  }
  if ($$vname) {
    if (open(XLIST, "<$$vname")) {
      # abort if the file is empty and is not /dev/null
      unless ($$vname eq "/dev/null" ) {
        die "User supplied file $$vname is empty\n Stopped"
          if (-z XLIST);
      }
      my $list_lines = '';
      while (my $xline = <XLIST>) {$list_lines .= $xline};
      close(XLIST);
      # add this file name to the list of files to be dumped to the log dir
      push @files_to_log,$$vname;
      my $tmp_list = "\n";
      my $fcount = 0;
      foreach my $line (split '\n',$list_lines) {
        # ignore blank lines and comments
        next if $line =~ /^\s*$/;
        if ($line =~ /^\s*#/) {
          # If this is a comment then allow lines of the form "#break"
          unless (($MODULE =~ /^DUMP_sublist$/i or $MODULE =~ /^LOAD_sublist$/i) and
                 ($line =~ /^\s*#\s*break\s*$/i or $line =~ /^\s*#\s*stop\s*$/i )) {
            next;
          }
        }
        my $is_sh_script = 0;
        # check for shell reserved words or built in commands
        foreach (@sh_words) {
          if ($line =~ /\b$_\b/) {$is_sh_script = 1; last};
        }
        if ((($MODULE =~ /^DUMP_sublist$/i or $MODULE =~ /^LOAD_sublist$/i) and
            ( $line =~ /^\s*#\s*break\s*$/i or $line =~ /^\s*#\s*stop\s*$/i )) or
            ($line =~ /^\s*[a-zA-Z_0-9\$\.\-\+\}\{]+\s*$/ and not $is_sh_script)) {
          # Assume this is a file name
          # strip leading and trailing space
          $line =~ s/\s*([^\s]*)\s*/$1/;
          # replace line with filen=line
          $tmp_list .= "file" . ++$fcount . "=" . $line . "\n";
        } else {
          # Otherwise simply pass the line through unaltered
          $tmp_list .= $line . "\n";
        }
      }
      # if any file names were found append a join=... definition
      if ($fcount) {$list_lines = $tmp_list . "join=$fcount\n"};
      # insert the resulting script into the job string
      foreach (@JOUT) {
        # do a case insensitive substitution
        s/^\s*# *<<$insert_at_string>> *$/$list_lines/mi;
      }
    } else {
      die "\n*** ERROR *** Cannot open user supplied file $$vname\n Stopped";
    }
  }
};
foreach (("DUMP_list","DUMP_sublist","LOAD_list","LOAD_sublist","DEL_list","RTRANS",
          "CCC2NC","PXDUMP","PXDIAG","PXDEL","OTHR2FE","LSARC","NCCRIP")) {
  &$insert_list($_);
}
# DO likewise for each clone of load_list, dump_list, del_list or rtrans
foreach (@{$clone{load_list}}) {
  my ($id) = /^load_list(\w+)$/;
  &$insert_list("LOAD_list$id");
}
foreach (@{$clone{load_sublist}}) {
  my ($id) = /^load_sublist(\w+)$/;
  &$insert_list("LOAD_sublist$id");
}
foreach (@{$clone{dump_list}}) {
  my ($id) = /^dump_list(\w+)$/;
  &$insert_list("DUMP_list$id");
}
foreach (@{$clone{dump_sublist}}) {
  my ($id) = /^dump_sublist(\w+)$/;
  &$insert_list("DUMP_sublist$id");
}
foreach (@{$clone{del_list}}) {
  my ($id) = /^del_list(\w+)$/;
  &$insert_list("DEL_list$id");
}
foreach (@{$clone{rtrans}}) {
  my ($id) = /^rtrans(\w+)$/;
  &$insert_list("RTRANS$id");
}
foreach (@{$clone{ccc2nc}}) {
  my ($id) = /^ccc2nc(\w+)$/;
  &$insert_list("CCC2NC$id");
}
foreach (@{$clone{lsarc}}) {
  my ($id) = /^lsarc(\w+)$/;
  &$insert_list("LSARC$id");
}
foreach (@{$clone{nccrip}}) {
  my ($id) = /^nccrip(\w+)$/;
  &$insert_list("NCCRIP$id");
}

# Add an info tag if requested
if ($AddTAG and $JOUT[0]) {
  # Add joblist descriptions to the tag
  $TAG .= "#\n# Job list:\n";
  foreach (@Joblist) {
    $Jobname = $_; # avoid changing the Joblist array
    $TAG .= sprintf "#%15s  ::  ",$Jobname;
    $Jobname =~ s/:.*$//; # strip options for use as a key string in the JOBDEF hash
    ($Jobname) = split '=',$Jobname; # strip any key modifiers
    if (defined $JOBDEF{description}{$Jobname}) {
      $TAG .= "$JOBDEF{description}{$Jobname}\n";
    } elsif (-r $Jobname or on_cccjob_path($Jobname)) {
      $TAG .= "file\n";
    } else {
      if (defined $JOBDEF{job}{$Jobname}) {$TAG .= "\n"}
      else {$TAG .= "Invalid\n"};
    };
  };
  # Add variable definitions to the tag
  $TAG .= "# Variable definitions used:\n";
  #foreach $vname (sort {$VARS{$a}{rank} <=> $VARS{$b}{rank}} keys %VARS) {
  foreach $vname (sort keys %VARS) {
    # check that vnames in vlist are present in the processed job string
    $found = 0;
    foreach (@JOUT) {/$vname=/ and do {$found = 1; last}};
    if ($vname eq "mload_mon_offset"     or
        $vname eq "mdump_mon_offset"     or
        $vname eq "mdelete_mon_offset"   or
        $vname eq "load_list_mon_offset" or
        $vname eq "dump_list_mon_offset" or
        $vname eq "custom_mon_offset"    or
        $vname eq "cloop_mon_offset"    or
        $vname eq "del_list_mon_offset"     ) {
      # these variables are typically not present in the job string but
      # are used in the subroutine expand_template.
      $found = 1;
    }
    if ($found) {
      $TAG .= sprintf "#%30s = %-30s\n",$vname,$VARS{$vname}{value};
    } else {
      $TAG .= sprintf "#%30s = %-30s ...not found in job string\n",
                $vname,$VARS{$vname}{value};
    }
  }

  $TAG .= sprintf("%s\n", '#'x80);

  if ($AddTAG) {
    # If the job string starts with a sehbang line then insert the tag just
    # after that line, otherwise insert at the top of the job string
    unless ($JOUT[0] =~ s<^\s*(#! */bin/[^\n]*\n)><$1$TAG>s) {
      $JOUT[0] = $TAG . $JOUT[0];
    };
  };
};

# create a disk file containing the processed job string

# always remove the file first to avoid leaving an old file
# when no output is created by cccjob
unlink $Outfile;

if ($JOUT[0]) {
  open(JOB_OUT, ">$Outfile") or die "Error opening $Outfile";
  print JOB_OUT @JOUT;
  close(JOB_OUT) or die "Error closing $Outfile";
  if ($Verbose > 0) {print "\nCreated file $Outfile\n"};
  push @files_to_log,$Outfile;
} else {
  print "The output job string is empty. No output file is created\n";
};

if ($LOG and $JOUT[0]) {
  # Create a log dir and dump certain info there
  chomp(my $stamp = `date '+%Y%b%d'`);
  $stamp .= "_$$";
  # The log dir will be relative to the invoking users home dir
  my $LOG_DIR = (getpwuid($<))[7]."/cccjobs/log/${runid}_$stamp";
  if (-d $LOG_DIR) {
    die "Log dir $LOG_DIR already exists. No log created.\n";
  };
  my @mkd = ("mkdir", "-p", "$LOG_DIR");
  my $rc = 0xffff & system(@mkd);
  if ($rc != 0) {die "*** ERROR *** @mkd:  $!\n"};

  # Read a user supplied message to append to the README file
  sub Interactive { return -t STDIN && -t STDOUT };
  sub log_prompt { print "LOG: " if Interactive() };
  print "\n### Enter a brief message to append to the log's README file.\n";
  print "### A single blank line will terminate your message.\n";
  # Insert an initial blank line to separate the user's message
  # from the rest of the README file
  my @usr_log = "\n";
  for (log_prompt(); $line = <STDIN>; log_prompt()) {
    # read lines from stdin and push onto @usr_log
    # a blank line will exit the loop
    last if $line =~ /^\n$/;
    push @usr_log, $line;
  };

  # Write $TAG to a README file
  my $readme = "$LOG_DIR/README";
  open(README, ">$readme") or
    die "*** ERROR *** Cannot create README file $readme\n ";
  print README $TAG;
  foreach (@warnings) {print README "#  $_\n"};
  foreach (@usr_log) {print README "$_"};
  close(README);

  # Put a copy of each jobdef used into the log dir
  foreach (@Joblist) {
    my $jobname = $_;
    # strip any options from the end of the keyword
    $jobname =~ s/:.*$//;
    # strip the file name specification from the end of the keyword
    $jobname =~ s/=.*$//;
    my $jobpath = "$LOG_DIR/$jobname";
    if (exists $JOBDEF{job}{$jobname}) {
      # Dump the copy found in JOBDEF
      open(JOBFILE, ">$jobpath") or
        die "*** ERROR *** Unable to open job file $jobpath\n";
      print JOBFILE $JOBDEF{job}{$jobname};
      close(JOBFILE);
    } else {
      warn "Unable to create a log entry for $jobname\n";
      push @warnings, "Unable to create a log entry for $jobname";
    };
  };

  # dump any other files used during this invocation
  foreach (@files_to_log) {
    my @cmd = ("cp", "$_", "$LOG_DIR");
    my $rc = 0xffff & system(@cmd);
    if ($rc != 0) {die "*** ERROR *** @cmd:  $!\n"};
  };

  # user_source, if defined, provides the name of a dir used to hold
  # modified versions of ocean or coupler source code. When user_source
  # is specified by the user, look for this dir on pollux.
  # If this dir exists then copy every file found in it to a
  # subdirectory of $LOG_DIR named user_source_ocn.
  if (my $ocn_updates = &$In_vlist("user_source")) {
    my $rls = '';
    if ($host{location} =~ /^dorval/i) {
      # the dir should be directly available to the invoking machine
      $rls = `ls -dl $ocn_updates`;
    } else {
      # the dir is on a remote machine, namely pollux
      $rls = `rsh pollux ls -dl $ocn_updates`;
    };
    if ( $rls =~ /^d/) {
      # The dir exists...copy it to the log dir
      my @cmd = ();
      if ($host{location} =~ /^dorval/i) {
        # the dir should be directly available to the invoking machine
        @cmd = ("cp", "-r", "$ocn_updates", "$LOG_DIR/user_source_ocn");
      } else {
        # the dir is on a remote machine, namely pollux
        @cmd = ("rcp", "-r", "pollux:$ocn_updates", "$LOG_DIR/user_source_ocn");
      };
      my $rc = 0xffff & system(@cmd);
      if ($rc != 0) {die "*** ERROR *** @cmd:  $!\n"};
      # remove any backup (*~) files from the log dir
      @cmd = ("find", "$LOG_DIR/user_source_ocn",'-name','*~','-type','f',
               '-exec', 'rm','-f','{}',';');
      $rc = 0xffff & system(@cmd);
      if ($rc != 0) {die "*** ERROR *** @cmd:  $!\n"};
    } else {
      print "*** WARNING *** user_source=$ocn_updates was supplied\n";
      print " by the user but no dir by this name was found on pollux.\n";
      print "No files from pollux:$ocn_updates were copied to the log dir.\n\n";
    };
  };

  # compress files in $LOG_DIR to conserve disk space
  my @cmd = ('find', "$LOG_DIR", '-type','f', '!','-name','*gz',
             '!','-name','README','-exec','gzip','{}',';');
  $rc = 0xffff & system(@cmd);
  if ($rc != 0) {die "*** ERROR *** @cmd:  $!\n"};

  # set permissions
  my $dir = cwd();
  chdir() or die "$!\n   Stopped";
  undef @cmd;
  @cmd = ('chmod','g+rx','cccjobs');
  $rc = 0xffff & system(@cmd);
  if ($rc != 0) {die "*** ERROR *** @cmd:  $!\n"};
  undef @cmd;
  @cmd = ('find', 'cccjobs', '-type','d','-exec','chmod','g+rx','{}',';');
  $rc = 0xffff & system(@cmd);
  if ($rc != 0) {die "*** ERROR *** @cmd:  $!\n"};
  undef @cmd;
  @cmd = ('find', 'cccjobs', '-type','f','-exec','chmod','g+r','{}',';');
  $rc = 0xffff & system(@cmd);
  if ($rc != 0) {die "*** ERROR *** @cmd:  $!\n"};
  chdir $dir;

  if ($Verbose > 0) {print "Created log directory $LOG_DIR\n"};
};

# Clean up temporary files
foreach (@TMPflist) {
  next unless (-r $_);
  unlink $_;
};

exit 0;
########################################################
##################### End of main ######################
########################################################

###############################################################
#
# The following subroutines are either single use (by $main) or
# should have access to $main's namespace. Therefore they are
# defined here rather than in an external package.

#########################################################################
sub line_var_vals {
  # Extract values of shell variables defined on a single line of script
  use strict;

  # The first arg is a line of script in which the shell variables will be found
  my $line = shift;

  # Remove any embedded "#" delimited comments from this line
  $line = rm_sh_comment_from_line($line);

  my %vals = ();

  # All remaining input parameters are variable names whose value is desired
  foreach my $var (@_) {
    # If the user has requested a variable value but that variable does not
    # appear on the input line then a hash entry is created for the variable
    # but a null value is provided
    $vals{$var} = '';

    # Ignore lines that do not contain the variable assignment
    next unless $line =~ /\b${var}=/;

    # Extract the value of the current variable from this line
    # This will take the value from the first variable definition found
    # on the line if there is more than one present
    ($vals{$var}) = $line =~ m/\b${var}=(`[^`]*`|"[^"]*"|'[^']*'|[^;\s]*)/;
    # Remove any back tic, single or double quotes around this value
    $vals{$var} =~ s/^\s*`([^`]*)`\s*$/$1/;
    $vals{$var} =~ s/^\s*'([^']*)'\s*$/$1/;
    $vals{$var} =~ s/^\s*"([^"]*)"\s*$/$1/;
  }

  # Return a hash ref pointing to a hash whose keys are the variable names
  return \%vals;
}

sub rm_sh_comment_from_line {
  # Remove any '#' delimited comments from a single line of shell script
  # The input line may not contain any newlines
  use strict;
  my $line = shift;

  # Ensure no newlines exists
  die "rm_sh_comment_from_line: Input -->\n$line\n<-- contains a newline\n"
      if $line =~ /\n/;

  # Store the comment string so that is may be returned, if requested
  my $comment = '';

  # Define subs used in the following loop to replace and then
  # later reinsert quoted segments found in this line
  sub new_token {
    # Return a unique token to be used as a place holder
    # for a substituted text segment
    # A list reference and a string segment
    use strict;

    # A list reference for a list that holds hash refs
    # that define each substitution
    my $listref = shift;

    # The string segment that will be replaced by the token defined here
    my $orig_strng = shift;

    # Define the token as part of an anonymous hash ref
    my $token = {};
    $token =~ s/HASH\((.*)\)$/xyz$1/;

    # Create a single element hash containing the token/substr pair
    # and push it onto the replacement list
    my $h ={};
    $h->{$token} = $orig_strng;
    push @{$listref}, $h if $listref;

    # Return the token defined above
    return $token;
  }

  sub orig_strng {
    # Reconstitute a string that has undergone substitutions
    # The modified string along with a list of substitutions is passed in
    # and the original string is returned
    use strict;

    # Input the modified string after is has undergone substitutions
    my $strng = shift;

    # The remainder of the input parameters are hash refs to hashes
    # containing one token/substr pair each
    foreach my $h ( reverse @_ ) {
      # This call to keys is used for the following check but will
      # also reset the iterator for this hash. This will be important
      # if the hash was used in a previous invocation of each.
      if ( scalar( keys %{$h} ) < 1 ) {
        die "orig_strng: Empty replacement hash.\n";
      }
      # The hash contains a single element whose key is the token that replaced
      # the string segment and the value is the original string segment
      my ($str, $orig) = each(%{$h});
      $strng =~ s/$str/$orig/;
    }
    return $strng;
  }

  # Remove embedded comments from the input line, if any
  if ($line =~ /#/) {
    # Remove '#' delimited comments
    my $line_in = $line;

    # An array to hold a list of hash refs that store replaced string segments
    my @repl = ();

    # Replace all back tic quoted strings with a unique string and add
    # a hash of the form {replacement string} -> "original string"
    # to a list of all replacements
    $line_in =~ s/`[^`]*?`/new_token(\@repl,$&)/eg;

    # Replace all single quoted strings with a unique string and add
    # a hash of the form {replacement string} -> "original string"
    # to a list of all replacements
    $line_in =~ s/'[^']*?'/new_token(\@repl,$&)/eg;

    # Replace all double quoted strings with a unique string and add
    # a hash of the form {replacement string} -> "original string"
    # to a list of all replacements
    $line_in =~ s/"[^"]*?"/new_token(\@repl,$&)/eg;

    # Replace all brace quoted strings with a unique string and add
    # a hash of the form {replacement string} -> "original string"
    # to a list of all replacements
    $line_in =~ s/{.*?}/new_token(\@repl,$&)/eg;

    # foreach (@repl) {
    #   my ($str, $orig) = each(%{$_});
    #   print "repl: $str == $orig\n";
    # }

    if ($line_in =~ /#/) {
      # If there is still a "#" after removing quoted segments then
      # detemine what part of the line is the comment
      ($comment) = $line_in =~ /(#.*)$/;

      # Replace, in the comment, any substitutions done above so that
      # we can match the comment found in the original line
      $comment = orig_strng($comment, @repl);

      # Remove this comment
      $line =~ s/^(.*)\Q$comment\E$/$1/;
    }
  }

  return wantarray ? ($line, $comment) : $line;

}

#########################################################################
sub hostid {
  # Get host name, domain name and aliases for current machine
  # Larry Solheim  May 9, 2007
  use strict;
  use Sys::Hostname;
  my ($hostn, $nsinfo, $fqhostn, $dname, $alias, $subnet);

  $fqhostn = '';
  my $resolv = `cat /etc/resolv.conf`;
  foreach (split /\n/,$resolv) {
    # Look for a "search" line in /etc/resolv.conf
    my $domain = '';
    if (/^\s*search/) {
      my @F = split /\s+/;
      $domain = $F[1] if $F[1];
      # Remove the 'int.' prefix found on some machines in Dorval
      $domain =~ s/^int\.// if $domain;
    }
    if ($domain) {
      my ($xxx) = split /\./, hostname();
      $fqhostn = "${xxx}.$domain";
    }
  }

  unless ($fqhostn) {
    my $nslookup = `which nslookup`;
    if ($nslookup =~ m@\s*/@) {
      # nslookup exists
      # use nslookup to determine a fully qualified host name
      $hostn = hostname();
      $nsinfo = `nslookup $hostn`;
      ($fqhostn) = $nsinfo =~ /^Name:\s*(.*)/m;
      $fqhostn = $hostn unless $fqhostn;
      $fqhostn =~ s/^\s+//;
    } else {
      $fqhostn = hostname();
    }
  }

  # define the host name as everthing up to the first '.'
  ($hostn) = $fqhostn =~ /^([\w-]*)/m;

  # define the domain name as everthing after the first '.'
  ($dname) = $fqhostn =~ /^[\w-]*\.(.*)/m;

  # determine aliases, if any
  $alias = $hostn;
  $alias = "azur"  if  $fqhostn =~ /^c1/;
  $alias = "rigel" if  $fqhostn =~ /^c3/;
  $alias = "maia"  if  $fqhostn =~ /^c4/;
  $alias = "naos"  if  $fqhostn =~ /^c5/;
  $alias = "saiph" if  $fqhostn =~ /^c6/;
  $alias = "zeta"  if  $fqhostn =~ /^c7/;
  $alias = "ib"    if  $fqhostn =~ /^ib/;
  $alias = "alef"  if  $fqhostn =~ /^alef/;
  $alias = "spica" if  $fqhostn =~ /^c1h/;
  $alias = "hadar" if  $fqhostn =~ /^c2h/;

  # find our subnet within the ec domain, if any
  # this may be used to determine physical location
  if ($dname =~ /ec\.gc\.ca$/i) {
    # within the ec domain
    ($subnet) = $dname =~ /^(.*)\.ec\.gc\.ca$/i;
    $subnet =~ s/^int\.//i;
  } else {
    # outside the ec domain
    $subnet = $dname;
  }

  return wantarray ? ($hostn, $dname, $alias, $subnet) : $fqhostn;
}

#########################################################################
sub read_diag_deck {
  use strict;
  # Process the argument list supplied to the --decks command line option
  #
  # Elements of this space separated list are all of the form:
  #    deck_in[[=value]:at_deck]
  # where deck_in is the name of a deck to add to the diagnostic
  # string and at_deck defines the insertion point. That is deck_in
  # will be inserted just before at_deck. It is an error for at_deck
  # not to exist in the diagnostic string (as defined by the %diag_deck
  # hash).
  # Existing decks may be deleted by supplying a null insertion point
  #    deck_in:
  # If a deck name is supplied without an insertion point as
  #    deck_in
  # then that deck is inserted near the end of the diagnostic
  # string (just before gppurg).
  #
  # There are a few special values allowed for deck_in that will result
  # in specific actions. These special values are:
  #  show   => the remainder of the deck options will be processed
  #            and a flag ($show_diag_deck) will be set that will
  #            cause the driver to print the diag list and stop
  #  par_def_var=val
  #         => A parameter definition for a variable named "var" with a
  #            value of "val" will be inserted. val must be quoted if it
  #            contains spaces. There are no spaces allowed elsewhere.
  # spstats => Include spectral statistics.
  # trstats => Include tracer statistics.
  # fromfile => Use the decks in the diagnostic string supplied in a file
  #             This will override all other diag_deck options.
  #
  # Deck names are processed in the order presented on the command line
  # so that a prior deck name may be used as an insertion point for a
  # deck that is specified later in the list.
  my $optname  = shift;
  my $deck_list  = shift;

  # Extract all par_def's from the input list and add them to %par_def
  my $var_rex = q@([^\s]*)@;
  my $val_rex = q@('[^']*'|"[^"]*"|[^:\s]*)@;
  # We need to define a temporary hash to avoid clobbering all existing
  # entries in par_def with the following assignment.
  my %new_par_def = $deck_list =~ /par_def_$var_rex=$val_rex/gi;
  foreach (keys %new_par_def) {
    unless ($_) {
      print "User input syntax error ---> par_def_ <---\n";
      print "Context ---> $deck_list <---\n";
      die;
    }
    $new_par_def{$_} =~ s/"([^"]*)"/$1/;
    $new_par_def{$_} =~ s/'([^']*)'/$1/;
    $par_def{$_}=$new_par_def{$_};
  }
  undef %new_par_def;

  # Replace "par_def_var=val[:deck]" with "par_def_var[:deck]" so that the
  # following loop will add the par_def keys to diag_deck.
  my $decks_tmp = $deck_list;
  $decks_tmp =~ s/par_def_$var_rex=$val_rex/par_def_$1/gi;

  # Process the remainder of the list
  foreach  my $dkin (split /\s+/,$decks_tmp) {
    next unless $dkin;
    if ($dkin =~ /=/ or $dkin =~ /^par_def$/) {
      # No equals signs should remain
      print "User input syntax error ---> $dkin <---\n";
      print "Context ---> $deck_list <---\n";
      die;
    }
    # strip any positioning info from the deck name
    my ($opt) = $dkin =~ /(:.*)$/;
    # dkin is the input deck name
    $dkin =~ s/:.*$//;
    unless ($dkin) {
      print "Empty input diagnostic deck name ---> $opt <---\n";
      print "Context ---> $deck_list <---\n";
      die;
    }
    if ($dkin =~ /^par_def_?$/) {
      # No bare par_def allowed
      print "User input syntax error ---> $dkin <---\n";
      print "Context ---> $deck_list <---\n";
      die;
    }
    if ($dkin =~ /^fromfile/) {
      $main::replace_diag_decks = 0;
    }
    if ("$dkin" eq "show") {
      # Set a flag to display decks
      $main::show_diag_deck = 1;
      next;
    }
    if ("$dkin" eq "spstats") {
      # Include spectral statistics
      $diag_deck{spvdqtz}   = 1;
      $diag_deck{spmodinfo} = 1;
      $diag_deck{sphum}     = 1;
      $diag_deck{spvort}    = 1;
      $diag_deck{spdiv}     = 1;
      $diag_deck{sptemp}    = 1;
      $diag_deck{spintr}    = 1;
      $diag_deck{vdelphi}   = 1;
      $diag_deck{sppurg}    = 1;
      next;
    }
    if ("$dkin" eq "trstats") {
      # Include tracer statistics
      $diag_deck{par_def_trac} = 1;
      $diag_deck{xstats}  = 1;
      $diag_deck{constat} = 1;
      $diag_deck{dust}    = 1;
      next;
    }
    # if dkat is an existing deck name then dkin will be
    # inserted just before dkat, otherwise dkin will be
    # inserted just before gppurg
    my $dkat = '';
    ($dkat) = $opt =~ /:(.*)$/ if $opt;
     if ($opt and $opt eq ':') {
      if (defined $diag_deck{$dkin}) {
        # A null insertion point implies removal of this deck if it exists
        $diag_deck{$dkin} = 0;
        next;
      } else {
        # A null insertion point is an error if the deck is not in the hash
        print "Null insertion point supplied for diagnostic deck \"$dkin\"\n";
        print "Context ---> $deck_list <---\n";
        die;
      }
    }
    # Detemine input rank, or set to default
    my $rank = $diag_deck{rank}{gppurg} - 1;
    my $shift = 1;
    if ($dkat) {
      # user requests an insertion point
      if (defined $diag_deck{rank}{$dkat}) {
        $rank = $diag_deck{rank}{$dkat} - 1;
        $shift = 1;
      } else {
        # Invalid insertion point
        print "Invalid insertion point before $dkat";
        print " was requested for diagnostic deck \"$dkin\"\n";
        print "  $dkat is not a recognized diagnostic deck name.\n";
        print "Context ---> $deck_list <---\n";
        die;
      }
    } else {
      # user has not requested an insertion point
      if (defined $diag_deck{rank}{$dkin}) {
        # if this is an existing deck use its rank
        $rank = $diag_deck{rank}{$dkin};
        $shift = 0;
      }
    }
     if ($shift) {
      # increment every $diag_deck{rank} > $rank by 1
      foreach (sort {$diag_deck{rank}{$a} <=> $diag_deck{rank}{$b}}
                  grep !/^(rank|ocean|comment)$/, keys %diag_deck) {
        next if $rank >= $diag_deck{rank}{$_};
        $diag_deck{rank}{$_}++;
      }
      $rank++;
    }
     # Assign hash with current deck name
    $diag_deck{$dkin} = 1;
    $diag_deck{rank}{$dkin} = $rank;
  }
}

#########################################################################
sub read_agcm_config {
  use strict;
  # Process the argument list supplied to the --agcm command line option
  # The argument list may contain 
  #
  my $optname  = shift;
  my $agcm_list = shift;

  # Process the list of AGCM configuration options
  foreach my $cpar (split /\s+/,$agcm_list) {
    next unless $cpar;

    $cpar =~ /=/i and do {
      # If it contains an equals sign assign as var=val
      my ($var,$val) = split(/=/,$cpar,2);
      unless ($var) {
        print "User input syntax error ---> $cpar <---\n";
        print "Context ---> $agcm_list <---\n";
        die;
      }
      if (defined $agcm{$var}) {
        print "*** WARNING *** Previously defined AGCM config parameter $var=";
        print "$agcm{$var} will be overwritten with a new value, $val\n";
      }
      $agcm{$var}=$val;
      next;
    };

    # Version number
    $cpar =~ /^v/i and do {
      # If it starts with the letter "V" (case insensitive) then set version
      my $ver = $cpar;
      # strip everything up to the first digit
      $ver =~ s/^\D*(.*)/$1/;
      unless ($ver) {
        print "Invalid AGCM version number found on command line.\n";
        print "\-\-$optname option: $cpar\n";
        print "Context ---> $agcm_list <---\n";
        die;
      }
      if (defined $agcm{version}) {
        print "*** WARNING *** Previously defined AGCM version, ";
        print "$agcm{version}, will be overwritten with a new value, $ver\n";
      }
      $agcm{version}=$ver;
      next;
    };

    # Horizontal resolution
    $cpar =~ /^T/i and do {
      # If it starts with the letter "T" (case insensitive) then set hres
      my $hres = $cpar;
      # strip everything up to the first digit
      $hres =~ s/^\D*(.*)/$1/;
      unless ($hres) {
        print "Invalid AGCM horizontal resolution found on command line.\n";
        print "\-\-$optname option: $cpar\n";
        print "Context ---> $agcm_list <---\n";
        die;
      }
      unless ($hres =~ /^\d+$/) {
        print "Invalid horizontal resolution, $cpar, supplied to \-\-$optname\n";
        die;
      }
      if (defined $agcm{hres}) {
        print "*** WARNING *** Previously defined AGCM horizontal resolution, ";
        print "$agcm{hres}, will be overwritten with a new value, T$hres\n";
      }
      $agcm{hres}="T$hres";
      next;
    };

    # Number of vertical levels
    $cpar =~ /^L/i and do {
      # If it starts with the letter "L" (case insensitive) then set nlev
      my $nlev = $cpar;
      # strip everything up to the first digit
      $nlev =~ s/^\D*(.*)/$1/;
      unless ($nlev) {
        print "Invalid number of vertical levels found on command line.\n";
        print "\-\-$optname option: $cpar\n";
        print "Context ---> $agcm_list <---\n";
        die;
      }
      unless ($nlev =~ /^\d+$/) {
        print "Invalid number of vertical levels, $cpar, supplied to \-\-$optname\n";
        die;
      }
      if (defined $agcm{levels}) {
        print "*** WARNING *** Previously defined number of vertical levels, ";
        print "$agcm{levels}, will be overwritten with a new value, $nlev\n";
      }
      $agcm{levels}="$nlev";
      next;
    };

    # Add any other keys verbatim and set it true
    $agcm{$cpar}=1;
  }
  #foreach (sort keys %agcm) {print "agcm: $_ = $agcm{$_}\n"}
}

#########################################################################
sub config_agcm {
  use strict;
  # Ensure that members of the agcm hash are current and consistent
  my $OPTS = shift;

  my $verbose = 0;
  $verbose = $OPTS->{VERBOSE} if defined $OPTS->{VERBOSE};

  # Determine a version number for the agcm
  unless ($agcm{version}) {
    # If agcm{version} was not set on the command line then look for the parameter
    # modver on the command line and set agcm{version} from the value of modver
    if ($parin{modver}) {
      # strip everything in $modver up to the first digit
      ($agcm{version}) = $parin{modver} =~ /^\D*(.*)/;
      $agcm{version} =~ s/\s+//;
    }
  }
#  unless ($agcm{version}) {
#    # If agcm{version} was not set on the command line and modver was not supplied
#    # then look through the jobs in the joblist for a definition of modver
#    my @Joblist = @main::Joblist;
#    foreach (@Joblist) {
#      my $job = $_;
#      next unless $job;
#      # strip any options from the end of the keyword
#      $job =~ s/:.*$//;
#      # strip any file names following an "=" sign
#      $job =~ s/=.*$//;
#      if (defined $JOBDEF{job}{$job}) {
#        # grab all values of modver found in this job
#        my @vals = var_find($JOBDEF{job}{$job}, 'modver');
#        foreach (@vals) {
#          # strip everything in $modver up to the first digit
#          ($agcm{version}) = $_ =~ /^\D*(.*)/;
#          $agcm{version} =~ s/\s+//;
#          # Use the first definition found and ignore the rest
#          last;
#        }
#      } else {
#        # If $job is not found in JOBDEF check to see if it is a file
#        my $fname = $job;
#        unless (-s $fname) {
#          ($fname) = on_cccjob_path($job);
#        }
#        # silently ignore any missing or empty files
#        next unless $fname;
#        next unless -s $fname;
#        my $job_file = '';
#        if (open(JOB_FILE, "<$fname")) {
#          while (<JOB_FILE>) {$job_file .= $_};
#          close(JOB_FILE);
#        } else {
#          die "\n*** ERROR *** Cannot open job file $fname.\n Stopped"
#        }
#        # silently ignore empty files
#        next unless $job_file;
#        my @vals = var_find($job_file, 'modver');
#        foreach (@vals) {
#          # strip everything in $modver up to the first digit
#          ($agcm{version}) = $_ =~ /^\D*(.*)/;
#          $agcm{version} =~ s/\s+//;
#          # Use the first definition found and ignore the rest
#          last;
#        }
#      }
#    }
#  }
  unless ($agcm{version}) {
    # If agcm{version} was not set on the command line and modver was not supplied
    # either through the command line or a predefined job then set a default
    $agcm{version} = '16';
    if ($verbose > 10) {
      print "Using default AGCM version: $agcm{version}\n";
    }
  }

  # Define major and minor version numbers
  # major version is the first 2 digits
  # minor version is the remainder, if any
  $agcm{major_version} = substr($agcm{version},0,2);
  if (length($agcm{version}) > 2) {
    $agcm{minor_version} = substr($agcm{version},2);
  } else {
    $agcm{minor_version} = '';
  }

  # Check validity of the version number
  unless ($agcm{version} =~ /^\d+\w(\.\d+)?$/i) {
    print "*** WARNING *** AGCM version number ";
    print "---> $agcm{version} <--- looks suspicious.\n";
    if (-t STDIN && -t STDOUT) {
      # Interactive session
      print "Do you want to continue (n) ? ";
      my $line = <STDIN>;
      die unless $line =~ /^y/i;
    } else {
      # no tty
      die;
    }
  }

  if ($verbose > 3) {print "AGCM version: $agcm{version}\n"};
  if ($verbose > 10) {
    print "  AGCM_major_ver=$agcm{major_version}";
    print "  AGCM_minor_ver=$agcm{minor_version}\n";
  }

  # Determine agcm resolution
  unless ($agcm{hres}) {
    # If agcm{hres} was not set on the command line then look for the parameter
    # lmt on the command line and set agcm{hres} from the value of lmt
    if ($parin{lmt}) {
      # assign hres
      $agcm{hres} = "T$parin{lmt}";
      $agcm{hres} =~ s/\s+//;
    }
    # Also look for hres (this will override any value set by lmt)
    if ($parin{hres}) {
      # assign hres
      my ($hresn) = $parin{hres} =~ /^[tT](.*)/;
      unless ($hresn =~ /^\s*\d+\s*$/) {
        print "Invalid assignment --> hres=$parin{hres} <-- on command line.\n";
        print "  hres should be one of T31, T47, T63,... (case insensitive)\n";
        die "  Stopped";
      }
      $agcm{hres} = "T$hresn";
      $agcm{hres} =~ s/\s+//;
    }
  }
  unless ($agcm{hres}) {
    # If agcm{hres} was not set on the command line and lmt was not supplied
    # then look through the jobs in the joblist for a definition of lmt
    my @Joblist = @main::Joblist;
    foreach (@Joblist) {
      my $job = $_;
      next unless $job;
      # strip any options from the end of the keyword
      $job =~ s/:.*$//;
      # strip any file names following an "=" sign
      $job =~ s/=.*$//;
      if (defined $JOBDEF{job}{$job}) {
        # grab all values of lmt found in this job
        my @vals = var_find($JOBDEF{job}{$job}, 'lmt');
        foreach (@vals) {
          # assign hres
          $agcm{hres} = "T$_";
          $agcm{hres} =~ s/\s+//;
          # Use the first definition found and ignore the rest
          last;
        }
      } else {
        # If $job is not found in JOBDEF check to see if it is a file
        my $fname = $job;
        unless (-s $fname) {
          ($fname) = on_cccjob_path($job);
        }
        # silently ignore any missing or empty files
        next unless $fname;
        next unless -s $fname;
        my $job_file = '';
        if (open(JOB_FILE, "<$fname")) {
          while (<JOB_FILE>) {$job_file .= $_};
          close(JOB_FILE);
        } else {
          die "\n*** ERROR *** Cannot open job file $fname.\n Stopped"
        }
        # silently ignore empty files
        next unless $job_file;
        my @vals = var_find($job_file, 'lmt');
        foreach (@vals) {
          # assign hres
          $agcm{hres} = "T$_";
          $agcm{hres} =~ s/\s+//;
          # Use the first definition found and ignore the rest
          last;
        }
      }
    }
  }
  unless ($agcm{hres}) {
    # If agcm{hres} was not set on the command line and lmt was not supplied
    # either through the command line or a predefined job then set a default
    $agcm{hres} = 'T63';
    if ($verbose > 0) {
      print "Using default AGCM resolution: $agcm{hres}\n";
    }
  }
  # Determine nlat/nlon from spectral truncation wave number
  if ("$agcm{hres}" eq "T31") {
    $agcm{nlatd}=48;
    $agcm{nlond}=96;
    $agcm{nlat}=32;
    $agcm{nlon}=64;
    $agcm{delt}=1200 unless $agcm{delt};
  } elsif ("$agcm{hres}" eq "T47") {
    $agcm{nlatd}=72;
    $agcm{nlond}=144;
    $agcm{nlat}=48;
    $agcm{nlon}=96;
    $agcm{delt}=1200 unless $agcm{delt};
  } elsif ("$agcm{hres}" eq "T63") {
    $agcm{nlatd}=96;
    $agcm{nlond}=192;
    $agcm{nlat}=64;
    $agcm{nlon}=128;
    $agcm{delt}=900 unless $agcm{delt};
  }
  if ($verbose > 3) {print "AGCM resolution: $agcm{hres}\n"};

  if ($verbose > 1) {
    foreach (sort keys %agcm) {
      print "AGCM config: $_ = $agcm{$_}\n";
    }
  }
}
