#!/usr/bin/env perl 
#-----------------------------------------------------------------------------------------------
#
# create_newcase
#
# This utility allows the users to specify configuration
# options via a commandline interface.
#
#-----------------------------------------------------------------------------------------------

use strict;
use Cwd;
use English;
use Getopt::Long;
use IO::File;
use IO::Handle;


#-----------------------------------------------------------------------------------------------
# Setting autoflush (an IO::Handle method) on STDOUT helps in debugging.  It forces the test
# descriptions to be printed to STDOUT before the error messages start.

*STDOUT->autoflush();                  

#-----------------------------------------------------------------------------------------------
# Set the directory that contains the CESM configuration scripts.  If the create_newcase command was
# issued using a relative or absolute path, that path is in $ProgDir.  Otherwise assume the
# command was issued from the current working directory.

(my $ProgName = $0) =~ s!(.*)/!!;      # name of this script
my $ProgDir = $1;                      # name of directory containing this script -- may be a
                                       # relative or absolute path, or null if the script is in
                                       # the user's PATH
my $cwd = getcwd();                    # current working directory
my $cfgdir;                            # absolute pathname of directory that contains this script
if ($ProgDir) { 
    $cfgdir = absolute_path($ProgDir);
} else {
    $cfgdir = $cwd;
}

my $ccsmroot = absolute_path("$cfgdir/..");
(-d "$ccsmroot")  or  die <<"EOF";
** Cannot find ccsmroot directory \"$ccsmroot\" **
EOF


#-----------------------------------------------------------------------------------------------
if ($#ARGV == -1) {
    usage();
}
my $machdir="$cfgdir/ccsm_utils/Machines";

#-----------------------------------------------------------------------------------------------

sub usage {
    die <<EOF;
SYNOPSIS
     create_newcase [options]
OPTIONS
     User supplied values are denoted in angle brackets (<>).  Any value that contains
     white-space must be quoted.  Long option names may be supplied with either single
     or double leading dashes.  A consequence of this is that single letter options may
     NOT be bundled.

     -case <name>         Specifies the case name (required).
     -compset <name>      Specify a CESM compset (required).
     -res <name>          Specify a CESM grid resolution (required).
     -mach <name>         Specify a CESM machine (required).
     -compiler <name>     Specify a compiler for the target machine (optional)
                          default: default compiler for the target machine
     -mpilib <name>       Specify a mpi library for the target machine (optional)
                          default: default mpi library for the target machine
                          allowed: openmpi, mpich, ibm, mpi-serial, etc
                          redundant with _M confopts setting
     -mach_dir <path>     Specify the locations of the Machines directory (optional).
                          default: $machdir
     -confopts <value>    Specify some addition configuration options (optional)
                          _AOA = aoflux on atm grid
                          _AOE = aoflux on exch grid
                          _CG  = gregorian calendar
                          _D   = debug
                          _E   = esmf interfaces
                          _IOP*= PnetCDF IO test where * is A(atm), C(cpl), G(glc) , I(ice), L(clm), O(ocn) or blank (all components)
                          _L*  = set run length y, m, d, h, s, n(nsteps) plus integer (ie _Lm6 for 6 months) 
                          _M*  = set the mpilib where * is default, mpi-serial, mpich, etc
                          _N*  = set NINST_ env value to * where * is an integer
                          _P*  = set pecount to specific values include T,S,M,L,X,1,1x1,16,16x1,4x4, etc
                          _R*  = PTS_MODE test case, valid values are LA, LB, OA, OB
     -pecount <name>      Value of S,M,L,X1,X2 (optional). 
                          default: M, partially redundant with confopts _P
     -pes_file <name>     Full pathname of pes file to use (will overwrite default settings) (optional).
                          See sample_pes_file.xml for an example.
     -compset_file <name> Full pathname of compset file to use. (optional)
                          See sample_compset_file.xml for an example.
     -grid_file <name>    Full pathname of grid file to use (optional)
                          See sample_grid_file.xml for an example.
                          Note that compset components must support the new grid.
     -help [or -h]        Print usage to STDOUT (optional).
     -list                Only list valid values for compset, grid settings and machines (optional).
     -testlist            List valid values for tests [normally only invoked by create_test] (optional).
     -silent [or -s]      Turns on silent mode - only fatal messages issued (optional).
     -verbose [or -v]     Turn on verbose echoing of settings made by create_newcase (optional).
     -xmlmode <name>      Sets format of xml files; normal or expert (optional). (default is normal) 
     -nowarning           Turns off checking of the known_problems repository. (default is on)

     The following two arguments turn on single point mode. If one is given -- both MUST be given.

     -pts_lat <value>     Latitude of single point to operate on (optional)
     -pts_lon <value>     Longitude of single point to operate on (optional)

EXAMPLES

  ./create_newcase -case mycase1 -res f19_g16 -compset B1850CN -mach bluefire 
  ./create_newcase -case mycase2 -res f19_g16 -compset B1850CN -mach bluefire -confopts _D_P16
  ./create_newcase -case mycase3 -res f19_g16 -compset B1850CN -mach bluefire -pts_lon -160 -pts_lat 40
  ./create_newcase -case mycase4 -res f19_g16 -compset MYCOMP -compset_file mycompset_file -mach bluefire 
  ./create_newcase -case mycase5 -res f19_g16 -compset B1850CN -mach bluefire -confopts _CG_E -pes_file mypes_file
/
EOF
}

#-----------------------------------------------------------------------------------------------
# Save commandline
my $commandline = "create_newcase @ARGV";

#-----------------------------------------------------------------------------------------------
# Parse command-line options.
my %opts = (
              pts_lat => undef,
              pts_lon => undef,
              mach_dir => $machdir,
	    );
GetOptions(
    "case=s"                    => \$opts{'case'},
    "compset=s"                 => \$opts{'compset'},
    "confopts=s"                => \$opts{'confopts'},
    "compiler=s"                => \$opts{'compiler'},  
    "mpilib=s"                  => \$opts{'mpilib'},  
    "res=s"                     => \$opts{'res'},
    "h|help"                    => \$opts{'help'},
    "list"                      => \$opts{'list'},
    "mach=s"                    => \$opts{'mach'},
    "mach_dir=s"                => \$opts{'mach_dir'},
    "pecount=s"                 => \$opts{'pecount'},
    "pes_file=s"                => \$opts{'pes_file'}, 
    "compset_file=s"            => \$opts{'compset_file'},
    "grid_file=s"               => \$opts{'grid_file'},
    "s|silent"                  => \$opts{'silent'},
    "testname=s"                => \$opts{'testname'},
    "testlist"                  => \$opts{'testlist'},
    "v|verbose"                 => \$opts{'verbose'},
    "xmlmode=s"                 => \$opts{'xmlmode'},
    "pts_lat=s"                 => \$opts{'pts_lat'},
    "pts_lon=s"                 => \$opts{'pts_lon'},
    "nowarning"			=> \$opts{'nowarning'},
)  or usage();

# Give usage message.
usage() if $opts{'help'};


# Check for unparsed argumentss
if (@ARGV) {
    print "ERROR: unrecognized arguments: @ARGV\n";
    usage();
}

# Check that points mode is set correctly
if ( defined($opts{'pts_lat'}) && ! defined(($opts{'pts_lon'}) ) ) {
    print "ERROR: if pts_lat set -- pts_lon must also be set:  @ARGV\n";
    usage();
}
if ( defined($opts{'pts_lon'}) && ! defined(($opts{'pts_lat'}) ) ) {
    print "ERROR: if pts_lon set -- pts_lat must also be set:  @ARGV\n";
    usage();
}

# Check for manditory case input if not just listing valid values

my $case;
my $caseroot;
my $compset;
my $confopts;
my $grid;
my $mach;
my $testname;
my $pecount;
my $userdir;
my $xmlmode;
my $compiler;
my $mpilib;
if (!$opts{'list'} && !$opts{'testlist'}) {
    # Check for manditory case input
    if ($opts{'case'}) {
	$case = $opts{'case'};
    } else {
	die "ERROR: create_newcase must include the input argument, -case \n";
    }
    $caseroot = absolute_path("$case");
    if (-d $caseroot) {
	die "Caseroot directory $caseroot already exists \n";
    }
    my @dirs = split "/", $caseroot, -1;  # The -1 prevents split from stripping trailing nulls
    my $num = scalar @dirs;
    $case = $dirs[$num-1];

    # Check for manditory compset input
    if ($opts{'compset'}) {
	$compset = $opts{'compset'};
    } else {
	die "ERROR: create_newcase must include the input argument, -compset\n";
    }
    
    # Check for manditory grid input
    if ($opts{'res'}) {
	$grid = $opts{'res'};
    } else {
	die "ERROR: create_newcase must include the input argument, -res\n";
    }

    # Check for manditory machine input
    if ($opts{'mach'}) {
	$mach = $opts{'mach'};
    } else {
	die "ERROR: create_newcase must include the input argument, -mach \n";
    }

    # Check if machine compiler option is given
    if ($opts{'compiler'}) {
	$compiler = $opts{'compiler'};
    }

    # Check if machine mpilib option is given
    $mpilib = 'unset';
    if ($opts{'mpilib'}) {
       $mpilib = $opts{'mpilib'};
    }

    # Check for pecount setting
    $pecount = 'M';
    if ($opts{'pecount'}) {
	$pecount = $opts{'pecount'};
    }

    # Check for xmlmode setting
    $xmlmode = 'normal';
    if ($opts{'xmlmode'}) {
	$xmlmode = $opts{'xmlmode'};
    }

}
# Set machdir to default or value sent in on command line
$machdir=$opts{'mach_dir'};

# Define 3 print levels:
# 0 - only issue fatal error messages
# 1 - only informs what files are created (default)
# 2 - verbose
my $print = 1;
if ($opts{'silent'})  { $print = 0; }
if ($opts{'verbose'}) { $print = 2; }
my $eol = "\n";

my %cfg = ();           # build configuration

#
# Make sure we can find the Machines directory
# 

