This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
howto:bioinformatics:gnu-parallel [2016/05/06 09:45] dane [Fault tolerant Example] |
howto:bioinformatics:gnu-parallel [2023/04/17 12:40] (current) ischeepers |
||
|---|---|---|---|
| Line 8: | Line 8: | ||
| ==== Simple Example ==== | ==== Simple Example ==== | ||
| - | What this example does is that it finds a whole lot of scripts in a directory and gets gnu parallel to run them. The -j 6 options tells gnu parallel to assume that each sub-job requires | + | What this example does is that it finds a whole lot of scripts in a directory and gets gnu parallel to run them. The -j 6 options tells gnu parallel to run 6 jobs per node, while the -M and --sshdelay 0.2 options tells it to use ssh's ControlMaster and to have a delay between establishing links to the same node. |
| <file bash gnu_parallel.qsub> | <file bash gnu_parallel.qsub> | ||
| #!/bin/bash | #!/bin/bash | ||
| #PBS -e / | #PBS -e / | ||
| - | #PBS -o /mnt/lsutre/ | + | #PBS -o /mnt/lustre/ |
| #PBS -V | #PBS -V | ||
| #PBS -P PROGRAMMESHORTNAME | #PBS -P PROGRAMMESHORTNAME | ||
| Line 24: | Line 24: | ||
| #PBS -mb | #PBS -mb | ||
| - | module add chpc/gnu/parallel-20160422 | + | |
| + | module add chpc/BIOMODULES | ||
| + | module add gnu-parallel | ||
| WORKING_DIR=/ | WORKING_DIR=/ | ||
| Line 31: | Line 33: | ||
| cd ${WORKING_DIR} | cd ${WORKING_DIR} | ||
| - | ls ${WORKING_DIR}/ | + | ls ${WORKING_DIR}/ |
| </ | </ | ||
| Line 43: | Line 45: | ||
| </ | </ | ||
| - | After running <code bash> | + | After running <code bash> |
| Hello World! Main gnu parallel test thingy running here | Hello World! Main gnu parallel test thingy running here | ||
| Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0282. I'm running task / | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0282. I'm running task / | ||
| Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0282. I'm running task / | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0282. I'm running task / | ||
| - | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0281. I'm running task / | + | . |
| - | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0282. I'm running task / | + | . |
| - | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0281. I'm running task / | + | . |
| - | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:26 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:27 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:28 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:28 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:28 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:28 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:28 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:28 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:29 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:29 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:29 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:29 and I'm running on host: cnode0281. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:29 and I'm running on host: cnode0282. I'm running task / | + | |
| - | Hello World! The time is 05/05/2016 15:24:29 and I'm running on host: cnode0281. I'm running task / | + | |
| Hello World! The time is 05/05/2016 15:24:29 and I'm running on host: cnode0281. I'm running task / | Hello World! The time is 05/05/2016 15:24:29 and I'm running on host: cnode0281. I'm running task / | ||
| </ | </ | ||
| Line 108: | Line 80: | ||
| </ | </ | ||
| - | So if we run our same PBS script, but this time point at a directory containing, say 50, copies of the above script then we see: *will follow | + | So if we run our same PBS script, but this time point at a directory containing, say 50, copies of the above script then we see: |
| + | <code bash> | ||
| + | [dane@login1]$ ls gnup_scripts/ | ||
| + | 50 | ||
| + | 50 | ||
| + | [dane@login1]$ cat gnup_scripts/ | ||
| + | 50 | ||
| + | 34 | ||
| + | 0 | ||
| + | [dane@login1]$ cat gnu_parallel.stdout.out | grep " | ||
| + | 34 | ||
| + | 16 | ||
| + | [dane@login1]$ | ||
| + | </ | ||
| + | So we can see that it ran 50 times, and failed 16 out of 50 times. We can run again and we see: | ||
| + | <code bash> | ||
| + | [dane@login1]$ cat gnup_scripts/ | ||
| + | 100 | ||
| + | 45 | ||
| + | 34 | ||
| + | [dane@login1]$ cat gnu_parallel.stdout.out | grep " | ||
| + | 11 | ||
| + | 5 | ||
| + | </ | ||
| + | |||
| + | ==== Fault tolerant Blast ==== | ||
| + | |||
| + | So the idea around this script is that one has a working directory where you in turn have tree structure that might look something like <code bash> | ||
| + | |||
| + | <file bash blast.gnup.qsub> | ||
| + | # | ||
| + | #PBS -e / | ||
| + | #PBS -o / | ||
| + | #PBS -P PROGRAMMESHORTNAME | ||
| + | #PBS -M youremail@address | ||
| + | #PBS -l select=4: | ||
| + | #PBS -l walltime=00: | ||
| + | #PBS -q normal | ||
| + | #PBS -m be | ||
| + | |||
| + | cd ${PBS_O_WORKDIR} | ||
| + | |||
| + | module add chpc/ | ||
| + | module add blast | ||
| + | module add gnu-parallel | ||
| + | |||
| + | |||
| + | #The module sets the env variable below | ||
| + | # and provides the path to the databases | ||
| + | # | ||
| + | # To blast against eg, simply use -db nt | ||
| + | # To see all databases, "ls $BLASTDB/ | ||
| + | |||
| + | BLASTCMD=$(which blastn) | ||
| + | BLASTARGS=" | ||
| + | INPUTDIRS=" | ||
| + | |||
| + | ls ${INPUTDIRS}/ | ||
| + | </ | ||
| + | |||
| + | The idea around the fault tolerance is very simple -- when the blast has been successfully run, the file will be zipped and given a .gz suffix and won't be found in subsequent blasts (which explicitly look for .fasta). | ||
| + | |||
| + | |||
| + | ==== Advanced Fault tolerant Blast ==== | ||
| + | |||
| + | This takes the previous example one step further and is useful for large jobs with very many independent fasta files (and this individual blasts). The added step involves copying the blast database into ram disk at the beginning of the job. | ||
| + | |||
| + | <file bash blast.advanced.gnup.qsub> | ||
| + | # | ||
| + | #PBS -e / | ||
| + | #PBS -o / | ||
| + | #PBS -P PROGRAMMESHORTNAME | ||
| + | #PBS -M youremail@address | ||
| + | #PBS -l select=4: | ||
| + | #PBS -l walltime=00: | ||
| + | #PBS -q normal | ||
| + | #PBS -m be | ||
| + | |||
| + | cd ${PBS_O_WORKDIR} | ||
| + | |||
| + | |||
| + | module add chpc/ | ||
| + | module add blast | ||
| + | module add gnu-parallel | ||
| + | |||
| + | BLASTDB="/ | ||
| + | DB=" | ||
| + | BLASTCMD=$(which blastn) | ||
| + | BLASTARGS=" | ||
| + | INPUTDIRS=" | ||
| + | |||
| + | NODES=$(cat ${PBS_NODEFILE} | sort | uniq) | ||
| + | |||
| + | # copy blast databases to ram disk | ||
| + | for node in ${NODES} | ||
| + | do | ||
| + | ssh ${node} "mkdir -p / | ||
| + | done | ||
| + | |||
| + | wait # wait for parallel copies to finish | ||
| + | |||
| + | ls ${INPUTDIRS}/ | ||
| + | |||
| + | # clean up ram disk | ||
| + | for node in ${NODES} | ||
| + | do | ||
| + | ssh ${node} "rm -rf / | ||
| + | done | ||
| + | |||
| + | wait | ||
| + | </ | ||
| + | |||
| + | ==== Advanced Generalised Gnu Parallel ==== | ||
| + | |||
| + | The idea behind this example is that you can create a list of commands that need running and then give this to the script. If sub-jobs fail there will be an easily parsed log file so that individual steps can be quickly identified. If sub-jobs have been successfully run, they' | ||
| + | |||
| + | This first script | ||
| + | |||
| + | <file bash log_support.sh> | ||
| + | # | ||
| + | |||
| + | ## Some simple helper log functions to track jobs success / failure | ||
| + | ## Created by Dane Kennedy @ the Centre for High Performance Computing ## | ||
| + | |||
| + | ############## | ||
| + | |||
| + | # Echos the current date/time in a nice format | ||
| + | function now { echo -n "$( date +"%F %X" )"; } | ||
| + | |||
| + | # Appends to log file | ||
| + | function log () { | ||
| + | if [[ -v LOGFILE ]] | ||
| + | then | ||
| + | echo -e "$( now ): ${HOSTNAME}: | ||
| + | else | ||
| + | echo -e "$( now ): ${HOSTNAME}: | ||
| + | fi | ||
| + | } | ||
| + | |||
| + | #Appends to log file and exits | ||
| + | function log_fail () { | ||
| + | log " | ||
| + | exit 1 | ||
| + | } | ||
| + | |||
| + | # returns true (in the bash sense of 0 exit status meaning success) if line exists in log file | ||
| + | function check_log () { | ||
| + | if [[ -v LOGFILE ]] | ||
| + | then | ||
| + | if [[ -e ${LOGFILE} ]] | ||
| + | then | ||
| + | if $( grep -q " | ||
| + | then | ||
| + | return 0 | ||
| + | fi | ||
| + | fi | ||
| + | fi | ||
| + | return 1 | ||
| + | } | ||
| + | |||
| + | # Checks for a line on the LOG file. If is exists, it doesn' | ||
| + | # the run is successful is records it as such. | ||
| + | function check_run (){ | ||
| + | SUCCESS_LINE=" | ||
| + | FAIL_LINE=" | ||
| + | if check_log " | ||
| + | then | ||
| + | log " | ||
| + | return 0 | ||
| + | fi | ||
| + | log " | ||
| + | $@ \ | ||
| + | && { log " | ||
| + | || { log " | ||
| + | } | ||
| + | |||
| + | |||
| + | # Same as above but exit 1's on fail. | ||
| + | function check_run_abort (){ | ||
| + | if ! check_run " | ||
| + | then | ||
| + | log " | ||
| + | fi | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | |||
| + | <file bash myjob.sh> | ||
| + | # | ||
| + | #PBS -e / | ||
| + | #PBS -o / | ||
| + | #PBS -P PROGRAMMESHORTNAME | ||
| + | #PBS -M youremailaddress | ||
| + | #PBS -l select=2: | ||
| + | #PBS -l walltime=48: | ||
| + | #PBS -q normal | ||
| + | #PBS -m be | ||
| + | #PBS -r n | ||
| + | |||
| + | module add chpc/ | ||
| + | module add gnu-parallel | ||
| + | |||
| + | JOBSPERNODE=16 | ||
| + | |||
| + | # Make sure we start the job in the right place. | ||
| + | cd -P ${PBS_O_WORKDIR} | ||
| + | |||
| + | # Set up logging stuffs | ||
| + | source log_support.sh | ||
| + | LOGFILE=" | ||
| + | THIS=" | ||
| + | |||
| + | # First check if analysis has been run before. If it has abort... | ||
| + | if check_log " | ||
| + | then | ||
| + | log " | ||
| + | exit 0 | ||
| + | else | ||
| + | log " | ||
| + | fi | ||
| + | |||
| + | # Point to Job File -- this contains a list of commands to run, one per line | ||
| + | JOBFILE=" | ||
| + | |||
| + | # pass the commands on to gnu parallel which runs them with " | ||
| + | # a successful completion if all sub-jobs complete successfully :-). Woot. | ||
| + | |||
| + | cat ${JOBFILE} | parallel -M --sshdelay 0.2 -j ${JOBSPERNODE} -u --sshloginfile ${PBS_NODEFILE} \ | ||
| + | "cd -P \" | ||
| + | && log " | ||
| + | || log " | ||
| + | </ | ||