#!/usr/bin/env perl
#-----------------------------------------------------------------------------------------------
#
# build-namelist
#
# This script builds the namelists for the DRV configuration of CESM1.
#
# build-namelist uses a config_cache.xml file that current contains the ocean grid information.
# build-namelist reads this file to obtain information it needs to provide
# default values that are consistent with the driver/coupler library.  
#
# The simplest use of build-namelist is to execute it from the build directory where configure
# was run.  By default it will use the config_cache.xml file that was written by configure to
# determine the build time properties of the executable, and will write the files that contain 
# the output namelists in that same directory.  
#
#
# Date        Contributor      Modification
# -------------------------------------------------------------------------------------------
# 2012-01-30  Vertenstein      Original version
#--------------------------------------------------------------------------------------------
use strict;
use Cwd qw( getcwd abs_path chdir);
use English;
use Getopt::Long;
use IO::File;
#-----------------------------------------------------------------------------------------------

sub usage {
    die <<EOF;
SYNOPSIS
     build-namelist [options]
OPTIONS
     -infile "filepath"    Specify a file containing namelists to read values from.
     -namelist "namelist"  Specify namelist settings directly on the commandline by supplying 
                           a string containing FORTRAN namelist syntax, e.g.,
                              -namelist "&drv_nml dt=1800 /"
     -help [or -h]         Print usage to STDOUT.
     -silent               Turn on silent mode
     -verbose              Turn on verbose echoing of informational messages.
     -caseroot             CASEROOT directory variable
     -scriptsroot          SCRIPTSROOT directory variable
     -grid                 GRID variable
     -rof_grid             ROF_GRID variable
     -wav_grid             WAV_GRID variable
     -atm_grid             ATM_GRID variable
     -lnd_grid             LND_GRID variable 
     -ocn_grid             OCN_GRID variable

NOTE: The precedence for setting the values of namelist variables is (highest to lowest):
      1. namelist values set by specific command-line options, i.e. (none right now)
      2. values set on the command-line using the -namelist option,
      3. values read from the file specified by -infile,
      4. values from the namelist defaults file - or values specifically set in build-namelist 
EOF
}

#-----------------------------------------------------------------------------------------------
# Set the directory that contains the DRV configuration scripts.  If the 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 = abs_path($ProgDir);
} else {
    $cfgdir = $cwd;
}

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

# Process command-line options.

my %opts = ( help        => 0,
	     silent      => 0,
	     caseroot    => 0,
	     scriptsroot => 0,
	     grid        => 0,
	     rof_grid    => 0,
	     wav_grid    => 0,
	     atm_grid    => 0,
	     lnd_grid    => 0,
	     ocn_grid    => 0);

GetOptions(
    "h|help"        => \$opts{'help'},
    "infile=s"      => \$opts{'infile'},
    "namelist=s"    => \$opts{'namelist'},
    "s|silent"      => \$opts{'silent'},
    "v|verbose"     => \$opts{'verbose'},
    "caseroot=s"    => \$opts{'caseroot'},
    "scriptsroot=s" => \$opts{'scriptsroot'},
    "grid=s"        => \$opts{'grid'},	   
    "rof_grid=s"    => \$opts{'rof_grid'},
    "wav_grid=s"    => \$opts{'wav_grid'},
    "atm_grid=s"    => \$opts{'atm_grid'},
    "lnd_grid=s"    => \$opts{'lnd_grid'},
    "ocn_grid=s"    => \$opts{'ocn_grid'},
)  or usage();

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

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

# Define 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 $CASEROOT    = $opts{'caseroot'}; 
my $SCRIPTSROOT = $opts{'scriptsroot'};       
my $GRID        = $opts{'grid'}; 
my $ROF_GRID    = $opts{'rof_grid'}; 
my $WAV_GRID    = $opts{'wav_grid'}; 
my $ATM_GRID    = $opts{'atm_grid'}; 
my $LND_GRID    = $opts{'lnd_grid'}; 
my $OCN_GRID    = $opts{'ocn_grid'}; 

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

# Validate some of the commandline option values.
validate_options("commandline", \%opts);

#-----------------------------------------------------------------------------------------------
# Create config_cache.xml file (needed below)

my $config_cache = "${CASEROOT}/Buildconf/cplconf/config_cache.xml";
my  $fh = new IO::File;
$fh->open(">$config_cache") or die "** can't open file: $config_cache\n";
print $fh  <<"EOF";
<?xml version="1.0"?>
<config_definition>
<entry id="grid"     value="$GRID">
<entry id="rof_grid" value="$ROF_GRID">
<entry id="wav_grid" value="$WAV_GRID">
<entry id="atm_grid" value="$ATM_GRID">
<entry id="lnd_grid" value="$LND_GRID">
<entry id="ocn_grid" value="$OCN_GRID">
</config_definition>
EOF
$fh->close;
if ($print>=2) { print "Wrote file $config_cache $eol"; }
(-f "config_cache.xml")  or  die <<"EOF";
** $ProgName - Cannot find configuration cache file: config_cache.xml\" **
EOF
    
#-----------------------------------------------------------------------------------------------
# Make sure we can find required perl modules, definition, and defaults files.
# Look for them under the directory that contains the configure script.
    
# The root directory for the perl5 required utilities 
my $perl5lib_dir = "${SCRIPTSROOT}/../utils/perl5lib";

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

# The Build::Config module provides utilities to access the configuration information
# in the config_cache.xml file (see below)
(-f "$perl5lib_dir/Build/Config.pm")  or  die <<"EOF";
** $ProgName - Cannot find perl module \"Build/Config.pm\" in directory \"$perl5lib_dir\" **
EOF

# The Build::NamelistDefinition module provides utilities to validate that the output
# namelists are consistent with the namelist definition file
(-f "$perl5lib_dir/Build/NamelistDefinition.pm")  or  die <<"EOF";
** $ProgName - Cannot find perl module \"Build/NamelistDefinition.pm\" in directory \"$perl5lib_dir\" **
EOF

# The Build::NamelistDefaults module provides a utility to obtain default values of namelist
# variables based on finding a best fit with the attributes specified in the defaults file.
(-f "$perl5lib_dir/Build/NamelistDefaults.pm")  or  die <<"EOF";
** $ProgName - Cannot find perl module \"Build/NamelistDefaults.pm\" in directory \"$perl5lib_dir\" **
EOF

# The Build::Namelist module provides utilities to parse input namelists, to query and modify
# namelists, and to write output namelists.
(-f "$perl5lib_dir/Build/Namelist.pm")  or  die <<"EOF";
** $ProgName - Cannot find perl module \"Build/Namelist.pm\" in directory \"$perl5lib_dir\" **
EOF
my $nl_definition_file;
if (-f "${CASEROOT}/SourceMods/src.drv/namelist_definition_drv.xml") {
    $nl_definition_file = "${CASEROOT}/SourceMods/src.drv/namelist_definition_drv.xml";
}
if (! defined $nl_definition_file) {
    # default location of namelist definition file
    $nl_definition_file = "$cfgdir/namelist_files/namelist_definition_drv.xml";
    (-f "$nl_definition_file")  or  die <<"EOF";
    ** $ProgName - ERROR: Cannot find namelist definition file \"$nl_definition_file\" **
EOF
}
if ($print>=2) { print "Using namelist definition file $nl_definition_file$eol"; }