(-d "$machdir")   or  die <<"EOF";
** Cannot find ccsm Machines directory \"$machdir\" **
EOF


#-----------------------------------------------------------------------------------------------
# Make sure we can find required perl modules and configuration files.
# Look for them in the directory that contains the create_newcase script.

# Check for the configuration definition file.
my $config_def_file = "config_definition.xml";
(-f "$cfgdir/ccsm_utils/Case.template/$config_def_file")  or  die <<"EOF";
** Cannot find configuration definition file \"$config_def_file\" in directory 
    \"$cfgdir/ccsm_utils/Case.template/$config_def_file\" **
EOF

# Grid definition file.
my $grid_file = "";
if (defined $opts{'grid_file'}){
  $grid_file = $opts{'grid_file'};
  (-f $grid_file)  or  die <<"EOF";
  ** Cannot find user specified grid definition file \"$grid_file\"  **
EOF
} else {
  $grid_file = "$cfgdir/ccsm_utils/Case.template/config_grid.xml";
  (-f $grid_file)  or  die <<"EOF";
  ** Cannot find default grid definition file \"$grid_file\"  **
EOF
}

# Compset definition file.
my $compset_file="";
if (defined $opts{'compset_file'}){
  $compset_file = $opts{'compset_file'};
  (-f $compset_file)  or  die <<"EOF";
  ** Cannot find user specified compset parameters file \"$compset_file\"  **
EOF
} else {
  $compset_file = "$cfgdir/ccsm_utils/Case.template/config_compsets.xml";
  (-f $compset_file)  or  die <<"EOF";
  ** Cannot find default compset parameters file \"$compset_file\"  **
EOF
}


# Machines definition file.
my $machine_file = 'config_machines.xml';
(-f "$machdir/$machine_file")  or  die <<"EOF";
** Cannot find machine parameters file \"$machine_file\" in directory 
    \"$machdir\" **
EOF

# Machines definition file.
my $compiler_file = 'config_compilers.xml';
(-f "$machdir/$compiler_file")  or  die <<"EOF";
** Cannot find compiler parameters file \"$compiler_file\" in directory 
    \"$machdir\" **
EOF

# Tests file
my $tests_file = 'config_tests.xml';
(-f "$cfgdir/ccsm_utils/Testcases/$tests_file")  or  die <<"EOF";
** Cannot find test parameters file \"$tests_file\" in directory 
    \"$cfgdir/ccsm_utils/Testcases\" **
EOF

# The XML::Lite module is required to parse the XML configuration files.
(-f "$cfgdir/ccsm_utils/Tools/perl5lib/XML/Lite.pm")  or  die <<"EOF";
** Cannot find perl module \"XML/Lite.pm\" in directory 
    \"$cfgdir/ccsm_utils/Tools/perl5lib\" **
EOF

# The ConfigCase module provides utilities to store and manipulate the configuration.
(-f "$cfgdir/ccsm_utils/Case.template/ConfigCase.pm")  or  die <<"EOF";
** Cannot find perl module \"ConfigCase.pm\" in directory 
    \"$cfgdir/ccsm_utils/Case.template\" **
EOF

if ($print>=2) { print "Setting configuration directory to $cfgdir$eol"; }


#-----------------------------------------------------------------------------------------------
# Add $cfgdir/perl5lib to the list of paths that Perl searches for modules
my @dirs = ($cfgdir, "$cfgdir/ccsm_utils/Case.template", "$cfgdir/ccsm_utils/Tools", "$cfgdir/ccsm_utils/Tools/perl5lib");
unshift @INC, @dirs;
require XML::Lite;
require ConfigCase;

#-----------------------------------------------------------------------------------------------
# If just listing valid values then exit after completion of lists
if ($opts{'testlist'}) {
    print_tests("$cfgdir/ccsm_utils/Testcases/config_tests.xml");
}
if ($opts{'list'}) {
    print_grids("$cfgdir/ccsm_utils/Case.template/config_grid.xml");
    print_compsets($compset_file);
    ConfigCase::print_machines("$machdir/$machine_file");
	&check_known_problems();
    if ($print>=2) { print "finished listing valid values, now exiting $eol"; }
    exit;
}

#-----------------------------------------------------------------------------------------------
# jshollen: Check the repository for known problems that match the tag name.  If we find a matching file, 
# print the known problems file, and exit.  We want this done before the case root is made.  
# Broken, commented out for now. 
#-----------------------------------------------------------------------------------------------
if(! defined ($opts{'nowarning'}))
{
	&check_known_problems; 
}

#-----------------------------------------------------------------------------------------------
# Create new config object if not just listing valid values
my $cfg_ref = ConfigCase->new("$cfgdir/ccsm_utils/Case.template/$config_def_file"); 
#if ($print>=2) { print "A new config reference object was created$eol";}

#-----------------------------------------------------------------------------------------------
# (1) Grid parameters
my ($grid_longname, $grid_shortname) = set_grid($grid_file, $grid, $cfg_ref);

if ($print>=2) { print "Horizontal grid specifier: $grid.$eol"; }

#-----------------------------------------------------------------------------------------------
# (2) Compset parameters
my $sysmod = "mkdir -p $caseroot"; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";

my $file = "$caseroot/README.case";
my $fh = IO::File->new($file, '>' ) or die "can't open file: $file\n";
print $fh "$commandline\n\n\n";
$fh->close;

my ($compset_longname, $compset_shortname) = set_compset($compset_file, $compset, 
							 $grid_longname, $grid_shortname, 
							 $cfg_ref, $caseroot);
if ($print>=2) { print "Compset specifier: $compset.$eol"; }

# print README/disclaimer file in scripts dir
my $sysmod = "cp $cfgdir/README $caseroot/README.science_support";
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";

#-----------------------------------------------------------------------------------------------
# (3) Check if grid is valid for given compset
check_grid("$cfgdir/ccsm_utils/Case.template/config_grid.xml", $grid, 
	   $compset_longname);

if ($print>=2) { print "Grid is valid for input compset. $eol"; }

#-----------------------------------------------------------------------------------------------
# (4) Machine parameters

if ($mach =~ /(.*)_(.*)/){
    $mach = $1;
    $compiler = $2 unless defined($compiler);
    $cfg_ref->set_machine("$machdir/$machine_file", $mach, $print);
} else {
    $cfg_ref->set_machine("$machdir/$machine_file", $mach, $print);
}
$cfg_ref->set('CCSM_MACHDIR', "$machdir");

# Check that compiler request for target machine matches a supported value
# Or set default compiler - if not provided compiler request

my $compilers;
if ($mach =~ /userdefined/){
    $cfg_ref->set('COMPILER', "USERDEFINED_required_build");
} else { 
    $compilers = $cfg_ref->get('COMPILERS');
    my @compilers = split ",", $compilers, -1;
    if ($compiler) {
	if (! ($mach =~ "generic")){
	    my $found = 0;
	    foreach my $comp (@compilers) {
		if ($compiler eq $comp) {
		    $found = 1;
		}
	    }
	    if (!$found) {
		my $sysmod = "rm -rf $caseroot";
		system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
		die "ERROR: compiler setting of $compiler does not match supported values of $compilers \n";
	    }
	}
	$cfg_ref->set('COMPILER', "$compiler");
	if ($print>=2) { print "Machine compiler specifier: $compiler.$eol"; }
    } else {
	$compiler = $compilers[0];   
	$cfg_ref->set('COMPILER', "$compiler");
	if ($print>=2) { print "Machine compiler specifier: $compiler.$eol"; }
    }
}

if ($print>=2) { print "Machine specifier: $mach.$eol"; }

#-----------------------------------------------------------------------------------------------
# (5) Testname parameters
if (defined $opts{'testname'}) {
    $testname = $opts{'testname'};
    set_test("$cfgdir/ccsm_utils/Testcases/config_tests.xml", $testname, $cfg_ref);
}

#-----------------------------------------------------------------------------------------------
# (6) Configure Options
$userdir = 'unset';
if (defined $opts{'confopts'}) {
    $confopts = $opts{'confopts'};
    set_confopts($confopts, $cfg_ref);
}

#-----------------------------------------------------------------------------------------------
# (7) Points mode settings on command line
#-----------------------------------------------------------------------------------------------

if ( defined($opts{'pts_lat'}) && defined($opts{'pts_lon'}) ) {
   $cfg_ref->set('PTS_MODE', "TRUE" );
   $cfg_ref->set('PTS_LAT',  $opts{'pts_lat'} );
   $cfg_ref->set('PTS_LON',  $opts{'pts_lon'} );
   $cfg_ref->set('ATM_NX', 0);
   $cfg_ref->set('ATM_NY', 0);
   $cfg_ref->set('LND_NX', 0);
   $cfg_ref->set('LND_NY', 0);
   $cfg_ref->set('ICE_NX', 0);
   $cfg_ref->set('ICE_NY', 0);
   $cfg_ref->set('OCN_NX', 0);
   $cfg_ref->set('OCN_NY', 0);
   $cfg_ref->set('GLC_NX', 0);
   $cfg_ref->set('GLC_NY', 0);
   $cfg_ref->set('ROF_NX', 0);
   $cfg_ref->set('ROF_NY', 0);
}

#-----------------------------------------------------------------------------------------------
# (8) Determine pes for machine
#-----------------------------------------------------------------------------------------------

# Always match on the full grid name and input compset name

my $pmode  = $cfg_ref->get('PTS_MODE'); 
if ( $pmode eq "TRUE" ) {
   $pecount = 1;
}

##print "pes_match $grid_match $compset_match $mach $pecount \n";

