#!/usr/bin/env perl #----------------------------------------------------------------------------------------------- # # build-namelist # # This is the build-namelist script for the CESM datm (Data ATmosphere Model). # This is normally run under the covers as part of the CESM scripts. # # Date Contributor Modification # ------------------------------------------------------------------------------------------- # 2010-05-04 Kluzek Original version # 2012-03-10 Vertenstein Updated for cesm1.1 #-------------------------------------------------------------------------------------------- use strict; #use warnings; #use diagnostics; use Cwd; use English; use Getopt::Long; use IO::File; #----------------------------------------------------------------------------------------------- sub usage { die < 0, silent => 0, debug => 0, test => 0, caseroot => undef, scriptsroot => &absolute_path( "$cfgdir/../../../../scripts" ), inst_string => undef, ); GetOptions( "h|help" => \$opts{'help'}, "infile=s" => \$opts{'infile'}, "namelist=s" => \$opts{'namelist'}, "print=i" => \$opts{'print'}, "debug" => \$opts{'debug'}, "test" => \$opts{'test'}, "caseroot=s" => \$opts{'caseroot'}, "scriptsroot=s" => \$opts{'scriptsroot'}, "inst_string=s" => \$opts{'inst_string'}, ) 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 = $opts{'print'}; my $eol = "\n"; # Set caseroot for Debug mode if ( $opts{'debug'} && ! defined($opts{'caseroot'}) ) { $opts{'caseroot'} = "$cwd/unit_testers"; if ( $print > 1 ) { print "caseroot = $opts{'caseroot'}\n"; } } my $CASEROOT = $opts{'caseroot'}; my $SCRIPTSROOT = $opts{'scriptsroot'}; my $INST_STRING = $opts{'inst_string'}; #----------------------------------------------------------------------------------------------- my $bldconfdir = "$CASEROOT/Buildconf/datmconf"; if ( $opts{'debug'} ) { my $cmd = "mkdir -p $bldconfdir"; print "Execute: $cmd\n"; system( "$cmd" ); chdir( "$bldconfdir" ); } # build empty config_cache.xml file (needed below) my $config_cache = "$bldconfdir/$cfg_cache"; my $fh = new IO::File; $fh->open(">$config_cache") or die "** can't open file: $config_cache\n"; print $fh <<"EOF"; EOF $fh->close; if ($print>=1) { print "Wrote file $config_cache $eol"; } (-f "$cfg_cache") or die <<"EOF"; ** $ProgName - Cannot find configuration cache file: $cfg_cache\" ** 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 input data files must be specified. #The root directory to cesm utils Tools my $cesm_tools = &absolute_path( "${SCRIPTSROOT}/ccsm_utils/Tools" ); # 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_file = "$cfgdir/namelist_files/namelist_definition_datm.xml"; (-f "$nl_definition_file") or die <<"EOF"; ** $ProgName - Cannot find namelist definition file \"$nl_definition_file\" ** EOF if ($print>=2) { print "Using namelist definition file $nl_definition_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_datm.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 DATM config_cache.xml file. my $cfg = Build::Config->new( $cfg_cache ); # 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 DATM 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" ); } # Create an empty namelist object. Add values to it in order of precedence. my $nl = Build::Namelist->new(); #----------------------------------------------------------------------------------------------- # Some regular expressions... my $TRUE = '\.true\.'; my $FALSE = '\.false\.'; #----------------------------------------------------------------------------------------------- # 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'}) { foreach my $infile ( split( /,/, $opts{'infile'} ) ) { # Parse namelist input from a 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); } } #----------------------------------------------------------------------------------------------- #################################### # Required xml variables # #################################### my %xmlvars = (); my @files = <${CASEROOT}/env_*xml>; foreach my $file (@files) { if ( $print > 1 ) { print "Open env.xml file: $file\n"; } 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'}; } } foreach my $attr (keys %xmlvars) { if ( $xmlvars{$attr} =~ m/\$/ ) { $xmlvars{$attr} = &Streams::TemplateGeneric::expandXMLVar( $xmlvars{$attr}, \%xmlvars ); } } foreach my $var ( "RUN_TYPE", "DIN_LOC_ROOT", "ATM_DOMAIN_FILE", "ATM_DOMAIN_PATH", "DATM_MODE", "DATM_PRESAERO", "ATM_GRID", "GRID" ) { if ( $print > 1 ) { print "$var = $xmlvars{$var}\n"; } if ( ! defined($xmlvars{$var}) || $xmlvars{$var} =~ /^(UNSET|)$/ ) { die <<"EOF"; ** $ProgName - $var is NOT set ** EOF } } my $RUN_TYPE = $xmlvars{'RUN_TYPE'}; my $DIN_LOC_ROOT = $xmlvars{'DIN_LOC_ROOT'}; my $ATM_DOMAIN_FILE = &Streams::TemplateGeneric::expandXMLVar( $xmlvars{'ATM_DOMAIN_FILE'}, \%xmlvars ); my $ATM_DOMAIN_PATH = &Streams::TemplateGeneric::expandXMLVar( $xmlvars{'ATM_DOMAIN_PATH'}, \%xmlvars );; my $DATM_MODE = $xmlvars{'DATM_MODE'}; my $DATM_PRESAERO = $xmlvars{'DATM_PRESAERO'}; my $ATM_GRID = $xmlvars{'ATM_GRID'}; my $GRID = $xmlvars{'GRID'}; my $CLM_USRDAT_NAME = $xmlvars{'CLM_USRDAT_NAME'}; # Validate some of the env values. if ( $DATM_MODE =~ /CLM/ && $DATM_PRESAERO eq "none" ) { die "${nm}:: A DATM_MODE for CLM is incompatible with DATM_PRESAERO=none\n"; } if ( $GRID eq "CLM_USRDAT" && $CLM_USRDAT_NAME =~ /^(UNSET|)$/ ) { die <<"EOF"; ** $ProgName - GRID=CLM_USRDAT and CLM_USRDAT_NAME is NOT set ** EOF } (-d $DIN_LOC_ROOT) or die <<"EOF"; ** $ProgName - CESM inputdata root is not a directory: \"$DIN_LOC_ROOT\" ** EOF if ($print>=2) { print "CESM inputdata root directory: $DIN_LOC_ROOT$eol"; } if ($opts{'test'}) { (-d $DIN_LOC_ROOT) or die <<"EOF"; ** $ProgName - CESM inputdata root is not a directory: \"$DIN_LOC_ROOT\" ** EOF } my $test_files = $opts{'test'}; my $var = "DATM_MODE"; my $group = $definition->get_group_name($var); $nl->set_variable_value( $group, $var, "\'$xmlvars{$var}\'" ); my $var = "DATM_PRESAERO"; my $group = $definition->get_group_name($var); $nl->set_variable_value( $group, $var, "\'$xmlvars{$var}\'" ); #################################### # Streams file(s) # #################################### # Get defaults for data manipulation options associated with # each stream (mapping, filling, time-interp etc.) if ($print>=1) { print " datm mode is $DATM_MODE \n"; } if ($print>=1) { print " datm presaero mode is $DATM_PRESAERO \n"; } if ($print>=1) { print " datm cesm grid is $GRID \n"; } if ($print>=1) { print " datm atm grid is $ATM_GRID \n" }; # Create input data list (written to later) my $fh_out = new IO::File; $fh_out->open(">$CASEROOT/Buildconf/datm.input_data_list") or die "** can't open filepath file: datm.input_data_list\n"; # Hash for parsing default_namelist_datm.xml my %default_namelist_opts; $default_namelist_opts{'grid'} = $GRID; $default_namelist_opts{'datm_mode'} = $DATM_MODE; $default_namelist_opts{'presaero_mode'} = $DATM_PRESAERO; # Create streams template file(s) - loop over streams my $streams = $defaults->get_value( "streamslist", \%default_namelist_opts ); $streams = &Streams::TemplateGeneric::expandXMLVar( $streams, \%xmlvars ); my @streams = split ",", $streams, -1; if ($DATM_PRESAERO ne "none") { if ($DATM_PRESAERO eq "pt1_pt1") { push (@streams, "presaero.$DATM_PRESAERO.$GRID"); } else { push (@streams, "presaero.$DATM_PRESAERO"); } } my $ostreams = undef; my $omapalgo = undef; my $omapmask = undef; my $otintalgo = undef; my $otaxmode = undef; my $ofillalgo = undef; my $ofillmask = undef; my $odtlimit = undef; foreach my $stream ( @streams ) { # Exit if there is no prescribed aerosol stream if ($stream eq "presaero" && $DATM_PRESAERO eq "none" ) { next; } if ($print>=1) { print " datm stream is $stream$INST_STRING \n";} # Create hash needed to parse namelist_defaults.xml file # Hash default_namelist_opts contains the attribute values needed for $defaults->get_value $default_namelist_opts{'stream'} = $stream; if ($DATM_PRESAERO ne 'none') { $default_namelist_opts{'presaero_flag'} = "active"; } else { $default_namelist_opts{'presaero_flag'} = "none"; } if ( $stream =~ /presaero/ ) { $default_namelist_opts{'ispresaerostream'} = "TRUE"; } else { $default_namelist_opts{'ispresaerostream'} = "FALSE"; } $default_namelist_opts{'grid'} = $GRID; $default_namelist_opts{'presaero_mode'} = $DATM_PRESAERO; # Determine stream txt file my $outstream = "datm.streams.txt" . ".$stream" . "$INST_STRING"; if (-e "$CASEROOT/user_$outstream") { my $command = "cp -p $CASEROOT/user_$outstream $bldconfdir/$outstream"; system($command) == 0 or die "system $command failed: $? \n"; } else { # Create hash to initialze streams object my %stream_template_opts; if ( $print == 0 ) { $stream_template_opts{'printing'} = 0; } else { $stream_template_opts{'printing'} = 1; } $stream_template_opts{'test'} = $opts{'test'}; $stream_template_opts{'ProgName'} = $ProgName; $stream_template_opts{'ProgDir'} = "$cfgdir"; $stream_template_opts{'cmdline'} = $cmdline; $stream_template_opts{'offset'} = $defaults->get_value( "strm_offset" , \%default_namelist_opts ); $stream_template_opts{'filepath'} = $defaults->get_value( "strm_datdir" , \%default_namelist_opts ); $stream_template_opts{'filenames'} = $defaults->get_value( "strm_datfil" , \%default_namelist_opts ); $stream_template_opts{'domainpath'} = $defaults->get_value( "strm_domdir" , \%default_namelist_opts ); $stream_template_opts{'domain'} = $defaults->get_value( "strm_domfil" , \%default_namelist_opts ); $stream_template_opts{'datvarnames'}= $defaults->get_value( "strm_datvar" , \%default_namelist_opts ); $stream_template_opts{'domvarnames'}= $defaults->get_value( "strm_domvar" , \%default_namelist_opts ); $stream_template_opts{'yearfirst'} = $defaults->get_value( "strm_year_start", \%default_namelist_opts ); $stream_template_opts{'yearlast'} = $defaults->get_value( "strm_year_end" , \%default_namelist_opts ); $stream_template_opts{'filepath'} = &Streams::TemplateGeneric::expandXMLVar( $stream_template_opts{'filepath'}, \%xmlvars ); $stream_template_opts{'filenames'} = &Streams::TemplateGeneric::expandXMLVar( $stream_template_opts{'filenames'}, \%xmlvars ); $stream_template_opts{'domainpath'} = &Streams::TemplateGeneric::expandXMLVar( $stream_template_opts{'domainpath'}, \%xmlvars ); $stream_template_opts{'domain'} = &Streams::TemplateGeneric::expandXMLVar( $stream_template_opts{'domain'}, \%xmlvars ); $stream_template_opts{'yearfirst'} = &Streams::TemplateGeneric::expandXMLVar( $stream_template_opts{'yearfirst'}, \%xmlvars ); $stream_template_opts{'yearlast'} = &Streams::TemplateGeneric::expandXMLVar( $stream_template_opts{'yearlast'}, \%xmlvars ); foreach my $item ( "yearfirst", "yearlast", "offset", "filepath", "filenames", "domainpath", "domain", "datvarnames", "domvarnames" ) { if ( $stream_template_opts{$item} =~ /^[ ]*$/ ) { die "$ProgName ERROR:: bad $item for stream: $stream\n"; } } # Create the streams txt file for this stream (from a generic template) my $stream_template = Streams::TemplateGeneric->new( \%stream_template_opts ); $stream_template->Read( "${CASEROOT}/Buildconf/datm.template.streams.xml" ); $stream_template->Write( $outstream ); # Append to datm.input_data_list my @filenames = $stream_template->GetDataFilenames( 'domain'); my $i = 0; foreach my $file ( @filenames ) { $i++; print $fh_out "domain${i} = $file\n"; } my @filenames = $stream_template->GetDataFilenames( 'data'); my $i = 0; foreach my $file ( @filenames ) { $i++; print $fh_out "file${i} = $file\n"; } } # Stream specific namelist variables used below for $nl my $tintalgo = $defaults->get_value( "strm_tintalgo" , \%default_namelist_opts); my $mapalgo = $defaults->get_value( 'strm_mapalgo' , \%default_namelist_opts); my $mapmask = $defaults->get_value( 'strm_mapmask' , \%default_namelist_opts); my $taxmode = $defaults->get_value( "strm_taxmode" , \%default_namelist_opts); my $fillalgo = $defaults->get_value( 'strm_fillalgo' , \%default_namelist_opts); my $fillmask = $defaults->get_value( 'strm_fillmask' , \%default_namelist_opts); my $dtlimit = $defaults->get_value( 'strm_dtlimit' , \%default_namelist_opts); my $beg_year = $defaults->get_value( 'strm_year_start', \%default_namelist_opts); my $end_year = $defaults->get_value( 'strm_year_end' , \%default_namelist_opts); my $align_year = $defaults->get_value( 'strm_year_align', \%default_namelist_opts); $beg_year = &Streams::TemplateGeneric::expandXMLVar($beg_year, \%xmlvars ); $end_year = &Streams::TemplateGeneric::expandXMLVar($end_year, \%xmlvars ); $align_year = &Streams::TemplateGeneric::expandXMLVar($align_year, \%xmlvars ); foreach my $year ( $beg_year, $end_year, $align_year ) { if ( $year eq "" || $year !~ /[0-9]+/ ) { print "\n\nyear=$year is NOT set or NOT an integer\n"; die "$ProgName ERROR:: bad year to run stream over: $stream\n"; } } if ( $beg_year > $end_year ) { print "\n\nbeg_year=$beg_year end_year=$end_year\n"; die "$ProgName ERROR:: beg_year greater than end_year\n"; } if ( ! defined($ostreams) ) { $ostreams = "\"$outstream $align_year $beg_year $end_year\""; $omapalgo = "\'$mapalgo\'"; $omapmask = "\'$mapmask\'"; $otintalgo = "\'$tintalgo\'"; $otaxmode = "\'$taxmode\'"; $ofillalgo = "\'$fillalgo\'"; $ofillmask = "\'$fillmask\'"; $odtlimit = "$dtlimit"; } else { $ostreams = "$ostreams,\"$outstream $align_year $beg_year $end_year\""; $omapalgo .= ",\'$mapalgo\'"; $omapmask .= ",\'$mapmask\'"; $otintalgo .= ",\'$tintalgo\'"; $otaxmode .= ",\'$taxmode\'"; $ofillalgo .= ",\'$fillalgo\'"; $ofillmask .= ",\'$fillmask\'"; $odtlimit .= ",$dtlimit"; } } $fh_out->close; ########################################################## # namelist group: shr_strdata_nml (in file datm_atm_in) # ########################################################## my $datamode = $defaults->get_value( "datamode", \%default_namelist_opts ); my $vectors = $defaults->get_value( "vectors", \%default_namelist_opts ); add_default($nl, 'datamode', 'val' => "$datamode" ); add_default($nl, 'vectors', 'val' => "$vectors" ); add_default($nl, 'domainfile', 'val' => "${ATM_DOMAIN_PATH}/${ATM_DOMAIN_FILE}" ); add_default($nl, 'streams', 'val' => "$ostreams" ); add_default($nl, 'mapalgo', 'val' => "$omapalgo" ); add_default($nl, 'mapmask', 'val' => "$omapmask" ); add_default($nl, 'tintalgo', 'val' => "$otintalgo" ); add_default($nl, 'taxmode', 'val' => "$otaxmode" ); add_default($nl, 'fillalgo', 'val' => "$ofillalgo" ); add_default($nl, 'fillmask', 'val' => "$ofillmask" ); add_default($nl, 'dtlimit', 'val' => "$odtlimit" ); ########################################################## # namelist group: datm_nml (in file datm_in) # ########################################################## add_default($nl, 'atm_in' , 'val'=>"datm_atm_in${INST_STRING}"); add_default($nl, 'iradsw'); if ($DATM_MODE =~ /^CORE/) { my $factorfn = "$DIN_LOC_ROOT/atm/datm7/CORE2/COREv2.correction_factors.T62.121007.nc"; add_default($nl, 'factorfn', 'val'=>"$factorfn" ); } if ($DATM_PRESAERO eq "none" ) { add_default($nl, 'presaero', 'val'=>'.false.'); } else { add_default($nl, 'presaero', 'val'=>'.true.'); } add_default($nl, 'decomp', 'val'=>'1d'); add_default($nl, 'restfilm', 'val'=>'unset'); add_default($nl, 'restfils', 'val'=>'unset'); if ( $RUN_TYPE =~ /branch/ ) { # The datm master restart file is currently unused #if (not defined $nl->get_value('restfils')) { # die "$ProgName ERROR:: restfilm is required for a branch type.\n"; #} } #----------------------------------------------------------------------------------------------- # Validate that the entire resultant namelist is valid # $definition->validate($nl); #----------------------------------------------------------------------------------------------- # Write output files # my $note = ""; # datm_atm_in my @groups = qw(shr_strdata_nml); my $outfile = "./datm_atm_in"; $nl->write($outfile, 'groups'=>\@groups, 'note'=>"$note" ); if ($print>=2) { print "Writing datm_dshr namelist to $outfile $eol"; } # datm_in @groups = qw(datm_nml); $outfile = "./datm_in"; $nl->write($outfile, 'groups'=>\@groups, 'note'=>"$note" ); if ($print>=2) { print "Writing datm_in namelist to $outfile $eol"; } # atm_modelio @groups = qw(modelio); $outfile = "./atm_modelio.nml"; $nl->set_variable_value( "modelio", "logfile", "'atm.log'" ); $nl->write($outfile, 'groups'=>\@groups, 'note'=>"$note" ); if ($print>=2) { print "Writing atm_modelio.nml namelist to $outfile $eol"; } # Test that input files exist locally. check_input_files($nl, $DIN_LOC_ROOT, "$CASEROOT/Buildconf/datm.input_data_list"); # 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 # $DIN_LOC_ROOT -- 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; } } unless ( defined($val) ) { unless ($opts{'nofail'}) { die "$ProgName - No default value found for $var.\n" . " Are defaults provided? \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, $DIN_LOC_ROOT); if ( $test_files ) { if ($print>=2) { print "Test that $val exists$eol"; } if ( ($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) { 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 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 quote_string { my $str = shift; $str =~ s/^\s+//; $str =~ s/\s+$//; unless ($str =~ /^['"]/) { #"' $str = "\'$str\'"; } return $str; }