# The namelist defaults file contains default values for all required namelist variables.
my $nl_defaults_file = "$cfgdir/namelist_files/namelist_defaults_drv.xml";
(-f "$nl_defaults_file")  or  die <<"EOF";
** $ProgName - Cannot find namelist defaults file \"$nl_defaults_file\" **
EOF
if ($print>=2) { print "Using namelist defaults file $nl_defaults_file$eol"; }

my $nl_modio_definition_file = "$cfgdir/namelist_files/namelist_definition_modio.xml";
(-f "$nl_modio_definition_file")  or  die <<"EOF";
** $ProgName - Cannot find namelist modio definition file \"$nl_modio_definition_file\" **
EOF
if ($print>=2) { print "Using namelist modio definition file $nl_modio_definition_file$eol"; }

#-----------------------------------------------------------------------------------------------
# Add $perl5lib_dir to the list of paths that Perl searches for modules
unshift @INC, "$perl5lib_dir";
require XML::Lite;
require Build::Config;
require Build::NamelistDefinition;
require Build::NamelistDefaults;
require Build::Namelist;

#-----------------------------------------------------------------------------------------------
# Create a configuration object from the DRV config_cache.xml file-  created by 
# pop2.cpl7.template in $CASEROOT/Buildconf/cplconf
my $cfg = Build::Config->new('config_cache.xml');

# Create a namelist definition object.  This object provides a method for verifying that the
# output namelist variables are in the definition file, and are output in the correct
# namelist groups.
my $definition = Build::NamelistDefinition->new($nl_definition_file);

# Create a namelist defaults object.  This object provides default values for variables
# contained in the input defaults file.  The configuration object provides attribute
# values that are relevent for the cpl library for which the namelist is being produced.
my $defaults = Build::NamelistDefaults->new($nl_defaults_file, $cfg);

# Create an empty namelist object.  Add values to it in order of precedence.
my $nl = Build::Namelist->new();

#-----------------------------------------------------------------------------------------------
# Process the user input in order of precedence.  At each point we'll only add new
# values to the namelist and not overwrite previously specified specified values which
# have higher precedence.

# Process the commandline args that provide specific namelist values.

# Process the -namelist arg.
if (defined $opts{'namelist'}) {
    # Parse commandline namelist
    my $nl_arg = Build::Namelist->new($opts{'namelist'});

    # Validate input namelist -- trap exceptions
    my $nl_arg_valid;
    eval { $nl_arg_valid = $definition->validate($nl_arg); };
    if ($@) {
	die "$ProgName - ERROR: Invalid namelist variable in commandline arg '-namelist'.\n $@";
    }

    # Merge input values into namelist.  Previously specified values have higher precedence
    # and are not overwritten.
    $nl->merge_nl($nl_arg_valid);
}

# Process the -infile arg.
if (defined $opts{'infile'}) {
    # Parse namelist input from a file
    my $nl_infile = Build::Namelist->new($opts{'infile'});
    print " infile is $opts{'infile'} \n";

    # Validate input namelist -- trap exceptions
    my $nl_infile_valid;
    eval { $nl_infile_valid = $definition->validate($nl_infile); };
    if ($@) {
	die "$ProgName - ERROR: Invalid namelist variable in '-infile' $opts{'infile'}.\n $@";
    }

    # Merge input values into namelist.  Previously specified values have higher precedence
    # and are not overwritten.
    $nl->merge_nl($nl_infile_valid);
}

#-----------------------------------------------------------------------------------------------
# Determine xml variables
#-----------------------------------------------------------------------------------------------