my %decomp = (NTASKS_ATM=>16, NTHRDS_ATM=>1, ROOTPE_ATM=>0, NINST_ATM=>1,
	      NTASKS_LND=>16, NTHRDS_LND=>1, ROOTPE_LND=>0, NINST_LND=>1,
	      NTASKS_ICE=>16, NTHRDS_ICE=>1, ROOTPE_ICE=>0, NINST_ICE=>1,
	      NTASKS_OCN=>16, NTHRDS_OCN=>1, ROOTPE_OCN=>0, NINST_OCN=>1,
	      NTASKS_CPL=>16, NTHRDS_CPL=>1, ROOTPE_CPL=>0,
	      NTASKS_GLC=>16, NTHRDS_GLC=>1, ROOTPE_GLC=>0, NINST_GLC=>1,
	      NTASKS_ROF=>16, NTHRDS_ROF=>1, ROOTPE_ROF=>0, NINST_ROF=>1,
              PIO_NUMTASKS=>-1, PIO_STRIDE=>-1, PIO_TYPENAME=>'netcdf',PIO_ROOT=>1,PIO_DEBUG_LEVEL=>0,
              ATM_PIO_NUMTASKS=>-99, ATM_PIO_STRIDE=>-99, ATM_PIO_TYPENAME=>'nothing',ATM_PIO_ROOT=>-99,
              LND_PIO_NUMTASKS=>-99, LND_PIO_STRIDE=>-99, LND_PIO_TYPENAME=>'nothing',LND_PIO_ROOT=>-99,
              ICE_PIO_NUMTASKS=>-99, ICE_PIO_STRIDE=>-99, ICE_PIO_TYPENAME=>'nothing',ICE_PIO_ROOT=>-99,
              OCN_PIO_NUMTASKS=>-99, OCN_PIO_STRIDE=>-99, OCN_PIO_TYPENAME=>'nothing',OCN_PIO_ROOT=>-99,
              CPL_PIO_NUMTASKS=>-99, CPL_PIO_STRIDE=>-99, CPL_PIO_TYPENAME=>'nothing',CPL_PIO_ROOT=>-99,
              GLC_PIO_NUMTASKS=>-99, GLC_PIO_STRIDE=>-99, GLC_PIO_TYPENAME=>'nothing',GLC_PIO_ROOT=>-99,
              ROF_PIO_NUMTASKS=>-99, ROF_PIO_STRIDE=>-99, ROF_PIO_TYPENAME=>'nothing',ROF_PIO_ROOT=>-99,
              PES_LEVEL=>0);

# Reset the pes if a pes file is specified
if (defined $opts{'pes_file'}) {
    my $pes_file = $opts{'pes_file'};
    (-f "$pes_file")  or  die "** Cannot find pes_file \"$pes_file\" ***\n";
    $cfg_ref->reset_setup("$pes_file");    
} else {
    
    if ($pecount =~ m!^([0-9]+)$!) {
      my $ntasks = $1;
      my $nthrds = 1;
      $decomp{NTASKS_ATM} = $ntasks; $decomp{NTHRDS_ATM} = $nthrds;
      $decomp{NTASKS_LND} = $ntasks; $decomp{NTHRDS_LND} = $nthrds;
      $decomp{NTASKS_OCN} = $ntasks; $decomp{NTHRDS_OCN} = $nthrds;
      $decomp{NTASKS_ICE} = $ntasks; $decomp{NTHRDS_ICE} = $nthrds;
      $decomp{NTASKS_GLC} = $ntasks; $decomp{NTHRDS_GLC} = $nthrds;
      $decomp{NTASKS_ROF} = $ntasks; $decomp{NTHRDS_ROF} = $nthrds;
      $decomp{NTASKS_CPL} = $ntasks; $decomp{NTHRDS_CPL} = $nthrds;
    } elsif ($pecount =~ m!^([0-9]+)x([0-9]+)$!) {
      my $ntasks = $1;
      my $nthrds = $2;
      $decomp{NTASKS_ATM} = $ntasks; $decomp{NTHRDS_ATM} = $nthrds;
      $decomp{NTASKS_LND} = $ntasks; $decomp{NTHRDS_LND} = $nthrds;
      $decomp{NTASKS_OCN} = $ntasks; $decomp{NTHRDS_OCN} = $nthrds;
      $decomp{NTASKS_ICE} = $ntasks; $decomp{NTHRDS_ICE} = $nthrds;
      $decomp{NTASKS_GLC} = $ntasks; $decomp{NTHRDS_GLC} = $nthrds;
      $decomp{NTASKS_ROF} = $ntasks; $decomp{NTHRDS_ROF} = $nthrds;
      $decomp{NTASKS_CPL} = $ntasks; $decomp{NTHRDS_CPL} = $nthrds;
    } else {
      $cfg_ref->set_pes("$machdir/config_pes.xml", \%decomp, $pecount, $print);
    }

    if ($decomp{NTASKS_ATM} == 1 && $decomp{NTASKS_LND} == 1 &&
        $decomp{NTASKS_OCN} == 1 && $decomp{NTASKS_ICE} == 1 &&
        $decomp{NTASKS_ROF} == 1 && $decomp{NTASKS_GLC} == 1 &&
        $decomp{NTASKS_CPL} == 1 && $mpilib =~ 'unset') { $mpilib = "mpi-serial"; }

    $cfg_ref->set('NTASKS_ATM', $decomp{'NTASKS_ATM'});
    $cfg_ref->set('NTASKS_LND', $decomp{'NTASKS_LND'});
    $cfg_ref->set('NTASKS_ICE', $decomp{'NTASKS_ICE'});
    $cfg_ref->set('NTASKS_OCN', $decomp{'NTASKS_OCN'});
    $cfg_ref->set('NTASKS_CPL', $decomp{'NTASKS_CPL'});
    $cfg_ref->set('NTASKS_GLC', $decomp{'NTASKS_GLC'});
    $cfg_ref->set('NTASKS_ROF', $decomp{'NTASKS_ROF'});
    
    $cfg_ref->set('NTHRDS_ATM', $decomp{'NTHRDS_ATM'});
    $cfg_ref->set('NTHRDS_LND', $decomp{'NTHRDS_LND'});
    $cfg_ref->set('NTHRDS_ICE', $decomp{'NTHRDS_ICE'});
    $cfg_ref->set('NTHRDS_OCN', $decomp{'NTHRDS_OCN'});
    $cfg_ref->set('NTHRDS_CPL', $decomp{'NTHRDS_CPL'});
    $cfg_ref->set('NTHRDS_GLC', $decomp{'NTHRDS_GLC'});
    $cfg_ref->set('NTHRDS_ROF', $decomp{'NTHRDS_ROF'});
   
    $cfg_ref->set('ROOTPE_ATM', $decomp{'ROOTPE_ATM'});
    $cfg_ref->set('ROOTPE_LND', $decomp{'ROOTPE_LND'});
    $cfg_ref->set('ROOTPE_ICE', $decomp{'ROOTPE_ICE'});
    $cfg_ref->set('ROOTPE_OCN', $decomp{'ROOTPE_OCN'});
    $cfg_ref->set('ROOTPE_CPL', $decomp{'ROOTPE_CPL'});
    $cfg_ref->set('ROOTPE_GLC', $decomp{'ROOTPE_GLC'});
    $cfg_ref->set('ROOTPE_ROF', $decomp{'ROOTPE_ROF'});

    # tcraig, assume NINST is not set in config_pes.xml so it can be
    # set by _N, but a pes_file could override above
    # $cfg_ref->set('NINST_ATM', $decomp{'NINST_ATM'});
    # $cfg_ref->set('NINST_LND', $decomp{'NINST_LND'});
    # $cfg_ref->set('NINST_ICE', $decomp{'NINST_ICE'});
    # $cfg_ref->set('NINST_OCN', $decomp{'NINST_OCN'});
    # $cfg_ref->set('NINST_GLC', $decomp{'NINST_GLC'});
    # $cfg_ref->set('NINST_ROF', $decomp{'NINST_ROF'});

    $cfg_ref->set('PIO_TYPENAME'    , $decomp{'PIO_TYPENAME'});
    $cfg_ref->set('ATM_PIO_TYPENAME', $decomp{'ATM_PIO_TYPENAME'});
    $cfg_ref->set('LND_PIO_TYPENAME', $decomp{'LND_PIO_TYPENAME'});
    $cfg_ref->set('OCN_PIO_TYPENAME', $decomp{'OCN_PIO_TYPENAME'});
    $cfg_ref->set('ICE_PIO_TYPENAME', $decomp{'ICE_PIO_TYPENAME'});
    $cfg_ref->set('CPL_PIO_TYPENAME', $decomp{'CPL_PIO_TYPENAME'});
    $cfg_ref->set('GLC_PIO_TYPENAME', $decomp{'GLC_PIO_TYPENAME'});
    $cfg_ref->set('ROF_PIO_TYPENAME', $decomp{'ROF_PIO_TYPENAME'});

    $cfg_ref->set('PIO_NUMTASKS',     $decomp{'PIO_NUMTASKS'});
    $cfg_ref->set('PIO_STRIDE',       $decomp{'PIO_STRIDE'});
    $cfg_ref->set('PIO_ROOT',         $decomp{'PIO_ROOT'});
    $cfg_ref->set('PIO_DEBUG_LEVEL',  $decomp{'PIO_DEBUG_LEVEL'});
    
    $cfg_ref->set('ATM_PIO_NUMTASKS', $decomp{'ATM_PIO_NUMTASKS'});
    $cfg_ref->set('ATM_PIO_STRIDE',   $decomp{'ATM_PIO_STRIDE'});
    $cfg_ref->set('ATM_PIO_ROOT',     $decomp{'ATM_PIO_ROOT'});

    $cfg_ref->set('LND_PIO_NUMTASKS', $decomp{'LND_PIO_NUMTASKS'});
    $cfg_ref->set('LND_PIO_STRIDE',   $decomp{'LND_PIO_STRIDE'});
    $cfg_ref->set('LND_PIO_ROOT',     $decomp{'LND_PIO_ROOT'});
    
    $cfg_ref->set('OCN_PIO_NUMTASKS', $decomp{'OCN_PIO_NUMTASKS'});
    $cfg_ref->set('OCN_PIO_STRIDE',   $decomp{'OCN_PIO_STRIDE'});
    $cfg_ref->set('OCN_PIO_ROOT',     $decomp{'OCN_PIO_ROOT'});
    
    $cfg_ref->set('ICE_PIO_NUMTASKS', $decomp{'ICE_PIO_NUMTASKS'});
    $cfg_ref->set('ICE_PIO_STRIDE',   $decomp{'ICE_PIO_STRIDE'});
    $cfg_ref->set('ICE_PIO_ROOT',     $decomp{'ICE_PIO_ROOT'});
    
    $cfg_ref->set('CPL_PIO_NUMTASKS', $decomp{'CPL_PIO_NUMTASKS'});
    $cfg_ref->set('CPL_PIO_STRIDE',   $decomp{'CPL_PIO_STRIDE'});
    $cfg_ref->set('CPL_PIO_ROOT',     $decomp{'CPL_PIO_ROOT'});
    
    $cfg_ref->set('GLC_PIO_NUMTASKS', $decomp{'GLC_PIO_NUMTASKS'});
    $cfg_ref->set('GLC_PIO_STRIDE',   $decomp{'GLC_PIO_STRIDE'});
    $cfg_ref->set('GLC_PIO_ROOT',     $decomp{'GLC_PIO_ROOT'});
    
    $cfg_ref->set('ROF_PIO_NUMTASKS', $decomp{'ROF_PIO_NUMTASKS'});
    $cfg_ref->set('ROF_PIO_STRIDE',   $decomp{'ROF_PIO_STRIDE'});  
    $cfg_ref->set('ROF_PIO_ROOT',     $decomp{'ROF_PIO_ROOT'});    
    
    $cfg_ref->set('PES_LEVEL' , $decomp{'PES_LEVEL'});
}

