#!/bin/bash # # submit.sh # # Portable bash script to run LPJ-GUESS version: # BINARY # as a parallel job using PBS on Platon. # # Created automatically on DATE # # Usage: # # 1. Copy script to the directory where you want output written. # This will be called the RUN DIRECTORY. # 2. In an editor, set appropriate values for the variables NPROCESS, # INSFILE, GRIDLIST and OUTFILES (NB: no space after the = sign): NPROCESS=16 # NB: Should be multiple of 8 on Platon! WALLTIME=150:00:00 INSFILE=guess.ins INPUT_MODULE=cru_ncep GRIDLIST=gridlist.txt OUTFILES='*.out' # Where: # NPROCESS = number of processes in parallel job # WALLTIME = maximum wall (real) time for job hh:mm:ss # INSFILE = path to ins file from run directory # INPUT_MODULE = input module to use # GRIDLIST = path to gridlist file from run directory # OUTFILES = list of LPJ-GUESS output files in single quotes, # and separated by spaces (filenames only, including # extension, no directory.) Shell wildcards are allowed. # # 3. Run the script using the command: # ./submit.sh # or: # ./submit.sh [-n ] [-s ] [-i ] # # All arguments are optional and interpreted as: # name = the name of the job (shown in PBS queue) # file = filename of a file which can override the variables # above # ins-file = instruction file to use, overrides the INSFILE # variable above # # Nothing to change past here ######################################################################## # Exit if any command fails set -e # Handle the command line arguments while getopts ":n:s:i:" opt; do case $opt in n ) name=$OPTARG ;; s ) submit_vars_file=$OPTARG ;; i ) ins=$OPTARG ;; esac done # Override the submit variables with the contents of a file, if given if [ -n "$submit_vars_file" ]; then source $submit_vars_file fi # Override INSFILE with the ins-file parameter, if given if [ -n "$ins" ]; then INSFILE=$ins fi # Figure out how many _nodes_ we should request. On Platon the policy is # that whole nodes (with 8 cores) should be allocated, so we should request # nodes with the nodes=:ppn=8 format. CORES_PER_NODE=8 if [[ $((NPROCESS%CORES_PER_NODE)) == 0 ]]; then nodes=$((NPROCESS/CORES_PER_NODE)) else nodes=$((NPROCESS/CORES_PER_NODE+1)) fi # Convert INSFILE to an absolute path since we will be starting the # guess instances from different directories. # Please note when porting this script: readlink may not be available # on non-Linux systems. Also, using absolute path names means the # instruction file needs to be in a place accessible from the nodes. INSFILE=$(readlink -f "$INSFILE") GRIDLIST_FILENAME=$(basename $GRIDLIST) # This function creates the gridlist files for each run by splitting # the original gridlist file into approximately equal parts. function split_gridlist { # Create empty gridlists first to make sure each run gets one for ((a=1; a <= NPROCESS ; a++)) do echo > run$a/$GRIDLIST_FILENAME done # Figure out suitable number of lines per gridlist, get the number of # lines in original gridlist file, divide by NPROCESS and round up. local lines_per_run=$(wc -l $GRIDLIST | \ awk '{ x = $1/'$NPROCESS'; d = (x == int(x)) ? x : int(x)+1; print d}') # Use the split command to split the files into temporary files split --suffix-length=4 --lines $lines_per_run $GRIDLIST tmpSPLITGRID_ # Move the temporary files into the runX-directories local files=$(ls tmpSPLITGRID_*) local i=1 for file in $files do mv $file run$i/$GRIDLIST_FILENAME i=$((i+1)) done } # Create header of progress.sh script echo "##############################################################" > progress.sh echo "# PROGRESS.SH" >> progress.sh echo "# Upload current guess.log files from local nodes and check" >> progress.sh echo "# Usage: sh progress.sh" >> progress.sh echo >> progress.sh # Create a run subdirectory for each process and clean up for ((a=1; a <= NPROCESS ; a++)) do mkdir -p run$a cd run$a ; rm -f guess.log ; rm -f $GRIDLIST_FILENAME ; cd .. echo "echo '********** Last few lines of ./run${a}/guess.log: **********'" >> progress.sh echo "tail ./run${a}/guess.log" >> progress.sh done split_gridlist # Create PBS script to request place in queue cat < guess.cmd #!/bin/bash #PBS -l nodes=$nodes:ppn=$CORES_PER_NODE #PBS -l walltime=$WALLTIME set -e cd \$PBS_O_WORKDIR if ! type -P mpiexec &> /dev/null; then echo "Didn't find mpiexec! Make sure an MPI module is loaded in your" >&2 echo "login script (~/.bashrc) and recompile LPJ-GUESS with MPI support!" >&2 exit 1 fi # If there's a script for setting up files on local disk, run it if [ -f setup_local.sh ]; then mpiexec -pernode setup_local.sh fi # In each run directory, create a symbolic link to the node local storage for ((a=1; a <= $NPROCESS ; a++)) do cd run\$a if [ -h local ]; then rm local fi ln -s \$PBS_O_LOCAL local cd .. done mpiexec -n $NPROCESS BINARY -parallel -input $INPUT_MODULE $INSFILE EOF cat < append.cmd #!/bin/bash #PBS -l nodes=1:ppn=1 #PBS -l walltime=$WALLTIME set -e cd \$PBS_O_WORKDIR function append_files { local number_of_jobs=\$1 local file=\$2 cp run1/\$file \$file local i="" for ((i=2; i <= number_of_jobs; i++)) do if [ -f run\$i/\$file ]; then cat run\$i/\$file | awk 'NR!=1 || NF==0 || \$1 == \$1+0 { print \$0 }' >> \$file fi done } pushd run1 &> /dev/null outfiles_unexpanded='$OUTFILES' outfiles_expanded=\$(echo \$outfiles_unexpanded) popd &> /dev/null for file in \$outfiles_expanded do append_files $NPROCESS \$file done EOF # Submit guess job append_dependency=$(qsub -N ${name:-"guess"} guess.cmd) # Submit append job qsub -W depend=afterok:$append_dependency -N ${name:-"guess"}"_append" append.cmd