my %xmlvars = ();
my @files = <${CASEROOT}/*xml>;
foreach my $file (@files) {
    my $xml = XML::Lite->new( "$file" );
    my @e = $xml->elements_by_name('entry');
    while ( my $e = shift @e ) {
	my %a = $e->get_attributes();
	$xmlvars{$a{'id'}} = $a{'value'};
    }
}
my $DIN_LOC_ROOT = $xmlvars{'DIN_LOC_ROOT'};
(-d $DIN_LOC_ROOT)  or  die <<"EOF";
** $ProgName - CCSM inputdata root is not a directory: \"$DIN_LOC_ROOT\" **
EOF
if ($print>=2) { print "CESM inputdata root directory: $DIN_LOC_ROOT$eol"; }

# Note - $USER is not in the config_defintion.xml file - it is only in the environment
$xmlvars{'USER'} = $ENV{'USER'};
unshift @INC, "$CASEROOT/Tools";
require XML::Lite;
require SetupTools;

my %xmlvars = ();
SetupTools::getxmlvars($CASEROOT, \%xmlvars);
foreach my $attr (keys %xmlvars) {
  $xmlvars{$attr} = SetupTools::expand_xml_var($xmlvars{$attr}, \%xmlvars);
}

#-----------------------------------------------------------------------------------------------
# Determine drv namelist 
#-----------------------------------------------------------------------------------------------

#############################################
# namelist group: seq_cplflds_inparm        #
#############################################

my $CCSM_BGC      = "$xmlvars{'CCSM_BGC'}";
my $flds_co2a     = ".false.";
my $flds_co2b     = ".false.";
my $flds_co2c     = ".false.";
my $flds_co2_dmsa = ".false.";
my $flds_bgc      = ".false.";
if ($CCSM_BGC eq 'CO2A') {
    $flds_co2a = ".true.";
} elsif ($CCSM_BGC eq 'CO2B') {
    $flds_co2b = ".true.";
} elsif ($CCSM_BGC eq 'CO2C') {
    $flds_co2c =  ".true.";
} elsif ($CCSM_BGC eq 'CO2_DMSA') {
    $flds_co2_dmsa = ".true.";
}

my $glc_nec = "$xmlvars{'GLC_NEC'}";
if ( ! defined $glc_nec ) {$glc_nec = 0;}

add_default($nl, 'flds_co2a',    'val'=>"$flds_co2a");
add_default($nl, 'flds_co2b',    'val'=>"$flds_co2b");
add_default($nl, 'flds_co2c',    'val'=>"$flds_co2c");
add_default($nl, 'flds_co2_dmsa','val'=>"$flds_co2_dmsa");
add_default($nl, 'flds_bgc',     'val'=>"$flds_bgc");
add_default($nl, 'glc_nec',      'val'=>"$glc_nec");
    
#############################################
# namelist group: seq_cplflds_userspec      #
#############################################

add_default($nl, 'cplflds_custom', 'val'=>'');

#############################################
# namelist group: seq_infodata_inparm       #
#############################################

my $PTS_MODE = "$xmlvars{'PTS_MODE'}"; 

my $start_type;
my $RUN_TYPE     = "$xmlvars{'RUN_TYPE'}";
my $CONTINUE_RUN = "$xmlvars{'CONTINUE_RUN'}";
if ($RUN_TYPE eq 'startup' ) {$start_type = "startup";}
if ($RUN_TYPE eq 'hybrid'  ) {$start_type = "startup";}
if ($RUN_TYPE eq 'branch'  ) {$start_type = "branch";}
if ($CONTINUE_RUN eq 'TRUE') {$start_type = "continue";}
add_default($nl, 'start_type', 'val'=>"$start_type",'xml'=>'RUN_TYPE');

add_default($nl, 'case_name',     'val'=>"$xmlvars{'CASE'}",         'xml'=>'CASE');
add_default($nl, 'case_desc',     'val'=>"$xmlvars{'CASESTR'}",      'xml'=>'CASESTR');
add_default($nl, 'username' ,     'val'=>"$xmlvars{'CCSMUSER'}",     'xml'=>'CCSMUSER');
add_default($nl, 'hostname' ,     'val'=>"$xmlvars{'MACH'}",         'xml'=>'MACH');
add_default($nl, 'model_version', 'val'=>"$xmlvars{'CCSM_REPOTAG'}", 'xml'=>'CCSM_REPOTAG');

add_default($nl, 'atm_gnam',      'val'=>"$xmlvars{'ATM_GRID'}",     'xml'=>'ATM_GRID');
add_default($nl, 'lnd_gnam',      'val'=>"$xmlvars{'LND_GRID'}",     'xml'=>'LND_GRID');
add_default($nl, 'ocn_gnam',      'val'=>"$xmlvars{'OCN_GRID'}",     'xml'=>'OCN_GRID');
add_default($nl, 'ice_gnam',      'val'=>"$xmlvars{'ICE_GRID'}",     'xml'=>'ICE_GRID');
add_default($nl, 'rof_gnam',      'val'=>"$xmlvars{'ROF_GRID'}",     'xml'=>'ROF_GRID');
add_default($nl, 'glc_gnam',      'val'=>"$xmlvars{'GLC_GRID'}",     'xml'=>'GLC_GRID');
add_default($nl, 'wav_gnam',      'val'=>"$xmlvars{'WAV_GRID'}",     'xml'=>'WAV_GRID');

add_default($nl, 'brnch_retain_casename', 'BRNCH_RETAIN_CASENAME'=>"$xmlvars{'BRNCH_RETAIN_CASENAME'}");
add_default($nl, 'cpl_seq_option', 'val'=>"$xmlvars{'CPL_SEQ_OPTION'}");
add_default($nl, 'drv_threading', 'DRV_THREADING'=>"$xmlvars{'DRV_THREADING'}");

add_default($nl, 'run_barriers', 'COMP_RUN_BARRIERS'=>"$xmlvars{'COMP_RUN_BARRIERS'}");
add_default($nl, 'bfbflag', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'do_budgets','BUDGETS'=>"$xmlvars{'BUDGETS'}");

add_default($nl, 'flux_diurnal');
add_default($nl, 'flux_albav');
add_default($nl, 'flux_epbal');
add_default($nl, 'info_debug');
add_default($nl, 'cpl_decomp');
add_default($nl, 'wall_time_limit');
add_default($nl, 'force_stop_at');

add_default($nl, 'do_histinit');
add_default($nl, 'shr_map_dopole');
add_default($nl, 'cpl_cdf64');
add_default($nl, 'aoflux_grid');

add_default($nl, 'orb_mode');
add_default($nl, 'orb_iyear');
add_default($nl, 'orb_iyear_align');

add_default($nl, 'wv_sat_scheme');
add_default($nl, 'wv_sat_transition_start');
add_default($nl, 'wv_sat_use_tables');
add_default($nl, 'wv_sat_table_spacing');

add_default($nl, 'budget_inst');
add_default($nl, 'budget_daily');
add_default($nl, 'budget_month');
add_default($nl, 'budget_ann');
add_default($nl, 'budget_ltann');
add_default($nl, 'budget_ltend');

add_default($nl, 'histaux_a2x');
add_default($nl, 'histaux_a2x3hr');
add_default($nl, 'histaux_a2x3hrp');
add_default($nl, 'histaux_a2x24hr');
add_default($nl, 'histaux_l2x');
add_default($nl, 'histaux_r2x');
add_default($nl, 'histaux_l2x1yr');
add_default($nl, 'histavg_atm');
add_default($nl, 'histavg_lnd');
add_default($nl, 'histavg_ocn');
add_default($nl, 'histavg_ice');
add_default($nl, 'histavg_rof');
add_default($nl, 'histavg_glc');
add_default($nl, 'histavg_wav');
add_default($nl, 'histavg_xao');

add_default($nl, 'vect_map');
add_default($nl, 'eps_frac');
add_default($nl, 'eps_amask');
add_default($nl, 'eps_agrid');
add_default($nl, 'eps_aarea');
add_default($nl, 'eps_omask');
add_default($nl, 'eps_ogrid');
add_default($nl, 'eps_oarea');

add_default($nl, 'mct_usealltoall');
add_default($nl, 'mct_usevector');

add_default($nl, 'timing_dir');
add_default($nl, 'tchkpt_dir');

if ($PTS_MODE eq "TRUE") {
    add_default($nl, 'single_column', 'val'=>".true.");
    add_default($nl, 'scmlat', 'val'=>"$xmlvars{'PTS_LAT'}", 'xml'=>'PTS_LAT');
    add_default($nl, 'scmlon', 'val'=>"$xmlvars{'PTS_LON'}", 'xml'=>'PTS_LON');
}

# Error checks
my $runtype = $nl->get_value('start_type');
my $brnch_retain_casename = $nl->get_value('brnch_retain_casename');
my $case = $xmlvars{'CASE'};
my $run_refcase = $xmlvars{'RUN_REFCASE'};
if (($runtype ne 'startup') && ($case eq $run_refcase) && ($brnch_retain_casename ne '.true.')) {
print "$ProgName ERROR: CASE and RUN_REFCASE are identical \n";
die   "      Set BRNCH_RETAIN_CASENAME to TRUE in env_run.xml \n";
}

#############################################
# namelist group: seq_timemgr_inparm        #
#############################################

my $CALENDAR = "$xmlvars{'CALENDAR'}";
add_default($nl, 'calendar', 'val'=>"$xmlvars{'CALENDAR'}");

my $NCPL_BASE_PERIOD  = "$xmlvars{'NCPL_BASE_PERIOD'}";
my $basedt;
if ($NCPL_BASE_PERIOD eq 'hour') {
    $basedt = 3600;
} elsif ($NCPL_BASE_PERIOD eq 'day') {
    $basedt = 3600 * 24;
} elsif ($NCPL_BASE_PERIOD eq 'year') {
    if ($CALENDAR eq 'NO_LEAP') {
	$basedt = 3600 * 24 * 365;
    } else {
	die "$ProgName: ERROR invalid CALENDAR for NCPL_BASE_PERIOD $NCPL_BASE_PERIOD \n";
    }
} elsif ($NCPL_BASE_PERIOD eq 'decade') {
    if ($CALENDAR eq 'NO_LEAP') {
	$basedt = 3600 * 24 * 365 * 10; 
    } else {
	die "$ProgName: ERROR invalid CALENDAR for NCPL_BASE_PERIOD $NCPL_BASE_PERIOD \n";
    }
} else {
    die "$ProgName: ERROR invalid NCPL_BASE_PERIOD $NCPL_BASE_PERIOD \n";
}
if ($basedt < 0) {
   die "$ProgName: ERROR basedt invalid overflow for NCPL_BASE_PERIOD $NCPL_BASE_PERIOD \n";
} 

my $ATM_NCPL = "$xmlvars{'ATM_NCPL'}";
my $LND_NCPL = "$xmlvars{'LND_NCPL'}";
my $OCN_NCPL = "$xmlvars{'OCN_NCPL'}";
my $ICE_NCPL = "$xmlvars{'ICE_NCPL'}";
my $GLC_NCPL = "$xmlvars{'GLC_NCPL'}";
my $ROF_NCPL = "$xmlvars{'ROF_NCPL'}"; 
my $WAV_NCPL = "$xmlvars{'WAV_NCPL'}"; 

my $atm_cpl_dt = $basedt / $ATM_NCPL;
my $lnd_cpl_dt = $basedt / $LND_NCPL;
my $ocn_cpl_dt = $basedt / $OCN_NCPL;
my $ice_cpl_dt = $basedt / $ICE_NCPL;
my $glc_cpl_dt = $basedt / $GLC_NCPL;
my $rof_cpl_dt = $basedt / $ROF_NCPL;
my $wav_cpl_dt = $basedt / $WAV_NCPL;

my $totaldt = $atm_cpl_dt * $ATM_NCPL;
if ($totaldt != $basedt) { die "ERROR atm NCPL doesn't divide base dt evenly";}
my $totaldt = $lnd_cpl_dt * $LND_NCPL;
if ($totaldt != $basedt) { die "ERROR lnd NCPL doesn't divide base dt evenly";}
my $totaldt = $ocn_cpl_dt * $OCN_NCPL;
if ($totaldt != $basedt) { die "ERROR ocn NCPL doesn't divide base dt evenly";}
my $totaldt = $ice_cpl_dt * $ICE_NCPL;
if ($totaldt != $basedt) { die "ERROR ice NCPL doesn't divide base dt evenly";}
my $totaldt = $glc_cpl_dt * $GLC_NCPL;
if ($totaldt != $basedt) { die "ERROR glc NCPL doesn't divide base dt evenly";}
my $totaldt = $rof_cpl_dt * $ROF_NCPL;
if ($totaldt != $basedt) { die "ERROR rof NCPL doesn't divide base dt evenly";}
my $totaldt = $wav_cpl_dt * $WAV_NCPL;
if ($totaldt != $basedt) { die "ERROR wav NCPL doesn't divide base dt evenly";}

add_default($nl, 'atm_cpl_dt', 'val'=>"$atm_cpl_dt", 'xml'=>'ATM_NCPL');
add_default($nl, 'lnd_cpl_dt', 'val'=>"$lnd_cpl_dt", 'xml'=>'LND_NCPL');
add_default($nl, 'ocn_cpl_dt', 'val'=>"$ocn_cpl_dt", 'xml'=>'OCN_NCPL');
add_default($nl, 'ice_cpl_dt', 'val'=>"$ice_cpl_dt", 'xml'=>'ICE_NCPL');
add_default($nl, 'glc_cpl_dt', 'val'=>"$glc_cpl_dt", 'xml'=>'GLC_NCPL');
add_default($nl, 'rof_cpl_dt', 'val'=>"$rof_cpl_dt", 'xml'=>'ROF_NCPL');
add_default($nl, 'wav_cpl_dt', 'val'=>"$wav_cpl_dt", 'xml'=>'WAV_NCPL');

my $RUN_STARTDATE = "$xmlvars{'RUN_STARTDATE'}"; $RUN_STARTDATE =~ s/[-]//g;
add_default($nl, 'start_ymd', 'val'=>"$RUN_STARTDATE",        'xml'=>'RUN_STARTDATE');
add_default($nl, 'start_tod', 'val'=>"$xmlvars{'START_TOD'}", 'xml'=>'START_TOD');

add_default($nl, 'stop_option');
add_default($nl, 'stop_n');
add_default($nl, 'stop_ymd');

add_default($nl, 'restart_option');
add_default($nl, 'restart_n');
add_default($nl, 'restart_ymd');
add_default($nl, 'end_restart');

add_default($nl, 'history_option');
add_default($nl, 'history_n');
add_default($nl, 'history_ymd');

add_default($nl, 'histavg_option');
add_default($nl, 'histavg_n');
add_default($nl, 'histavg_ymd');

# This would be better handled inside the alarm logic in the driver routines.
# Here supporting only nday(s), nmonth(s), and nyear(s).
my $tprofmult = 1;
my $tprofoption = 'never';
my $STOP_OPTION = "$xmlvars{'STOP_OPTION'}";
if ($STOP_OPTION =~ m/nyear/) {
    $tprofoption = 'ndays';
    $tprofmult = 365;
}
elsif ($STOP_OPTION =~ m/nmonth/){ 
    $tprofoption = 'ndays';
    $tprofmult = 30;
}
elsif ($STOP_OPTION =~ m/nday/) {
    $tprofoption = 'ndays';
}

my $tprofn;
my $STOP_DATE = "$xmlvars{'STOP_DATE'}";
my $TPROF_TOTAL = "$xmlvars{'TPROF_TOTAL'}"; 
if (($TPROF_TOTAL > 0) && ($STOP_DATE < 0) && ($tprofoption =~ m/ndays/)) {
    my $STOP_N = "$xmlvars{'STOP_N'}";
    my $stopn = $tprofmult * $STOP_N;
    $tprofn = int($stopn / $TPROF_TOTAL);
    if ($tprofn < 1){
        $tprofn = 1;
    }
    add_default($nl, 'tprof_option', 'val'=>"$tprofoption");
    add_default($nl, 'tprof_n',      'val'=>"$tprofn");
} else {
    add_default($nl, 'tprof_option');
    add_default($nl, 'tprof_n');  
}
add_default($nl, 'tprof_ymd');
add_default($nl, 'max_cplstep_time');

#############################################
# namelist group: ccsm_pes                  #
#############################################


add_default($nl, 'atm_ntasks',   'val'=>"$xmlvars{'NTASKS_ATM'}", 'xml'=>'NTASKS_ATM');
add_default($nl, 'atm_nthreads', 'val'=>"$xmlvars{'NTHRDS_ATM'}", 'xml'=>'NTHRDS_ATM');
add_default($nl, 'atm_rootpe',   'val'=>"$xmlvars{'ROOTPE_ATM'}", 'xml'=>'ROOTPE_ATM');
add_default($nl, 'atm_pestride', 'val'=>"$xmlvars{'PSTRID_ATM'}", 'xml'=>'PSTRID_ATM');
add_default($nl, 'atm_layout',   'val'=>"$xmlvars{'NINST_ATM_LAYOUT'}", 'xml'=>'NINST_ATM_LAYOUT');

add_default($nl, 'lnd_ntasks',   'val'=>"$xmlvars{'NTASKS_LND'}", 'xml'=>'NTASKS_LND');
add_default($nl, 'lnd_nthreads', 'val'=>"$xmlvars{'NTHRDS_LND'}", 'xml'=>'NTHRDS_LND');
add_default($nl, 'lnd_rootpe',   'val'=>"$xmlvars{'ROOTPE_LND'}", 'xml'=>'ROOTPE_LND');
add_default($nl, 'lnd_pestride', 'val'=>"$xmlvars{'PSTRID_LND'}", 'xml'=>'PSTRID_LND');
add_default($nl, 'lnd_layout',   'val'=>"$xmlvars{'NINST_LND_LAYOUT'}", 'xml'=>'NINST_LND_LAYOUT');

add_default($nl, 'ice_ntasks',   'val'=>"$xmlvars{'NTASKS_ICE'}", 'xml'=>'NTASKS_ICE');
add_default($nl, 'ice_nthreads', 'val'=>"$xmlvars{'NTHRDS_ICE'}", 'xml'=>'NTHRDS_ICE');
add_default($nl, 'ice_rootpe',   'val'=>"$xmlvars{'ROOTPE_ICE'}", 'xml'=>'ROOTPE_ICE');
add_default($nl, 'ice_pestride', 'val'=>"$xmlvars{'PSTRID_ICE'}", 'xml'=>'PSTRID_ICE');
add_default($nl, 'ice_layout',   'val'=>"$xmlvars{'NINST_ICE_LAYOUT'}", 'xml'=>'NINST_ICE_LAYOUT');

add_default($nl, 'ocn_ntasks',   'val'=>"$xmlvars{'NTASKS_OCN'}", 'xml'=>'NTASKS_OCN');
add_default($nl, 'ocn_nthreads', 'val'=>"$xmlvars{'NTHRDS_OCN'}", 'xml'=>'NTHRDS_OCN');
add_default($nl, 'ocn_rootpe',   'val'=>"$xmlvars{'ROOTPE_OCN'}", 'xml'=>'ROOTPE_OCN');
add_default($nl, 'ocn_pestride', 'val'=>"$xmlvars{'PSTRID_OCN'}", 'xml'=>'PSTRID_OCN');
add_default($nl, 'ocn_layout',   'val'=>"$xmlvars{'NINST_OCN_LAYOUT'}", 'xml'=>'NINST_OCN_LAYOUT');

add_default($nl, 'glc_ntasks',   'val'=>"$xmlvars{'NTASKS_GLC'}", 'xml'=>'NTASKS_GLC');
add_default($nl, 'glc_nthreads', 'val'=>"$xmlvars{'NTHRDS_GLC'}", 'xml'=>'NTHRDS_GLC');
add_default($nl, 'glc_rootpe',   'val'=>"$xmlvars{'ROOTPE_GLC'}", 'xml'=>'ROOTPE_GLC');
add_default($nl, 'glc_pestride', 'val'=>"$xmlvars{'PSTRID_GLC'}", 'xml'=>'PSTRID_GLC');
add_default($nl, 'glc_layout',   'val'=>"$xmlvars{'NINST_GLC_LAYOUT'}", 'xml'=>'NINST_GLC_LAYOUT');

add_default($nl, 'rof_ntasks',   'val'=>"$xmlvars{'NTASKS_ROF'}", 'xml'=>'NTASKS_ROF');
add_default($nl, 'rof_nthreads', 'val'=>"$xmlvars{'NTHRDS_ROF'}", 'xml'=>'NTHRDS_ROF');
add_default($nl, 'rof_rootpe',   'val'=>"$xmlvars{'ROOTPE_ROF'}", 'xml'=>'ROOTPE_ROF');
add_default($nl, 'rof_pestride', 'val'=>"$xmlvars{'PSTRID_ROF'}", 'xml'=>'PSTRID_ROF');
add_default($nl, 'rof_layout',   'val'=>"$xmlvars{'NINST_ROF_LAYOUT'}", 'xml'=>'NINST_ROF_LAYOUT');

add_default($nl, 'wav_ntasks',   'val'=>"$xmlvars{'NTASKS_WAV'}", 'xml'=>'NTASKS_WAV');
add_default($nl, 'wav_nthreads', 'val'=>"$xmlvars{'NTHRDS_WAV'}", 'xml'=>'NTHRDS_WAV');
add_default($nl, 'wav_rootpe',   'val'=>"$xmlvars{'ROOTPE_WAV'}", 'xml'=>'ROOTPE_WAV');
add_default($nl, 'wav_pestride', 'val'=>"$xmlvars{'PSTRID_WAV'}", 'xml'=>'PSTRID_WAV');
add_default($nl, 'wav_layout',   'val'=>"$xmlvars{'NINST_WAV_LAYOUT'}", 'xml'=>'NINST_WAV_LAYOUT');

add_default($nl, 'cpl_ntasks',   'val'=>"$xmlvars{'NTASKS_CPL'}", 'xml'=>'NTASKS_CPL');
add_default($nl, 'cpl_nthreads', 'val'=>"$xmlvars{'NTHRDS_CPL'}", 'xml'=>'NTHRDS_CPL');
add_default($nl, 'cpl_rootpe',   'val'=>"$xmlvars{'ROOTPE_CPL'}", 'xml'=>'ROOTPE_CPL');
add_default($nl, 'cpl_pestride', 'val'=>"$xmlvars{'PSTRID_CPL'}", 'xml'=>'PSTRID_CPL');


#############################################
# namelist group: prof_inparm               #
#   in utils/timing/perf_mod.F90   
#############################################

my $timer_level = $xmlvars{'TIMER_LEVEL'};
if ( $timer_level <= 0) {
    add_default($nl, 'profile_disable'    , 'TIMER_LEVEL'=>"neg");
    add_default($nl, 'profile_depth_limit', 'TIMER_LEVEL'=>"neg");
} elsif ($timer_level >= 1 ) {
    add_default($nl, 'profile_disable'    , 'TIMER_LEVEL'=>"pos");
    add_default($nl, 'profile_depth_limit', 'TIMER_LEVEL'=>"pos");
}

my $timer_detail = $xmlvars{'TIMER_DETAIL'};
if ( $timer_detail <= 0) {
    add_default($nl, 'profile_detail_limit', 'TIMER_DETAIL'=>"neg");
} elsif ($timer_level >= 1 ) {
    add_default($nl, 'profile_detail_limit', 'TIMER_DETAIL'=>"pos");
}

add_default($nl, 'profile_papi_enable', 'PROFILE_PAPI_ENABLE'=>"$xmlvars{'PROFILE_PAPI_ENABLE'}");

add_default($nl, 'profile_timer', 'MPILIB'=>"$xmlvars{'MPILIB'}", 'OS'=>"$xmlvars{'OS'}",'MACH'=>"$xmlvars{'MACH'}");
add_default($nl, 'profile_global_stats');
add_default($nl, 'profile_single_file');
add_default($nl, 'profile_detail_limit');
add_default($nl, 'profile_barrier');
add_default($nl, 'profile_outpe_num');
add_default($nl, 'profile_ovhd_measurement');

#############################################
# namelist group: papi_inparm               #
#   in utils/timing/perf_mod.F90            #
#############################################

add_default($nl, 'papi_ctr1_str');
add_default($nl, 'papi_ctr2_str');
add_default($nl, 'papi_ctr3_str');
add_default($nl, 'papi_ctr4_str');

#############################################
# namelist group: pio_default_inparm        #
#############################################

add_default($nl, 'pio_async_interface', 'PIO_ASYNC_INTERFACE'=>"$xmlvars{'PIO_ASYNC_INTERFACE'}");
add_default($nl, 'pio_stride');
add_default($nl, 'pio_root');
add_default($nl, 'pio_numiotasks');
add_default($nl, 'pio_typename');
add_default($nl, 'pio_debug_level');
add_default($nl, 'pio_blocksize');
add_default($nl, 'pio_buffer_size_limit');
add_default($nl, 'pio_rearr_comm_type', 'PIO_REARR_COMM_TYPE'=>"$xmlvars{'PIO_REARR_COMM_TYPE'}");
add_default($nl, 'pio_rearr_comm_fcd', 'PIO_REARR_COMM_FCD'=>"$xmlvars{'PIO_REARR_COMM_FCD'}");
add_default($nl, 'pio_rearr_comm_max_pend_req', 'PIO_REARR_COMM_MAX_PEND_REQ'=>"$xmlvars{'PIO_REARR_COMM_MAX_PEND_REQ'}");
add_default($nl, 'pio_rearr_comm_enable_hs', 'PIO_REARR_COMM_ENABLE_HS'=>"$xmlvars{'PIO_REARR_COMM_ENABLE_HS'}");
add_default($nl, 'pio_rearr_comm_enable_isend', 'PIO_REARR_COMM_ENABLE_ISEND'=>"$xmlvars{'PIO_REARR_COMM_ENABLE_ISEND'}");

# Note: pio_async_interface=.true. is not yet supported
# If pio_async_interface is .true. or {component}_PIO_* variable is not set or set to -99
# the component variable will be set using the pio_* value.

#-----------------------------------------------------------------------------------------------
# (1) Write output namelist file (drv_in) and input dataset list (drv.input_data_list) 
#-----------------------------------------------------------------------------------------------

# Write out drv_in namelist groups 
my @groups = qw(seq_cplflds_inparm
		seq_cplflds_userspec
		seq_infodata_inparm
		seq_timemgr_inparm
 		ccsm_pes
		prof_inparm
		papi_inparm
                pio_default_inparm);

my $outfile = "./drv_in";
$nl->write($outfile, 'groups'=>\@groups);
if ($print>=2) { print "Writing driver namelist to $outfile $eol"; }
               
#-----------------------------------------------------------------------------------------------
# (2) Write out seq_map.rc file
#-----------------------------------------------------------------------------------------------

my $fh = new IO::File;
$fh->open(">seq_maps.rc") or die "** can't open file: seq_maps.rc\n";
print $fh <<"EOF";
##################################################################
#
# seq_maps.rc
#
# This is a resource file which lists the names of mapping
# weight files to use in a sequential CCSM run (mapname).
# You can also set when data is rearranged in the mapping (maptype).
#
# This file is read during the map_model2model_init calls.
#
# For maptype:  X = Rearrange the input so that the output
#                   is on the correct processor.
#               Y = Rearrange the output and sum partial outputs
#                   if necessary
#
# NOTE:  For bfb on different processor counts, set all maptypes to "X".
################################################################## 
EOF
$fh->close();

my @mapgroup = qw(seq_maps);

add_default($nl, 'atm2ocn_fmapname');
add_default($nl, 'atm2ocn_smapname');
add_default($nl, 'atm2ocn_vmapname');
add_default($nl, 'ocn2atm_fmapname');
add_default($nl, 'ocn2atm_smapname');
add_default($nl, 'atm2lnd_fmapname');
add_default($nl, 'atm2lnd_smapname');
add_default($nl, 'lnd2atm_fmapname');
add_default($nl, 'lnd2atm_smapname');
add_default($nl, 'lnd2rof_fmapname');
add_default($nl, 'rof2lnd_fmapname');
add_default($nl, 'rof2ocn_fmapname');
add_default($nl, 'rof2ocn_rmapname');
add_default($nl, 'glc2ocn_rmapname');
add_default($nl, 'glc2ice_rmapname');
add_default($nl, 'lnd2glc_fmapname');
add_default($nl, 'lnd2glc_smapname');
add_default($nl, 'glc2lnd_fmapname');
add_default($nl, 'glc2lnd_smapname');
add_default($nl, 'atm2wav_smapname');
add_default($nl, 'ocn2wav_smapname');
add_default($nl, 'ice2wav_smapname');
add_default($nl, 'wav2ocn_smapname');

my $atm2ice_fmapname = $nl->get_value('atm2ocn_fmapname');
my $atm2ice_smapname = $nl->get_value('atm2ocn_smapname');
my $atm2ice_vmapname = $nl->get_value('atm2ocn_vmapname');
my $ice2atm_fmapname = $nl->get_value('ocn2atm_fmapname');
my $ice2atm_smapname = $nl->get_value('ocn2atm_smapname');
add_default($nl, 'atm2ice_fmapname', 'val'=>"$atm2ice_fmapname");
add_default($nl, 'atm2ice_smapname', 'val'=>"$atm2ice_smapname");
add_default($nl, 'atm2ice_vmapname', 'val'=>"$atm2ice_vmapname");
add_default($nl, 'ice2atm_fmapname', 'val'=>"$ice2atm_fmapname");
add_default($nl, 'ice2atm_smapname', 'val'=>"$ice2atm_smapname");

add_default($nl, 'atm2ocn_fmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'atm2ocn_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'atm2ocn_vmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'ocn2atm_fmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'ocn2atm_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'atm2lnd_fmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'atm2lnd_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'lnd2atm_fmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'lnd2atm_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'lnd2rof_fmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'rof2lnd_fmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'rof2ocn_fmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'rof2ocn_rmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'glc2ocn_rmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'glc2ice_rmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'lnd2glc_fmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'lnd2glc_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'glc2lnd_fmaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'glc2lnd_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'atm2wav_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'ocn2wav_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'ice2wav_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");
add_default($nl, 'wav2ocn_smaptype', 'BFBFLAG'=>"$xmlvars{'BFBFLAG'}");

my $atm2ice_fmaptype = $nl->get_value('atm2ocn_fmaptype');
my $atm2ice_smaptype = $nl->get_value('atm2ocn_smaptype');
my $atm2ice_vmaptype = $nl->get_value('atm2ocn_vmaptype');
my $ice2atm_fmaptype = $nl->get_value('ocn2atm_fmaptype');
my $ice2atm_smaptype = $nl->get_value('ocn2atm_smaptype');
add_default($nl, 'atm2ice_fmaptype', 'val'=>"$atm2ice_fmaptype");
add_default($nl, 'atm2ice_smaptype', 'val'=>"$atm2ice_smaptype");
add_default($nl, 'atm2ice_vmaptype', 'val'=>"$atm2ice_vmaptype");
add_default($nl, 'ice2atm_fmaptype', 'val'=>"$ice2atm_fmaptype");
add_default($nl, 'ice2atm_smaptype', 'val'=>"$ice2atm_smaptype");

$nl->write_rc('./seq_maps.rc', 'groups'=>\@mapgroup, 'append'=>"1");
if ($print>=2) { print "Writing map namelist to seq_maps.rc $eol"; }	

#-----------------------------------------------------------------------------------------------
# (3) Write input dataset list.
#-----------------------------------------------------------------------------------------------

check_input_files($nl, $DIN_LOC_ROOT, "../cpl.input_data_list");

#-----------------------------------------------------------------------------------------------
# (4) Write out model_modelio.nml files
#-----------------------------------------------------------------------------------------------

$definition = Build::NamelistDefinition->new($nl_modio_definition_file);

foreach my $model (qw(cpl atm lnd ice ocn glc rof wav)) {

    my $LID     = "$ENV{'LID'}";
    my $moddiri = "$xmlvars{'EXEROOT'}/${model}";
    my $moddiro = "$xmlvars{'RUNDIR'}";
    my $logfile;
    
    my $inst_count;
    if ($model eq 'cpl') {$inst_count = 1;}
    if ($model eq 'atm') {$inst_count = "$xmlvars{'NINST_ATM'}";}
    if ($model eq 'lnd') {$inst_count = "$xmlvars{'NINST_LND'}";}
    if ($model eq 'rof') {$inst_count = "$xmlvars{'NINST_ROF'}";}
    if ($model eq 'wav') {$inst_count = "$xmlvars{'NINST_WAV'}";}
    if ($model eq 'ice') {$inst_count = "$xmlvars{'NINST_ICE'}";}
    if ($model eq 'ocn') {$inst_count = "$xmlvars{'NINST_OCN'}";}
    if ($model eq 'glc') {$inst_count = "$xmlvars{'NINST_GLC'}";}
    
    my $inst_index = 1;
    my $inst_string;
    my $modeliofile;
    my @iogroup = qw(modelio pio_inparm);
    while ($inst_index <= $inst_count) {

	$inst_string = "$inst_index";
	if ($inst_index <= 999) {$inst_string = "0${inst_string}"};
	if ($inst_index <=  99) {$inst_string = "0${inst_string}"};
	if ($inst_index <=   9) {$inst_string = "0${inst_string}"};

	if ($inst_count > 1) {
	    $modeliofile = "${model}_modelio.nml_${inst_string}";
	} else {
	    $modeliofile = "./${model}_modelio.nml";
	}

	my $nlio = Build::Namelist->new();

	if ($inst_count > 1) {
	    $logfile = "${model}_${inst_string}.log.$LID";
	} else {
	    $logfile = "${model}.log.$LID";
	}
	add_default($nlio, 'diri',   'val'=>"$moddiri");
	add_default($nlio, 'diro',   'val'=>"$moddiro");
	add_default($nlio, 'logfile','val'=>"$logfile");

	if ($model eq 'cpl') {
	    add_default($nlio, 'pio_stride',    'val'=>"$xmlvars{'CPL_PIO_STRIDE'}");
	    add_default($nlio, 'pio_root',      'val'=>"$xmlvars{'CPL_PIO_ROOT'}");
	    add_default($nlio, 'pio_numiotasks','val'=>"$xmlvars{'CPL_PIO_NUMTASKS'}");
	    add_default($nlio, 'pio_typename',  'val'=>"$xmlvars{'CPL_PIO_TYPENAME'}");
	}
	if ($model eq 'atm') {
	    add_default($nlio, 'pio_stride',     'val'=>"$xmlvars{'ATM_PIO_STRIDE'}");
	    add_default($nlio, 'pio_root',       'val'=>"$xmlvars{'ATM_PIO_ROOT'}");
	    add_default($nlio, 'pio_numiotasks', 'val'=>"$xmlvars{'ATM_PIO_NUMTASKS'}");
	    add_default($nlio, 'pio_typename',   'val'=>"$xmlvars{'ATM_PIO_TYPENAME'}");
	}
	if ($model eq 'lnd') {
	    add_default($nlio, 'pio_stride',     'val'=>"$xmlvars{'LND_PIO_STRIDE'}");
	    add_default($nlio, 'pio_root',       'val'=>"$xmlvars{'LND_PIO_ROOT'}");
	    add_default($nlio, 'pio_numiotasks', 'val'=>"$xmlvars{'LND_PIO_NUMTASKS'}");
	    add_default($nlio, 'pio_typename',   'val'=>"$xmlvars{'LND_PIO_TYPENAME'}");
	}
	if ($model eq 'rof') {
	    add_default($nlio, 'pio_stride',     'val'=>"$xmlvars{'ROF_PIO_STRIDE'}");
	    add_default($nlio, 'pio_root',       'val'=>"$xmlvars{'ROF_PIO_ROOT'}");
	    add_default($nlio, 'pio_numiotasks', 'val'=>"$xmlvars{'ROF_PIO_NUMTASKS'}");
	    add_default($nlio, 'pio_typename',   'val'=>"$xmlvars{'ROF_PIO_TYPENAME'}");
	}
	if ($model eq 'wav') {
	    add_default($nlio, 'pio_stride',     'val'=>"$xmlvars{'WAV_PIO_STRIDE'}");
	    add_default($nlio, 'pio_root',       'val'=>"$xmlvars{'WAV_PIO_ROOT'}");
	    add_default($nlio, 'pio_numiotasks', 'val'=>"$xmlvars{'WAV_PIO_NUMTASKS'}");
	    add_default($nlio, 'pio_typename',   'val'=>"$xmlvars{'WAV_PIO_TYPENAME'}");
	}
	if ($model eq 'ice') {
	    add_default($nlio, 'pio_stride',     'val'=>"$xmlvars{'ICE_PIO_STRIDE'}");
	    add_default($nlio, 'pio_root',       'val'=>"$xmlvars{'ICE_PIO_ROOT'}");
	    add_default($nlio, 'pio_numiotasks', 'val'=>"$xmlvars{'ICE_PIO_NUMTASKS'}");
	    add_default($nlio, 'pio_typename',   'val'=>"$xmlvars{'ICE_PIO_TYPENAME'}");
	}
	if ($model eq 'ocn') {
	    add_default($nlio, 'pio_stride',     'val'=>"$xmlvars{'OCN_PIO_STRIDE'}");
	    add_default($nlio, 'pio_root',       'val'=>"$xmlvars{'OCN_PIO_ROOT'}");
	    add_default($nlio, 'pio_numiotasks', 'val'=>"$xmlvars{'OCN_PIO_NUMTASKS'}");
	    add_default($nlio, 'pio_typename',   'val'=>"$xmlvars{'OCN_PIO_TYPENAME'}");
	}
	if ($model eq 'glc') {
	    add_default($nlio, 'pio_stride',     'val'=>"$xmlvars{'GLC_PIO_STRIDE'}");
	    add_default($nlio, 'pio_root',       'val'=>"$xmlvars{'GLC_PIO_ROOT'}");
	    add_default($nlio, 'pio_numiotasks', 'val'=>"$xmlvars{'GLC_PIO_NUMTASKS'}");
	    add_default($nlio, 'pio_typename',   'val'=>"$xmlvars{'GLC_PIO_TYPENAME'}");
	}
	$nlio->write($modeliofile, 'groups'=>\@iogroup);
	if ($print>=2) { print "Writing io namelist to $modeliofile $eol"; }	
	$inst_index = $inst_index + 1;
    }
}


#-----------------------------------------------------------------------------------------------
# END OF MAIN SCRIPT
#===============================================================================================

#===============================================================================================
sub add_default {

# Add a value for the specified variable to the specified namelist object.  The variables
# already in the object have the higher precedence, so if the specified variable is already
# defined in the object then don't overwrite it, just return.
#
# This method checks the definition file and adds the variable to the correct
# namelist group.
#
# The value can be provided by using the optional argument key 'val' in the
# calling list.  Otherwise a default value is obtained from the namelist
# defaults object.  If no default value is found this method throws an exception
# unless the 'nofail' option is set true.
#
# Additional optional keyword=>value pairs may be specified.  If the keyword 'val' is
# not present, then any other keyword=>value pairs that are specified will be used to
# match attributes in the defaults file.
#
# Example 1: Specify the default value $val for the namelist variable $var in namelist
#            object $nl:
#
#  add_default($nl, $var, 'val'=>$val)
#
# Example 2: Add a default for variable $var if an appropriate value is found.  Otherwise
#            don't add the variable
#
#  add_default($nl, $var, 'nofail'=>1)
#
#
# ***** N.B. ***** This routine assumes the following variables are in package main::
#  $definition        -- the namelist definition object
#  $inputdata_rootdir -- CCSM inputdata root directory

    my $nl = shift;     # namelist object
    my $var = shift;    # name of namelist variable
    my %opts = @_;      # options

    my $val = undef;

    # Query the definition to find which group the variable belongs to.  Exit if not found.
    my $group = $definition->get_group_name($var);
    unless ($group) {
	my $fname = $definition->get_file_name();
	die "$ProgName - ERROR: variable \"$var\" not found in namelist definition file $fname.\n";
    }

    # check whether the variable has a value in the namelist object -- if so then return IF
    # there is also not a corresponding value in the val input
    $val = $nl->get_variable_value($group, $var);
    if (defined $val) {
	if (defined $opts{'val'}) {
	    if (defined $opts{'xml'}) {
		die "$ProgName - ERROR: $var can only by reset by changing the xml variable $opts{'xml'}, not in user_nl_cpl \n";
	    } elsif (defined $opts{'der'}) {
		die "$ProgName - ERROR: $var is derived - cannot be changed in user_nl_cpl \n";
	    }
	}  	    
	return;
    }

    if (defined $opts{'val'}) {

	# Look for a specified value in the options hash
	$val = $opts{'val'};

    } else {

	# Get a value from namelist defaults object.
	# Note that if the 'val' key isn't in the hash, then just pass anything else
	# in %opts to the get_value method to be used as attributes that are matched
	# when looking for default values.
	$val = get_default_value($var, \%opts);
	$val = SetupTools::expand_xml_var($val, \%xmlvars);
    }

    # if no value is found then exit w/ error (unless 'nofail' option set)
    unless (defined $val) {
	unless ($opts{'nofail'}) {
	    print "$ProgName - ERROR: No default value found for $var\n".
		"user defined attributes:\n";
	    foreach my $key (keys(%opts)) {
		if ($key ne 'nofail' and $key ne 'val') {
		    print "key=$key  val=$opts{$key}\n";
		}
	    }
	    die;
	}
	else {
	    return;
	}
    }

    # query the definition to find out if the variable is an input pathname
    my $is_input_pathname = $definition->is_input_pathname($var);

    # The default values for input pathnames are relative.  If the namelist
    # variable is defined to be an absolute pathname, then prepend
    # the CCSM inputdata root directory.
    if ($is_input_pathname eq 'abs') {
	$val =~ s/ //g;
	if ($val eq 'idmap') {
	    # do nothing
	}  else {
	    $val = set_abs_filepath($val, $DIN_LOC_ROOT);
	}
    }

    # query the definition to find out if the variable takes a string value.
    # The returned string length will be >0 if $var is a string, and 0 if not.
    my $str_len = $definition->get_str_len($var);

    # If the variable is a string, then add quotes if they're missing
    if ($str_len > 0) {
	$val = quote_string($val);
    }

    # set the value in the namelist
    $nl->set_variable_value($group, $var, $val);
}

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

sub get_default_value {

# Return a default value for the requested variable.
# Return undef if no default found.
#
# ***** N.B. ***** This routine assumes the following variables are in package main::
#  $defaults          -- the namelist defaults object
#  $uc_defaults       -- the use case defaults object

    my $var_name    = lc(shift);   # name of namelist variable (case insensitive interface)
    my $usr_att_ref = shift;       # reference to hash containing user supplied attributes

    # Check in the namelist defaults
    return $defaults->get_value($var_name, $usr_att_ref);

}

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

sub check_input_files {

# For each variable in the namelist which is an input dataset, check to see if it
# exists locally.
#
# ***** N.B. ***** This routine assumes the following variables are in package main::
#  $definition        -- the namelist definition object

    my $nl = shift;     # namelist object
    my $inputdata_rootdir = shift;    # if false prints test, else creates inputdata file
    my $outfile = shift;
    open(OUTFILE, ">$outfile") if defined $inputdata_rootdir;

    # Look through all namelist groups
    my @groups = $nl->get_group_names();
    foreach my $group (@groups) {

	# Look through all variables in each group
	my @vars = $nl->get_variable_names($group);
	foreach my $var (@vars) {

	    # Is the variable an input dataset?
	    my $input_pathname_type = $definition->is_input_pathname($var);

	    # If it is, check whether it exists locally and print status
	    if ($input_pathname_type) {

		# Get pathname of input dataset
		my $pathname = $nl->get_variable_value($group, $var);
		# Need to strip the quotes
		$pathname =~ s/[\'\"]//g;

                if ($pathname =~ /idmap$/) {
                    # do nothing
                }  
		elsif ($input_pathname_type eq 'abs') {
                    if ($inputdata_rootdir) {
                        print OUTFILE "$var = $pathname\n";
                    }
                    else {
		        if (-e $pathname) {  # use -e rather than -f since the absolute pathname
			                     # might be a directory
			    print "OK -- found $var = $pathname\n";
		        }
		        else {
			    print "NOT FOUND:  $var = $pathname\n";
		        }
                    }
		}
		elsif ($input_pathname_type =~ m/rel:(.+)/o) {
		    # The match provides the namelist variable that contains the
		    # root directory for a relative filename
		    my $rootdir_var = $1;
		    my $rootdir = $nl->get_variable_value($group, $rootdir_var);
		    $rootdir =~ s/[\'\"]//g;
                    if ($inputdata_rootdir) {
                        $pathname = "$rootdir/$pathname";
                        print OUTFILE "$var = $pathname\n";
                    }
                    else {
		        if (-f "$rootdir/$pathname") {
			    print "OK -- found $var = $rootdir/$pathname\n";
		        }
		        else {
			    print "NOT FOUND:  $var = $rootdir/$pathname\n";
		        }
                    }
		}
	    }
	}
    }
    close OUTFILE if defined $inputdata_rootdir;
    return 0 if defined $inputdata_rootdir;
}

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

sub set_abs_filepath {

# check whether the input filepath is an absolute path, and if it isn't then
# prepend a root directory

    my ($filepath, $rootdir) = @_;

    # strip any leading/trailing whitespace
    $filepath =~ s/^\s+//;
    $filepath =~ s/\s+$//;
    $rootdir  =~ s/^\s+//;
    $rootdir  =~ s/\s+$//;

    # strip any leading/trailing quotes
    $filepath =~ s/^['"]+//;
    $filepath =~ s/["']+$//;
    $rootdir =~ s/^['"]+//;
    $rootdir =~ s/["']+$//;

    my $out = $filepath;
    unless ( $filepath =~ /^\// ) {  # unless $filepath starts with a /
	$out = "$rootdir/$filepath"; # prepend the root directory
    }
    return $out;
}

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

sub valid_option {

    my ($val, @expect) = @_;
    my ($expect);

    $val =~ s/^\s+//;
    $val =~ s/\s+$//;
    foreach $expect (@expect) {
	if ($val =~ /^$expect$/i) { return $expect; }
    }
    return undef;
}

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

sub validate_options {

    my $source = shift;   # text string declaring the source of the options being validated
    my $opts   = shift;   # reference to hash that contains the options

    my ($opt, $old, @expect);

}

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

sub quote_string {
    my $str = shift;
    $str =~ s/^\s+//;
    $str =~ s/\s+$//;
    unless ($str =~ /^['"]/) {        #"'
        $str = "\'$str\'";
    }
    return $str;
}