# mpilib can be set by -mpilib, confopts, or automatically
# precedent is confopts, mpilib, automatic, default

my $mpilibs = $cfg_ref->get('MPILIBS');
my @mpilibs = split ",", $mpilibs, -1;

if ( $mpilib =~ 'default' || $mpilib =~ m/^\s*$/ ) {
   $mpilib = $mpilibs[0];
}

if (! ($mpilib =~ 'unset')) {
    # check that mpilib request for target machine matches a supported value
#    if ($mach =~ "userdefined"){
#       unless (defined($ENV{MPI_PATH})){
#	    print "\nSet environment variable MPI_PATH prior to building case\n\n";
#	}
#    }else{
	my $found = 0;
	foreach my $mpi (@mpilibs) {
	    if ($mpilib eq $mpi) {
		$found = 1;
	    }
	}
	if (!$found) {
	    my $sysmod = "rm -rf $caseroot";
	    system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
	    die "ERROR: mpilib setting of $mpilib does not match supported values of $mpilibs \n";
	}
#    }
} else {
    if ($mach =~ /userdefined/){
       $mpilib = "USERDEFINED_required_build";
    }else{
       $mpilib = $mpilibs[0];
    }
}    
$cfg_ref->set('MPILIB', "$mpilib");
if ($print>=2) { print "Machine mpilib specifier: $mpilib.$eol"; }
  
# resolve the dollar referenced values for the pe stuff
# allow up to 4 depths then stop
# so, check the var value, if it starts with dollar, remove
#     the dollar and check the next value.  continue until a
#     non dollar value is found up to max depths.

my @xvars = qw(NTASKS_ATM NTHRDS_ATM ROOTPE_ATM NINST_ATM
	       NTASKS_LND NTHRDS_LND ROOTPE_LND NINST_LND
	       NTASKS_ICE NTHRDS_ICE ROOTPE_ICE NINST_ICE
	       NTASKS_OCN NTHRDS_OCN ROOTPE_OCN NINST_OCN
	       NTASKS_GLC NTHRDS_GLC ROOTPE_GLC NINST_GLC
	       NTASKS_ROF NTHRDS_ROF ROOTPE_ROF NINST_ROF
	       NTASKS_CPL NTHRDS_CPL ROOTPE_CPL
               ATM_PIO_NUMTASKS ATM_PIO_STRIDE ATM_PIO_TYPENAME ATM_PIO_ROOT
               LND_PIO_NUMTASKS LND_PIO_STRIDE LND_PIO_TYPENAME LND_PIO_ROOT
               ICE_PIO_NUMTASKS ICE_PIO_STRIDE ICE_PIO_TYPENAME ICE_PIO_ROOT
               OCN_PIO_NUMTASKS OCN_PIO_STRIDE OCN_PIO_TYPENAME OCN_PIO_ROOT
               CPL_PIO_NUMTASKS CPL_PIO_STRIDE CPL_PIO_TYPENAME CPL_PIO_ROOT
               GLC_PIO_NUMTASKS GLC_PIO_STRIDE GLC_PIO_TYPENAME GLC_PIO_ROOT
               ROF_PIO_NUMTASKS ROF_PIO_STRIDE ROF_PIO_TYPENAME ROF_PIO_ROOT);
my $xvarf;
my $xvar1;
my $xvar2;
my $xvar3;
my $xvar4;
foreach my $xvar ( @xvars ) {
    $xvar1 = $cfg_ref->get("$xvar");
    $xvar2 = "";
    $xvar3 = "";
    $xvar4 = "";
    $xvarf = $xvar1;
    if ($xvarf =~ m/^\$.+$/) {
	$xvar2 = $xvarf;
        $xvar2 =~ s/^\$(.+$)/$1/ ;
	$xvar2 = $cfg_ref->get("$xvar2");
	$xvarf = $xvar2;
        if ($xvarf =~ m/^\$.+$/) {
   	   $xvar3 = $xvarf;
           $xvar3 =~ s/^\$(.+$)/$1/;
           $xvar3 = $cfg_ref->get("$xvar3");
	   $xvarf = $xvar3;
           if ($xvarf =~ m/^\$.+$/) {
      	      $xvar4 = $xvarf;
              $xvar4 =~ s/^\$(.+$)/$1/ ;
              $xvar4 = $cfg_ref->get("$xvar4");
	      $xvarf = $xvar4;
              if ($xvar4 =~ m/^\$.+$/) {
		  die "xvar recursive search failed $xvar $xvar1 $xvar2 $xvar3 $xvar4 \n";
              }
           }
        }
    }
   $cfg_ref->set("$xvar", "$xvarf");
}

		
#-----------------------------------------------------------------------------------------------
# (9) Set key xml variables 
#-----------------------------------------------------------------------------------------------

my $ccsmuser = "$ENV{'LOGNAME'}";
$cfg_ref->set('CCSMUSER', "$ccsmuser");
$cfg_ref->set('CASEROOT', "$caseroot");
$cfg_ref->set('CASE'    , "$case");
$cfg_ref->set('CCSMROOT', "$ccsmroot");
$cfg_ref->set('XMLMODE' , "$xmlmode");

my $repotag;
if (-f "$ccsmroot/ChangeLog") { 
    $repotag =`cat $ccsmroot/ChangeLog | grep 'Tag name:' | head -1`;
} else {
    $repotag =`cat $ccsmroot/models/atm/cam/doc/ChangeLog | grep 'Tag name:' | head -1`;
}
my @repotag = split(/ /,$repotag); 
$repotag = $repotag[2]; 
chomp($repotag);
$cfg_ref->set('CCSM_REPOTAG', $repotag);
			
#-----------------------------------------------------------------------------------------------
# (10) Create the $caseroot directory tree
#-----------------------------------------------------------------------------------------------

my $sysmod;
my $scriptsroot = "$ccsmroot/scripts";

print "Creating $caseroot $eol";

# Create relevant directories in $caseroot
my @mkdirs = qw(. SourceMods LockedFiles Buildconf Tools/XML/Lite);
foreach my $mkdir ( @mkdirs ) {
    $sysmod = "mkdir -p $caseroot/$mkdir"; 
    system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
}
			
# Copy relevant files into $caseroot
my @files = (
             "$scriptsroot/ccsm_utils/Tools/cesm_setup", 
	     "$scriptsroot/ccsm_utils/Tools/check_input_data", 
	     "$scriptsroot/ccsm_utils/Tools/archive_metadata.sh", 
	     "$scriptsroot/ccsm_utils/Tools/check_case", 
	     "$scriptsroot/ccsm_utils/Tools/create_production_test", 
	     "$scriptsroot/ccsm_utils/Tools/xmlchange",
	     "$scriptsroot/ccsm_utils/Tools/xmlquery",
            ); 
foreach my $file (@files) {
    $sysmod = "cp -p $file $caseroot"; 
    system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
}
$sysmod = "chmod u+w $caseroot/create_production_test";
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";

if ($userdir ne "unset") {
    open(my $fh, ">", "$caseroot/userdir") or die "ERROR: open $caseroot/userdir";
    print $fh "$userdir\n";
    close $fh;
}

# Copy relevant files into $caseroot/Tools/
@files = ("$scriptsroot/ccsm_utils/Tools/cesm_prerun_setup", 
	  "$scriptsroot/ccsm_utils/Tools/cesm_postrun_setup", 
	  "$scriptsroot/ccsm_utils/Tools/cesm_runloop", 
	  "$scriptsroot/ccsm_utils/Tools/cesm_buildstart", 
	  "$scriptsroot/ccsm_utils/Tools/cesm_buildnml", 
	  "$scriptsroot/ccsm_utils/Tools/cesm_prestage",
	  "$scriptsroot/ccsm_utils/Tools/cesm_buildexe", 
	  "$scriptsroot/ccsm_utils/Tools/ccsm_getenv", 
	  "$scriptsroot/ccsm_utils/Tools/ccsm_check_lockedfiles", 
	  "$scriptsroot/ccsm_utils/Tools/taskmaker.pl", 
	  "$scriptsroot/ccsm_utils/Tools/xml2env",
	  "$scriptsroot/ccsm_utils/Tools/lt_archive.sh", 
	  "$scriptsroot/ccsm_utils/Tools/st_archive.sh", 
	  "$scriptsroot/ccsm_utils/Tools/user_nlcreate", 
	  "$scriptsroot/ccsm_utils/Tools/user_nl_add",
	  "$scriptsroot/ccsm_utils/Tools/timing/getTiming.csh", 
	  "$scriptsroot/ccsm_utils/Tools/timing/getTiming2.pl",
	  "$scriptsroot/ccsm_utils/Case.template/config_definition.xml",
	  "$scriptsroot/ccsm_utils/Case.template/config_grid.xml",
	  "$scriptsroot/ccsm_utils/Case.template/ConfigCase.pm",
	  "$scriptsroot/ccsm_utils/Tools/SetupTools.pm",
	  "$scriptsroot/ccsm_utils/Machines/Makefile",
	  "$scriptsroot/ccsm_utils/Machines/mkSrcfiles", 
	  "$scriptsroot/ccsm_utils/Machines/mkDepends", 
	  "$scriptsroot/ccsm_utils/Machines/mkbatch.$mach");
