#!/usr/bin/env perl #----------------------------------------------------------------------------------------------- # # build-namelist # # This script builds the namelists for CLM # # 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. But if multiple runs are to made using the # same executable, successive invocations of build-namelist will overwrite previously generated # namelist files. So generally the best strategy is to invoke build-namelist from the run # directory and use the -config option to provide the filepath of the config_cache.xml file. # # # Date Contributor Modification # ------------------------------------------------------------------------------------------- # 2009-01-20 Vertenstein Original version # 2010-04-27 Kluzek Add ndep streams capability # 2011-07-25 Kluzek Add multiple ensemble's of namelists # 2012-03-23 Kluzek Add megan namelist and do checking on it # 2012-07-01 Kluzek Add some common CESM namelist options #-------------------------------------------------------------------------------------------- use strict; #use warnings; #use diagnostics; use Cwd; use English; use Getopt::Long; use IO::File; use File::Glob ':glob'; #----------------------------------------------------------------------------------------------- sub usage { die < 0 for determining fglcmask Default: gland5UM (i.e. gland20, gland10 etcetera) -glc_nec Glacier number of elevation classes [0 | 3 | 5 | 10] (default is 0) (standard option with land-ice model is 10) -glc_smb Only used if glc_nec > 0 If .true., pass surface mass balance info to GLC If .false., pass positive-degree-day info to GLC Default: true -help [or -h] Print usage to STDOUT. -ignore_ic_date Ignore the date on the initial condition files when determining what input initial condition file to use. -ignore_ic_year Ignore just the year part of the date on the initial condition files when determining what input initial condition file to use. -infile "filepath" Specify a file (or list of files) containing namelists to read values from. If used with a CLM build with multiple ensembles (ninst_lnd>1) and the filename entered is a directory to files of the form filepath/filepath and filepath/filepath_\$n where \$n is the ensemble member number. the "filepath/filepath" input namelist file is the master input namelist file that is applied to ALL ensemble members. (by default for CESM this is setup for files of the form \$CASEDIR/user_nl_clm/user_nl_clm_????) -inputdata "filepath" Writes out a list containing pathnames for required input datasets in file specified. -irrig Seek surface datasets with irrigation turned on. -l_ncpl "LND_NCPL" Number of CLM coupling time-steps in a day. -lnd_frac "domainfile" Land fraction file (the input domain file) -mask "landmask" Type of land-mask (default, navy, gx3v5, gx1v5 etc.) "-mask list" to list valid land masks. -megan Also produce a megan_emis_nl namelist that will go into the "drv_flds_in" file for the driver to pass VOC's to atm. MEGAN (Model of Emissions of Gases and Aerosols from Nature) (Note: buildnml.csh copies the file for use by the driver) -namelist "namelist" Specify namelist settings directly on the commandline by supplying a string containing FORTRAN namelist syntax, e.g., -namelist "&clm_inparm dt=1800 /" -[no-]note Add note to output namelist [do NOT add note] about the arguments to build-namelist. -rcp "value" Representative concentration pathway (rcp) to use for future scenarios. "-rcp list" to list valid rcp settings. -res "resolution" Specify horizontal grid. Use nlatxnlon for spectral grids; dlatxdlon for fv grids (dlat and dlon are the grid cell size in degrees for latitude and longitude respectively) "-res list" to list valid resolutions. -s Turns on silent mode - only fatal messages issued. -sim_year "year" Year to simulate for input datasets (i.e. 1850, 2000, 1850-2000, 1850-2100) "-sim_year list" to list valid simulation years -test Enable checking that input datasets exist on local filesystem. -verbose [or -v] Turn on verbose echoing of informational messages. -use_case "case" Specify a use case which will provide default values. "-use_case list" to list valid use-cases. -version Echo the SVN tag name used to check out this CLM distribution. Note: The precedence for setting the values of namelist variables is (highest to lowest): 0. namelist values set by specific command-line options, like, -d, -sim_year (i.e. CLM_BLDNML_OPTS env_run variable) 1. values set on the command-line using the -namelist option, (i.e. CLM_NAMELIST_OPTS env_run variable) 2. values read from the file(s) specified by -infile, (i.e. user_nl_clm files) 3. datasets from the -clm_usr_name option, (i.e. CLM_USRDAT_NAME env_run variable) 4. values set from a use-case scenario, e.g., -use_case (i.e. CLM_NML_USE_CASE env_run variable) 5. values from the namelist defaults file. EOF } #----------------------------------------------------------------------------------------------- # Set the directory that contains the CLM 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 $cmdline = "@ARGV"; # Command line arguments to script my $cwd = getcwd(); # current working directory my $cfgdir; # absolute pathname of directory that contains this script my $nm = "${ProgName}::"; # name to use if script dies if ($ProgDir) { $cfgdir = absolute_path($ProgDir); } else { $cfgdir = $cwd; } my $cfg_cache = "config_cache.xml"; # Default name of configuration cache file my $outdirname = "$cwd"; # Default name of output directory name #----------------------------------------------------------------------------------------------- # Process command-line options. my %opts = ( config => $cfg_cache, csmdata => undef, clm_usr_name => undef, co2_type => undef, co2_ppmv => undef, clm_demand => "null", clm_startfile => undef, help => 0, glc_nec => "default", glc_grid => "default", glc_smb => "default", l_ncpl => undef, lnd_frac => undef, dir => $outdirname, rcp => "default", sim_year => "default", chk_res => undef, note => undef, drydep => 0, megan => 0, irrig => undef, res => "default", silent => 0, mask => "default", test => 0, ); GetOptions( "clm_demand=s" => \$opts{'clm_demand'}, "clm_startfile=s" => \$opts{'clm_startfile'}, "co2_ppmv=f" => \$opts{'co2_ppmv'}, "co2_type=s" => \$opts{'co2_type'}, "config=s" => \$opts{'config'}, "csmdata=s" => \$opts{'csmdata'}, "clm_usr_name=s" => \$opts{'clm_usr_name'}, "drydep" => \$opts{'drydep'}, "chk_res!" => \$opts{'chk_res'}, "note!" => \$opts{'note'}, "megan" => \$opts{'megan'}, "glc_nec=i" => \$opts{'glc_nec'}, "glc_grid=s" => \$opts{'glc_grid'}, "glc_smb=s" => \$opts{'glc_smb'}, "irrig" => \$opts{'irrig'}, "d|d=s" => \$opts{'dir'}, "h|help" => \$opts{'help'}, "ignore_ic_date" => \$opts{'ignore_ic_date'}, "ignore_ic_year" => \$opts{'ignore_ic_year'}, "infile=s" => \$opts{'infile'}, "lnd_frac=s" => \$opts{'lnd_frac'}, "l_ncpl=i" => \$opts{'l_ncpl'}, "inputdata=s" => \$opts{'inputdata'}, "mask=s" => \$opts{'mask'}, "namelist=s" => \$opts{'namelist'}, "res=s" => \$opts{'res'}, "rcp=s" => \$opts{'rcp'}, "s|silent" => \$opts{'silent'}, "sim_year=s" => \$opts{'sim_year'}, "clm_start_type=s" => \$opts{'clm_start_type'}, "test" => \$opts{'test'}, "use_case=s" => \$opts{'use_case'}, "v|verbose" => \$opts{'verbose'}, "version" => \$opts{'version'}, ) or usage(); # Give usage message. usage() if $opts{'help'}; # Echo version info. version($cfgdir) if $opts{'version'}; # 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"; if ($print>=2) { print "Setting CLM configuration script directory to $cfgdir$eol"; } # Add the location of the use case defaults files to the options hash $opts{'use_case_dir'} = "$cfgdir/namelist_files/use_cases"; #----------------------------------------------------------------------------------------------- # 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 input data files must be specified. #The root directory to cesm utils Tools my $cesm_tools = &absolute_path( "$cfgdir/../../../../scripts/ccsm_utils/Tools" ); my $datmblddir = &absolute_path( "$cfgdir/../../../../models/atm/datm/bld" ); my $drvblddir = &absolute_path( "$cfgdir/../../../../models/drv//bld" ); # The XML::Lite module is required to parse the XML files. (-f "$cesm_tools/perl5lib/XML/Lite.pm") or die <<"EOF"; ** $ProgName - Cannot find perl module \"XML/Lite.pm\" in directory \"$cesm_tools/perl5lib\" ** EOF # The Build::Config module provides utilities to access the configuration information # in the config_cache.xml file (-f "$cesm_tools/perl5lib/Build/Config.pm") or die <<"EOF"; ** $ProgName - Cannot find perl module \"Build/Config.pm\" in directory \"$cesm_tools/perl5lib\" ** EOF # The namelist definition file contains entries for all namelist variables that # can be output by build-namelist. my @nl_definition_files = ( "$drvblddir/namelist_files/namelist_definition_drv.xml", "$drvblddir/namelist_files/namelist_definition_modio.xml", "$cfgdir/namelist_files/namelist_definition.xml" ); foreach my $nl_defin_file ( @nl_definition_files ) { (-f "$nl_defin_file") or die <<"EOF"; ** $ProgName - Cannot find namelist definition file \"$nl_defin_file\" ** EOF if ($print>=2) { print "Using namelist definition file $nl_defin_file$eol"; } } # The Build::NamelistDefinition module provides utilities to validate that the output # namelists are consistent with the namelist definition file (-f "$cesm_tools/perl5lib/Build/NamelistDefinition.pm") or die <<"EOF"; ** $ProgName - Cannot find perl module \"Build/NamelistDefinition.pm\" in directory \"$cesm_tools/perl5lib\" ** EOF # The namelist defaults file contains default values for all required namelist variables. my @nl_defaults_files = ( "$cfgdir/namelist_files/namelist_defaults_overall.xml", "$cfgdir/namelist_files/namelist_defaults_clm.xml", "$drvblddir/namelist_files/namelist_defaults_drv.xml", "$cfgdir/namelist_files/namelist_defaults_drydep.xml" ); if (defined $opts{'use_case'}) { if ( $opts{'use_case'} ne "list" ) { unshift( @nl_defaults_files, "$opts{'use_case_dir'}/$opts{'use_case'}.xml" ); } } foreach my $nl_defaults_file ( @nl_defaults_files ) { (-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"; } } # 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 "$cesm_tools/perl5lib/Build/NamelistDefaults.pm") or die <<"EOF"; ** $ProgName - Cannot find perl module \"Build/NamelistDefaults.pm\" in directory \"$cesm_tools//perl5lib\" ** EOF # The Build::Namelist module provides utilities to parse input namelists, to query and modify # namelists, and to write output namelists. (-f "$cesm_tools/perl5lib/Build/Namelist.pm") or die <<"EOF"; ** $ProgName - Cannot find perl module \"Build/Namelist.pm\" in directory \"$cesm_tools/perl5lib\" ** EOF #----------------------------------------------------------------------------------------------- # Add $cfgdir/perl5lib to the list of paths that Perl searches for modules my @dirs = ( $cfgdir, "$cesm_tools/perl5lib"); unshift @INC, @dirs; require XML::Lite; require Build::Config; require Build::NamelistDefinition; require Build::NamelistDefaults; require Build::Namelist; require Streams::TemplateGeneric; #----------------------------------------------------------------------------------------------- # Create a configuration object from the default config_definition file my $configfile; if ( -f $opts{'config'} ) { $configfile = $opts{'config'}; } else { $configfile = "$cfgdir/config_files/config_definition.xml"; } my $cfg = Build::Config->new("$configfile"); # 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( shift(@nl_definition_files) ); foreach my $nl_defin_file ( @nl_definition_files ) { $definition->add( "$nl_defin_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 CLM executable for which the namelist is being produced. my $defaults = Build::NamelistDefaults->new( shift( @nl_defaults_files ), $cfg); foreach my $nl_defaults_file ( @nl_defaults_files ) { $defaults->add( "$nl_defaults_file" ); } # # List valid values if asked for # list_options( \%opts ); # Validate some of the commandline option values. validate_options("commandline", $cfg, \%opts); # Check that configuration cache file exists. if ($print>=2) { print "Using CLM configuration cache file $opts{'config'}$eol"; } if ( $configfile ne $opts{'config'} ) { die <<"EOF"; ** $ProgName - Cannot find configuration cache file: \"$opts{'config'}\" ** EOF } # Create an empty namelist object. Add values to it in order of precedence. my $nl = Build::Namelist->new(); # Check that the CESM inputdata root directory has been specified. This must be # a local or nfs mounted directory. my $inputdata_rootdir = undef; if (defined($opts{'csmdata'})) { $inputdata_rootdir = $opts{'csmdata'}; } elsif (defined $ENV{'CSMDATA'}) { $inputdata_rootdir = $ENV{'CSMDATA'}; } else { die "$ProgName - ERROR: CESM inputdata root directory must be specified by either -csmdata argument\n" . " or by the CSMDATA environment variable. :"; } if ( $ENV{'DIN_LOC_ROOT'} eq "" ) { $ENV{'DIN_LOC_ROOT'} = $inputdata_rootdir; } if ($opts{'test'}) { (-d $inputdata_rootdir) or die <<"EOF"; ** $ProgName - CESM inputdata root is not a directory: \"$inputdata_rootdir\" ** EOF } my $test_files = $opts{'test'}; if ($print>=2) { print "CESM inputdata root directory: $inputdata_rootdir$eol"; } #----------------------------------------------------------------------------------------------- # Some regular expressions... ###my $TRUE = qr/\.true\./i; ###my $FALSE = qr/\.false\./i; # **N.B.** the use of qr// for precompiling regexps isn't supported until perl 5.005. my $TRUE = '\.true\.'; my $FALSE = '\.false\.'; # Get BGC mode my $bgc = $cfg->get('bgc'); # Get spinup mode my $spinup = $cfg->get('spinup'); # Get max number of Plant function types my $maxpft = $cfg->get('maxpft'); # crop mode my $crop = $cfg->get('crop'); #----------------------------------------------------------------------------------------------- # 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. # # First get the command-line specified overall values or their defaults # Obtain default values for the following build-namelist input arguments # : res, mask, rcp, sim_year, and sim_year_range. # my $val; my $res; my $group; my $var; # check resolution/land-mask $var = "chk_res"; if ( ! defined($opts{$var}) ) { $opts{$var} = $defaults->get_value($var); } # resolution $var = "res"; if ( $opts{$var} ne "default" ) { $val = $opts{$var}; } else { $val= $defaults->get_value($var); } $res = $val; if ($print>=2) { print "CLM atm resolution is $res $eol"; } $opts{$var} = $val; if ( $opts{'chk_res'} ) { $val = "e_string( $res ); $group = $definition->get_group_name($var); if ( ! $definition->is_valid_value( $var, $val ) ) { my @valid_values = $definition->get_valid_values( $var ); if ( ! defined($opts{'clm_usr_name'}) || $res ne $opts{'clm_usr_name'} ) { die "$nm $var has a value ($val) that is NOT valid. Valid values are: @valid_values\n"; } } } # mask $var = "mask"; if ( $opts{$var} ne "default" ) { $val = $opts{$var}; } else { my %opts = ( 'hgrid'=>$res ); $val = $defaults->get_value($var, \%opts ); } my $mask = $val; $opts{'mask'} = $mask; if ( $opts{'chk_res'} ) { $val = "e_string( $val ); $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, $val); if ( ! $definition->is_valid_value( $var, $val ) ) { my @valid_values = $definition->get_valid_values( $var ); die "$nm $var has a value ($val) that is NOT valid. Valid values are: @valid_values\n"; } } if ($print>=2) { print "CLM land mask is $mask $eol"; } # glc_nec $var = "glc_nec"; if ( $opts{$var} ne "default" ) { $val = $opts{$var}; } else { $val = $defaults->get_value($var); } my $glc_nec = $val; $opts{'glc_nec'} = $glc_nec; $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, $val); if ( ! $definition->is_valid_value( $var, $val ) ) { my @valid_values = $definition->get_valid_values( $var ); die "$nm $var has a value ($val) that is NOT valid. Valid values are: @valid_values\n"; } if ($print>=2) { print "Glacier number of elevation classes is $glc_nec $eol"; } # irrigation $var = "irrig"; if ( ! defined($opts{$var}) ) { $val = $defaults->get_value($var); } else { $val = ".true."; } my $irrig = $val; $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, $val); if ( ! $definition->is_valid_value( $var, $val ) ) { my @valid_values = $definition->get_valid_values( $var ); die "$nm $var has a value ($val) that is NOT valid. Valid values are: @valid_values\n"; } if ( $irrig =~ /$TRUE/i && $crop eq "on" ) { print "You've turned on both irrigation and crop. Irrigation is only applied to generic crop currently, which negates it's practical usage.\n" . "We also have a known problem when both are on (see bug 1326 in the models/lnd/clm/doc/KnownBugs file)\n"; die "$nm both irrigation and crop should NOT be on.\n"; } if ($print>=2) { print "Irrigation $irrig $eol"; } # representative concentration pathway $var = "rcp"; if ( $opts{$var} ne "default" ) { $val = $opts{$var}; } else { $val = $defaults->get_value($var); } my $rcp = $val; $opts{'rcp'} = $rcp; $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, $val); if ( ! $definition->is_valid_value( $var, $val ) ) { my @valid_values = $definition->get_valid_values( $var ); die "$nm $var has a value ($val) that is NOT valid. Valid values are: @valid_values\n"; } if ($print>=2) { print "CLM future scenario representative concentration is $rcp $eol"; } # simulation year and simulation year range my $var = "sim_year"; if ( $opts{$var} ne "default" ) { $val = $opts{$var}; } else { $val = $defaults->get_value($var); } my $sim_year_range = $defaults->get_value("sim_year_range"); my $sim_year = $val; if ( $val =~ /([0-9]+)-([0-9]+)/ ) { $sim_year = $1; $sim_year_range = $val; } $val = $sim_year; $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, $val ); if ( ! $definition->is_valid_value( $var, $val, 'noquotes'=>1 ) ) { my @valid_values = $definition->get_valid_values( $var ); die "$nm $var of $val is NOT valid. Valid values are: @valid_values\n"; } $nl->set_variable_value($group, $var, $val ); if ($print>=2) { print "CLM sim_year is $sim_year $eol"; } my $var = "sim_year_range"; $val = $sim_year_range; if ( $val ne "constant" ) { $opts{$var} = $val; $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, $val ); if ( ! $definition->is_valid_value( $var, $val, 'noquotes'=>1 ) ) { my @valid_values = $definition->get_valid_values( $var ); die "$nm $var of $val is NOT valid. Valid values are: @valid_values\n"; } $val = "'".$defaults->get_value($var)."'"; $nl->set_variable_value($group, $var, $val ); if ($print>=2) { print "CLM sim_year_range is $sim_year_range $eol"; } } # Get clm run type my $clm_start_type; my $var = "clm_start_type"; if (defined $opts{$var}) { if ($opts{$var} eq "default" ) { add_default($nl, $var ); } else { my $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, quote_string( $opts{$var} ) ); } } else { add_default($nl, $var ); } $clm_start_type = $nl->get_value($var); #----------------------------------------------------------------------------------------------- # 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. # First 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'}) { my @infiles = split( /,/, $opts{'infile'} ); foreach my $infile ( @infiles ) { # Make sure a valid file was found if ( -f "$infile" ) { # Otherwise abort as a valid file doesn't exist } else { die "$ProgName - ERROR: input namelist file does NOT exist $infile.\n $@"; } # Parse namelist input from the next file my $nl_infile = Build::Namelist->new($infile); # 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' $infile.\n $@"; } # Merge input values into namelist. Previously specified values have higher precedence # and are not overwritten. $nl->merge_nl($nl_infile_valid); } } # Process the -clm_usr_name argument if (defined $opts{'clm_usr_name'}) { # The user files definition is contained in an xml file with the same format as the defaults file. # The one difference is that variables are expanded. # Create a new NamelistDefaults object. my $nl_defaults_file = "$cfgdir/namelist_files/namelist_defaults_usr_files.xml"; my $uf_defaults = Build::NamelistDefaults->new("$nl_defaults_file", $cfg ); # Loop over the variables specified in the user files # Add each one to the namelist. my @vars = $uf_defaults->get_variable_names(); my %settings; $settings{'mask'} = $mask; $settings{'sim_year'} = $sim_year; $settings{'rcp'} = $rcp; $settings{'sim_year_range'} = $sim_year_range; $settings{'clm_usr_name'} = $opts{'clm_usr_name'}; if ( $inputdata_rootdir eq "\$DIN_LOC_ROOT" ) { $settings{'csmdata'} = $ENV{'DIN_LOC_ROOT'}; } else { $settings{'csmdata'} = $inputdata_rootdir; } my $nvars = 0; my $nl_usrfile = Build::Namelist->new(); foreach my $var (@vars) { my $val = $uf_defaults->get_usr_file($var, $definition, \%settings); if ($val) { print "adding clm user file defaults for var $var with val $val \n"; add_default($nl_usrfile, $var, 'val'=>$val); $nvars++; } } if ( $nvars == 0 ) { die "$nm setting clm_usr_name -- but did NOT find any user datasets: $opts{'clm_usr_name'}\n"; } # Merge input values into namelist. Previously specified values have higher precedence # and are not overwritten. $nl->merge_nl($nl_usrfile); } # Now process the -use_case arg. if (defined $opts{'use_case'}) { # The use case definition is contained in an xml file with the same format as the defaults file. # Create a new NamelistDefaults object. my $uc_defaults = Build::NamelistDefaults->new("$opts{'use_case_dir'}/$opts{'use_case'}.xml", $cfg); my %settings; $settings{'res'} = $res; $settings{'rcp'} = $rcp; $settings{'mask'} = $mask; $settings{'sim_year'} = $sim_year; $settings{'sim_year_range'} = $sim_year_range; # Loop over the variables specified in the use case. # Add each one to the namelist. my @vars = $uc_defaults->get_variable_names(); my $nl_usecase = Build::Namelist->new(); foreach my $var (@vars) { my $val = $uc_defaults->get_value($var, \%settings ); if ( defined($val) ) { print "CLM adding use_case $opts{'use_case'} defaults for var $var with val $val \n"; add_default($nl_usecase, $var, 'val'=>$val); } } # Merge input values into namelist. Previously specified values have higher precedence # and are not overwritten. $nl->merge_nl($nl_usecase); } #----------------------------------------------------------------------------------------------- # Add default values for required namelist variables that have not been previously set. # This is done either by using the namelist default object, or directly with inline logic. # Run type for driver namelist - note that arb_ic implies that the run is startup my $var = "start_type"; if ($clm_start_type eq "'cold'" || $clm_start_type eq "'arb_ic'") { add_default($nl, $var, 'val'=>'startup' ); } else { add_default($nl, $var, 'val'=>$clm_start_type ); } ############################## # namelist group: clm_inparm # ############################## $var = "clm_startfile"; if ( defined($opts{$var}) ) { $val = $opts{$var}; my $file = $val; if ( $clm_start_type =~ /startup/ || $clm_start_type =~ /arb_ic/ ) { if ( defined($nl->get_value('finidat')) ) { die "$ProgName ERROR:: Can NOT set both -clm_startfile option AND finidat on namelist\n"; } add_default($nl, 'finidat','val'=>"$val",'no_abspath'=>1); } elsif ($clm_start_type =~ /branch/ ) { if ( defined($nl->get_value('nrevsn')) ) { die "$ProgName ERROR:: Can NOT set both -clm_startfile option AND nrevsn on namelist\n"; } add_default($nl, 'nrevsn', 'val'=>"$val",'no_abspath'=>1); } elsif ( $clm_start_type =~ /cold/ ) { die "$ProgName ERROR:: Can only set -clm_startfile option for cold clm_start_type\n"; } } $var = "lnd_frac"; if ( defined($opts{$var}) ) { if ( defined($nl->get_value('fatmlndfrc')) ) { die "$ProgName ERROR:: Can NOT set both -lnd_frac option (set via LND_DOMAIN_PATH/LND_DOMAIN_FILE env variables) AND fatmlndfrac on namelist\n"; } my $lnd_frac = &Streams::TemplateGeneric::expandXMLVar( $opts{$var}, \%ENV ); add_default($nl, 'fatmlndfrc','val'=>$lnd_frac ); } $var = "co2_type"; if ( defined($opts{$var}) ) { if ( ! defined($nl->get_value($var)) ) { add_default($nl, 'co2_type','val'=>"$opts{'co2_type'}"); } else { die "$ProgName ERROR:: co2_type set on namelist as well as -co2_type option.\n"; } } $var = "start_type"; my $drv_start_type = $nl->get_value($var); my $my_start_type = $clm_start_type; my $nsrest = $nl->get_value('override_nsrest'); if ( defined($nsrest) ) { if ( $nsrest == 0 ) { $my_start_type = "startup"; } if ( $nsrest == 1 ) { $my_start_type = "continue"; } if ( $nsrest == 3 ) { $my_start_type = "branch"; } if ( "$my_start_type" eq "$drv_start_type" ) { die "$ProgName ERROR:: no need to set override_nsrest to same as start_type.\n"; } if ( "$drv_start_type" !~ /startup/ ) { die "$ProgName ERROR:: can NOT set override_nsrest if driver is NOT a startup type.\n"; } } if ( defined($opts{'l_ncpl'}) ) { my $l_ncpl = $opts{'l_ncpl'}; if ( $l_ncpl <= 0 ) { die "$ProgName ERROR:: bad value for -l_ncpl option.\n"; } $val = ( 3600 * 24 ) / $l_ncpl; my $dtime = $nl->get_value('dtime'); if ( ! defined($dtime) ) { add_default($nl, 'dtime', 'val'=>$val); } elsif ( $dtime ne $val ) { die "$ProgName ERROR:: can NOT set both -l_ncpl option (via LND_NCPL env variable) AND dtime namelist variable.\n"; } } else { add_default($nl, 'dtime', 'hgrid'=>$res); } if ( $my_start_type =~ /branch/ ) { if (not defined $nl->get_value('nrevsn')) { die "$ProgName ERROR:: nrevsn is required for a branch type.\n"; } } else { if (defined $nl->get_value('nrevsn')) { die "$ProgName ERROR:: nrevsn should ONLY be set for a branch type.\n"; } } if ( $my_start_type =~ /branch/ ) { if (not defined $nl->get_value('nrevsn')) { die "$ProgName ERROR:: nrevsn is required for a branch type.\n"; } } else { if (defined $nl->get_value('nrevsn')) { die "$ProgName ERROR:: nrevsn should ONLY be set for a branch type.\n"; } } # Set the number of segments per clump add_default($nl, 'nsegspc', 'hgrid'=>$res); # Get the fraction file if (defined $nl->get_value('fatmlndfrc')) { # do nothing - use value provided by config_grid.xml and clm.cpl7.template } else { die "$ProgName ERROR:: fatmlndfrc was NOT sent into CLM build-namelist.\n"; } add_default($nl, 'outnc_large_files' ); add_default($nl, 'fsnowoptics' ); add_default($nl, 'fsnowaging' ); # # Glacier multiple elevation class options # my $var = "maxpatch_glcmec"; add_default($nl, $var, 'val'=>$glc_nec ); $val = $nl->get_value($var); if ( $val != $glc_nec ) { die "$nm $var set to $val does NOT agree with -glc_nec argument of $glc_nec (set with GLC_NEC env variable)\n"; } if ( $glc_nec > 0 ) { foreach my $var ( "glc_grid", "glc_smb" ) { if ( $opts{$var} ne "default" ) { add_default($nl, $var, 'val'=>$opts{$var} ); $val = $nl->get_value($var); $val =~ s/['"]//g; my $ucvar = $var; $ucvar =~ tr/a-z/A-Z/; if ( $val ne $opts{$var} ) { die "$nm $var set to $val does NOT agree with -$var argument of $opts{$var} (set with $ucvar env variable)\n"; } } else { add_default($nl, $var, 'glc_nec'=>$glc_nec ); } $val = $nl->get_value($var); if ($print>=2) { print "Glacier model $var is $val $eol"; } if ( ! defined($val) ) { die "$ProgName ERROR:: $var is NOT set, but glc_nec is positive\n"; } } my $glc_grid = $nl->get_value('glc_grid'); $glc_grid =~ s/['"]//g; add_default($nl, 'flndtopo' , 'hgrid'=>$res, 'mask'=>$mask ); add_default($nl, 'fglcmask' , 'hgrid'=>$res, 'glc_grid'=>$glc_grid ); add_default($nl, 'albice' , 'glc_nec'=>$glc_nec ); } else { my $glc_grid = $nl->get_value('glc_grid'); $glc_grid =~ s/['"]//g; if ( defined($glc_grid) ) { die "$ProgName ERROR:: glc_grid is set, but glc_nec is zero\n"; } # Error checking for glacier multiple elevation class options when glc_nec off # Make sure create_glacier_mec_landunit/glc_smb/glc_dyntopo are NOT true, and fglcmask # is NOT set my $create_glcmec = $nl->get_value('create_glacier_mec_landunit'); if ( defined($create_glcmec) ) { if ( $create_glcmec =~ /$TRUE/i ) { die "$ProgName ERROR:: create_glacer_mec_landunit is true, but glc_nec is equal to zero\n"; } } my $glc_smb = $nl->get_value('glc_smb'); if ( defined($glc_smb) ) { if ( $glc_smb =~ /$TRUE/i ) { die "$ProgName ERROR:: glc_smb is true, but glc_nec is equal to zero\n"; } } my $glc_dyntopo= $nl->get_value('glc_dyntopo'); if ( defined($glc_dyntopo) ) { if ( $glc_dyntopo =~ /$TRUE/i ) { die "$ProgName ERROR:: glc_dyntopo is true, but glc_nec is equal to zero\n"; } } my $fglcmask = $nl->get_value('fglcmask'); if ( defined($fglcmask) ) { die "$ProgName ERROR:: fglcmask is set, but glc_nec is equal to zero\n"; } } add_default($nl, 'fpftcon'); if ( $crop eq "on" ) { add_default($nl, 'create_crop_landunit' ); } add_default($nl, 'co2_type'); if ( $nl->get_value('co2_type') =~ /constant/ ) { my $var = 'co2_ppmv'; if ( defined($opts{$var}) ) { if ( $opts{$var} <= 0.0 ) { die "$ProgName ERROR:: co2_ppmv can NOT be less than or equal to zero.\n"; } my $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, $opts{$var}); } else { add_default($nl, $var, 'sim_year'=>$sim_year ); } } #add_default($nl, 'wrtdia', 'val'=> '.false.'); add_default($nl, 'urban_hac'); add_default($nl, 'urban_traffic'); # # Deal with options that the user has said are required... # my %settings = {}; $settings{'hgrid'} = $res; $settings{'sim_year'} = $sim_year; $settings{'sim_year_range'} = $sim_year_range; $settings{'mask'} = $mask; $settings{'crop'} = $crop; $settings{'irrig'} = $irrig; $settings{'rcp'} = $rcp; $settings{'glc_nec'} = $glc_nec; my $demand = $nl->get_value('clm_demand'); $demand =~ s/\'//g; # Remove quotes if ( $demand =~ /.+/ ) { $opts{'clm_demand'} .= ",$demand"; } my $demand = $defaults->get_value('clm_demand', \%settings); $demand =~ s/\'//g; # Remove quotes if ( $demand =~ /.+/ ) { $opts{'clm_demand'} .= ",$demand"; } my @demandlist = split( ",", $opts{'clm_demand'} ); foreach my $item ( @demandlist ) { if ( $item eq "null" ) { next; } add_default($nl, $item, %settings ); } # # Get surface dataset after fpftdyn so that we can get surface data consistent with it # my $fpftdyn = $nl->get_value('fpftdyn'); $fpftdyn =~ s!(.*)/!!; $fpftdyn =~ s/\'//; $fpftdyn =~ s/\"//; if ( $fpftdyn eq "" ) { $fpftdyn = "null"; } add_default($nl, 'fsurdat', 'hgrid'=>$res, 'sim_year'=>$sim_year, 'irrig'=>$irrig, 'crop'=>$crop, 'glc_nec'=>$glc_nec ); my $fsurdat = $nl->get_value( 'fsurdat' ); # Initial conditions # The initial date is an attribute in the defaults file which should be matched unless # the user explicitly requests to ignore the initial date via the -ignore_ic_date option, # or just ignore the year of the initial date via the -ignore_ic_year option. if ( $clm_start_type =~ /cold/ ) { if (defined $nl->get_value('finidat')) { die "$ProgName ERROR:: setting finidat is incomptable with using start_type=cold.\n"; } add_default($nl, 'finidat', 'val'=>"' '", 'no_abspath'=>1); } if (not defined $nl->get_value('finidat')) { my $ic_date = $nl->get_value('start_ymd'); my $nofail = 1; my $var = "finidat"; if ( $clm_start_type =~ /startup/ ) { $nofail = 0; } if ($opts{'ignore_ic_date'}) { if ( $crop eq "on" ) { die "$ProgName ERROR:: using ignore_ic_date is incompatable with crop!\n"; } add_default($nl, $var, 'hgrid'=>$res, 'mask'=>$mask, 'nofail'=>$nofail, 'fpftdyn'=>$fpftdyn, 'sim_year'=>$sim_year, 'maxpft'=>$maxpft, 'irrig'=>$irrig, 'glc_nec'=>$glc_nec ); } elsif ($opts{'ignore_ic_year'}) { add_default($nl, $var, 'hgrid'=>$res, 'mask'=>$mask, 'ic_md'=>$ic_date, 'nofail'=>$nofail, 'fpftdyn'=>$fpftdyn, 'sim_year'=>$sim_year, 'maxpft'=>$maxpft, 'irrig'=>$irrig, 'glc_nec'=>$glc_nec ); } else { add_default($nl, $var, 'hgrid'=>$res, 'mask'=>$mask, 'ic_ymd'=>$ic_date, 'nofail'=>$nofail, 'fpftdyn'=>$fpftdyn, 'sim_year'=>$sim_year, 'maxpft'=>$maxpft, 'irrig'=>$irrig, 'glc_nec'=>$glc_nec ); } my $finidat = $nl->get_value($var); if ( (not defined $finidat ) || $finidat =~ /null/ ) { my $group = $definition->get_group_name($var); $nl->set_variable_value($group, $var, "' '" ); } } # # Create crop land unit # add_default($nl, 'create_crop_landunit', 'irrig'=>$irrig ); # # Supplemental Nitrogen for prognostic crop cases # if ( $bgc =~ /^cn/ && $crop eq "on" ) { add_default($nl, 'suplnitro' ); } # # Nitrogen deposition for bgc=CN # if ( $bgc =~ /^cn/ ) { ############################### # namelist group: ndepdyn_nml # ############################### add_default($nl, 'ndepmapalgo', 'hgrid'=>$res ); add_default($nl, 'stream_year_first_ndep', 'sim_year'=>$sim_year, 'sim_year_range'=>$sim_year_range); add_default($nl, 'stream_year_last_ndep', 'sim_year'=>$sim_year, 'sim_year_range'=>$sim_year_range); # Set align year, if first and last years are different if ( $nl->get_value('stream_year_first_ndep') != $nl->get_value('stream_year_last_ndep') ) { add_default($nl, 'model_year_align_ndep', 'sim_year'=>$sim_year, 'sim_year_range'=>$sim_year_range); } add_default($nl, 'stream_fldfilename_ndep', 'rcp'=>$rcp, 'hgrid'=>"1.9x2.5" ); # # If bgc is NOT CN/CNDV then make sure none of the ndep settings are set # } else { if ( defined($nl->get_value('stream_year_first_ndep')) || defined($nl->get_value('stream_year_last_ndep')) || defined($nl->get_value('model_year_align_ndep')) || defined($nl->get_value('stream_fldfilename_ndep')) ) { die "$ProgName ERROR:: When bgc is NOT CN or CNDV none of: stream_year_first_ndep," . "stream_year_last_ndep, model_year_align_ndep, nor stream_fldfilename_ndep" . " can be set!\n"; } } if ( ($bgc ne "cn") && ($bgc ne "cndv") && defined $nl->get_value('scaled_harvest') ) { die "$ProgName ERROR:: scaled_harvest is set, but neither CN nor CNDV is active!\n"; } # # Error checking for suplnitro # my $suplnitro = $nl->get_value('suplnitro'); if ( defined($suplnitro) ) { if ( $bgc !~ /^cn/ ) { die "$ProgName ERROR:: supplemental Nitrogen (suplnitro) is set, but neither CN nor CNDV is active!\n"; } if ( $crop ne "on" && $suplnitro =~ /PROG_CROP_ONLY/i ) { die "$ProgName ERROR:: supplemental Nitrogen is set to run over prognostic crops, but prognostic crop is NOT active!\n"; } if ( $suplnitro =~ /ALL/i ) { if ( $spinup ne "normal" ) { print "There is no need to use a spinup mode when supplemental Nitrogen is on for all PFT's, as these modes spinup Nitrogen\n"; die "$ProgName ERROR:: when spinup != normal you can NOT set supplemental Nitrogen (suplnitro) to ALL\n"; } } } ################################# # namelist group: drydep_inparm # ################################# if ($opts{'drydep'} ) { add_default($nl, 'drydep_list'); add_default($nl, 'drydep_method'); } else { if ( defined($nl->get_value('drydep_list')) || defined($nl->get_value('drydep_method')) ) { die "$ProgName - ERROR: drydep_list or drydep_method defined, but drydep option NOT set\n" } } ################################# # namelist group: megan_emis_nl # ################################# if ($opts{'megan'} ) { add_default($nl, 'megan_specifier'); check_megan_spec( $nl, $definition ); add_default($nl, 'megan_factors_file'); } else { if ( defined($nl->get_value('megan_specifier')) || defined($nl->get_value('megan_factors_file')) ) { die "$ProgName - ERROR: megan_specifier or megan_factors_file defined, but megan option NOT set\n" } } #----------------------------------------------------------------------------------------------- # Validate that the entire resultant namelist is valid # $definition->validate($nl); # Groups to go through for clm namelists my @groups = qw(clm_inparm ndepdyn_nml); #----------------------------------------------------------------------------------------------- # Write output files # check resolution/land-mask my $note = ""; $var = "note"; if ( ! defined($opts{$var}) ) { $opts{$var} = $defaults->get_value($var); } if ( $opts{$var} ) { $note = "Comment:\n" . "This namelist was created using the following command-line:\n" . " $cfgdir/$ProgName $cmdline\n" . "For help on options use: $cfgdir/$ProgName -help"; } # CLM component my $outfile; $outfile = "$opts{'dir'}/lnd_in"; $nl->write($outfile, 'groups'=>\@groups, 'note'=>"$note" ); if ($print>=2) { print "Writing clm namelist to $outfile $eol"; } # Drydep or MEGAN namelist if ($opts{'drydep'} || $opts{'megan'} ) { @groups = qw(drydep_inparm megan_emis_nl); $outfile = "$opts{'dir'}/drv_flds_in"; $nl->write($outfile, 'groups'=>\@groups, 'note'=>"$note" ); if ($print>=2) { print "Writing @groups namelists to $outfile $eol"; } } #----------------------------------------------------------------------------------------------- # Output input dataset list. if ($opts{'inputdata'}) { check_input_files($nl,$inputdata_rootdir,$opts{'inputdata'}); } # 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. # # 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 # $defaults -- the namelist defaults object # $inputdata_rootdir -- CESM inputdata root directory my $nl = shift; # namelist object my $var = shift; # name of namelist variable my %opts = @_; # options # If variable has quotes around it if ( $var =~ /'(.+)'/ ) { $var = $1; } # 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 skip to end my $val = $nl->get_variable_value($group, $var); if (! defined $val) { # Look for a specified value in the options hash if (defined $opts{'val'}) { $val = $opts{'val'}; } # or 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. else { $val = $defaults->get_value($var, \%opts); # Truncate model_version appropriately if ( $var eq "model_version" ) { $val =~ /(URL: https:\/\/[a-zA-Z0-9._-]+\/)([a-zA-Z0-9\/._-]+)(\/bld\/.+)/; $val = $2; } } # if no value is found then exit w/ error (unless 'nofail' option set) unless ( defined($val) ) { unless ($opts{'nofail'}) { die "$ProgName - No default value found for $var.\n" . " Are defaults provided for this resolution and land mask?\n"; } 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 CESM inputdata root directory. if (not defined $opts{'no_abspath'}) { if (defined $opts{'set_abspath'}) { $val = set_abs_filepath($val, $opts{'set_abspath'}); } else { if ($is_input_pathname eq 'abs') { $val = set_abs_filepath($val, $inputdata_rootdir); if ( $test_files and ($val !~ /null/) and (! -f "$val") ) { die "$ProgName - file not found: $var = $val\n"; } } } } # 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 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 ($input_pathname_type eq 'abs') { if ($inputdata_rootdir) { #MV $pathname =~ s:$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"; #MV $pathname =~ s:$inputdata_rootdir::; 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 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 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 check_use_case_name { # # Check the use-case name and ensure it follows the naming convention. # my $use_case = shift; my $diestring = "$ProgName - bad use_case name $use_case, follow the conventions " . "in namelist_files/use_cases/README\n"; my $desc = "[a-zA-Z0-9]*"; my $rcp = "rcp[0-9\.]+"; if ( $use_case =~ /^[0-9]+-[0-9]+([a-zA-Z0-9_\.]*)_transient$/ ) { my $string = $1; if ( $string =~ /^_($rcp)_*($desc)$/ ) { # valid name } elsif ( $string =~ /^_*($desc)$/ ) { # valid name } else { die "$diestring"; } } elsif ( $use_case =~ /^20thC([a-zA-Z0-9_\.]*)_transient$/ ) { my $string = $1; if ( $string =~ /^_($rcp)_*($desc)$/ ) { # valid name } elsif ( $string =~ /^_*($desc)$/ ) { # valid name } else { die "$diestring"; } } elsif ( $use_case =~ /^([0-9]+)_*($desc)_control$/ ) { # valid name } elsif ( $use_case =~ /^($desc)_pd$/ ) { # valid name } else { die "$diestring"; } } #------------------------------------------------------------------------------- sub validate_options { my $source = shift; # text string declaring the source of the options being validated my $cfg = shift; # configure object my $opts = shift; # reference to hash that contains the options my ($opt, $old, @expect); # use_case $opt = 'use_case'; if (defined $opts->{$opt}) { if ( $opts->{$opt} ne "list" ) { # create the @expect array by listing the files in $use_case_dir # and strip off the ".xml" part of the filename @expect = (); my @files = glob("$opts->{'use_case_dir'}/*.xml"); foreach my $file (@files) { $file =~ m{.*/(.*)\.xml}; &check_use_case_name( $1 ); push @expect, $1; } $old = $opts->{$opt}; $opts->{$opt} = valid_option($old, @expect) or die "$ProgName - invalid value of $opt ($old) specified in $source\n". "expected one of: @expect\n"; } else { print "Use cases are:...\n\n"; my @ucases; foreach my $file( sort( glob($opts{'use_case_dir'}."/*.xml") ) ) { my $use_case; if ( $file =~ /\/([^\/]+)\.xml$/ ) { &check_use_case_name( $1 ); $use_case = $1; } else { die << "EOF"; ** $ProgName - Bad name for use case file = $file EOF } my $uc_defaults = Build::NamelistDefaults->new("$file", $cfg); printf "%15s = %s\n", $use_case, $uc_defaults->get_value("use_case_desc"); push @ucases, $use_case; } die <{$var} eq "list" ) { $val = "default"; } elsif ( $optsref->{$var} eq "default" ) { $val = $defaults->get_value($var, \%opts ); } else { $val = $optsref->{$var}; } my $vname = $var; if ( $vname eq "res" ) { $vname = "hgrid"; } $opts{$vname} = $val; } foreach my $opt ( @opts_list ) { if (defined $optsref->{$opt}) { if ( $optsref->{$opt} eq "list" ) { $var = $opt; my @valid_values = $definition->get_valid_values( $var ); if ( $opt eq "sim_year" ) { unshift( @valid_values, $definition->get_valid_values( "sim_year_range" ) ); } unshift( @valid_values, "default" ); # Strip out quotes and the constant value for( my $i = 0; $i <= $#valid_values; $i++ ) { $valid_values[$i] =~ s/('|')//g; if ( $valid_values[$i] eq "constant" ) { $valid_values[$i] = undef; } } $val= $defaults->get_value($var, \%opts); my $doc = $definition->get_var_doc( $var ); $doc =~ s/\n//; chomp( $doc ); die <{$opt}) { if ( $optsref->{$opt} eq "list" ) { my @vars = $definition->get_var_names( ); my @demands = ( "null" ); foreach my $var ( @vars ) { if ( $definition->get_group_name( $var ) ne "clm_inparm" ) { next; } if ( defined($defaults->get_value($var, \%opts ) ) ) { push( @demands, $var ); } } my $doc = $definition->get_var_doc( 'clm_demand' ); $doc =~ s/\n//; chomp( $doc ); die <get_value('megan_specifier'); my @megan_spec_list = split( /\s*,\s*/, $megan_spec ); foreach $megan_spec ( @megan_spec_list ) { if ( $megan_spec =~ /^['"]+[A-Za-z0-9]+\s*\=\s*([\sA-Za-z0-9+_-]+)["']+$/ ) { my $megan_list = $1; my @megan_cmpds = split( /\s*\+\s*/, $megan_list ); my $var = "megan_cmpds"; my $warn = 0; foreach my $megan_cmpd ( @megan_cmpds ) { if ( ! $definition->is_valid_value( $var, $megan_cmpd, 'noquotes'=>1 ) ) { print "$ProgName - WARNING: megan_compound $megan_cmpd NOT found in list\n"; $warn++; } } if ( $warn > 0 ) { my @valid_values = $definition->get_valid_values( $var, 'noquotes'=>1 ); print "$ProgName - list of megan compounds includes:\n"; print "@valid_values\n"; print "Does your megan_factors_file include more coumpounds?\n"; print "If NOT your simulation will fail.\n"; } } else { die "$ProgName - ERROR: Bad format for megan_specifier = $megan_spec\n" } } } #------------------------------------------------------------------------------- sub quote_string { my $str = shift; $str =~ s/^\s+//; $str =~ s/\s+$//; unless ($str =~ /^['"]/) { #"' $str = "\'$str\'"; } return $str; } #------------------------------------------------------------------------------- sub version { # The version is found in CLM ChangeLog file. # $cfgdir is set by the configure script to the name of its directory. my ($cfgdir) = @_; my $logfile = "$cfgdir/../doc/ChangeLog"; my $fh = IO::File->new($logfile, '<') or die "** $ProgName - can't open ChangeLog file: $logfile\n"; while (my $line = <$fh>) { if ($line =~ /^Tag name:\s*([a-zA-Z0-9_. -]*[clmcesm0-9_.-]+)$/ ) { print "$1\n"; exit; } } } #-------------------------------------------------------------------------------