#!/usr/bin/env perl #----------------------------------------------------------------------------------------------- # cesm_setup - create the $caseroot/$case.run script and user_nl_xxx component namelist mod files #----------------------------------------------------------------------------------------------- use strict; use Cwd; use English; use Getopt::Long; use IO::File; use IO::Handle; #----------------------------------------------------------------------------------------------- # Setting autoflush (an IO::Handle method) on STDOUT helps in debugging. It forces the test # descriptions to be printed to STDOUT before the error messages start. *STDOUT->autoflush(); #----------------------------------------------------------------------------------------------- # Set the directory that contains this script. my $caseroot = getcwd(); # current working directory $ENV{CASEROOT}=$caseroot; # put this in environment my $eol = "\n"; #----------------------------------------------------------------------------------------------- sub usage { die < \$opts{'help'}, "clean" => \$opts{'clean'}, ) or usage(); # Give usage message. usage() if $opts{'help'}; # Check for unparsed argumentss if (@ARGV) { print "ERROR: unrecognized arguments: @ARGV\n"; usage(); } my $clean = 0; if ($opts{'clean'}) { $clean = 1; } #----------------------------------------------------------------------------------------------- # The XML::Lite module is required to parse the XML configuration files. #----------------------------------------------------------------------------------------------- (-f "$caseroot/Tools/XML/Lite.pm") or die <<"EOF"; ** Cannot find perl module \"XML/Lite.pm\" in directory \"$caseroot/ccsm_utils/Tools/perl5lib\" ** EOF my $dirs = "$caseroot/Tools"; unshift @INC, $dirs; require XML::Lite; require SetupTools; my %xmlvars=(); SetupTools::getxmlvars($caseroot, \%xmlvars); # Check that userdefine settings are specified before expanding variable my $fail_setup = 0; my $strmacros; my $strbuild; my $strrun; foreach my $attr (keys %xmlvars) { if ( $xmlvars{$attr} =~ m/USERDEFINED_required_macros/ ) { $strmacros.="ERROR: must set xml variable $attr to generate Macros file \n"; $fail_setup = 1; } if ( $xmlvars{$attr} =~ m/USERDEFINED_required_build/ ) { $strbuild.="ERROR: must set xml variable $attr to build the model \n"; $fail_setup = 1; } if ( $xmlvars{$attr} =~ m/USERDEFINED_required_run/ ) { $strrun.="ERROR: must set xml variable $attr to run the model \n"; $fail_setup = 1; } $xmlvars{$attr} = SetupTools::expand_env_var($xmlvars{$attr}, \%xmlvars); } if ($fail_setup) { print "$strmacros" if(defined($strmacros)); print "$strbuild" if(defined($strbuild)); print "$strrun" if(defined($strrun)); die "Correct above and issue cesm_setup again \n"; } #----------------------------------------------------------------------------------------------- # Create batch script #----------------------------------------------------------------------------------------------- if (! $clean ) { my $mach = $xmlvars{'MACH'}; my $case = $xmlvars{'CASE'}; # Create Macros only if it does not exist if (!-f "./Macros") { print "Creating Macros file for $mach$eol"; SetupTools::set_compiler($xmlvars{OS},"$xmlvars{CCSM_MACHDIR}/config_compilers.xml", $xmlvars{COMPILER},$xmlvars{MACH}, $xmlvars{MPILIB}, 0, 'Macros' ); } else { print "Macros script already created ...skipping$eol "; } # Set tasks to 1 if mpi-serial library if($xmlvars{MPILIB} eq "mpi-serial"){ foreach my $attr (keys %xmlvars){ if($attr =~ /NTASKS_/){ $xmlvars{$attr}=1; my $sysmod = "./xmlchange -file env_mach_pes.xml -id $attr -val 1"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} } } } my $ninst_fail = 0; foreach my $comp (qw(ATM LND ROF OCN ICE GLC WAV)){ my $ninst = "NINST_".$comp; my $ntasks = "NTASKS_".$comp; if ($xmlvars{$ninst} > $xmlvars{$ntasks}) { if($xmlvars{$ntasks}==1){ system("./xmlchange $ntasks=$xmlvars{$ninst}"); $xmlvars{$ntasks}=$xmlvars{$ninst}; }else{ die "ERROR cesm_setup: $comp NINST value greater than $comp NTASKS"; } } } if (-f "$caseroot/$case.run") { print "Machine/Decomp/Pes configuration has already been done ...skipping$eol "; } else { my $sysmod = "./Tools/ccsm_check_lockedfiles"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} my $pestot = `$caseroot/Tools/taskmaker.pl -sumonly`; $sysmod = "./xmlchange -file env_mach_pes.xml -id TOTALPES -val $pestot"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} # Compute cost based on PE count my $pval = 1; my $pcnt = 0; while ($pval < $pestot) { $pval = $pval * 2; #$pcnt = $pcnt + 10; # (perfect scaling) $pcnt = $pcnt + 6; # (scaling like sqrt(6/10)) } my $pcost = 3 - int($pcnt / 10); # (3 is 64 with 6) # Compute cost based on DEBUG my $ccsm_dcost = 0; if ($xmlvars{'DEBUG'} == "TRUE") {$ccsm_dcost = 3;} # Compute cost based on run length # For simplicity, we use a heuristic just based on STOP_OPTION (not considering # STOP_N), and only deal with options longer than ndays my $ccsm_lcost = 0; if ($xmlvars{'STOP_OPTION'} =~ /nmonth/) { # N months costs 30x as much as N days; since cost is based on log-base-2, add 5 $ccsm_lcost = 5; } elsif ($xmlvars{'STOP_OPTION'} =~ /nyear/) { # N years costs 365x as much as N days; since cost is based on log-base-2, add 9 $ccsm_lcost = 9; } my $CCSM_CCOST = $xmlvars{'CCSM_CCOST'}; my $CCSM_GCOST = $xmlvars{'CCSM_GCOST'}; my $CCSM_TCOST = $xmlvars{'CCSM_TCOST'}; my $CCSM_MCOST = $xmlvars{'CCSM_MCOST'}; my $estcost = $CCSM_CCOST + $CCSM_GCOST + $CCSM_MCOST + $CCSM_TCOST + $pcost + $ccsm_dcost + $ccsm_lcost; $sysmod = "./xmlchange -file env_mach_pes.xml -id TOTALPES -val $pestot"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "./xmlchange -file env_mach_pes.xml -id CCSM_PCOST -val $pcost"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "./xmlchange -file env_mach_pes.xml -id CCSM_ESTCOST -val $estcost"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} print "Creating batch script $case.run $eol"; my $fh = new IO::File; my $file = "${caseroot}/${case}.run"; $sysmod = "env PHASE=set_batch CASEROOT=$caseroot ./Tools/mkbatch.${mach}"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "${caseroot}/Tools/taskmaker.pl -document >> $file"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "echo cd $caseroot >> $file"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "cat ${caseroot}/Tools/cesm_prerun_setup >> $file"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "cat ${caseroot}/Tools/cesm_buildnml >> $file"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "cat ${caseroot}/Tools/cesm_prestage >> $file"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "env PHASE=set_exe CASE=$case CASEROOT=$caseroot ./Tools/mkbatch.${mach}"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "cat ${caseroot}/Tools/cesm_postrun_setup >> $file"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} $fh->close(); $sysmod = "chmod 755 $file"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} # Make a copy of env_mach_pes.xml in order to be able # to check that it does not change once cesm_setup is invoked if (! -e "LockedFiles/env_mach_pes.xml.locked") { $sysmod = "cp env_mach_pes.xml LockedFiles/env_mach_pes.xml.locked"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} print "Locking file env_mach_pes.xml $eol"; } } # Create user_nl files for the required number of instances if (!-f "./user_nl_cpl") { print "Creating user_nl_xxx files for components and cpl$eol"; } my $buildconf = "$caseroot/Buildconf"; my @models = qw( COMP_ATM COMP_LND COMP_ICE COMP_OCN COMP_GLC COMP_ROF COMP_WAV ); foreach my $model (@models) { my $comp = $xmlvars{"$model"}; if (-f "$buildconf/$comp.user_nl.csh") { my $sysmod = "env CASEROOT=$caseroot CODEROOT=$xmlvars{CCSMROOT}/models"; $sysmod = "$sysmod CASEBUILD=$xmlvars{CASEROOT}/Buildconf"; $sysmod = "$sysmod NINST_ATM=$xmlvars{NINST_ATM}"; $sysmod = "$sysmod NINST_LND=$xmlvars{NINST_LND}"; $sysmod = "$sysmod NINST_ROF=$xmlvars{NINST_ROF}"; $sysmod = "$sysmod NINST_ICE=$xmlvars{NINST_ICE}"; $sysmod = "$sysmod NINST_OCN=$xmlvars{NINST_OCN}"; $sysmod = "$sysmod NINST_GLC=$xmlvars{NINST_GLC}"; $sysmod = "$sysmod NINST_WAV=$xmlvars{NINST_WAV}"; $sysmod = "$sysmod $buildconf/$comp.user_nl.csh"; qx/$sysmod/ == 0 or die "ERROR: $sysmod failed: $?\n"; } } my $sysmod = "env CASEROOT=$caseroot CODEROOT=$xmlvars{CCSMROOT}/models $buildconf/cpl.user_nl.csh"; qx/$sysmod/ == 0 or die "ERROR: $sysmod failed: $?\n"; print "Running preview_namelist script $eol"; my $sysmod = "$caseroot/preview_namelists"; system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n"; print "See ./CaseDoc for component namelists $eol"; print "If an old case build already exists, might want to run ${case}.clean_build before building $eol"; # If this is a testcase, and the PESSETupHist directory doesn't exist, recreate the .test script if( -e "$caseroot/env_test.xml" && -e "$caseroot/PESetupHist") { print "creating test script $xmlvars{'CASE'}.test\n"; # Run mkbatch for the test script my $mkbatch ="./Tools/mkbatch.$xmlvars{'MACH'}"; $mkbatch = "$xmlvars{'CCSM_MACHDIR'}/mkbatch.$xmlvars{'MACH'}" if(! -e $mkbatch); my $sysmod = "env PHASE=set_batch TESTMODE=test $mkbatch"; system($sysmod) == 0 or die "ERROR: $sysmod failed: $?$eol"; $sysmod = " $xmlvars{'CCSMROOT'}/scripts/ccsm_utils/Tools/testcase_env.csh"; system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n"; my $testbatch = "$xmlvars{'CASE'}.test"; open my $testfh, ">>", $testbatch or die "couldn't open $testbatch for appending, $!"; print $testfh "cd $xmlvars{'CASEROOT'}\n"; print $testfh "./Tools/ccsm_check_lockedfiles || exit -4$eol"; print $testfh "source ./Tools/ccsm_getenv || exit -5$eol"; close $testfh; # Finish recreating the test script. $sysmod = "cat $xmlvars{'CCSMROOT'}/scripts/ccsm_utils/Tools/testcase_begin >> $testbatch"; system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n"; $sysmod = "cat $xmlvars{'CCSMROOT'}/scripts/ccsm_utils/Testcases/$xmlvars{'TESTCASE'}" . "_script" . " >> $testbatch"; system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n"; $sysmod = "cat $xmlvars{'CCSMROOT'}/scripts/ccsm_utils/Tools/testcase_end >> $testbatch"; system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n"; $sysmod = "chmod 755 $xmlvars{'CASE'}.test"; system($sysmod) == 0 or die "ERROR: $sysmod failed: $?\n"; } } #----------------------------------------------------------------------------------------------- # Clean batch script #----------------------------------------------------------------------------------------------- if ($clean) { my $case = $xmlvars{'CASE'}; if (!-f "$caseroot/$case.run" ) { print "clean option has already been invoked ...skipping $eol"; exit; } my $sysmod; my $id = `date +%y%m%d-%H%M%S`; my $backupdir = "PESetupHist/b.${id}"; if (!-d ${backupdir}) { $sysmod = "mkdir -p ${backupdir}"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} } $sysmod = "cp ${case}.run env_build.xml env_mach_pes.xml ${backupdir}"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} # If this is a testcase, remove the .test script if( -e "$caseroot/$case.test") { $sysmod = "cp ${case}.test ${backupdir}"; system ($sysmod); if($? == -1) {die "$sysmod failed: $!\n";} $sysmod = "rm ./${case}.test"; system ($sysmod); if($? == -1) {die "$sysmod failed: $!\n";} print "Successfully cleaned test script $case.test $eol"; } $sysmod = "rm ./${case}.run"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} my @files = <${caseroot}/LockedFiles/*build* ${caseroot}/LockedFiles/*mach_pes*>; foreach my $file (@files) { $sysmod = "rm $file"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} } # must rebuild the models (even on restart) $sysmod = "./xmlchange -file env_build.xml -id BUILD_COMPLETE -val FALSE"; system ($sysmod); if ($? == -1) {die "$sysmod failed: $!\n";} print "Successfully cleaned batch script $case.run $eol"; print "Some files have been saved to ${backupdir}$eol"; my $fh = new IO::File; $fh->open(">>CaseStatus") or die "can't open file: CaseStatus\n"; my $sdate = `date +"%Y-%m-%d %H:%M:%S"`; print $fh "cesm_setup -clean $sdate $eol"; $fh->close(); } exit;