foreach my $file (@files) {
    $sysmod = "cp -p $file $caseroot/Tools/."; 
    system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
}

# Create $case.build
my $file = "${caseroot}/${case}.build";
$sysmod = "cp ${caseroot}/Tools/cesm_buildstart $file";
system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";}
$sysmod = "cat ${caseroot}/Tools/cesm_buildnml >> $file";
system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";}
$sysmod = "cat ${caseroot}/Tools/cesm_prestage >> $file";
system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";}
$sysmod = "cat ${caseroot}/Tools/cesm_buildexe >> $file";
system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";}
$sysmod = "chmod 755 $file";
system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";}

# Create $case.clean_build
$sysmod = "cp $scriptsroot/ccsm_utils/Tools/cesm_clean_build $caseroot/$case.clean_build"; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";

# Create $case.submit
$sysmod = "cp  $scriptsroot/ccsm_utils/Tools/cesm_submit $caseroot/$case.submit"; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";

# Create $case.l_archive
my $sysmod = "env CCSMUSER=$ccsmuser CASE=$case CASEROOT=$caseroot env PHASE=set_larch ${machdir}/mkbatch.$mach";
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";

# Create preview_namelist file
my $file = "${caseroot}/preview_namelists"; 
$sysmod = "cp  $scriptsroot/ccsm_utils/Tools/preview_namelists $file"; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
$sysmod = "chmod 755 $file";
system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";}

# Copy relevant files into $caseroot/Tools/XML directory
$sysmod = "cp -p $scriptsroot/ccsm_utils/Tools/perl5lib/XML/Lite.pm $caseroot/Tools/XML"; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
$sysmod = "cp -p $scriptsroot/ccsm_utils/Tools/perl5lib/XML/Lite/Element.pm $caseroot/Tools/XML/Lite"; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";

# Create relevant files in the $caseroot/Buildconf
my $buildconf  = "$caseroot/Buildconf";
my @models     = qw(COMP_ATM COMP_LND COMP_ICE COMP_OCN COMP_GLC COMP_ROF);
my @comps_atm  = $cfg_ref->get_valid_values("COMP_ATM"); 
my @comps_lnd  = $cfg_ref->get_valid_values("COMP_LND"); 
my @comps_ocn  = $cfg_ref->get_valid_values("COMP_OCN"); 
my @comps_ice  = $cfg_ref->get_valid_values("COMP_ICE"); 
my @comps_glc  = $cfg_ref->get_valid_values("COMP_GLC"); 
my @comps_rof  = $cfg_ref->get_valid_values("COMP_ROF"); 
my @comps      = (@comps_atm, @comps_lnd, @comps_ocn, @comps_ice, @comps_glc, @comps_rof); 
foreach my $comp (@comps)  {
    $comp =~ s/\'//g;
    foreach my $model (@models) {
	my $model_name = $cfg_ref->get($model);
	if ($model_name eq $comp) {
	    my $blddir;
	    if ($model eq 'COMP_ATM') {$blddir = "$ccsmroot/models/atm/$comp/bld"}; 
	    if ($model eq 'COMP_LND') {$blddir = "$ccsmroot/models/lnd/$comp/bld"}; 
	    if ($model_name eq 'ctsm') {$blddir = "$ccsmroot/models/lnd/$comp/bld_rasm"}; 
	    if ($model eq 'COMP_ICE') {$blddir = "$ccsmroot/models/ice/$comp/bld"}; 
	    if ($model eq 'COMP_OCN') {$blddir = "$ccsmroot/models/ocn/$comp/bld"}; 
	    if ($model eq 'COMP_GLC') {$blddir = "$ccsmroot/models/glc/$comp/bld"}; 
	    if ($model eq 'COMP_ROF') {$blddir = "$ccsmroot/models/rof/$comp/bld"}; 
	    $sysmod = "cp -p $blddir/*build*.csh $buildconf/.";
            system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
	    if (-f "$blddir/$comp.template" || -f "$blddir/$comp.cpl7.template") {
		$sysmod = "cp -p $blddir/${comp}*template $buildconf/${comp}.user_nl.csh";
		system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
	    }
	}
    }
}
$sysmod = "cp -p $ccsmroot/models/drv/bld/*.build*.csh $buildconf/.";
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
$sysmod = "cp -p $ccsmroot/models/drv/bld/cpl.template $buildconf/cpl.user_nl.csh";
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
foreach my $comp (@comps)  {
    foreach my $model (@models) {
	if ($cfg_ref->get($model) eq $comp) {
	    my $component = $cfg_ref->get($model); 
	    if ($component eq 'cice') {
		$sysmod = "cp $ccsmroot/models/ice/cice/bld/generate_cice_decomp.pl $buildconf/."; 
		system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
		$sysmod = "cp $ccsmroot/models/ice/cice/bld/cice_decomp.xml $buildconf/."; 
		system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
	    }
	    if ($component eq 'pop2') {
		$sysmod = "cp $ccsmroot/models/ocn/pop2/bld/generate_pop_decomp.pl $buildconf/."; 
		system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
		$sysmod = "cp $ccsmroot/models/ocn/pop2/bld/pop_decomp.xml $buildconf/."; 
		system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
	    } 
	}
    }
}
foreach my $lib ("gptl", "mct", "csm_share", "pio") {
    my $sysmod = "cp ${machdir}/buildlib.${lib} $caseroot/Buildconf/.";
    system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
}

# Create the relevant $caseroot/Sourcemods directories
my $moddir = "$caseroot/SourceMods";
foreach my $comp (@comps)  {
    foreach my $model (@models) {
	if ($cfg_ref->get($model) eq $comp) {
	    $sysmod = "mkdir -p $moddir/src.$comp"; 
	    system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
	}
    }
}
$sysmod = "mkdir -p $caseroot/SourceMods/src.share"; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
$sysmod = "mkdir -p $caseroot/SourceMods/src.drv"  ; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
$sysmod = "chmod -R u+w $caseroot/SourceMods"; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";

# Create machine specific environment file (env_mach_specific)
if(-e "$machdir/env_mach_specific.$mach" ) {
  $sysmod = "cp $machdir/env_mach_specific.$mach $caseroot/env_mach_specific";
}else{
  $sysmod = "touch $caseroot/env_mach_specific";
}
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";

# Copy Depends files if they exist
if( -e "$machdir/Depends.$mach" ) {
  $sysmod = "cp $machdir/Depends.$mach $caseroot/";
  system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
}
if( -e "$machdir/Depends.$compiler" ) {
  $sysmod = "cp $machdir/Depends.$compiler $caseroot/";
  system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
}

# Write the xml files
$cfg_ref->write_file("$caseroot/env_case.xml"   , "xml");
print "Created $caseroot/env_case.xml \n";
$cfg_ref->write_file("$caseroot/env_mach_pes.xml", "xml");  
print "Created $caseroot/env_mach_pes.xml \n";
$cfg_ref->write_file("$caseroot/env_build.xml"  , "xml");
print "Created $caseroot/env_build.xml \n";
$cfg_ref->write_file("$caseroot/env_run.xml"    , "xml");
print "Created $caseroot/env_run.xml \n";

# Copy env_case.xml in to locked files
$sysmod = "cp $caseroot/env_case.xml $caseroot/LockedFiles/env_case.xml.locked"; 
system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n";
print "Locking file $caseroot/env_case.xml \n";

my $file = "$caseroot/CaseStatus";
my $fh = IO::File->new($file, '>' ) or die "can't open file: $file\n";
my $time = time;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
$year = 1900+$year;
$mon = 1+$mon;
print $fh "$commandline\n";
printf ($fh "case created %04u-%02u-%02u %02u:%02u:%02u\n",$year,$mon,$mday,$hour,$min,$sec);

#-----------------------------------------------------------------------------------------------
# (11) Create $caseroot run setup scripts (other than batch)
#-----------------------------------------------------------------------------------------------

print "Successfully created the case for $mach \n";
if ($print>=2) { print "create_xml done.$eol"; }

# Finished create_newcase
exit;

#-----------------------------------------------------------------------------------------------
# FINNISHED ####################################################################################
#-----------------------------------------------------------------------------------------------

