#!/usr/bin/env perl #----------------------------------------------------------------------------------------------- # # configure # # # This utility allows the CICE user to specify compile-time configuration # options via a commandline interface. The output from configure is a # Makefile and a cache file that contains all configuration parameters # required to produce the Makefile. A subsequent invocation of configure # can use the cache file as input (via the -defaults argument) to reproduce # the CICE configuration contained in it. Note that when a cache file is # used to set default values only the model parameters are used. The # parameters that are platform dependent (e.g., compiler options, library # locations, etc) are ignored. # # As the build time configurable options of CICE are changed, this script # must also be changed. Thus configure is maintained under revision # control in the CICE source tree and it is assumed that only the version of # configure in the source tree will be used to build CICE. Thus we assume # that the root of the source tree can be derived from the location of this # script. # # configure has an optional test mode to check that the Fortran90 compiler # works and that external references to the netCDF and MPI libraries can be # resolved at link time. # # Date Contributor Modification # ----------------------------------------------------------------------------------------------------- # 2009-01-18 Mariana Vertenstein Original version #----------------------------------------------------------------------------------------------- use strict; #use warnings; #use diagnostics; use Cwd qw( getcwd abs_path chdir); use English; use Getopt::Long; use IO::File; use IO::Handle; #----------------------------------------------------------------------------------------------- sub usage { die <). Any value that contains white-space must be quoted. Long option names may be supplied with either single or double leading dashes. A consequence of this is that single letter options may NOT be bundled. ***************************************************** To obtain the cice decompsition, can do any of the following Set all of -bsizex, -bsizey, -maxblocks, -decomptype explicitly sets the cice decomposition OR -ntasks, -nthreads used to determine defaults for the cice decomposition. OR -nodecomp the cice decompsition is determined external to configure ****************************************************** -cache Name of output cache file (default: config_cache.xml). -cachedir Name of directory where output cache file is written (default: CICE build directory). -cice_mode Mode [prognostic | thermo_only | prescribed ]. (default: prognostic) -comp_intf Component interface to use (ESMF, MCT) (default MCT) -bsizex Size of cice block in first horizontal dimension. -bsizey Size of cice block in second horizontal dimension. -maxblocks Max number of cice blocks per processor. -decomptype Decomposition type ( valid values are [cartesian | spacecurve]) -help [or -h] Print usage to STDOUT. -hgrid 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); -nx Number of longitudes -ny Number of latitudes -ntasks Number of MPI tasks. -nthreads Number of OMP threads per process. Setting nthreads > 0 implies smp. -nodecomp Turns off the generation of cice decomposition (default: not defined - decomp is generated) -ntr_aero Turns on aerosal tracers (valid values are 0,1,2,3,4,5,6, default is 3) -silent [or -s] Turns on silent mode - only fatal messages issued. -verbose [or -v] Turn on verbose echoing of settings made by configure. -version Echo the CVS tag name used to check out this CICE distribution. EOF } #----------------------------------------------------------------------------------------------- # Setting autoflush (an IO::Handle method) on STDOUT helps in debugging. It forces the test # descriptions to be printed to STDOUT before the error messages start. *STDOUT->autoflush(); #----------------------------------------------------------------------------------------------- # Set the directory that contains the CICE configuration scripts. If the configure command was # issued using a relative or absolute path, that path is in $ProgDir. Otherwise assume the # command was issued from the current working directory. (my $ProgName = $0) =~ s!(.*)/!!; # name of this script my $ProgDir = $1; # name of directory containing this script -- may be a # relative or absolute path, or null if the script is in # the user's PATH my $cwd = getcwd(); # current working directory my $cfgdir; # absolute pathname of directory that contains this script if ($ProgDir) { $cfgdir = abs_path($ProgDir); } else { $cfgdir = $cwd; } #----------------------------------------------------------------------------------------------- # Save commandline my $commandline = "$cfgdir/configure @ARGV"; #----------------------------------------------------------------------------------------------- # Parse command-line options. my %opts = ( cache => "config_cache.xml", ); GetOptions( "cache=s" => \$opts{'cache'}, "cachedir=s" => \$opts{'cachedir'}, "cice_mode=s" => \$opts{'cice_mode'}, "comp_intf=s" => \$opts{'comp_intf'}, "bsizex=s" => \$opts{'bsizex'}, "bsizey=s" => \$opts{'bsizey'}, "maxblocks=s" => \$opts{'maxblocks'}, "decomptype=s" => \$opts{'decomptype'}, "h|help" => \$opts{'help'}, "hgrid=s" => \$opts{'hgrid'}, "nx=i" => \$opts{'nx'}, "ny=i" => \$opts{'ny'}, "ntr_aero=s" => \$opts{'ntr_aero'}, "nicelyr=s" => \$opts{'nicelyr'}, "nsnwlyr=s" => \$opts{'nsnwlyr'}, "trlvl=s" => \$opts{'trlvl'}, "trpnd=s" => \$opts{'trpnd'}, "trage=s" => \$opts{'trage'}, "trfy=s" => \$opts{'trfy'}, "trbri=s" => \$opts{'trbri'}, "trbgcs=s" => \$opts{'trbgcs'}, "nbgclyr=s" => \$opts{'nbgclyr'}, "ntasks=s" => \$opts{'ntasks'}, "nthreads=s" => \$opts{'nthreads'}, "nodecomp!" => \$opts{'nodecomp'}, "s|silent" => \$opts{'silent'}, "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 argumentss if (@ARGV) { print "ERROR: unrecognized arguments: @ARGV\n"; usage(); } # Define 3 print levels: # 0 - only issue fatal error messages # 1 - only informs what files are created (default) # 2 - verbose my $print = 1; if ($opts{'silent'}) { $print = 0; } if ($opts{'verbose'}) { $print = 2; } my $eol = "\n"; my %cfg = (); # build configuration #----------------------------------------------------------------------------------------------- # CESM root directory and perl5lib root directories my $cesmroot = abs_path("$cfgdir/../../../"); my $perl5lib = "$cesmroot/cime/utils/perl5lib"; if (! -e $cesmroot) { die "** Invalid CESM root directory: $cesmroot ** "; } if (! -e "$perl5lib") { die "** Invalid perl5lib root directory: $perl5lib ** "; } if ($print>=2) { print "Setting CESM root directory to $cesmroot$eol"; } #----------------------------------------------------------------------------------------------- # Make sure we can find required perl modules and configuration files. # Look for them in the directory that contains the configure script. # Check for the configuration definition file. my $config_def_file = "config_files/definition.xml"; (-f "$cfgdir/$config_def_file") or die <<"EOF"; ** Cannot find configuration definition file \"$config_def_file\" in directory \"$cfgdir\" ** EOF # The Build::Config module provides utilities to store and manipulate the configuration. (-f "$perl5lib/Build/Config.pm") or die <<"EOF"; ** Cannot find perl module \"Build/Config.pm\" in directory \"$perl5lib\" ** EOF if ($print>=2) { print "Setting CICE configuration script directory to $cfgdir$eol"; } #----------------------------------------------------------------------------------------------- # Add $cfgdir/perl5lib to the list of paths that Perl searches for modules unshift @INC, "$perl5lib"; require Build::Config; # Initialize the configuration. The $config_def_file provides the definition of a CICE # configuration. $cfg_ref is a reference to the new configuration object. my $cfg_ref = Build::Config->new("$cfgdir/$config_def_file"); #----------------------------------------------------------------------------------------------- # CICE build directory. my $cice_bld; if (defined $opts{'cice_bld'}) { $cice_bld = abs_path($opts{'cice_bld'}); } else { # use default value $cice_bld = abs_path($cfg_ref->get('cice_bld')); } if (-d $cice_bld or mkdirp($cice_bld)) { # If the build directory exists or can be made then set the value... $cfg_ref->set('cice_bld', $cice_bld); } else { die <<"EOF"; ** Could not create the specified CICE build directory: $cice_bld EOF } if ($print>=2) { print "Setting CICE build directory to $cice_bld$eol"; } #----------------------------------------------------------------------------------------------- # configuration cache directory and file. my $config_cache_dir; my $config_cache_file; if (defined $opts{'cachedir'}) { $config_cache_dir = abs_path($opts{'cachedir'}); } else { $config_cache_dir = $cfg_ref->get('cice_bld'); } if (-d $config_cache_dir or mkdirp($config_cache_dir)) { $config_cache_file = "$config_cache_dir/$opts{'cache'}"; } else { die <<"EOF"; ** Could not create the specified directory for configuration cache file: $config_cache_dir EOF } if ($print>=2) { print "The configuration cache file will be created in $config_cache_file$eol"; } #----------------------------------------------------------------------------------------------- # Comp_intf option if (defined $opts{'comp_intf'}) { $cfg_ref->set('comp_intf', $opts{'comp_intf'}); } my $comp_intf = $cfg_ref->get('comp_intf'); if ($print>=2) { print "Using $comp_intf for comp_intf.$eol"; } #----------------------------------------------------------------------------------------------- # Set the cice mode my $cice_mode; if ($opts{'cice_mode'}) { $cice_mode = $opts{'cice_mode'}; $cfg_ref->set('cice_mode', $cice_mode); } else { $cice_mode = $cfg_ref->get('cice_mode'); } if ($print>=2) { print "cice : mode is $cice_mode $eol";} #----------------------------------------------------------------------------------------------- # Set the number of ice categories my $ncat; if (($cice_mode eq 'prognostic') || ($cice_mode eq 'thermo_only')) { $ncat = 5; } elsif ($cice_mode eq 'prescribed') { $ncat = 1; } else { die <<"EOF"; ** ERROR: following mode is not supported: $cice_mode EOF } if ($print>=2) { print "cice : ncat is $ncat $eol";} if (defined $opts{'nsnwlyr'}) { $cfg_ref->set('nsnwlyr', $opts{'nsnwlyr'}); } else { $cfg_ref->set('nsnwlyr', 1); } # Tracers if (defined $opts{'ntr_aero'}) { $cfg_ref->set('ntr_aero', $opts{'ntr_aero'}); } else { if ($cice_mode eq 'prescribed') { $cfg_ref->set('ntr_aero', 0); } else { $cfg_ref->set('ntr_aero', 3); } } if (defined $opts{'trage'}) { $cfg_ref->set('trage', $opts{'trage'}); } else { $cfg_ref->set('trage', 1); } if (defined $opts{'trfy'}) { $cfg_ref->set('trfy', $opts{'trfy'}); } else { $cfg_ref->set('trfy', 1); } if (defined $opts{'trpnd'}) { $cfg_ref->set('trpnd', $opts{'trpnd'}); } else { $cfg_ref->set('trpnd', 1); } if (defined $opts{'trlvl'}) { $cfg_ref->set('trlvl', $opts{'trlvl'}); } else { $cfg_ref->set('trlvl', 0); } if (defined $opts{'trbri'}) { $cfg_ref->set('trbri', $opts{'trbri'}); } else { $cfg_ref->set('trbri', 0); } #BGC if (defined $opts{'nbgclyr'}) { $cfg_ref->set('nbgclyr', $opts{'nbgclyr'}); } else { if ($cice_mode eq 'prescribed') { $cfg_ref->set('nbgclyr', 0); } else { $cfg_ref->set('nbgclyr', 3); } } if (defined $opts{'trbgcs'}) { $cfg_ref->set('trbgcs', $opts{'trbgcs'}); } else { $cfg_ref->set('trbgcs', 0); } #----------------------------------------------------------------------------------------------- # Horizontal grid parameters if (defined $opts{'hgrid'}) { $cfg_ref->set('hgrid', $opts{'hgrid'}); } my $hgrid = $cfg_ref->get('hgrid'); if ($print>=2) { print "Horizontal grid specifier: $hgrid.$eol"; } my $hgrid = $cfg_ref->get('hgrid'); my $nlon = $opts{'nx'}; my $nlat = $opts{'ny'}; $cfg_ref->set('nlat',$nlat); $cfg_ref->set('nlon',$nlon); # Need to override for extended grid configurations if ($hgrid =~ 'ar9v4') { $cfg_ref->set('nlat',720); $cfg_ref->set('nlon',1280); } if (defined $opts{'nicelyr'}) { $cfg_ref->set('nicelyr', $opts{'nicelyr'}); } else { if ($hgrid =~ m/ar9v.*/ ) { $cfg_ref->set('nicelyr', 7); } else { $cfg_ref->set('nicelyr', 4); } } # Override resolution settings to configure for SCAM mode. The override is needed # because in SCAM mode the -hgrid option is used to specify the resolution of default # datasets from which single data columns are extracted. my $scam = $cfg_ref->get('scam'); if ($scam) { $cfg_ref->set('nlat', 1); $cfg_ref->set('nlon', 1); } my $nlon = $cfg_ref->get('nlon'); my $nlat = $cfg_ref->get('nlat'); if ($print>=2) { print "cice : grid is $hgrid $eol";} if ($print>=2) { print "cice : nlon is $nlon $eol";} if ($print>=2) { print "cice : nlat is $nlat $eol";} #----------------------------------------------------------------------------------------------- # If the cice decomposition is not to be specified, then set the cpp variables and stop my $ntr_aero = $cfg_ref->get('ntr_aero'); if (defined $opts{'ntr_aero'}) {$ntr_aero = $opts{'ntr_aero'}}; if ($ntr_aero gt "6"){ die <<"EOF"; ** ERROR: ntr_aero value of $ntr_aero is invalid, only values less than 6 are supported EOF } my $nicelyr = $cfg_ref->get('nicelyr'); my $nsnwlyr = $cfg_ref->get('nsnwlyr'); my $nbgclyr = $cfg_ref->get('nbgclyr'); my $trage = $cfg_ref->get('trage'); my $trfy = $cfg_ref->get('trfy'); my $trlvl = $cfg_ref->get('trlvl'); my $trpnd = $cfg_ref->get('trpnd'); my $trbri = $cfg_ref->get('trbri'); my $trbgcs = $cfg_ref->get('trbgcs'); my $cice_cppdefs = " -DCCSMCOUPLED -Dncdf -DNUMIN=11 -DNUMAX=99 -DNICECAT=$ncat -DNXGLOB=$nlon -DNYGLOB=$nlat -DNTRAERO=$ntr_aero -DNBGCLYR=$nbgclyr -DNICELYR=$nicelyr -DNSNWLYR=$nsnwlyr -DTRAGE=$trage -DTRFY=$trfy -DTRLVL=$trlvl -DTRPND=$trpnd -DTRBRI=$trbri -DTRBGCS=$trbgcs"; # Trigger RASM options with ar9v grid, otherwise set CESM options if ($hgrid =~ m/ar9v.*/ ) { $cice_cppdefs = "$cice_cppdefs -DRASM_MODS"; } #----------------------------------------------------------------------------------------------- # Determine if will set the cice decomposition my $set_decomp = (defined $opts{'nodecomp'}) ? 0 : 1; # Set the CICE decomposition my $bsizex; my $bsizey; my $maxblocks; my $decomptype; if ($set_decomp) { # First, check to see if user has specified it. my $cice_decomp_params = 0; if (defined $opts{'bsizex'}) { $bsizex = $opts{'bsizex'}; ++$cice_decomp_params; } if (defined $opts{'bsizey'}) { $bsizey = $opts{'bsizey'}; ++$cice_decomp_params; } if (defined $opts{'maxblocks'}) { $maxblocks = $opts{'maxblocks'}; ++$cice_decomp_params; } if (defined $opts{'decomptype'}) { $decomptype = $opts{'decomptype'}; ++$cice_decomp_params; } # Check that if the user has specified any of the decomp parameters, # they must all be specified. if ($cice_decomp_params) { unless ($cice_decomp_params == 4) { die <<"EOF"; ** ERROR: If any of the CICE decomposition parameters are specified, then they ** must all be specified. The settings were: ** bsizex=$bsizex bsizey=$bsizey maxblocks=$maxblocks decomptype=$decomptype EOF } unless (defined $opts{'decomptype'}) { die <<"EOF"; ** ERROR: Whend the CICE decomposition parameters are specified, the ** cice decomptype must also be specified as ant input argument. EOF } $cfg_ref->set('cice_decomptype', $decomptype); } if ($cice_decomp_params == 0) { # Check whether ntasks is specified. my $ntasks; if (defined $opts{'ntasks'}) { $cfg_ref->set('ntasks', $opts{'ntasks'}); $ntasks = $opts{'ntasks'}; # Check for legal ntasks value if ($ntasks < 1) { die "ERROR: ntasks value < 1: ntasks= $ntasks $eol"; } } # Check whether nthreads is specified. my $nthreads; if (defined $opts{'nthreads'}) { $cfg_ref->set('nthreads', $opts{'nthreads'}); $nthreads = $opts{'nthreads'}; # Check for legal nthreads value if ($nthreads < 1) { die "ERROR: nthreads value < 1: nthreads= $nthreads $eol"; } } # User hasn't specified CICE decomp so get default values. # The defaults depend on the total number of execution threads available. These must # be specified by the user. my $cice_ntasks; my $cice_nthreads; # User must define both ntasks and nthreads if (defined $ntasks and defined $nthreads) { $cice_ntasks = $ntasks; $cice_nthreads = $nthreads; } else { die <<"EOF"; ** ERROR: If CICE decomposition parameters are not specified, then either ** -ntasks and -nthreads must be specified to determine a default decomposition ** OR -nodecomp must be set as an argument. EOF } if ($cice_ntasks == 1 && $cice_nthreads ==1 ) { # serial case: $bsizex = $nlon; $bsizey = $nlat; $maxblocks = 1; $decomptype = 'cartesian'; $cfg_ref->set('cice_decomptype', $decomptype); } else { # If not serial, use the script provided by CICE to generate the default decomposition my $cice_decomp_gen = "$cesmroot/components/cice/bld/generate_cice_decomp.pl"; $cice_decomp_gen .= "-ccsmroot $cesmroot -res $hgrid "; $cice_decomp_gen .= "-nproc $cice_ntasks -thrds $cice_nthreads -output all"; $cice_decomp_gen .= " -nx $nlon -ny $nlat" if (defined ($nlon) && defined ($nlat)); my $cice_decomp = `$cice_decomp_gen `; # check for error if ($cice_decomp =~ /No Decomp Created/) { die <<"EOF"; ** ERROR: CICE decomposition generator returns: $cice_decomp ** May need to explicitly specify the decomposition using the arguments -bsizex, -bsizey, and -maxblocks EOF } else { # output is blank separated values for nlons, nlats, bsizex, bsizey, maxblocks, decomptype my @tokens = split " ", $cice_decomp; $bsizex = $tokens[2]; $bsizey = $tokens[3]; $maxblocks = $tokens[4]; $decomptype = $tokens[5]; $cfg_ref->set('cice_decomptype', $decomptype); } } } } if ($print>=2) { print "cice : bsizex is $bsizex $eol";} if ($print>=2) { print "cice : bsizey is $bsizey $eol";} if ($print>=2) { print "cice : mxblcks is $maxblocks $eol";} if ($print>=2) { print "cice : decomp type is $decomptype $eol";} if ($set_decomp) { $cice_cppdefs = "$cice_cppdefs -DBLCKX=$bsizex -DBLCKY=$bsizey -DMXBLCKS=$maxblocks"; } #----------------------------------------------------------------------------------------------- # Makefile configuration ####################################################################### #----------------------------------------------------------------------------------------------- # the CPP definitions that were explicitly set in the defaults my $cfg_cppdefs = ' '; # CICE ice model $cfg_cppdefs .= $cice_cppdefs; # CPP defines to put on Makefile my $make_cppdefs = "$cfg_cppdefs"; if ($print>=2) { print "CPP definitions set by configure: \'$cfg_cppdefs\'$eol"; } #----------------------------------------------------------------------------------------------- # Write configuration files #################################################################### #----------------------------------------------------------------------------------------------- my $fp_filename = 'Filepath'; # name of output filepath file my $cpp_filename = 'CICE_cppdefs'; # name of output file for cice's cppdefs in ccsm # Write the filepath file for ccsm. write_filepath("$cice_bld/$fp_filename", $cesmroot); if ($print>=2) { print "creating $cice_bld/$fp_filename\n"; } # Write the file for cice's cppdefs needed in ccsm. write_cppdefs("$cice_bld/$cpp_filename", $make_cppdefs); if ($print>=2) { print "creating $cice_bld/$cpp_filename\n"; } # Write the configuration file. $cfg_ref->write_file("$config_cache_file", $commandline); if ($print>=2) { print "creating $cice_bld/$config_cache_file\n"; } #----------------------------------------------------------------------------------------------- # Finished unless testing requested ############################################################ #----------------------------------------------------------------------------------------------- # Done testing. chdir( $cwd ) || die <<"EOF"; ** Trouble changing directory back to $cwd ** EOF if ($print) { print "CICE configure done.\n"; } exit; #----------------------------------------------------------------------------------------------- # REALLY FINNISHED ############################################################################# #----------------------------------------------------------------------------------------------- sub write_filepath { my ($file, $cesmroot) = @_; my $fh = new IO::File; my $CASEROOT = "$ENV{'CASEROOT'}"; $fh->open(">$file") or die "** can't open filepath file: $file\n"; print $fh "$CASEROOT/SourceMods/src.cice\n"; print $fh "$cesmroot/components/cice/src/drivers/cesm\n"; print $fh "$cesmroot/components/cice/src/io_pio\n"; #print $fh "$cesmroot/components/cice/src/io_netcdf\n"; print $fh "$cesmroot/components/cice/src/mpi\n"; print $fh "$cesmroot/components/cice/src/source\n"; $fh->close; } #------------------------------------------------------------------------------- sub write_cppdefs { my ($file, $make_cppdefs) = @_; my $fh = new IO::File; $fh->open(">$file") or die "** can't open cpp defs file: $file\n"; print $fh "$make_cppdefs\n"; $fh->close; } #------------------------------------------------------------------------------- #------------------------------------------------------------------------------- sub mkdirp { my ($dir) = @_; my (@dirs) = split /\//, $dir; my (@subdirs, $path); # if $dir is absolute pathname then @dirs will start with "" if ($dirs[0] eq "") { push @subdirs, shift @dirs; } while ( @dirs ) { # check that each subdir exists and mkdir if it doesn't push @subdirs, shift @dirs; $path = join '/', @subdirs; unless (-d $path or mkdir($path, 0777)) { return 0; } } return 1; } #------------------------------------------------------------------------------- sub version { # The version is found in CICE's 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 "** can't open ChangeLog file: $logfile\n"; while (my $line = <$fh>) { if ($line =~ /^Tag name:\s*(\w+)/ ) { print "$1\n"; exit; } } }