sub absolute_path {
#
# Convert a pathname into an absolute pathname, expanding any . or .. characters.
# Assumes pathnames refer to a local filesystem.
# Assumes the directory separator is "/".
#
  my $path = shift;
  my $cwd = getcwd();  # current working directory
  my $abspath;         # resulting absolute pathname

# Strip off any leading or trailing whitespace.  (This pattern won't match if
# there's embedded whitespace.
  $path =~ s!^\s*(\S*)\s*$!$1!;

# Convert relative to absolute path.

  if ($path =~ m!^\.$!) {          # path is "."
      return $cwd;
  } elsif ($path =~ m!^\./!) {     # path starts with "./"
      $path =~ s!^\.!$cwd!;
  } elsif ($path =~ m!^\.\.$!) {   # path is ".."
      $path = "$cwd/..";
  } elsif ($path =~ m!^\.\./!) {   # path starts with "../"
      $path = "$cwd/$path";
  } elsif ($path =~ m!^[^/]!) {    # path starts with non-slash character
      $path = "$cwd/$path";
  }

  my ($dir, @dirs2);
  my @dirs = split "/", $path, -1;   # The -1 prevents split from stripping trailing nulls
                                     # This enables correct processing of the input "/".

  # Remove any "" that are not leading.
  for (my $i=0; $i<=$#dirs; ++$i) {
      if ($i == 0 or $dirs[$i] ne "") {
	  push @dirs2, $dirs[$i];
      }
  }
  @dirs = ();

  # Remove any "."
  foreach $dir (@dirs2) {
      unless ($dir eq ".") {
	  push @dirs, $dir;
      }
  }
  @dirs2 = ();

  # Remove the "subdir/.." parts.
  foreach $dir (@dirs) {
    if ( $dir !~ /^\.\.$/ ) {
        push @dirs2, $dir;
    } else {
        pop @dirs2;   # remove previous dir when current dir is ..
    }
  }
  if ($#dirs2 == 0 and $dirs2[0] eq "") { return "/"; }
  $abspath = join '/', @dirs2;
  return( $abspath );
}

#-------------------------------------------------------------------------------

sub subst_env_path {
#
# Substitute for any environment variables contained in a pathname.
# Assumes the directory separator is "/".
#
  my $path = shift;
  my $newpath;         # resulting pathname

# Strip off any leading or trailing whitespace.  (This pattern won't match if
# there's embedded whitespace.
  $path =~ s!^\s*(\S*)\s*$!$1!;

  my ($dir, @dirs2);
  my @dirs = split "/", $path, -1;   # The -1 prevents split from stripping trailing nulls
                                     # This enables correct processing of the input "/".

  foreach $dir (@dirs) {
    if ( $dir =~ /^\$(.+)$/ ) {
        push @dirs2, $ENV{$1};
    } else {
        push @dirs2, $dir;
    }
  }
  $newpath = join '/', @dirs2;
  return( $newpath );
}

#-------------------------------------------------------------------------------

sub get_option {

    my ($mes, @expect) = @_;
    my ($ans, $expect, $max_tries);

    $max_tries = 5;
    print $mes;
    while ($max_tries) {
	$ans = <>; chomp $ans;
	--$max_tries;
	$ans =~ s/^\s+//;
	$ans =~ s/\s+$//;
	# Check for null response which indicates that default is accepted.
	unless ($ans) { return ""; }
	foreach $expect (@expect) {
	    if ($ans =~ /^$expect$/i) { return $expect; }
	}
	if ($max_tries > 1) {
	    print "$ans does not match any of the expected values: @expect\n";
	    print "Please try again: ";
	} elsif ($max_tries == 1) {
	    print "$ans does not match any of the expected values: @expect\n";
	    print "Last chance! ";
	}
    }
    die "Failed to get answer to question: $mes\n";
}

#-------------------------------------------------------------------------------

sub print_hash {
    my %h = @_;
    my ($k, $v);
    while ( ($k,$v) = each %h ) { print "$k => $v\n"; }
}

#-------------------------------------------------------------------------------

sub set_grid
{
    # Set the parameters for the specified horizontal grid.  The
    # parameters are read from an input file, and if no grid matches are
    # found then issue error message.
    # This routine uses the configuration defined at the package level ($cfg_ref).

    my ($grid_file, $grid, $cfg_ref) = @_;
    my $xml = XML::Lite->new( $grid_file );
    my $root = $xml->root_element();

    # Check for valid root node
    my $name = $root->get_name();
    $name eq "config_horiz_grid" or die
	"file $grid_file is not a horizontal grid parameters file\n";

    # Read the grid parameters from $grid_file.
    my @e = $xml->elements_by_name( "horiz_grid" );
    my %a = ();

    # Search for matching grid.
    my $found = 0;
  HGRID:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ( ! defined($a{'GRID'}     ) ) { next; }
	if ( ! defined($a{'SHORTNAME'}) ) { next; }
	if ( ($grid eq $a{'GRID'}) || ($grid eq $a{'SHORTNAME'})) {
	    $found = 1;
	    last HGRID;
	}
    }

    # Die unless search was successful.
    unless ($found) { 
	print "set_grid: no match for grid $grid - possible grid values are \n";
	my @e_err = $xml->elements_by_name( "horiz_grid" );
	my %a_err = ();
	while ( my $e_err = shift @e_err ) {
	    %a_err = $e_err->get_attributes();
	    if (defined($a_err{'SHORTNAME'})) { 
		print " $a_err{'GRID'} (SHORTNAME: $a_err{'SHORTNAME'}) \n";
	    }
	}
	die "set_grid: exiting\n"; 
    }
    my $shortname = $a{'SHORTNAME'};
    my $longname  = $a{'GRID'};

    my @ids = keys %$cfg_ref;
    foreach my $id (sort @ids) {
	foreach my $attr (keys %a) {
	    if ( ! $cfg_ref->is_ignore_name($attr) and ! $cfg_ref->is_valid_name($attr) ) { 
                die "set_grid: invalid id $attr in grid $grid in file $grid_file exiting\n"; 
	    }
	    if ($attr eq $id) {
		my $value = $a{$attr};
		$cfg_ref->set($id, $value);
	    }
	}
    }

    # Search for matching grid for each component

    my $atm_grid = $a{'ATM_GRID'};
    my $lnd_grid = $a{'LND_GRID'};
    my $ice_grid = $a{'ICE_GRID'};
    my $ocn_grid = $a{'OCN_GRID'};
    my $rof_grid = $a{'ROF_GRID'};

    @e = $xml->elements_by_name( "horiz_grid" );
    %a = ();
    $found = 0;
  HGRID:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ( $atm_grid eq $a{'GLOB_GRID'} ) {
	    $found = 1;
	    last HGRID;
	}
    }
    if ($found) {
	$cfg_ref->set('ATM_NX', $a{'nx'});
	$cfg_ref->set('ATM_NY', $a{'ny'});
    }

    @e = $xml->elements_by_name( "horiz_grid" );
    %a = ();
    $found = 0;
  HGRID:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ( $lnd_grid eq $a{'GLOB_GRID'} ) {
	    $found = 1;
	    last HGRID;
	}
    }
    if ($found) {
	$cfg_ref->set('LND_NX', $a{'nx'});
	$cfg_ref->set('LND_NY', $a{'ny'});
    }

    @e = $xml->elements_by_name( "horiz_grid" );
    %a = ();
    $found = 0;
  HGRID:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ( $ice_grid eq $a{'GLOB_GRID'} ) {
	    $found = 1;
	    last HGRID;
	}
    }
    if ($found) {
	$cfg_ref->set('ICE_NX', $a{'nx'});
	$cfg_ref->set('ICE_NY', $a{'ny'});
    }

    @e = $xml->elements_by_name( "horiz_grid" );
    %a = ();
    $found = 0;
  HGRID:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ( $lnd_grid eq $a{'GLOB_GRID'} ) {
	    $found = 1;
	    last HGRID;
	}
    }
    if ($found) {
	$cfg_ref->set('GLC_NX', $a{'nx'});
	$cfg_ref->set('GLC_NY', $a{'ny'});
    }

    @e = $xml->elements_by_name( "horiz_grid" );
    %a = ();
    $found = 0;
  HGRID:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ( $ocn_grid eq $a{'GLOB_GRID'} ) {
	    $found = 1;
	    last HGRID;
	}
    }
    if ($found) {
	$cfg_ref->set('OCN_NX', $a{'nx'});
	$cfg_ref->set('OCN_NY', $a{'ny'});
    }

    @e = $xml->elements_by_name( "horiz_grid" );
    %a = ();
    $found = 0;
  HGRID:
    while ( my $e = shift @e ) {
        %a = $e->get_attributes();
        if ( $rof_grid eq $a{'GLOB_GRID'} ) {
	    $found = 1;
	    last HGRID;
        }
    }
    if ($found) {
	$cfg_ref->set('ROF_NX', $a{'nx'});
	$cfg_ref->set('ROF_NY', $a{'ny'});
    }

    return ($longname, $shortname); 
 }

#-------------------------------------------------------------------------------

sub set_compset
{
    # Set the parameters for the specified compset.  The
    # parameters are read from an input file, and if no compset matches are
    # found then issue error message.
    # This routine uses the configuration defined at the package level ($cfg_ref).

    my ($compset_file, $compset, $grid_longname, $grid_shortname, $cfg_ref, $caseroot) = @_;

    my $fh = new IO::File;
    $fh->open(">>$caseroot/README.case") or die "can't open file: $file\n";

    my $xml = XML::Lite->new( $compset_file );
    my $root = $xml->root_element();
    my $name = $root->get_name();
    $name eq "config_compset" or die
	"file $compset_file is not a compset parameters file\n";

    # Read the compset parameters from $compset_file.
    my @e = $xml->elements_by_name( "compset" );

    my $comment; 
    my $desc;
    my $compset_longname; 
    my $compset_shortname; 
    my $temp; 
    my $found;
    my $valid_grid = 0;

    # ========================================================
    # 0. Match on name (exactly)
    # ========================================================
    my @e = $xml->elements_by_name( "compset" );
    my %a = ();

    $found = 0;
  NAME:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ( ($compset eq $a{'NAME'}) || ($compset eq $a{'SHORTNAME'})) {
	    $found = 1;
	    $compset_longname  = $a{'NAME'};
	    $compset_shortname = $a{'SHORTNAME'};
	   #print     "(COMPSET)  $compset_longname \n";
	    print $fh "(COMPSET)  $compset_longname \n";
	    $temp = $a{'NAME'}." (".$a{'SHORTNAME'}.")"; $cfg_ref->set('CCSM_COMPSET' , "$temp");
	    $temp = $a{'NAME'}                         ; $cfg_ref->set('CCSM_LCOMPSET', "$temp");
	    $temp = $a{'SHORTNAME'}                    ; $cfg_ref->set('CCSM_SCOMPSET', "$temp");
	    if ($a{'DESC'}) {
		$desc = $a{'DESC'};
	    }
	    last NAME;
	}
    }
    unless ($found) { 
	print "set_compset: no match for compset $compset - possible compset values are \n";
	print_compsets($compset_file);
	die "set_compset: exiting\n"; 
    }

    # ========================================================
    # 1. Match on default compset settings 
    # ========================================================
    @e = $xml->elements_by_name( "compset" );
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ($a{'BEG_COMPSET_MATCH'}) {
	    if ($compset_longname =~ /^$a{'BEG_COMPSET_MATCH'}/)  {
		my @ids = keys %$cfg_ref;
		foreach my $id (sort @ids) {
		    foreach my $attr (keys %a) {
			if ( ! $cfg_ref->is_ignore_name($attr) and ! $cfg_ref->is_valid_name($attr) ) { 
			    die "set_compset: invalid id $attr in compset $compset in file $compset_file exiting\n"; 
			}
			if ($attr eq $id) {
			    my $value = $a{$attr};
			   #print     "(default)  $attr: $value \n";
			    print $fh "(default)  $attr: $value \n";
			    $cfg_ref->set($id, $value);
			}
		    }
		}
	    }
	}
    }

    # ========================================================
    # 2. Match on general compset settings
    # ========================================================
    @e = $xml->elements_by_name( "compset" );
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ($a{'GEN_COMPSET_MATCH'}) {
	    if ($compset_longname =~ /$a{'GEN_COMPSET_MATCH'}/)  {
		my $first_time = 1;
		my @ids = keys %$cfg_ref;
		foreach my $id (sort @ids) {
		    foreach my $attr (keys %a) {
			if ( ! $cfg_ref->is_ignore_name($attr) and ! $cfg_ref->is_valid_name($attr) ) { 
			    die "set_compset: invalid id $attr in compset $compset in file $compset_file exiting\n"; 
			}
			if ($attr eq $id) {
			    my $attrib = $a{$attr};
			    my $curval = $cfg_ref->get($id);
			    my $newval = $attrib;
                            # For *_CONFIG_OPTS and *_NAMELIST_OPTS, we want to merge, not replace, options.
			    if (($attr =~ m/\s*_CONFIG_OPTS/) || ($attr =~ m/\s*_NAMELIST_OPTS/)) {
				if  ($curval !~ m/^\s*$/)  { 
                                    $newval = $curval;
                                    # Split into separate options, separated by '-'.
                                    # The regex is used to ensure '-' is only noticed if it is
                                    # either the first character or follows a space.
                                    # Note that the '-' will be stripped off.
                                    my @attropts = split(/(?:^|\s)-/, $attrib);
                                    my @curopts = split(/(?:^|\s)-/, $curval);
                                    # First item in each array will be space or empty string, so
                                    # remove it with shift.
                                    shift @attropts;
                                    shift @curopts;
                                    # Iterate through new options
                                    foreach my $attropt (@attropts) {
                                        # Grab option name.
                                        my ($attrname) = $attropt =~ m/^(\w+)\s/;
                                        my $attr_found = 0;
                                        # Check current options for values to replace.
                                        foreach my $curopt (@curopts) {
                                            if ($curopt =~ m/^$attrname\s/) {
                                                $attr_found = 1;
                                                # Substitute, adding one space just in case.
                                                $newval =~ s/$curopt/$attropt /;
                                            }
                                        }
                                        # If the new option was not found in existing options, append it.
                                        if ( ! $attr_found) {
                                            $newval = "$newval -$attropt";
                                        }
                                    }
                                    # Get rid of extra spaces.
                                    $newval =~ s/\s+/ /g; # spaces in middle
                                    $newval =~ s/\s*$//; # spaces at end
				}
			    }
			   #print     "(compset)  $attr: $newval \n";
			    print $fh "(compset)  $attr: $newval \n";
			    $cfg_ref->set($id, $newval);
			}
		    }
		}
		if ($a{'VALID_GRID_MATCH'}) {
		    my $match = $a{'VALID_GRID_MATCH'};
		    if (($grid_longname =~ /$match/) || ($grid_shortname =~ /$match/)) {
			# do nothing
		    } else {
			$match =~ s/\|/ or /g;
			print "\n *** ERROR: invalid grid for this compset *** \n ";
			print "    supported grids for this compset must match \n ";
			print "          $match \n"; 
			die "     please retry this compset at one of the supported resolutions \n \n";
		    }
		}		
	    }
	}
    }

    # ========================================================
    # 3. Match on compset AND grid
    # ========================================================
    @e = $xml->elements_by_name( "compset" );
    $found = 0;
  COMPGRID_MATCH:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if (($a{'GRID_MATCH'} && $a{'RES_COMPSET_MATCH'}) || 
	    ($a{'GRID_MATCH'} && $a{'SSTICE_COMPSET_MATCH'})) {
	    my $compset_match = 0;
	    my $grid_match = 0;
	    if ($a{'RES_COMPSET_MATCH'}) {
		if ($compset_longname eq $a{'RES_COMPSET_MATCH'})  {
		    $compset_match = 1;
		}
	    }
	    if ($a{'SSTICE_COMPSET_MATCH'}) {
		if ($compset_longname =~ /^$a{'SSTICE_COMPSET_MATCH'}/)  {
		    $compset_match = 1;
		}
	    }
	    if ($grid_longname =~ /$a{'GRID_MATCH'}/ || $grid_shortname =~ /$a{'GRID_MATCH'}/ ) {
		$grid_match = 1;
		$valid_grid = 1;
	    }
	    if ($grid_match && $compset_match) {
		my @ids = keys %$cfg_ref;
		foreach my $id (sort @ids) {
		    foreach my $attr (keys %a) {
			if ( ! $cfg_ref->is_ignore_name($attr) and ! $cfg_ref->is_valid_name($attr) ) { 
			    die "set_compset: invalid id $attr in compset $compset in file $compset_file exiting\n"; 
			}
			if ($attr eq $id) {
			    my $value = $a{$attr};
			   #print     "(gridset)  $attr: $value \n";
			    print $fh "(gridset)  $attr: $value \n";
			    $cfg_ref->set($id, $value);
			}
		    }
		}
	    }
	}
    }

    # ========================================================
    # 3. Match on name exactly
    # ========================================================
    @e = $xml->elements_by_name( "compset" );
  NAME:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ($compset_longname eq $a{'NAME'}) {
	    my @ids = keys %$cfg_ref;
	    foreach my $id (sort @ids) {
		foreach my $attr (keys %a) {
		    if ( ! $cfg_ref->is_ignore_name($attr) and ! $cfg_ref->is_valid_name($attr) ) { 
			die "set_compset: invalid id $attr in compset $compset in file $compset_file exiting\n"; 
		    }
		    if ($attr eq $id) {
			my $value = $a{$attr};
		       #print     "(name   )  $attr: $value \n";
		        print $fh "(name   )  $attr: $value \n";
			$cfg_ref->set($id, $value);
		    }
		}
	    }
	    last NAME;
	}
    }

    print     "\n";
    print     "Component set     : $compset_longname ($compset_shortname) \n";
    print     "Desc              : $desc \n";

    print $fh "Component set     : $compset_longname ($compset_shortname) \n";
    print $fh "Desc              : $desc \n";
    
    # Print config_ref values
    my @ids = keys %$cfg_ref;
    foreach my $id (sort @ids) {
	my $value = $cfg_ref->get($id);
	# print     " $id: $value \n";
	# print $fh " <entry id=\"$id\" value=\"$value\" /> \n";
    }
    return ($compset_longname, $compset_shortname); 
}

#-------------------------------------------------------------------------------

sub check_grid
{
    # Set the parameters for the specified horizontal grid.  The
    # parameters are read from an input file, and if no grid matches are
    # found then issue error message.
    # This routine uses the configuration defined at the package level ($cfg_ref).

    my ($grid_file, $grid, $compset_longname) = @_;
    my $xml = XML::Lite->new( $grid_file );
    my $root = $xml->root_element();

    # Check for valid root node
    my $name = $root->get_name();
    $name eq "config_horiz_grid" or die
	"file $grid_file is not a horizontal grid parameters file\n";

    # Read the grid parameters from $grid_file.
    my @e = $xml->elements_by_name( "horiz_grid" );
    my %a = ();

    # Search for matching grid.
  HGRID:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ($grid eq $a{'GRID'} || $grid eq $a{'SHORTNAME'}) {
	    print "$grid $a{'GRID'} $a{'SHORTNAME'} $a{'VALID_COMPSET_MATCH'}\n" if($print>1);
	    if (defined $a{'VALID_COMPSET_MATCH'}) {
		if ($compset_longname =~ /^$a{'VALID_COMPSET_MATCH'}/)  {
		    # do nothing
		} else {
		    die <<"EOF";
*** Invalid compset - $compset - for grid - $grid - only valid compset must match $a{'VALID_COMPSET_MATCH'} ***
*** Invoke create_newcase again with a vaid compset for this grid ***
EOF
                }
	    }
	    last HGRID; 

	}
    }
		    if($print>1){
			foreach (keys %a){
			    print "GRIDMATCH: $_ $a{$_}\n";
			}
		    }

}

#-------------------------------------------------------------------------------

sub set_test
{
    # Set the parameters for the specified testname.  The
    # parameters are read from an input file, and if no testname matches are
    # found then issue error message.
    # This routine uses the configuration defined at the package level ($cfg_ref).

    my ($test_file, $testname, $cfg_ref) = @_;
    my $xml = XML::Lite->new( $test_file );
    my $root = $xml->root_element();

    # Check for valid root node
    my $name = $root->get_name();
    $name eq "config_ccsmtest" or die
	"file $test_file is not a test parameters file\n";

    # Read the test parameters from $test_file.
    my @e = $xml->elements_by_name( "ccsmtest" );
    my %a = ();

    # Search for matching test.
    my $found = 0;
  CCSMTEST:
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if ( ($testname eq $a{'NAME'}) )  {
	    $found = 1;
	    last CCSMTEST;
	}
    }

    # Die unless search was successful.
    unless ($found) { 
	print "set_test: no match for test $testname - possible testnames are \n";
	my @e_err = $xml->elements_by_name( "ccsmtest" );
	my %a_err = ();
	while ( my $e_err = shift @e_err ) {
	    %a_err = $e_err->get_attributes();
	    print " $a_err{'NAME'} ($a_err{'DESC'}) \n" ;
	}
	die "set_test: exiting\n"; 
    }

    # Loop through all entry_ids of the $cfg_ref object and if the corresponding 
    # attributed is defined in the testname hash, then reset the cfg_ref object to
    # that value

     my @ids = keys %$cfg_ref;
     foreach my $id (sort @ids) {
	 foreach my $attr (keys %a) {
             if ( ! $cfg_ref->is_ignore_name($attr) and ! $cfg_ref->is_valid_name($attr) ) { 
                die "set_test: invalid id $attr in test $testname in file $test_file exiting\n"; 
             }
	     if ($attr eq $id) {
	         my $value = $a{$attr};
	         $cfg_ref->set($id, $value);
             }
	 }
     }

}

#-------------------------------------------------------------------------------

sub set_confopts
{
    # Print all currently supported valid grids

    my ($coptions, $cfg_ref) = @_;

    print "  confopts = $coptions\n";
    if ($coptions =~ "_D" || $coptions =~ "_ED") {
	$cfg_ref->set('DEBUG', "TRUE");
        print "    confopts DEBUG ON \n";
    }
    if ($coptions =~ "_E" || $coptions =~ "_DE") {
	$cfg_ref->set('USE_ESMF_LIB', "TRUE");
	$cfg_ref->set('COMP_INTERFACE', "ESMF");
        print "    confopts COMP_INTERFACE ESMF set \n";
    }
    if ($coptions =~ "_P") {
        my $popt = $coptions;
        $popt =~ s/.*_P([A-Za-z0-9]*)_?.*/$1/;
        $pecount = $popt;
        print "    confopts pecount set to $pecount \n";
    }
    if ($coptions =~ "_U") {
        my $uopt = $coptions;
        $uopt =~ s/.*_U([A-Za-z0-9]*)_?.*/$1/;
        $userdir = $uopt;
        print "    userdir set to $userdir \n";
    }
    if ($coptions =~ "_M") {
        my $mopt = $coptions;
        $mopt =~ s/.*_M([A-Za-z0-9\-]*)_?.*/$1/;
        $mpilib = $mopt;
        print "    mpilib set to $mpilib \n";
    }
    if ($coptions =~ "_L") {
        my $lopt = $coptions;
        $lopt =~ s/.*_L([A-Za-z0-9]*)_?.*/$1/;
        my $loptc = substr($lopt,0,1);
        my $lopti = substr($lopt,1);
        my $lopts = 'unknown';
        if ($loptc =~ "y") {$lopts = 'nyears'}
        if ($loptc =~ "m") {$lopts = 'nmonths'}
        if ($loptc =~ "d") {$lopts = 'ndays'}
        if ($loptc =~ "h") {$lopts = 'nhours'}
        if ($loptc =~ "s") {$lopts = 'nseconds'}
        if ($loptc =~ "n") {$lopts = 'nsteps'}
        if ($lopts =~ "unknown") {
	    print "$0 _L confopts run length undefined, only y m d h s n allowed\n\n";
	    exit -1;
	}
	$cfg_ref->set('STOP_OPTION', $lopts);
	$cfg_ref->set('STOP_N', $lopti);
        print "    confopts run length set to $lopt . $lopts . $lopti \n";
    }
    if ($coptions =~ "_N") {
        my $nopt = $coptions;
        $nopt =~ s/.*_N([0-9]*)_?.*/$1/;
	$cfg_ref->set('NINST_ATM', $nopt);
	$cfg_ref->set('NINST_LND', $nopt);
	$cfg_ref->set('NINST_OCN', $nopt);
	$cfg_ref->set('NINST_ICE', $nopt);
	$cfg_ref->set('NINST_GLC', $nopt);
	$cfg_ref->set('NINST_ROF', $nopt);
        print "    confopts instances set to $nopt \n";
    }
    if ($coptions =~ "_CG") {
	$cfg_ref->set('CALENDAR', "GREGORIAN");
        print "    confopts CALENDAR set to GREGORIAN \n";
    }
    if ($coptions =~ "_AOA") {
	$cfg_ref->set('AOFLUX_GRID', "atm");
        print "    confopts AOFLUX_GRID set to atm \n";
    }
    if ($coptions =~ "_AOE") {
	$cfg_ref->set('AOFLUX_GRID', "exch");
        print "    confopts AOFLUX_GRID set to exch \n";
    }

    if ($coptions =~ "_R") {
	if($cfg_ref->get('OS') eq 'BGP'){
	    print "$0 single_point cases not supported on bluegene systems\n\n";
	    exit -1;
	}
        my $ptsmode = "undef";
        if ($coptions =~ "_RLA" ) {
           $cfg_ref->set('PTS_LAT',  42 );
           $cfg_ref->set('PTS_LON',  260 );
           my $ptsmode = "valid";
           print "    confopts single point mode set to RLA \n";
        }
        if ($coptions =~ "_RLB" ) {
           $cfg_ref->set('PTS_LAT',  -5 );
           $cfg_ref->set('PTS_LON',  290 );
           my $ptsmode = "valid";
           print "    confopts single point mode set to RLB \n";
        }
        if ($coptions =~ "_ROA" ) {
           $cfg_ref->set('PTS_LAT',  30 );
           $cfg_ref->set('PTS_LON',  315 );
           my $ptsmode = "valid";
           print "    confopts single point mode set to ROA \n";
        }
        if ($coptions =~ "_ROB" ) {
           $cfg_ref->set('PTS_LAT',  0 );
           $cfg_ref->set('PTS_LON',  160 );
           my $ptsmode = "valid";
           print "    confopts single point mode set to ROB \n";
        }
        if ($ptsmode ==  "valid" ) {
           $cfg_ref->set('PTS_MODE', "TRUE" );
           $cfg_ref->set('ATM_NX', 0);
           $cfg_ref->set('ATM_NY', 0);
           $cfg_ref->set('LND_NX', 0);
           $cfg_ref->set('LND_NY', 0);
           $cfg_ref->set('ICE_NX', 0);
           $cfg_ref->set('ICE_NY', 0);
           $cfg_ref->set('OCN_NX', 0);
           $cfg_ref->set('OCN_NY', 0);
           $cfg_ref->set('GLC_NX', 0);
           $cfg_ref->set('GLC_NY', 0);
           $cfg_ref->set('ROF_NX', 0);
           $cfg_ref->set('ROF_NY', 0);
        } else {
	   print "create_newcase exited on invalid _R confopts setting\n\n";
           exit -1;
        }
    }
}

#-------------------------------------------------------------------------------

sub print_grids
{
    # Print all currently supported valid grids

    my ($grid_file) = @_;
    my $xml = XML::Lite->new( $grid_file );
    my $root = $xml->root_element();

    # Check for valid root node
    my $name = $root->get_name();
    $name eq "config_horiz_grid" or die
	"file $grid_file is not a horizontal grid parameters file\n";

    print ("  \n");
    print ("  RESOLUTIONS:  name (shortname) \n");

    # Read the grid parameters from $grid_file.
    my @e = $xml->elements_by_name( "horiz_grid" );
    my %a = ();
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if (defined($a{'SHORTNAME'})) { 
	    print "    $a{'GRID'} ($a{'SHORTNAME'})  \n";
	    print "       $a{'DESC'} \n";
	}
    }
}

#-------------------------------------------------------------------------------

sub print_compsets
{
    # Print all currently supported valid grids

    my ($compset_file, $grid_longname, $grid_shortname) = @_;
    my $xml = XML::Lite->new( $compset_file );
    my $root = $xml->root_element();

    # Check for valid root node
    my $name = $root->get_name();
    $name eq "config_compset" or die
	"file $compset_file is not a compset parameters file\n";

    print ("  \n");
    print ("  COMPSETS:  name (shortname): description \n");
    
    # Read the compset parameters from $compset_file.
    my @e = $xml->elements_by_name( "compset" );
    my %a = ();
    my $e; 
    while ( $e = shift @e ) {
	%a = $e->get_attributes();
	if ($a{'NAME'}) {
	    print "    $a{'NAME'} ($a{'SHORTNAME'}) \n";
	    print "         $a{'DESC'}  \n";
	}
    }
}

#-------------------------------------------------------------------------------

sub print_tests
{
    # Print all currently supported tests

    my ($test_file) = @_;
    my $xml = XML::Lite->new( $test_file );
    my $root = $xml->root_element();

    # Check for valid root node
    my $name = $root->get_name();
    $name eq "config_ccsmtest" or die
	"file $test_file is not a ccsmtest parameters file\n";

    print ("  \n");
    print ("  TESTS:  name (description) \n");
    
    my @e = $xml->elements_by_name( "ccsmtest" );
    my %a = ();
    while ( my $e = shift @e ) {
	%a = $e->get_attributes();
	if (defined($a{'DESC'})) { 
	    print "    $a{'NAME'} ($a{'DESC'}) \n";
	}
    }
}

# Check the CESM repository for known problems, see the SVN url below.  
# If there is a known_problems file in the repository that matches 
sub check_known_problems
{
    print "-------------------------------------------------------------------------------\n";
    print "          IMPORTANT INFORMATION ABOUT SCIENTIFIC VALIDATION - THERE\n";
    print "        ARE ONLY LIMITED SCIENTIFICALLY VALIDATED COMPONENT SETS IN CESM1.1\n";
    print "             (Currently, only WACCM-X under solar minimum conditions)\n\n";
    print "     For both a quick start and details of creating and running a CESM model case\n";
    print "        see the CESM1.1 User's Guide at http://www.cesm.ucar.edu/models/cesm1.1\n\n";

    print "-------------------------------------------------------------------------------\n";
    print "  For a list of potential issues in the current tag, please point your web browser to:\n";
    print "        http://www.cesm.ucar.edu/models/cesm1.1/tags/cesm1_1/knownproblems.html\n";
    print "-------------------------------------------------------------------------------\n";
}
