<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://giwiki.gi.ucsc.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Anovak</id>
	<title>UCSC Genomics Institute Computing Infrastructure Information - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://giwiki.gi.ucsc.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Anovak"/>
	<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Special:Contributions/Anovak"/>
	<updated>2026-05-08T08:12:14Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.40.0</generator>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=728</id>
		<title>Slurm Tips for Toil</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=728"/>
		<updated>2025-12-11T22:11:55Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some tips for running Toil workflows on the Phoenix Slurm cluster. Mostly you might want to run WDL workflows, but you can use some of these for other workflows like Cactus. You can also consult [https://github.com/DataBiosphere/toil/blob/master/docs/wdl/running.rst the Toil documentation on WDL workflows].&lt;br /&gt;
&lt;br /&gt;
* Install Toil with WDL support with:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
To use a development version of Toil, you can install from source instead:&lt;br /&gt;
&lt;br /&gt;
 pipx 'toil[wdl]@git+https://github.com/DataBiosphere/toil.git'&lt;br /&gt;
&lt;br /&gt;
Or for a particular branch:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]@git+https://github.com/DataBiosphere/toil.git@issues/123-abc'&lt;br /&gt;
&lt;br /&gt;
If you don't have &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt;, you would first need to:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may in turn need you to log out and back in.&lt;br /&gt;
&lt;br /&gt;
* For Toil options, you will want '''--batchSystem slurm''' to make it use Slurm and '''--batchLogsDir ./logs''' (or some other location on a shared filesystem) for the Slurm logs to not get lost.&lt;br /&gt;
&lt;br /&gt;
* You may be able to speed up your workflow with '''--caching true''', to cache data on nodes to be shared among multiple simultaneous tasks.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you might want to add '''--jobStore ./jobStore''' to make sure the job store is in a defined, shared location so that you can use '''--restart''' later.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you will want to set the '''SINGULARITY_CACHEDIR''' and '''MINIWDL__SINGULARITY__IMAGE_CACHE''' environment variables for your workflow to locations on shared storage, and possibly to the default cache locations in your home directory. Otherwise Toil will set them to temporary per-workflow node-local directories for each node, and thus re-download images for each workflow run, and for each cluster node. To avoid this, you could, for example, before your run or in your '''~/.bashrc''' you could:&lt;br /&gt;
&lt;br /&gt;
 export SINGULARITY_CACHEDIR=$HOME/.singularity/cache&lt;br /&gt;
 export MINIWDL__SINGULARITY__IMAGE_CACHE=$HOME/.cache/miniwdl&lt;br /&gt;
&lt;br /&gt;
If you run a lot of workflows, or a workflow with a lot of containers, you will run out of space in your home directory. In that case, you can try using semi-persistent per-node storage for your image caches instead:&lt;br /&gt;
&lt;br /&gt;
 export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;&lt;br /&gt;
 export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=727</id>
		<title>Slurm Tips for Toil</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=727"/>
		<updated>2025-12-11T22:11:10Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some tips for running Toil workflows on the Phoenix Slurm cluster. Mostly you might want to run WDL workflows, but you can use some of these for other workflows like Cactus. You can also consult [https://github.com/DataBiosphere/toil/blob/master/docs/wdl/running.rst the Toil documentation on WDL workflows].&lt;br /&gt;
&lt;br /&gt;
* Install Toil with WDL support with:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
To use a development version of Toil, you can install from source instead:&lt;br /&gt;
&lt;br /&gt;
 pipx 'toil[wdl]@git+https://github.com/DataBiosphere/toil.git'&lt;br /&gt;
&lt;br /&gt;
Or for a particular branch:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]@git+https://github.com/DataBiosphere/toil.git@issues/123-abc'&lt;br /&gt;
&lt;br /&gt;
If you don't have &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt;, you would first need to:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may in turn need you to log out and back in.&lt;br /&gt;
&lt;br /&gt;
* For Toil options, you will want '''--batchSystem slurm''' to make it use Slurm and '''--batchLogsDir ./logs''' (or some other location on a shared filesystem) for the Slurm logs to not get lost.&lt;br /&gt;
&lt;br /&gt;
* You may be able to speed up your workflow with '''--caching true''', to cache data on nodes to be shared among multiple simultaneous tasks.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you might want to add '''--jobStore ./jobStore''' to make sure the job store is in a defined, shared location so that you can use '''--restart''' later.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you will want to set the '''SINGULARITY_CACHEDIR''' and '''MINIWDL__SINGULARITY__IMAGE_CACHE''' environment variables for your workflow to locations on shared storage, and possibly to the default cache locations in your home directory. Otherwise Toil will set them to temporary node-local directories for each node, and thus re-download images for each workflow run, and for each cluster node. To avoid this, you could, for example, before your run or in your '''~/.bashrc''' you could:&lt;br /&gt;
&lt;br /&gt;
 export SINGULARITY_CACHEDIR=$HOME/.singularity/cache&lt;br /&gt;
 export MINIWDL__SINGULARITY__IMAGE_CACHE=$HOME/.cache/miniwdl&lt;br /&gt;
&lt;br /&gt;
If you run a lot of workflows, or a workflow with a lot of containers, you will run out of space in your home directory. In that case, you can try using persistent per-node storage for your image caches instead:&lt;br /&gt;
&lt;br /&gt;
 export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;&lt;br /&gt;
 export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=725</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=725"/>
		<updated>2025-11-18T15:09:06Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Frequently Asked Questions */ Add an error someone asked about&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may instruct you to **log out and log back in** or take some other action to adopt the new &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
When installing Toil, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
To change what extras are used when you have an existing Toil installation, you will need to use the &amp;lt;code&amp;gt;--force&amp;lt;/code&amp;gt; option.&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
If you see something from &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt; like:&lt;br /&gt;
&lt;br /&gt;
     - cwltoil (symlink missing or pointing to unexpected location)&lt;br /&gt;
&lt;br /&gt;
Then &amp;lt;code&amp;gt;pipx uninstall toil&amp;lt;/code&amp;gt;, remove the offending file from &amp;lt;code&amp;gt;~/.local/bin&amp;lt;/code&amp;gt;, and try again.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:24.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:24.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:24.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --slurmTime 00:10:00 --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
===How do I delete files in WDL?===&lt;br /&gt;
WDL doesn't have a built-in way to delete files; if you run a task that deletes a file, it will still exist in Toil's job store storage.&lt;br /&gt;
&lt;br /&gt;
Toil [https://github.com/DataBiosphere/toil/commit/2de6eea2cc2e688b53062a98687445f0cca56669 recently gained support] for deleting files at the ''end'' of WDL workflows. So if you have a large file that you only need for part of your workflow, consider writing the part that creates and uses it as a separate sub-&amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; and invoking it with &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt;. Then the file will be cleaned up when the child workflow ends, leaving more space for files created in the parent workflow.&lt;br /&gt;
&lt;br /&gt;
===I am unable to allocate resources!===&lt;br /&gt;
You might get an error like:&lt;br /&gt;
&lt;br /&gt;
 srun: error: Unable to allocate resources: Invalid account or account/partition combination specified&lt;br /&gt;
&lt;br /&gt;
If this happens, you probably haven't been granted access to the Phoenix cluster, or at least to the partition you are trying to use. You should email &amp;lt;code&amp;gt;cluster-admin@soe.ucsc.edu&amp;lt;/code&amp;gt; to ask for access.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=667</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=667"/>
		<updated>2025-03-18T20:48:24Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Writing the file */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may instruct you to **log out and log back in** or take some other action to adopt the new &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
When installing Toil, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
To change what extras are used when you have an existing Toil installation, you will need to use the &amp;lt;code&amp;gt;--force&amp;lt;/code&amp;gt; option.&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
If you see something from &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt; like:&lt;br /&gt;
&lt;br /&gt;
     - cwltoil (symlink missing or pointing to unexpected location)&lt;br /&gt;
&lt;br /&gt;
Then &amp;lt;code&amp;gt;pipx uninstall toil&amp;lt;/code&amp;gt;, remove the offending file from &amp;lt;code&amp;gt;~/.local/bin&amp;lt;/code&amp;gt;, and try again.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:24.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:24.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:24.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --slurmTime 00:10:00 --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
===How do I delete files in WDL?===&lt;br /&gt;
WDL doesn't have a built-in way to delete files; if you run a task that deletes a file, it will still exist in Toil's job store storage.&lt;br /&gt;
&lt;br /&gt;
Toil [https://github.com/DataBiosphere/toil/commit/2de6eea2cc2e688b53062a98687445f0cca56669 recently gained support] for deleting files at the ''end'' of WDL workflows. So if you have a large file that you only need for part of your workflow, consider writing the part that creates and uses it as a separate sub-&amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; and invoking it with &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt;. Then the file will be cleaned up when the child workflow ends, leaving more space for files created in the parent workflow.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=666</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=666"/>
		<updated>2025-03-18T20:46:04Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Writing your own workflow */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may instruct you to **log out and log back in** or take some other action to adopt the new &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
When installing Toil, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
To change what extras are used when you have an existing Toil installation, you will need to use the &amp;lt;code&amp;gt;--force&amp;lt;/code&amp;gt; option.&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
If you see something from &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt; like:&lt;br /&gt;
&lt;br /&gt;
     - cwltoil (symlink missing or pointing to unexpected location)&lt;br /&gt;
&lt;br /&gt;
Then &amp;lt;code&amp;gt;pipx uninstall toil&amp;lt;/code&amp;gt;, remove the offending file from &amp;lt;code&amp;gt;~/.local/bin&amp;lt;/code&amp;gt;, and try again.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --slurmTime 00:10:00 --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
===How do I delete files in WDL?===&lt;br /&gt;
WDL doesn't have a built-in way to delete files; if you run a task that deletes a file, it will still exist in Toil's job store storage.&lt;br /&gt;
&lt;br /&gt;
Toil [https://github.com/DataBiosphere/toil/commit/2de6eea2cc2e688b53062a98687445f0cca56669 recently gained support] for deleting files at the ''end'' of WDL workflows. So if you have a large file that you only need for part of your workflow, consider writing the part that creates and uses it as a separate sub-&amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; and invoking it with &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt;. Then the file will be cleaned up when the child workflow ends, leaving more space for files created in the parent workflow.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=665</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=665"/>
		<updated>2025-03-18T18:43:57Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* How do I delete files in WDL? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may instruct you to **log out and log back in** or take some other action to adopt the new &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
When installing Toil, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
To change what extras are used when you have an existing Toil installation, you will need to use the &amp;lt;code&amp;gt;--force&amp;lt;/code&amp;gt; option.&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
If you see something from &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt; like:&lt;br /&gt;
&lt;br /&gt;
     - cwltoil (symlink missing or pointing to unexpected location)&lt;br /&gt;
&lt;br /&gt;
Then &amp;lt;code&amp;gt;pipx uninstall toil&amp;lt;/code&amp;gt;, remove the offending file from &amp;lt;code&amp;gt;~/.local/bin&amp;lt;/code&amp;gt;, and try again.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
===How do I delete files in WDL?===&lt;br /&gt;
WDL doesn't have a built-in way to delete files; if you run a task that deletes a file, it will still exist in Toil's job store storage.&lt;br /&gt;
&lt;br /&gt;
Toil [https://github.com/DataBiosphere/toil/commit/2de6eea2cc2e688b53062a98687445f0cca56669 recently gained support] for deleting files at the ''end'' of WDL workflows. So if you have a large file that you only need for part of your workflow, consider writing the part that creates and uses it as a separate sub-&amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; and invoking it with &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt;. Then the file will be cleaned up when the child workflow ends, leaving more space for files created in the parent workflow.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=664</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=664"/>
		<updated>2025-03-18T18:43:13Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* How do I delete files in WDL? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may instruct you to **log out and log back in** or take some other action to adopt the new &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
When installing Toil, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
To change what extras are used when you have an existing Toil installation, you will need to use the &amp;lt;code&amp;gt;--force&amp;lt;/code&amp;gt; option.&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
If you see something from &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt; like:&lt;br /&gt;
&lt;br /&gt;
     - cwltoil (symlink missing or pointing to unexpected location)&lt;br /&gt;
&lt;br /&gt;
Then &amp;lt;code&amp;gt;pipx uninstall toil&amp;lt;/code&amp;gt;, remove the offending file from &amp;lt;code&amp;gt;~/.local/bin&amp;lt;/code&amp;gt;, and try again.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
===How do I delete files in WDL?===&lt;br /&gt;
WDL doesn't have a built-in way to delete files; if you run a task that deletes a file, it will still exist in Toil's job store storage.&lt;br /&gt;
&lt;br /&gt;
Toil [https://github.com/DataBiosphere/toil/commit/2de6eea2cc2e688b53062a98687445f0cca56669 recently gained support] for deleting files at the ''end'' of WDL workflows. So if you have a large file that you only need for part of your workflow, consider writing that part as a separate sub-&amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; and invoking it with &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt;. Then the file will be cleaned up when the child workflow ends, leaving more space fro files created in the parent workflow.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=663</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=663"/>
		<updated>2025-03-18T18:43:03Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* How do I delete files in WDL? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may instruct you to **log out and log back in** or take some other action to adopt the new &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
When installing Toil, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
To change what extras are used when you have an existing Toil installation, you will need to use the &amp;lt;code&amp;gt;--force&amp;lt;/code&amp;gt; option.&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
If you see something from &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt; like:&lt;br /&gt;
&lt;br /&gt;
     - cwltoil (symlink missing or pointing to unexpected location)&lt;br /&gt;
&lt;br /&gt;
Then &amp;lt;code&amp;gt;pipx uninstall toil&amp;lt;/code&amp;gt;, remove the offending file from &amp;lt;code&amp;gt;~/.local/bin&amp;lt;/code&amp;gt;, and try again.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
===How do I delete files in WDL?===&lt;br /&gt;
WDL doesn't have a built-in way to delete files; if you run a task that deletes a file, it will still exist in Toil's job store storage.&lt;br /&gt;
&lt;br /&gt;
Toil [https://github.com/DataBiosphere/toil/commit/2de6eea2cc2e688b53062a98687445f0cca56669 recently gained support] for deleting files at the 'end' of WDL workflows. So if you have a large file that you only need for part of your workflow, consider writing that part as a separate sub-&amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; and invoking it with &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt;. Then the file will be cleaned up when the child workflow ends, leaving more space fro files created in the parent workflow.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=662</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=662"/>
		<updated>2025-03-18T18:42:50Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Frequently Asked Questions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may instruct you to **log out and log back in** or take some other action to adopt the new &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
When installing Toil, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
To change what extras are used when you have an existing Toil installation, you will need to use the &amp;lt;code&amp;gt;--force&amp;lt;/code&amp;gt; option.&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
If you see something from &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt; like:&lt;br /&gt;
&lt;br /&gt;
     - cwltoil (symlink missing or pointing to unexpected location)&lt;br /&gt;
&lt;br /&gt;
Then &amp;lt;code&amp;gt;pipx uninstall toil&amp;lt;/code&amp;gt;, remove the offending file from &amp;lt;code&amp;gt;~/.local/bin&amp;lt;/code&amp;gt;, and try again.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
===How do I delete files in WDL?===&lt;br /&gt;
WDL doesn't have a built-in way to delete files; if you run a task that deletes a file, it will still exist in Toil's job store storage.&lt;br /&gt;
&lt;br /&gt;
Toil [https://github.com/DataBiosphere/toil/commit/2de6eea2cc2e688b53062a98687445f0cca56669 recently gained support] for deleting files at the **end** of WDL workflows. So if you have a large file that you only need for part of your workflow, consider writing that part as a separate sub-&amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; and invoking it with &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt;. Then the file will be cleaned up when the child workflow ends, leaving more space fro files created in the parent workflow.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_Windows&amp;diff=661</id>
		<title>Converting From Non-MFA VPN to the MFA-Enabled VPN on Windows</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_Windows&amp;diff=661"/>
		<updated>2025-03-17T14:19:14Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;If you are using OpenVPN Connect on Windows 10 or 11 to connect to the GI VPN, and you are looking to convert to the new MFA-enabled GI VPN, you have come to the right place.  You must already have Duo set up with your CruzID (which most of you do).  If for some reason you don't have Duo set up yet on your phone, go here to enroll a device and configure Push Notifications with Duo before continuing:&lt;br /&gt;
&lt;br /&gt;
 https://its.ucsc.edu/mfa/enroll.html&lt;br /&gt;
&lt;br /&gt;
OK!  Let's get to it.&lt;br /&gt;
&lt;br /&gt;
Disconnect from the VPN if you are already connected.&lt;br /&gt;
&lt;br /&gt;
Then you will need to download the new OpenVPN config file from here:&lt;br /&gt;
&lt;br /&gt;
 https://giwiki.gi.ucsc.edu/downloads/prism-duo.ovpn&lt;br /&gt;
&lt;br /&gt;
The credentials to access that website are username: '''genecats''' and password: '''KiloKluster'''&lt;br /&gt;
&lt;br /&gt;
Download that file by right-clicking on the link above and selecting &amp;quot;Save Link As...&amp;quot;, and save it to your Desktop or some other area you will remember..  Launch the '''OpenVPN GUI''' app (usually there is an icon for it on your Desktop, but you can search for it if not).  It will launch and appear in your system tray on the bottom right (the system tray icon kind of looks like a '''^''' icon).  You should see the OpenVPN icon there, it looks like a little computer screen with a lock on it.  Right click on the OpenVPN icon in the system tray, and you should see a small menu appear.  Select &amp;quot;Import file&amp;quot;.  In the resulting window, browse to your Desktop or wherever you saved the '''prism-duo.ovpn''' file.  Select that file an click &amp;quot;Open&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Once you import the file, you should be able to right click on OpenVPN Connect again in the system tray and select the profile you want to connect to.  It should show multiple profiles, one for your old profile and one for your new profile.  Select the new one, then select &amp;quot;Connect&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
That's it!  It will ask you for your usual GI PRISM username and password that you usually use to connect to our VPN, and after that it will send a Duo Push notification to your phone, and then you should be logged in.  Other than the Duo Push, the VPN behaves exactly like it did before.  If you need to use an authentication method other than Duo Push, you can append a comma, and then the name of the method (like &amp;quot;push&amp;quot;, &amp;quot;sms&amp;quot;, or &amp;quot;phone&amp;quot;), or a second factor code, to you password when you submit it.&lt;br /&gt;
&lt;br /&gt;
If you have issues you can always revert back to the old configuration, which will still work for a while.  We will disable the old VPN soon though, so make every effort to get the new VPN setup working.&lt;br /&gt;
&lt;br /&gt;
As always, please email '''cluster-admin@soe.ucsc.edu''' if you need help or have any questions.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_MacOS&amp;diff=660</id>
		<title>Converting From Non-MFA VPN to the MFA-Enabled VPN on MacOS</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_MacOS&amp;diff=660"/>
		<updated>2025-03-17T14:18:57Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;If you are using Tunnelblick on MacOS and you are looking to convert to the new MFA-enabled GI VPN, you have come to the right place.  You must already have Duo set up with your CruzID (which most of you do).  If for some reason you don't have Duo set up yet on your phone, go here to enroll a device and configure Push Notifications with Duo before continuing:&lt;br /&gt;
&lt;br /&gt;
 https://its.ucsc.edu/mfa/enroll.html&lt;br /&gt;
&lt;br /&gt;
OK!  Let's get to it.&lt;br /&gt;
&lt;br /&gt;
Disconnect from the VPN if you are already connected.&lt;br /&gt;
&lt;br /&gt;
Then you will need to download the new OpenVPN config file from here:&lt;br /&gt;
&lt;br /&gt;
 https://giwiki.gi.ucsc.edu/downloads/&lt;br /&gt;
&lt;br /&gt;
The credentials to access that website are username: '''genecats''' and password: '''KiloKluster'''&lt;br /&gt;
&lt;br /&gt;
Go to the link above and right-click on '''prism-duo.ovpn''' and selecting &amp;quot;Save Link As...&amp;quot;, and save it to your Desktop or some other area you will remember.  Then open Tunnelblick and click on the Tunnelblick icon on the top right of your screen next to the date.  It kind of looks like a small tunnel.  In the window that opens, select &amp;quot;VPN Details...&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In the resulting window, select the &amp;quot;Configurations&amp;quot; tab on the top.  You will see a list of Configurations on the left, and it should include the current configuration you use to connect.  It may be called 'prism' or maybe 'client'.&lt;br /&gt;
&lt;br /&gt;
Drag the new configuration called '''prism-duo.ovpn''' (from your Desktop) into the Configurations area beneath your old configuration.  It should import the configuration.  It will ask you if you want to install it for &amp;quot;Only You&amp;quot; or &amp;quot;All Users&amp;quot;.  Click &amp;quot;Only You&amp;quot;.  You will also be asked to type in your laptop password.&lt;br /&gt;
&lt;br /&gt;
That's it!  Select the new configuration on the left and click the &amp;quot;Connect&amp;quot; button on the bottom right.  It will ask you for your usual GI PRISM username and password that you usually use to connect to our VPN, and after that it will send a Duo Push notification to your phone, and then you should be logged in.  Other than the Duo Push, the VPN behaves exactly like it did before.  If you need to use an authentication method other than Duo Push, you can append a comma, and then the name of the method (like &amp;quot;push&amp;quot;, &amp;quot;sms&amp;quot;, or &amp;quot;phone&amp;quot;), or a second factor code, to you password when you submit it.&lt;br /&gt;
&lt;br /&gt;
If you have issues you can always revert back to the old configuration, which will still work for a while.  We will disable the old VPN soon though, so make every effort to get the new VPN setup working.&lt;br /&gt;
&lt;br /&gt;
Once you have the new VPN working, feel free to delete the old profile from Tunnelblick by clicking on the old profile in the &amp;quot;Configurations&amp;quot; window, then click on the '''&amp;quot;-&amp;quot;''' button below to remove the old configuration.&lt;br /&gt;
&lt;br /&gt;
As always, please email '''cluster-admin@soe.ucsc.edu''' if you need help or have any questions.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_Linux&amp;diff=659</id>
		<title>Converting From Non-MFA VPN to the MFA-Enabled VPN on Linux</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_Linux&amp;diff=659"/>
		<updated>2025-03-17T14:18:44Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;If you are using OpenVPN on Linux to connect to the GI VPN and you are looking to convert to the new MFA-enabled GI VPN, you have come to the right place.  You must already have Duo set up with your CruzID (which most of you do).  If for some reason you don't have Duo set up yet on your phone, go here to enroll a device and configure Push Notifications with Duo before continuing:&lt;br /&gt;
&lt;br /&gt;
 https://its.ucsc.edu/mfa/enroll.html&lt;br /&gt;
&lt;br /&gt;
OK!  Let's get to it.&lt;br /&gt;
&lt;br /&gt;
Disconnect from the VPN if you are already connected.  All the various flavors and versions of Linux vary in the specifics, so you may not be following these exact instructions to get it to work.  This is based on the Network Manager in Ubuntu, but most Ubuntu/Debian variants will be similar.&lt;br /&gt;
&lt;br /&gt;
Then you will need to download the new OpenVPN config file from here:&lt;br /&gt;
&lt;br /&gt;
 https://giwiki.gi.ucsc.edu/downloads/prism-duo.ovpn&lt;br /&gt;
&lt;br /&gt;
The credentials to access that website are username: '''genecats''' and password: '''KiloKluster'''&lt;br /&gt;
&lt;br /&gt;
Download that file right-clicking on the link above and selecting &amp;quot;Save Link As...&amp;quot;, and save it to your Desktop or some other area you will remember. or some other easy to remember location.&lt;br /&gt;
&lt;br /&gt;
We will be installing the Prism VPN profile via the Network Manager GUI interface.&lt;br /&gt;
&lt;br /&gt;
Open '''Network Manager''' from '''Gnome Settings''' option and select the '''Network''' tab and click on the '''VPN +''' symbol:&lt;br /&gt;
&lt;br /&gt;
[[File:Configuring_1.png|600px]]&lt;br /&gt;
&lt;br /&gt;
From the '''Add VPN''' window, click on the '''Import from file...''' option:&lt;br /&gt;
&lt;br /&gt;
[[File:Configuring_2.png|600px]]&lt;br /&gt;
&lt;br /&gt;
You must navigate to your .ovpn file (/path/to/your/prism-duo.ovpn) and click on '''Open''' button:&lt;br /&gt;
&lt;br /&gt;
[[File:Configuring_3.png|600px]]&lt;br /&gt;
&lt;br /&gt;
Click on the '''Add''' button:&lt;br /&gt;
&lt;br /&gt;
[[File:Configuring_4.png|600px]]&lt;br /&gt;
&lt;br /&gt;
Finally, click the '''On/Off''' button to start on the new VPN:&lt;br /&gt;
&lt;br /&gt;
[[File:Configuring_5.png|600px]]&lt;br /&gt;
  &lt;br /&gt;
That's it!  It will ask you for your usual GI PRISM username and password that you usually use to connect to our VPN, and after that it will send a Duo Push notification to your phone, and then you should be logged in.  Other than the Duo Push, the VPN behaves exactly like it did before.  If you need to use an authentication method other than Duo Push, you can append a comma, and then the name of the method (like &amp;quot;push&amp;quot;, &amp;quot;sms&amp;quot;, or &amp;quot;phone&amp;quot;), or a second factor code, to you password when you submit it.&lt;br /&gt;
&lt;br /&gt;
If you have issues you can always revert back to the old configuration, which will still work for a while.  We will disable the old VPN soon though, so make every effort to get the new VPN setup working.&lt;br /&gt;
&lt;br /&gt;
Once you have the new VPN working, feel free to delete the old profile from the Network Manager.&lt;br /&gt;
&lt;br /&gt;
As always, please email '''cluster-admin@soe.ucsc.edu''' if you need help or have any questions.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_MacOS&amp;diff=658</id>
		<title>Converting From Non-MFA VPN to the MFA-Enabled VPN on MacOS</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_MacOS&amp;diff=658"/>
		<updated>2025-03-17T14:18:16Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;If you are using Tunnelblick on MacOS and you are looking to convert to the new MFA-enabled GI VPN, you have come to the right place.  You must already have Duo set up with your CruzID (which most of you do).  If for some reason you don't have Duo set up yet on your phone, go here to enroll a device and configure Push Notifications with Duo before continuing:&lt;br /&gt;
&lt;br /&gt;
 https://its.ucsc.edu/mfa/enroll.html&lt;br /&gt;
&lt;br /&gt;
OK!  Let's get to it.&lt;br /&gt;
&lt;br /&gt;
Disconnect from the VPN if you are already connected.&lt;br /&gt;
&lt;br /&gt;
Then you will need to download the new OpenVPN config file from here:&lt;br /&gt;
&lt;br /&gt;
 https://giwiki.gi.ucsc.edu/downloads/&lt;br /&gt;
&lt;br /&gt;
The credentials to access that website are username: '''genecats''' and password: '''KiloKluster'''&lt;br /&gt;
&lt;br /&gt;
Go to the link above and right-click on '''prism-duo.ovpn''' and selecting &amp;quot;Save Link As...&amp;quot;, and save it to your Desktop or some other area you will remember.  Then open Tunnelblick and click on the Tunnelblick icon on the top right of your screen next to the date.  It kind of looks like a small tunnel.  In the window that opens, select &amp;quot;VPN Details...&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In the resulting window, select the &amp;quot;Configurations&amp;quot; tab on the top.  You will see a list of Configurations on the left, and it should include the current configuration you use to connect.  It may be called 'prism' or maybe 'client'.&lt;br /&gt;
&lt;br /&gt;
Drag the new configuration called '''prism-duo.ovpn''' (from your Desktop) into the Configurations area beneath your old configuration.  It should import the configuration.  It will ask you if you want to install it for &amp;quot;Only You&amp;quot; or &amp;quot;All Users&amp;quot;.  Click &amp;quot;Only You&amp;quot;.  You will also be asked to type in your laptop password.&lt;br /&gt;
&lt;br /&gt;
That's it!  Select the new configuration on the left and click the &amp;quot;Connect&amp;quot; button on the bottom right.  It will ask you for your usual GI PRISM username and password that you usually use to connect to our VPN, and after that it will send a Duo Push notification to your phone, and then you should be logged in.  Other than the Duo Push, the VPN behaves exactly like it did before.  If you need to use an authentication method other than Duo Push, you can append a comma, and then the name of the method (like &amp;quot;push&amp;quot;, &amp;quot;sms&amp;quot;, or &amp;quot;phone&amp;quot;), or a numeric second factor code, to you password when you submit it.&lt;br /&gt;
&lt;br /&gt;
If you have issues you can always revert back to the old configuration, which will still work for a while.  We will disable the old VPN soon though, so make every effort to get the new VPN setup working.&lt;br /&gt;
&lt;br /&gt;
Once you have the new VPN working, feel free to delete the old profile from Tunnelblick by clicking on the old profile in the &amp;quot;Configurations&amp;quot; window, then click on the '''&amp;quot;-&amp;quot;''' button below to remove the old configuration.&lt;br /&gt;
&lt;br /&gt;
As always, please email '''cluster-admin@soe.ucsc.edu''' if you need help or have any questions.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_Windows&amp;diff=657</id>
		<title>Converting From Non-MFA VPN to the MFA-Enabled VPN on Windows</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Converting_From_Non-MFA_VPN_to_the_MFA-Enabled_VPN_on_Windows&amp;diff=657"/>
		<updated>2025-03-17T14:17:49Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;If you are using OpenVPN Connect on Windows 10 or 11 to connect to the GI VPN, and you are looking to convert to the new MFA-enabled GI VPN, you have come to the right place.  You must already have Duo set up with your CruzID (which most of you do).  If for some reason you don't have Duo set up yet on your phone, go here to enroll a device and configure Push Notifications with Duo before continuing:&lt;br /&gt;
&lt;br /&gt;
 https://its.ucsc.edu/mfa/enroll.html&lt;br /&gt;
&lt;br /&gt;
OK!  Let's get to it.&lt;br /&gt;
&lt;br /&gt;
Disconnect from the VPN if you are already connected.&lt;br /&gt;
&lt;br /&gt;
Then you will need to download the new OpenVPN config file from here:&lt;br /&gt;
&lt;br /&gt;
 https://giwiki.gi.ucsc.edu/downloads/prism-duo.ovpn&lt;br /&gt;
&lt;br /&gt;
The credentials to access that website are username: '''genecats''' and password: '''KiloKluster'''&lt;br /&gt;
&lt;br /&gt;
Download that file by right-clicking on the link above and selecting &amp;quot;Save Link As...&amp;quot;, and save it to your Desktop or some other area you will remember..  Launch the '''OpenVPN GUI''' app (usually there is an icon for it on your Desktop, but you can search for it if not).  It will launch and appear in your system tray on the bottom right (the system tray icon kind of looks like a '''^''' icon).  You should see the OpenVPN icon there, it looks like a little computer screen with a lock on it.  Right click on the OpenVPN icon in the system tray, and you should see a small menu appear.  Select &amp;quot;Import file&amp;quot;.  In the resulting window, browse to your Desktop or wherever you saved the '''prism-duo.ovpn''' file.  Select that file an click &amp;quot;Open&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Once you import the file, you should be able to right click on OpenVPN Connect again in the system tray and select the profile you want to connect to.  It should show multiple profiles, one for your old profile and one for your new profile.  Select the new one, then select &amp;quot;Connect&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
That's it!  It will ask you for your usual GI PRISM username and password that you usually use to connect to our VPN, and after that it will send a Duo Push notification to your phone, and then you should be logged in.  Other than the Duo Push, the VPN behaves exactly like it did before.  If you need to use an authentication method other than Duo Push, you can append a comma, and then the name of the method (like &amp;quot;push&amp;quot;, &amp;quot;sms&amp;quot;, or &amp;quot;phone&amp;quot;), or a numeric second factor code, to you password when you submit it.&lt;br /&gt;
&lt;br /&gt;
If you have issues you can always revert back to the old configuration, which will still work for a while.  We will disable the old VPN soon though, so make every effort to get the new VPN setup working.&lt;br /&gt;
&lt;br /&gt;
As always, please email '''cluster-admin@soe.ucsc.edu''' if you need help or have any questions.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=646</id>
		<title>Slurm Tips for Toil</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=646"/>
		<updated>2025-02-14T19:12:39Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some tips for running Toil workflows on the Phoenix Slurm cluster. Mostly you might want to run WDL workflows, but you can use some of these for other workflows like Cactus. You can also consult [https://github.com/DataBiosphere/toil/blob/master/docs/wdl/running.rst the Toil documentation on WDL workflows].&lt;br /&gt;
&lt;br /&gt;
* Install Toil with WDL support with:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
To use a development version of Toil, you can install from source instead:&lt;br /&gt;
&lt;br /&gt;
 pipx 'toil[wdl]@git+https://github.com/DataBiosphere/toil.git'&lt;br /&gt;
&lt;br /&gt;
Or for a particular branch:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]@git+https://github.com/DataBiosphere/toil.git@issues/123-abc'&lt;br /&gt;
&lt;br /&gt;
If you don't have &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt;, you would first need to:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may in turn need you to log out and back in.&lt;br /&gt;
&lt;br /&gt;
* For Toil options, you will want '''--batchSystem slurm''' to make it use Slurm and '''--batchLogsDir ./logs''' (or some other location on a shared filesystem) for the Slurm logs to not get lost.&lt;br /&gt;
&lt;br /&gt;
* You may be able to speed up your workflow with '''--caching true''', to cache data on nodes to be shared among multiple simultaneous tasks.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you might want to add '''--jobStore ./jobStore''' to make sure the job store is in a defined, shared location so that you can use '''--restart''' later.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you will want to set the '''SINGULARITY_CACHEDIR''' and '''MINIWDL__SINGULARITY__IMAGE_CACHE''' environment variables for your workflow to locations on shared storage, and possibly to the default cache locations in your home directory. Otherwise Toil will set them to node-local directories for each node, and thus re-download images for each workflow run, and for each cluster node. To avoid this, you could, for example, before your run or in your '''~/.bashrc''' you could:&lt;br /&gt;
&lt;br /&gt;
 export SINGULARITY_CACHEDIR=$HOME/.singularity/cache&lt;br /&gt;
 export MINIWDL__SINGULARITY__IMAGE_CACHE=$HOME/.cache/miniwdl&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=645</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=645"/>
		<updated>2025-02-14T19:09:08Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Installing Toil with WDL support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may instruct you to **log out and log back in** or take some other action to adopt the new &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
When installing Toil, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
To change what extras are used when you have an existing Toil installation, you will need to use the &amp;lt;code&amp;gt;--force&amp;lt;/code&amp;gt; option.&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
If you see something from &amp;lt;code&amp;gt;pipx&amp;lt;/code&amp;gt; like:&lt;br /&gt;
&lt;br /&gt;
     - cwltoil (symlink missing or pointing to unexpected location)&lt;br /&gt;
&lt;br /&gt;
Then &amp;lt;code&amp;gt;pipx uninstall toil&amp;lt;/code&amp;gt;, remove the offending file from &amp;lt;code&amp;gt;~/.local/bin&amp;lt;/code&amp;gt;, and try again.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=644</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=644"/>
		<updated>2025-02-14T19:03:18Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
This may instruct you to log out and log back in or take some other action to adopt the new &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
When installing Toil, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
To change what extras are used when you have an existing Toil installation, you will need to use the &amp;lt;code&amp;gt;--force&amp;lt;/code&amp;gt; option.&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=643</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=643"/>
		<updated>2025-02-14T19:00:28Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Installing Toil with WDL support */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows.&lt;br /&gt;
&lt;br /&gt;
Toil is written in Python, and the modern way to install Python command line tools is with pipx. So [https://pipx.pypa.io/latest/installation/ install pipx]:&lt;br /&gt;
&lt;br /&gt;
 python3 -m pip install --user pipx&lt;br /&gt;
 python3 -m pipx ensurepath&lt;br /&gt;
&lt;br /&gt;
When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pipx install 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;python3 -m pipx ensurepath&amp;lt;/code&amp;gt; command should have added the &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt; directory to your &amp;lt;code&amp;gt;PATH&amp;lt;/code&amp;gt; environment variable, to ensure you can find these commands.&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=627</id>
		<title>Slurm Tips for Toil</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=627"/>
		<updated>2025-02-11T20:49:14Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Change to new extras syntax from https://github.com/pypa/pip/pull/11617&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some tips for running Toil workflows on the Phoenix Slurm cluster. Mostly you might want to run WDL workflows, but you can use some of these for other workflows like Cactus. You can also consult [https://github.com/DataBiosphere/toil/blob/master/docs/wdl/running.rst the Toil documentation on WDL workflows].&lt;br /&gt;
&lt;br /&gt;
* Install Toil with WDL support with:&lt;br /&gt;
&lt;br /&gt;
 pip3 install --upgrade 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
To use a development version of Toil, you can install from source instead:&lt;br /&gt;
&lt;br /&gt;
 pip3 install 'toil[wdl]@git+https://github.com/DataBiosphere/toil.git'&lt;br /&gt;
&lt;br /&gt;
Or for a particular branch:&lt;br /&gt;
&lt;br /&gt;
 pip3 install 'toil[wdl]@git+https://github.com/DataBiosphere/toil.git@issues/123-abc'&lt;br /&gt;
&lt;br /&gt;
* You will then need to make sure your '''~/.local/bin''' directory is on your PATH. Open up your '''~/.bashrc''' file and add:&lt;br /&gt;
&lt;br /&gt;
 export PATH=$PATH:$HOME/.local/bin&lt;br /&gt;
&lt;br /&gt;
Then make sure to log out and back in again.&lt;br /&gt;
&lt;br /&gt;
* For Toil options, you will want '''--batchSystem slurm''' to make it use Slurm and '''--batchLogsDir ./logs''' (or some other location on a shared filesystem) for the Slurm logs to not get lost.&lt;br /&gt;
&lt;br /&gt;
* You may be able to speed up your workflow with '''--caching true''', to cache data on nodes to be shared among multiple simultaneous tasks.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you might want to add '''--jobStore ./jobStore''' to make sure the job store is in a defined, shared location so that you can use '''--restart''' later.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you will want to set the '''SINGULARITY_CACHEDIR''' and '''MINIWDL__SINGULARITY__IMAGE_CACHE''' environment variables for your workflow to locations on shared storage, and possibly to the default cache locations in your home directory. Otherwise Toil will set them to node-local directories for each node, and thus re-download images for each workflow run, and for each cluster node. To avoid this, you could, for example, before your run or in your '''~/.bashrc''' you could:&lt;br /&gt;
&lt;br /&gt;
 export SINGULARITY_CACHEDIR=$HOME/.singularity/cache&lt;br /&gt;
 export MINIWDL__SINGULARITY__IMAGE_CACHE=$HOME/.cache/miniwdl&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=626</id>
		<title>Slurm Tips for Toil</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=626"/>
		<updated>2025-02-11T20:42:22Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Add quotes to protect brackets&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some tips for running Toil workflows on the Phoenix Slurm cluster. Mostly you might want to run WDL workflows, but you can use some of these for other workflows like Cactus. You can also consult [https://github.com/DataBiosphere/toil/blob/master/docs/wdl/running.rst the Toil documentation on WDL workflows].&lt;br /&gt;
&lt;br /&gt;
* Install Toil with WDL support with:&lt;br /&gt;
&lt;br /&gt;
 pip3 install --upgrade 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
To use a development version of Toil, you can install from source instead:&lt;br /&gt;
&lt;br /&gt;
 pip3 install 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
Or for a particular branch:&lt;br /&gt;
&lt;br /&gt;
 pip3 install 'git+https://github.com/DataBiosphere/toil.git@issues/123-abc#egg=toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
* You will then need to make sure your '''~/.local/bin''' directory is on your PATH. Open up your '''~/.bashrc''' file and add:&lt;br /&gt;
&lt;br /&gt;
 export PATH=$PATH:$HOME/.local/bin&lt;br /&gt;
&lt;br /&gt;
Then make sure to log out and back in again.&lt;br /&gt;
&lt;br /&gt;
* For Toil options, you will want '''--batchSystem slurm''' to make it use Slurm and '''--batchLogsDir ./logs''' (or some other location on a shared filesystem) for the Slurm logs to not get lost.&lt;br /&gt;
&lt;br /&gt;
* You may be able to speed up your workflow with '''--caching true''', to cache data on nodes to be shared among multiple simultaneous tasks.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you might want to add '''--jobStore ./jobStore''' to make sure the job store is in a defined, shared location so that you can use '''--restart''' later.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you will want to set the '''SINGULARITY_CACHEDIR''' and '''MINIWDL__SINGULARITY__IMAGE_CACHE''' environment variables for your workflow to locations on shared storage, and possibly to the default cache locations in your home directory. Otherwise Toil will set them to node-local directories for each node, and thus re-download images for each workflow run, and for each cluster node. To avoid this, you could, for example, before your run or in your '''~/.bashrc''' you could:&lt;br /&gt;
&lt;br /&gt;
 export SINGULARITY_CACHEDIR=$HOME/.singularity/cache&lt;br /&gt;
 export MINIWDL__SINGULARITY__IMAGE_CACHE=$HOME/.cache/miniwdl&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_vg&amp;diff=540</id>
		<title>Slurm Tips for vg</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_vg&amp;diff=540"/>
		<updated>2025-01-16T16:58:39Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Setting Up */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page explains how to set up a development environment for [https://github.com/vgteam/vg vg] on the Phoenix cluster.&lt;br /&gt;
&lt;br /&gt;
==Setting Up==&lt;br /&gt;
&lt;br /&gt;
1. After connecting to the VPN, connect to an interactive node:&lt;br /&gt;
&lt;br /&gt;
 ssh razzmatazz.prism&lt;br /&gt;
&lt;br /&gt;
This node is relatively small, so you shouldn't run real work on it, but it is the place you need to be to submit Slurm jobs.&lt;br /&gt;
&lt;br /&gt;
2. Make yourself a user directory under '''/private/groups''', which is where large data must be stored. For example, if you are in the Paten lab:&lt;br /&gt;
&lt;br /&gt;
 mkdir /private/groups/patenlab/$USER&lt;br /&gt;
&lt;br /&gt;
3. (Optional) Link it over to your home directory, so it is easy to use storage there to store your repos. The '''/private/groups''' storage may be faster than the home directory storage.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p /private/groups/patenlab/$USER/workspace&lt;br /&gt;
 ln -s /private/groups/patenlab/$USER/workspace ~/workspace&lt;br /&gt;
&lt;br /&gt;
4. Make sure you have SSH keys created and add them to Github.&lt;br /&gt;
&lt;br /&gt;
 cat ~/.ssh/id_ed25519.pub || (ssh-keygen -t ed25519 &amp;amp;&amp;amp; cat  ~/.ssh/id_ed25519.pub)&lt;br /&gt;
 # Paste into https://github.com/settings/ssh/new&lt;br /&gt;
&lt;br /&gt;
5. Make a place to put your clone, and clone vg:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p ~/workspace&lt;br /&gt;
 cd ~/workspace&lt;br /&gt;
 git clone --recursive git@github.com:vgteam/vg.git&lt;br /&gt;
 cd vg&lt;br /&gt;
&lt;br /&gt;
6. vg's dependencies should already be installed on the cluster nodes. If any of them seem to be missing, tell cluster-admin@soe.ucsc.edu to install them.&lt;br /&gt;
&lt;br /&gt;
7. Build vg as a Slurm job. This will send the build out to the cluster as a 64-core, 80G memory job, and keep the output logs in your terminal.&lt;br /&gt;
&lt;br /&gt;
 srun -c 64 --mem=80G --time=00:30:00 make -j64&lt;br /&gt;
&lt;br /&gt;
This will leave your vg binary at '''~/workspace/vg/bin/vg'''.&lt;br /&gt;
&lt;br /&gt;
==Misc Tips==&lt;br /&gt;
&lt;br /&gt;
* For a lightweight job that outputs to your terminal or that can be waited for in a Bash script, run an individual command directly from &amp;lt;code&amp;gt;srun&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 srun -c1 --mem 2G --partition short --time 1:00:00 sleep 10&lt;br /&gt;
&lt;br /&gt;
* If you need to run a few commands in the same shell, use &amp;lt;code&amp;gt;sbatch --wrap&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 sbatch -c1 --mem 2G --partition short --time 1:00:00 --wrap &amp;quot;. venv/bin/activate; ./script1.py &amp;amp;&amp;amp; ./script2.py&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* To watch a batch job's output live, look at the &amp;lt;code&amp;gt;Submitted batch job 5244464&amp;lt;/code&amp;gt; line from &amp;lt;code&amp;gt;sbatch&amp;lt;/code&amp;gt; and run:&lt;br /&gt;
&lt;br /&gt;
 tail -f slurm-5244464.out&lt;br /&gt;
&lt;br /&gt;
* '''Danger!''' If you ''really'' need an interactive session with appreciable resources, you can schedule one with &amp;lt;code&amp;gt;srun --pty&amp;lt;/code&amp;gt;. But it is '''very easy''' to waste resources like this, since the job will happily sit there not doing anything until it hits the timeout. Only do this for testing! For real work, use one of the other methods!&lt;br /&gt;
&lt;br /&gt;
 srun -c 16 --mem 120G --time=08:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
* To send out a job without making a script file for it, use '''sbatch --wrap &amp;quot;your command here&amp;quot;'''.&lt;br /&gt;
&lt;br /&gt;
* You can use arguments from SBATCH lines on the command line!&lt;br /&gt;
&lt;br /&gt;
* You can use [https://github.com/CLIP-HPC/SlurmCommander#readme Slurm Commander] to watch the state of the cluster with the '''scom''' command.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Using_Docker_under_Slurm&amp;diff=539</id>
		<title>Using Docker under Slurm</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Using_Docker_under_Slurm&amp;diff=539"/>
		<updated>2024-12-04T14:45:05Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Explain how to pass the right GPUs&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__TOC__&lt;br /&gt;
&lt;br /&gt;
Sometimes it is convenient to ask Slurm to run your job in a docker container.  This is just fine, however, you will need to fully test your job in a docker container beforehand (on mustard or emerald, for example) to see how much RAM and CPU resources it requires, so you can accurately describe in your slurm job submission file how many resources it needs.&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
You can run your container on mustard then look at 'top' to see how much RAM and CPU it needs.&lt;br /&gt;
&lt;br /&gt;
You also will need to be aware that you will need to pull your docker image from a registry, like DockerHub or Quay.  And you should also run you docker container with the '--rm' flag, so the container cleans itself up after running.  So your workflow would look something like this:&lt;br /&gt;
&lt;br /&gt;
 1: Pull image from DockerHub&lt;br /&gt;
 2: docker run --rm docker/welcome-to-docker&lt;br /&gt;
&lt;br /&gt;
Optionally you can clean up your image as well, but only if you don't have many jobs using that image on the same node.  For example, if I wanted to remove the image laballed &amp;quot;weiler/mytools&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
 $ docker image ls&lt;br /&gt;
 REPOSITORY                          TAG                    IMAGE ID       CREATED         SIZE&lt;br /&gt;
 weiler/mytools                     latest                 be6777ad00cf   19 hours ago    396MB&lt;br /&gt;
 somedude/tools                     latest                 9b1d1f6fbf6f   3 weeks ago     607MB&lt;br /&gt;
 &lt;br /&gt;
 $ docker image rm be6777ad00cf&lt;br /&gt;
&lt;br /&gt;
== Resource Limits ==&lt;br /&gt;
&lt;br /&gt;
When running docker containers on Slurm, slurm cannot limit the resources that docker uses.  Therefore, when you launch a container, you will need to know how much resources (RAM, CPU) it uses beforehand, determined by your testing.  Then launch your job with the following --cpus and --memory parameters so docker itslef will limit what it uses:&lt;br /&gt;
&lt;br /&gt;
 docker run --rm '''--cpus=16 --memory=1024m''' docker/welcome-to-docker&lt;br /&gt;
&lt;br /&gt;
The --memory argument is in megabytes (hence the 'm' at the end).  So the above example will set a memory limit of 1GB.&lt;br /&gt;
&lt;br /&gt;
== Docker and GPUs ==&lt;br /&gt;
&lt;br /&gt;
If you are using GPUs with Docker, you need to make sure that your Docker container requests access to the ''correct'' GPUs: the ones which Slurm assigned to your job. These will be passed in the &amp;lt;code&amp;gt;SLURM_STEP_GPUS&amp;lt;/code&amp;gt; (for GPUs for a single step) or &amp;lt;code&amp;gt;SLURM_JOB_GPUS&amp;lt;/code&amp;gt; (for GPUs for a whole job) environment variables. They need to be passed to Docker like this:&lt;br /&gt;
&lt;br /&gt;
 docker run --gpus=&amp;quot;\&amp;quot;device=${SLURM_STEP_GPUS:-$SLURM_JOB_GPUS}\&amp;quot;&amp;quot; nvidia/cuda nvidia-smi&lt;br /&gt;
&lt;br /&gt;
'''Note the escaped quotes'''; the Docker command needs to have double-quotes ''inside'' the argument value. The &amp;lt;code&amp;gt;${:-}&amp;lt;/code&amp;gt; syntax will use &amp;lt;code&amp;gt;SLURM_STEP_GPUS&amp;lt;/code&amp;gt; if it is set and &amp;lt;code&amp;gt;SLURM_JOB_GPUS&amp;lt;/code&amp;gt; if it isn't; if you know which will be set for your job, you can use just that one.&lt;br /&gt;
&lt;br /&gt;
If you are using Nextflow, you will need to set &amp;lt;code&amp;gt;docker.runOptions&amp;lt;/code&amp;gt; to include this flag.&lt;br /&gt;
&lt;br /&gt;
 docker.runOptions=&amp;quot;--gpus \\\&amp;quot;device=$SLURM_JOB_GPUS\\\&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you are using Toil to run CWL or WDL, the correct GPUs will be passed to containers automatically.&lt;br /&gt;
&lt;br /&gt;
== Cleaning Scripts ==&lt;br /&gt;
&lt;br /&gt;
We also have auto-cleaning scripts running that will delete any containers and images that were created/pulled more than 7 days ago.  This includes the cluster nodes and also the phoenix head node itself.  If you need a place to have your images/containers remain longer than that, please put them on mustard, emerald, crimson or razzmatazz.&lt;br /&gt;
&lt;br /&gt;
Also, there are cleaning scripts in place that will destroy any running containers that have been running for over 7 days.  We assume that such a container was not launched with '''--rm''' and needs to be cleaned up.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_vg&amp;diff=520</id>
		<title>Slurm Tips for vg</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_vg&amp;diff=520"/>
		<updated>2024-10-11T17:05:25Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Misc Tips */ Discourage interactive shells&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page explains how to set up a development environment for [https://github.com/vgteam/vg vg] on the Phoenix cluster.&lt;br /&gt;
&lt;br /&gt;
==Setting Up==&lt;br /&gt;
&lt;br /&gt;
1. After connecting to the VPN, connect to the cluster head node:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
This node is relatively small, so you shouldn't run real work on it, but it is the place you need to be to submit Slurm jobs.&lt;br /&gt;
&lt;br /&gt;
2. Make yourself a user directory under '''/private/groups''', which is where large data must be stored. For example, if you are in the Paten lab:&lt;br /&gt;
&lt;br /&gt;
 mkdir /private/groups/patenlab/$USER&lt;br /&gt;
&lt;br /&gt;
3. (Optional) Link it over to your home directory, so it is easy to use storage there to store your repos. The '''/private/groups''' storage may be faster than the home directory storage.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p /private/groups/patenlab/$USER/workspace&lt;br /&gt;
 ln -s /private/groups/patenlab/$USER/workspace ~/workspace&lt;br /&gt;
&lt;br /&gt;
4. Make sure you have SSH keys created and add them to Github.&lt;br /&gt;
&lt;br /&gt;
 cat ~/.ssh/id_ed25519.pub || (ssh-keygen -t ed25519 &amp;amp;&amp;amp; cat  ~/.ssh/id_ed25519.pub)&lt;br /&gt;
 # Paste into https://github.com/settings/ssh/new&lt;br /&gt;
&lt;br /&gt;
5. Make a place to put your clone, and clone vg:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p ~/workspace&lt;br /&gt;
 cd ~/workspace&lt;br /&gt;
 git clone --recursive git@github.com:vgteam/vg.git&lt;br /&gt;
 cd vg&lt;br /&gt;
&lt;br /&gt;
6. vg's dependencies should already be installed on the cluster nodes. If any of them seem to be missing, tell cluster-admin@soe.ucsc.edu to install them.&lt;br /&gt;
&lt;br /&gt;
7. Build vg as a Slurm job. This will send the build out to the cluster as a 64-core, 80G memory job, and keep the output logs in your terminal.&lt;br /&gt;
&lt;br /&gt;
 srun -c 64 --mem=80G --time=00:30:00 make -j64&lt;br /&gt;
&lt;br /&gt;
This will leave your vg binary at '''~/workspace/vg/bin/vg'''.&lt;br /&gt;
&lt;br /&gt;
==Misc Tips==&lt;br /&gt;
&lt;br /&gt;
* For a lightweight job that outputs to your terminal or that can be waited for in a Bash script, run an individual command directly from &amp;lt;code&amp;gt;srun&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 srun -c1 --mem 2G --partition short --time 1:00:00 sleep 10&lt;br /&gt;
&lt;br /&gt;
* If you need to run a few commands in the same shell, use &amp;lt;code&amp;gt;sbatch --wrap&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 sbatch -c1 --mem 2G --partition short --time 1:00:00 --wrap &amp;quot;. venv/bin/activate; ./script1.py &amp;amp;&amp;amp; ./script2.py&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* To watch a batch job's output live, look at the &amp;lt;code&amp;gt;Submitted batch job 5244464&amp;lt;/code&amp;gt; line from &amp;lt;code&amp;gt;sbatch&amp;lt;/code&amp;gt; and run:&lt;br /&gt;
&lt;br /&gt;
 tail -f slurm-5244464.out&lt;br /&gt;
&lt;br /&gt;
* '''Danger!''' If you ''really'' need an interactive session with appreciable resources, you can schedule one with &amp;lt;code&amp;gt;srun --pty&amp;lt;/code&amp;gt;. But it is '''very easy''' to waste resources like this, since the job will happily sit there not doing anything until it hits the timeout. Only do this for testing! For real work, use one of the other methods!&lt;br /&gt;
&lt;br /&gt;
 srun -c 16 --mem 120G --time=08:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
* To send out a job without making a script file for it, use '''sbatch --wrap &amp;quot;your command here&amp;quot;'''.&lt;br /&gt;
&lt;br /&gt;
* You can use arguments from SBATCH lines on the command line!&lt;br /&gt;
&lt;br /&gt;
* You can use [https://github.com/CLIP-HPC/SlurmCommander#readme Slurm Commander] to watch the state of the cluster with the '''scom''' command.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=509</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=509"/>
		<updated>2024-07-16T20:40:10Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Configuring Toil for Phoenix */ Provide Julian's preferred storage paths, note Ceph bug and default home directory image storage.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we might not be able to keep these in your home directory.&lt;br /&gt;
&lt;br /&gt;
We would like to be able to store these on the cluster's large storage array, under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. However, Toil needs to use file locks in these directories to prevent simultaneous Singularity calls from producing internal Singularity errors, and Ceph currently has [https://tracker.ceph.com/issues/65607 a bug where these file locking operations can freeze the Ceph servers].&lt;br /&gt;
&lt;br /&gt;
If you have '''a small number of container images''' that will fit in your home directory, you can keep them there. [https://github.com/DataBiosphere/toil/commit/cb0b291bb7f6212bfe69221dd9f09d72f83e92fb Since Toil 6.1.0], this is the default behavior and you don't need to do anything. (Unless you previously set &amp;lt;code&amp;gt;SINGULARITY_CACHEDIR&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;MINIWDL__SINGULARITY__IMAGE_CACHE&amp;lt;/code&amp;gt;, in which case you need to unset them.)&lt;br /&gt;
&lt;br /&gt;
'''If you don't have room in your home directory''' for container images, currently the recommended approach is to use node-local storage under &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt;. This results in each node pulling each container image, but images will be saved across workflows.&lt;br /&gt;
&lt;br /&gt;
You can set that up for all your workflows with:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;/data/tmp/$(whoami)/cache/singularity&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;/data/tmp/$(whoami)/cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=508</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=508"/>
		<updated>2024-07-16T20:24:49Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Frequently Asked Questions */ Note there's no more XDG_RUNTIME_DIR warning.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/private/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should upgrade Toil. [https://github.com/DataBiosphere/toil/commit/ff6bf60ab798a675c20156c749817c4313644b96 Since Toil 6.1.0], Toil no longer issues this warning, and just puts up with bad &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; settings.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=507</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=507"/>
		<updated>2024-07-16T20:20:12Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Reading the Log */ Explain --writeLogs&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/private/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
If you would like individual task logs to be saved separately for later reference, you can use the &amp;lt;code&amp;gt;--writeLogs&amp;lt;/code&amp;gt; option to specify a directory to store them. For more information, see [https://toil.readthedocs.io/en/latest/wdl/running.html#managing-workflow-logs the Toil documentation of workflow task logs].&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=506</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=506"/>
		<updated>2024-07-16T20:16:39Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Reproducing Problems */ Explain new debug-job features&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/private/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this.&lt;br /&gt;
&lt;br /&gt;
=== Automatically Fetching Input Files ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt; command has a &amp;lt;code&amp;gt;--retrieveTaskDirectory&amp;lt;/code&amp;gt; option that lets you dump out a directory with all the files that a failing WDL task would use. You can use it like:&lt;br /&gt;
&lt;br /&gt;
 toil debug-job ./jobstore WDLTaskJob --retrieveTaskDirectory dumpdir&lt;br /&gt;
&lt;br /&gt;
If there are multiple failing tasks, you might need to replace &amp;lt;code&amp;gt;WDLTaskJob&amp;lt;/code&amp;gt; with the name of one of the failing jobs. See [https://toil.readthedocs.io/en/latest/running/debugging.html#fetching-job-inputs the Toil documentation on retrieving files] for more on how to use this command.&lt;br /&gt;
&lt;br /&gt;
=== Manually Finding Input Files ===&lt;br /&gt;
&lt;br /&gt;
If you can't use &amp;lt;code&amp;gt;toil debug-job&amp;lt;/code&amp;gt;, you might need to manually dig through the job store for files. In the log of your failing Toil task, look for lines like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=505</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=505"/>
		<updated>2024-07-16T20:06:55Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Connecting to Phoenix */ Use emerald login node&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to any of the machines with access to the Phoenix cluster. These interactive nodes are fairly large machines that can do some work locally, but you will still want to run larger workflows on the actual cluster. For this tutorial, we will use &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt; as our login node.&lt;br /&gt;
&lt;br /&gt;
To connect to the cluster:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh emerald.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@emerald.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'emerald.prism (10.50.1.67)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&lt;br /&gt;
 This key is not known by any other names.&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;emerald.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:8hJQShO6jhrym9UVyMldKsKOnOFtWRChgjK5cZNhkAI.&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/private/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=GPU_Resources&amp;diff=503</id>
		<title>GPU Resources</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=GPU_Resources&amp;diff=503"/>
		<updated>2024-06-28T16:32:15Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Add partition and time&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;When submitting jobs, you can ask for GPUs in one of two ways.  One is:&lt;br /&gt;
&lt;br /&gt;
 #SBATCH --partition=gpu&lt;br /&gt;
 #SBATCH --gres=gpu:1&lt;br /&gt;
&lt;br /&gt;
That will ask for 1 GPU generically on a node with a free GPU.  This request is more specific:&lt;br /&gt;
&lt;br /&gt;
 #SBATCH --partition=gpu&lt;br /&gt;
 #SBATCH --gres=gpu:A5500:3&lt;br /&gt;
&lt;br /&gt;
That requests 3 A5500 GPUs '''only'''.&lt;br /&gt;
&lt;br /&gt;
We have several GPU types on the cluster which may fit your specific needs:&lt;br /&gt;
&lt;br /&gt;
 nVidia RTX A5500           : 24GB RAM&lt;br /&gt;
 nVidia A100                : 80GB RAM&lt;br /&gt;
&lt;br /&gt;
For the most part, Slurm takes care of making sure that each job only sees and used the GPUs assigned to it. Within the job, '''CUDA_VISIBLE_DEVICES''' will be set in the environment, but it will always be set to a list of your requested number of GPUs, starting at 0. Slurm re-numbers the GPUs assigned to each job to appear to start at 0, within the job. If you need access to the &amp;quot;real&amp;quot; GPU numbers (to log or to pass along to Docker), they are available in the '''SLURM_JOB_GPUS''' (for '''sbatch''') or '''SLURM_STEP_GPUS''' (for '''srun''') environment variable.&lt;br /&gt;
&lt;br /&gt;
==Running GPU Workloads==&lt;br /&gt;
&lt;br /&gt;
To actually use an nVidia GPU, you need to run a program that uses the CUDA API. There are a few ways to obtain such a program.&lt;br /&gt;
&lt;br /&gt;
===Prebuilt CUDA Applications===&lt;br /&gt;
&lt;br /&gt;
The Slurm cluster nodes have the nVidia drivers installed, as well as basic CUDA tools like nvidia-smi.&lt;br /&gt;
&lt;br /&gt;
Some projects, such as tensorflow, may ship pre-built binaries that can use CUDA. You should be able to run these binaries directly, if you download them.&lt;br /&gt;
&lt;br /&gt;
===Building CUDA Applications===&lt;br /&gt;
&lt;br /&gt;
The cluster nodes do not have the full CUDA Toolkit. In particular, they do not have the '''nvcc''' CUDA-enabled compiler. If you want to compile applications that use CUDA, you will need to install the development environment yourself for your user.&lt;br /&gt;
&lt;br /&gt;
Once you have '''nvcc''' available to your user, building CUDA applications should work. To run them, you will have to submit them as jobs, because the head node does not have a GPU.&lt;br /&gt;
&lt;br /&gt;
===Containerized GPU Workloads===&lt;br /&gt;
&lt;br /&gt;
Instead of directly installing binaries, or installing and using the CUDA Toolkit, it is often easiest to use containers to download a prebuilt GPU workload and all of its libraries and dependencies. THere are a few options for running containerized GPU workloads on the cluster.&lt;br /&gt;
&lt;br /&gt;
====Running Containers in Singularity====&lt;br /&gt;
&lt;br /&gt;
You can run containers on the cluster using Singularity, and give them access to the GPUs that Slurm has selected using the '''--nv''' option. For example:&lt;br /&gt;
&lt;br /&gt;
 singularity pull docker://tensorflow/tensorflow:latest-gpu&lt;br /&gt;
 srun -c 8 --mem 10G --partition=gpu --time=00:20:00 --gres=gpu:1 singularity run --nv docker://tensorflow/tensorflow:latest-gpu python -c 'from tensorflow.python.client import device_lib; print(device_lib.list_local_devices())'&lt;br /&gt;
&lt;br /&gt;
This will produce output showing that the Tensorflow container is indeed able to talk to one GPU:&lt;br /&gt;
&lt;br /&gt;
 INFO:    Using cached SIF image&lt;br /&gt;
 2023-05-15 11:36:33.110850: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.&lt;br /&gt;
 To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.&lt;br /&gt;
 2023-05-15 11:36:38.799035: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1635] Created device /device:GPU:0 with 22244 MB memory:  -&amp;gt; device: 0, name: NVIDIA RTX A5500, pci bus id: 0000:03:00.0, compute  capability: 8.6&lt;br /&gt;
 [name: &amp;quot;/device:CPU:0&amp;quot;&lt;br /&gt;
 device_type: &amp;quot;CPU&amp;quot;&lt;br /&gt;
 memory_limit: 268435456&lt;br /&gt;
 locality {&lt;br /&gt;
 }&lt;br /&gt;
 incarnation: 8527638019084870106&lt;br /&gt;
 xla_global_id: -1&lt;br /&gt;
 , name: &amp;quot;/device:GPU:0&amp;quot;&lt;br /&gt;
 device_type: &amp;quot;GPU&amp;quot;&lt;br /&gt;
 memory_limit: 23324655616&lt;br /&gt;
 locality {&lt;br /&gt;
   bus_id: 1&lt;br /&gt;
   links {&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
 incarnation: 1860154623440434360&lt;br /&gt;
 physical_device_desc: &amp;quot;device: 0, name: NVIDIA RTX A5500, pci bus id: 0000:03:00.0, compute capability: 8.6&amp;quot;&lt;br /&gt;
 xla_global_id: 416903419&lt;br /&gt;
 ]&lt;br /&gt;
&lt;br /&gt;
Slurm's containment of the Slurm job to the correct set of GPUs is also passed through to the Singularity container; there is no need to specifically direct Singularity to use the right GPUs unless you are doing something unusual.&lt;br /&gt;
&lt;br /&gt;
====Running Containers in Slurm====&lt;br /&gt;
&lt;br /&gt;
Slurm itself also supports a '''--container''' option for jobs, which allows a whole job to be run inside a container. If you are able to [https://slurm.schedmd.com/containers.html convert your container to OCI Bundle format], you can pass it directly to Slurm instead of using Singularity from inside the job. However, Docker-compatible image specifiers can't be given to Slurm, only paths to OCI bundles on disk.&lt;br /&gt;
&lt;br /&gt;
Stnad-alone tools to download a Docker image from Docker Hub in OCI bundle format ('''skopeo''' and '''umoci''') are not yet installed on the cluster. But the method using the '''docker''' command should work.&lt;br /&gt;
&lt;br /&gt;
Slurm containers ''should'' have access to their assigned GPUs, but it is not clear if tools like '''nvidia-smi''' are injected into the container, as they would be with Singularity or the nVidia Container Runtime.&lt;br /&gt;
&lt;br /&gt;
====Running Containers in Docker====&lt;br /&gt;
&lt;br /&gt;
You might be used to running containers with Docker, or containerized GPU workloads with the nVidia Container Runtime or Toolkit. Docker is installed on all the nodes and the daemon is running; if the '''docker''' command does not work for you, ask cluster-admin to add you to the right groups.&lt;br /&gt;
&lt;br /&gt;
The '''nvidia''' runtime is set up and will automatically be used.&lt;br /&gt;
&lt;br /&gt;
While Slurm configures each Slurm job with a cgroup that directs it to the correct GPUs, '''using Docker to run another container escapes Slurm's confinement''', and using '''--gpus=1''' will ''always'' use the ''first'' GPU in the system, whether that GPU is assigned to your job or not. When using Docker, you ''must'' consult the '''SLURM_JOB_GPUS''' (for '''sbatch''') or '''SLRUM_STEP_GPUS''' (for '''srun''') environment variable and pass that along to your container. You should also impose limits on all other resources used by your Docker container, so that your whole job stays within the resources allocated by Slurm's scheduler. (TODO: find out how cgroups handles oversubscription between a Docker container and the Slurm container that launched it).&lt;br /&gt;
&lt;br /&gt;
An example of a working command is:&lt;br /&gt;
&lt;br /&gt;
 srun -c 1 --mem 4G --partition=gpu --time=00:20:00 --gres=gpu:2 bash -c 'docker run --rm --gpus=\&amp;quot;device=$SLURM_STEP_GPUS\&amp;quot; nvidia/cuda:12.1.1-base-ubuntu22.04 nvidia-smi'&lt;br /&gt;
&lt;br /&gt;
Note that the double-quotes are included in the argument to '''--gpus''' as seen by the Docker client, and that '''bash''' and single-quotes are used to ensure that '''$SLURM_STEP_GPUS''' is evaluated within the job itself, and not on the head node.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Queues_(Partitions)_and_Resource_Management&amp;diff=474</id>
		<title>Slurm Queues (Partitions) and Resource Management</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Queues_(Partitions)_and_Resource_Management&amp;diff=474"/>
		<updated>2024-03-25T20:29:53Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* My job is not running but I want it to be running! */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Partitions ==&lt;br /&gt;
&lt;br /&gt;
Due to heterogeneous workloads and different batch requirements, we have implemented partitions in slurm, which are similar to queues.&lt;br /&gt;
&lt;br /&gt;
Each partition has different default and maximum walltime limits (aka &amp;quot;runtime&amp;quot; limits).  You will need to select a partition to launch your jobs in based on what kind of jobs they are and how long they are expected to run.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|- style=&amp;quot;font-weight:bold;&amp;quot;&lt;br /&gt;
! Partition Name&lt;br /&gt;
! Default Walltime Limit&lt;br /&gt;
! Maximum Walltime Limit&lt;br /&gt;
! style=&amp;quot;border-color:inherit;&amp;quot; | Default Partition?&lt;br /&gt;
! Job Priority&lt;br /&gt;
! Maximum Nodes Utilized&lt;br /&gt;
|-&lt;br /&gt;
| short&lt;br /&gt;
| 10 minutes&lt;br /&gt;
| 1 hour&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | Yes&lt;br /&gt;
| Normal&lt;br /&gt;
| All&lt;br /&gt;
|-&lt;br /&gt;
| medium&lt;br /&gt;
| 1 hour&lt;br /&gt;
| 12 hours&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | No&lt;br /&gt;
| Normal&lt;br /&gt;
| 15&lt;br /&gt;
|-&lt;br /&gt;
| long&lt;br /&gt;
| 12 hours&lt;br /&gt;
| 7 days&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | No&lt;br /&gt;
| Normal&lt;br /&gt;
| 10&lt;br /&gt;
|-&lt;br /&gt;
| high_priority&lt;br /&gt;
| 10 minutes&lt;br /&gt;
| 7 days&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | No&lt;br /&gt;
| High&lt;br /&gt;
| All&amp;lt;br /&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| gpu&lt;br /&gt;
| 10 minutes&lt;br /&gt;
| 7 days&lt;br /&gt;
| No&lt;br /&gt;
| Normal&lt;br /&gt;
| 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If you do not specify a partition to run your job in (with e.g. &amp;lt;code&amp;gt;--partition=medium&amp;lt;/code&amp;gt;), it will automatically be assigned the &amp;quot;short&amp;quot; partition by default.  If you do not specify a walltime value in your job submission script (with e.g. &amp;lt;code&amp;gt;--time=00:30:00&amp;lt;/code&amp;gt;), it will inherit the &amp;quot;Default Walltime Limit&amp;quot; of the partition it is assigned.  Therefore, it is a very good idea to specify which partition your job will go in, and you should also specify a walltime limit, otherwise your jobs will inherit the default walltime limit in the chart above.&lt;br /&gt;
&lt;br /&gt;
This all means that it is very important to '''TEST''' your jobs before running many of them!  Submit one job and note how much resources it takes (RAM, CPU) and how long it takes to run.  Then when you submit many of those jobs, you can correctly specify the number of CPU cores your job needs, how much RAM it needs (pad it by about 20% just in case), and how much time it needs to run (pad it by 40% to account for environmental variables like disk IO load and CPU context switching load).&lt;br /&gt;
&lt;br /&gt;
You can test your jobs by running one job via '''srun''' with fairly high CPU, RAM and walltime limits (just so it isn't killed due to default limits), then noting how much in resources it consumed while running (after it finishes).&lt;br /&gt;
&lt;br /&gt;
'''Example'''&lt;br /&gt;
&lt;br /&gt;
 seff 769059&lt;br /&gt;
&lt;br /&gt;
'''Output'''&lt;br /&gt;
&lt;br /&gt;
 Job ID: 769059&lt;br /&gt;
 Cluster: phoenix&lt;br /&gt;
 User/Group: &amp;lt;user-name&amp;gt;/&amp;lt;group-name&amp;gt;&lt;br /&gt;
 State: COMPLETED (exit code 0)&lt;br /&gt;
 Nodes: 1&lt;br /&gt;
 Cores per node: 16&lt;br /&gt;
 CPU Utilized: 00:00:01&lt;br /&gt;
 CPU Efficiency: 0.11% of 00:15:28 core-walltime&lt;br /&gt;
 Job Wall-clock time: 00:00:58&lt;br /&gt;
 Memory Utilized: 4.79 MB&lt;br /&gt;
 Memory Efficiency: 4.79% of 100.00 MB&lt;br /&gt;
&lt;br /&gt;
So if I needed to run like 1000 of these jobs, and they were all similar, I would select the &amp;quot;short&amp;quot; partition, 1 CPU core, maybe specify 8MB RAM, and maybe 90 seconds walltime limit.  Note how I padded the RAM and walltime a bit to account for unexpected variable cluster conditions.&lt;br /&gt;
&lt;br /&gt;
== '''high_priority''' Partition Notes ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;high_priority&amp;quot; partition is special in that it will have the highest priority of all jobs on the cluster and will push all other jobs aside in an effort to finish jobs in that partition as fast as possible.  This is only available for emergency or mission critical batches that need to be completed in an unexpectedly critically fast way.  Access to this partition is only granted on a per request basis, and is temporary until your batch finishes.  Email '''cluster-admin@soe.ucsc.edu''' if you need to access the high_priority queue and make your case why it is necessary.&lt;br /&gt;
&lt;br /&gt;
== My job is not running but I want it to be running ==&lt;br /&gt;
&lt;br /&gt;
Even if your job is in the high-priority partition, that doesn't mean that the cluster will drop everything and run it immediately. Because we don't have pre-emption set up, high priority jobs still have to wait for currently-running jobs to finish, as well as for other high-priority jobs. And since, as noted above, jobs can be allowed to run for up to 7 days each, it is physically possible for even the highest-priority job in the whole cluster to not start for a whole week.&lt;br /&gt;
&lt;br /&gt;
Here is a [https://docs-research-it.berkeley.edu/services/high-performance-computing/user-guide/running-your-jobs/why-job-not-run/ good resource from Berkeley] about understanding and debugging Slurm job scheduling. Basically, Slurm uses the wall-clock limits of running jobs, and of jobs in the queue, to make a plan to start each job on some node at some time in the future. If jobs finish early, other jobs can start sooner than scheduled, and if there is space around higher-priority jobs, lower-priority jobs can be filled in.&lt;br /&gt;
&lt;br /&gt;
If you want to know when Slurm plans to run your job, and why that is not right now, you can use the &amp;lt;code&amp;gt;--start&amp;lt;/code&amp;gt; option for the &amp;lt;code&amp;gt;squeue&amp;lt;/code&amp;gt; command:&lt;br /&gt;
&lt;br /&gt;
    $ squeue -j 1719584 --start&lt;br /&gt;
                 JOBID PARTITION     NAME     USER ST          START_TIME  NODES SCHEDNODES           NODELIST(REASON)&lt;br /&gt;
               1719584     short snakemak flastnam PD 2024-01-22T10:20:00      1 phoenix-00           (Priority)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;START_TIME&amp;lt;/code&amp;gt; column is the time by which Slurm is sure it will be able to start your job if no higher-priority jobs come in first, and the &amp;lt;code&amp;gt;NODELIST(REASON)&amp;lt;/code&amp;gt; column shows the nodes the job is running on, or the reason it is not running now, in parentheses. In this case, the job is not running because higher-priority jobs are in the way.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=473</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=473"/>
		<updated>2024-03-21T16:23:01Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Preparing an input file */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/private/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an ''inputs file'', which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Cluster_Etiquette&amp;diff=471</id>
		<title>Cluster Etiquette</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Cluster_Etiquette&amp;diff=471"/>
		<updated>2024-03-06T17:24:37Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Link to the storage visualization&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;When running jobs on the cluster, you must be very aware of how those jobs will affect other users.&lt;br /&gt;
&lt;br /&gt;
1: Always test your job by running one first.  Just one.  Note how much RAM, how many CPU cores and how much time it takes to run.  Then, when you submit 50 or 100 of those, you can specify limits in your Slurm batch file on how long the job should run, how much RAM it should use and how much time it takes.  In that case, slurm can stop jibs that inadvertantly go too long or use too many resources.&lt;br /&gt;
&lt;br /&gt;
2: Don't run too many jobs at once if they use a lot of disk I/O.  If every job reads in a 100GB file, and you launch 20 of them at the same time, you could bring down the file server serving /private/groups.  Run only maybe 5 at once in that case, or introduce a random delay at the start of your jobs.  You can limit your concurrent jobs by specifying something like this in your job batch file:&lt;br /&gt;
&lt;br /&gt;
 #SBATCH --array=[1-279]%10&lt;br /&gt;
 &lt;br /&gt;
 inputList=$1&lt;br /&gt;
 &lt;br /&gt;
 input=$(sed -n &amp;quot;$SLURM_ARRAY_TASK_ID&amp;quot;p $inputList)&lt;br /&gt;
 &lt;br /&gt;
 some_command $input&lt;br /&gt;
&lt;br /&gt;
3: Don't use too much storage. Use http://logserv.gi.ucsc.edu/cgi-bin/private-groups.cgi to look at how your storage use is divided among your directories, and clean up large chunks of data that you do not need.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=470</id>
		<title>Slurm Tips for Toil</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=470"/>
		<updated>2024-02-16T19:08:32Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some tips for running Toil workflows on the Phoenix Slurm cluster. Mostly you might want to run WDL workflows, but you can use some of these for other workflows like Cactus. You can also consult [https://github.com/DataBiosphere/toil/blob/master/docs/wdl/running.rst the Toil documentation on WDL workflows].&lt;br /&gt;
&lt;br /&gt;
* Install Toil with WDL support with:&lt;br /&gt;
&lt;br /&gt;
 pip3 install --upgrade toil[wdl]&lt;br /&gt;
&lt;br /&gt;
To use a development version of Toil, you can install from source instead:&lt;br /&gt;
&lt;br /&gt;
 pip3 install git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl]&lt;br /&gt;
&lt;br /&gt;
Or for a particular branch:&lt;br /&gt;
&lt;br /&gt;
 pip3 install git+https://github.com/DataBiosphere/toil.git@issues/123-abc#egg=toil[wdl]&lt;br /&gt;
&lt;br /&gt;
* You will then need to make sure your '''~/.local/bin''' directory is on your PATH. Open up your '''~/.bashrc''' file and add:&lt;br /&gt;
&lt;br /&gt;
 export PATH=$PATH:$HOME/.local/bin&lt;br /&gt;
&lt;br /&gt;
Then make sure to log out and back in again.&lt;br /&gt;
&lt;br /&gt;
* For Toil options, you will want '''--batchSystem slurm''' to make it use Slurm and '''--batchLogsDir ./logs''' (or some other location on a shared filesystem) for the Slurm logs to not get lost.&lt;br /&gt;
&lt;br /&gt;
* You may be able to speed up your workflow with '''--caching true''', to cache data on nodes to be shared among multiple simultaneous tasks.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you might want to add '''--jobStore ./jobStore''' to make sure the job store is in a defined, shared location so that you can use '''--restart''' later.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you will want to set the '''SINGULARITY_CACHEDIR''' and '''MINIWDL__SINGULARITY__IMAGE_CACHE''' environment variables for your workflow to locations on shared storage, and possibly to the default cache locations in your home directory. Otherwise Toil will set them to node-local directories for each node, and thus re-download images for each workflow run, and for each cluster node. To avoid this, you could, for example, before your run or in your '''~/.bashrc''' you could:&lt;br /&gt;
&lt;br /&gt;
 export SINGULARITY_CACHEDIR=$HOME/.singularity/cache&lt;br /&gt;
 export MINIWDL__SINGULARITY__IMAGE_CACHE=$HOME/.cache/miniwdl&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=469</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=469"/>
		<updated>2024-02-13T23:01:15Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Debugging Workflows */ Explain restarting&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/private/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Restarting the Workflow==&lt;br /&gt;
&lt;br /&gt;
If you think your workflow failed from a transient problem (such as a Docker image not being available) that you have fixed, and you ran the workflow with &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; set manually to a directory that persists between attempts, you can add &amp;lt;code&amp;gt;--restart&amp;lt;/code&amp;gt; to your workflow command and make Toil try again. It will pick up from where it left off and rerun any failed tasks and then the rest of the workflow.&lt;br /&gt;
&lt;br /&gt;
This will ''will not'' pick up any changes to your WDL source code files; those are read once at the beginning and not re-read on restart.&lt;br /&gt;
&lt;br /&gt;
If restarting the workflow doesn't help, you may need to move on to more advanced debugging techniques.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=468</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=468"/>
		<updated>2024-02-06T15:09:09Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Reading the Log */ Update with new examples of the new log logging format.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/private/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stderr follows:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2024-01-16T20:12:19-0500] [Thread-3 (statsAndLoggingAggregator)] [I] [toil.statsAndLogging] hello_caller.0.hello.stdout follows:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=467</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=467"/>
		<updated>2024-01-31T15:28:10Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Turn of caching as is demanded&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/private/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. Until Toil [https://github.com/DataBiosphere/toil/issues/4775 gets support for data file caching on Slurm], we will also need the &amp;lt;code&amp;gt;--caching false&amp;lt;/code&amp;gt; option. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --caching false --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --caching false --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs and the output of commands run from WDL are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=466</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=466"/>
		<updated>2024-01-31T15:24:57Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Fix groups directory path&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/private/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/private/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /private/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Queues_(Partitions)_and_Resource_Management&amp;diff=456</id>
		<title>Slurm Queues (Partitions) and Resource Management</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Queues_(Partitions)_and_Resource_Management&amp;diff=456"/>
		<updated>2024-01-22T16:44:35Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Document how the priority system works and how to make the scheduler account for its choices&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Partitions ==&lt;br /&gt;
&lt;br /&gt;
Due to heterogeneous workloads and different batch requirements, we have implemented partitions in slurm, which are similar to queues.&lt;br /&gt;
&lt;br /&gt;
Each partition has different default and maximum walltime limits (aka &amp;quot;runtime&amp;quot; limits).  You will need to select a partition to launch your jobs in based on what kind of jobs they are and how long they are expected to run.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|- style=&amp;quot;font-weight:bold;&amp;quot;&lt;br /&gt;
! Partition Name&lt;br /&gt;
! Default Walltime Limit&lt;br /&gt;
! Maximum Walltime Limit&lt;br /&gt;
! style=&amp;quot;border-color:inherit;&amp;quot; | Default Partition?&lt;br /&gt;
! Job Priority&lt;br /&gt;
! Maximum Nodes Utilized&lt;br /&gt;
|-&lt;br /&gt;
| short&lt;br /&gt;
| 10 minutes&lt;br /&gt;
| 1 hour&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | Yes&lt;br /&gt;
| Normal&lt;br /&gt;
| All&lt;br /&gt;
|-&lt;br /&gt;
| medium&lt;br /&gt;
| 1 hour&lt;br /&gt;
| 12 hours&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | No&lt;br /&gt;
| Normal&lt;br /&gt;
| 15&lt;br /&gt;
|-&lt;br /&gt;
| long&lt;br /&gt;
| 12 hours&lt;br /&gt;
| 7 days&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | No&lt;br /&gt;
| Normal&lt;br /&gt;
| 10&lt;br /&gt;
|-&lt;br /&gt;
| high_priority&lt;br /&gt;
| 10 minutes&lt;br /&gt;
| 7 days&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | No&lt;br /&gt;
| High&lt;br /&gt;
| All&amp;lt;br /&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| gpu&lt;br /&gt;
| 10 minutes&lt;br /&gt;
| 7 days&lt;br /&gt;
| No&lt;br /&gt;
| Normal&lt;br /&gt;
| 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If you do not specify a partition to run your job in (with e.g. &amp;lt;code&amp;gt;--partition=medium&amp;lt;/code&amp;gt;), it will automatically be assigned the &amp;quot;short&amp;quot; partition by default.  If you do not specify a walltime value in your job submission script (with e.g. &amp;lt;code&amp;gt;--time=00:30:00&amp;lt;/code&amp;gt;), it will inherit the &amp;quot;Default Walltime Limit&amp;quot; of the partition it is assigned.  Therefore, it is a very good idea to specify which partition your job will go in, and you should also specify a walltime limit, otherwise your jobs will inherit the default walltime limit in the chart above.&lt;br /&gt;
&lt;br /&gt;
This all means that it is very important to '''TEST''' your jobs before running many of them!  Submit one job and note how much resources it takes (RAM, CPU) and how long it takes to run.  Then when you submit many of those jobs, you can correctly specify the number of CPU cores your job needs, how much RAM it needs (pad it by about 20% just in case), and how much time it needs to run (pad it by 40% to account for environmental variables like disk IO load and CPU context switching load).&lt;br /&gt;
&lt;br /&gt;
You can test your jobs by running one job via '''srun''' with fairly high CPU, RAM and walltime limits (just so it isn't killed due to default limits), then noting how much in resources it consumed while running (after it finishes).&lt;br /&gt;
&lt;br /&gt;
'''Example'''&lt;br /&gt;
&lt;br /&gt;
 seff 769059&lt;br /&gt;
&lt;br /&gt;
'''Output'''&lt;br /&gt;
&lt;br /&gt;
 Job ID: 769059&lt;br /&gt;
 Cluster: phoenix&lt;br /&gt;
 User/Group: &amp;lt;user-name&amp;gt;/&amp;lt;group-name&amp;gt;&lt;br /&gt;
 State: COMPLETED (exit code 0)&lt;br /&gt;
 Nodes: 1&lt;br /&gt;
 Cores per node: 16&lt;br /&gt;
 CPU Utilized: 00:00:01&lt;br /&gt;
 CPU Efficiency: 0.11% of 00:15:28 core-walltime&lt;br /&gt;
 Job Wall-clock time: 00:00:58&lt;br /&gt;
 Memory Utilized: 4.79 MB&lt;br /&gt;
 Memory Efficiency: 4.79% of 100.00 MB&lt;br /&gt;
&lt;br /&gt;
So if I needed to run like 1000 of these jobs, and they were all similar, I would select the &amp;quot;short&amp;quot; partition, 1 CPU core, maybe specify 8MB RAM, and maybe 90 seconds walltime limit.  Note how I padded the RAM and walltime a bit to account for unexpected variable cluster conditions.&lt;br /&gt;
&lt;br /&gt;
== '''high_priority''' Partition Notes ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;high_priority&amp;quot; partition is special in that it will have the highest priority of all jobs on the cluster and will push all other jobs aside in an effort to finish jobs in that partition as fast as possible.  This is only available for emergency or mission critical batches that need to be completed in an unexpectedly critically fast way.  Access to this partition is only granted on a per request basis, and is temporary until your batch finishes.  Email '''cluster-admin@soe.ucsc.edu''' if you need to access the high_priority queue and make your case why it is necessary.&lt;br /&gt;
&lt;br /&gt;
== My job is not running but I want it to be running! ==&lt;br /&gt;
&lt;br /&gt;
Even if your job is in the high-priority partition, that doesn't mean that the cluster will drop everything and run it immediately. Because we don't have pre-emption set up, high priority jobs still have to wait for currently-running jobs to finish, as well as for other high-priority jobs. And since, as noted above, jobs can be allowed to run for up to 7 days each, it is physically possible for even the highest-priority job in the whole cluster to not start for a whole week.&lt;br /&gt;
&lt;br /&gt;
Here is a [https://docs-research-it.berkeley.edu/services/high-performance-computing/user-guide/running-your-jobs/why-job-not-run/ good resource from Berkeley] about understanding and debugging Slurm job scheduling. Basically, Slurm uses the wall-clock limits of running jobs, and of jobs in the queue, to make a plan to start each job on some node at some time in the future. If jobs finish early, other jobs can start sooner than scheduled, and if there is space around higher-priority jobs, lower-priority jobs can be filled in.&lt;br /&gt;
&lt;br /&gt;
If you want to know when Slurm plans to run your job, and why that is not right now, you can use the &amp;lt;code&amp;gt;--start&amp;lt;/code&amp;gt; option for the &amp;lt;code&amp;gt;squeue&amp;lt;/code&amp;gt; command:&lt;br /&gt;
&lt;br /&gt;
    $ squeue -j 1719584 --start&lt;br /&gt;
                 JOBID PARTITION     NAME     USER ST          START_TIME  NODES SCHEDNODES           NODELIST(REASON)&lt;br /&gt;
               1719584     short snakemak flastnam PD 2024-01-22T10:20:00      1 phoenix-00           (Priority)&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;START_TIME&amp;lt;/code&amp;gt; column is the time by which Slurm is sure it will be able to start your job if no higher-priority jobs come in first, and the &amp;lt;code&amp;gt;NODELIST(REASON)&amp;lt;/code&amp;gt; column shows the nodes the job is running on, or the reason it is not running now, in parentheses. In this case, the job is not running because higher-priority jobs are in the way.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_vg&amp;diff=455</id>
		<title>Slurm Tips for vg</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_vg&amp;diff=455"/>
		<updated>2024-01-05T15:35:25Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Setting Up */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page explains how to set up a development environment for [https://github.com/vgteam/vg vg] on the Phoenix cluster.&lt;br /&gt;
&lt;br /&gt;
==Setting Up==&lt;br /&gt;
&lt;br /&gt;
1. After connecting to the VPN, connect to the cluster head node:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
This node is relatively small, so you shouldn't run real work on it, but it is the place you need to be to submit Slurm jobs.&lt;br /&gt;
&lt;br /&gt;
2. Make yourself a user directory under '''/private/groups''', which is where large data must be stored. For example, if you are in the Paten lab:&lt;br /&gt;
&lt;br /&gt;
 mkdir /private/groups/patenlab/$USER&lt;br /&gt;
&lt;br /&gt;
3. (Optional) Link it over to your home directory, so it is easy to use storage there to store your repos. The '''/private/groups''' storage may be faster than the home directory storage.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p /private/groups/patenlab/$USER/workspace&lt;br /&gt;
 ln -s /private/groups/patenlab/$USER/workspace ~/workspace&lt;br /&gt;
&lt;br /&gt;
4. Make sure you have SSH keys created and add them to Github.&lt;br /&gt;
&lt;br /&gt;
 cat ~/.ssh/id_ed25519.pub || (ssh-keygen -t ed25519 &amp;amp;&amp;amp; cat  ~/.ssh/id_ed25519.pub)&lt;br /&gt;
 # Paste into https://github.com/settings/ssh/new&lt;br /&gt;
&lt;br /&gt;
5. Make a place to put your clone, and clone vg:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p ~/workspace&lt;br /&gt;
 cd ~/workspace&lt;br /&gt;
 git clone --recursive git@github.com:vgteam/vg.git&lt;br /&gt;
 cd vg&lt;br /&gt;
&lt;br /&gt;
6. vg's dependencies should already be installed on the cluster nodes. If any of them seem to be missing, tell cluster-admin@soe.ucsc.edu to install them.&lt;br /&gt;
&lt;br /&gt;
7. Build vg as a Slurm job. This will send the build out to the cluster as a 64-core, 80G memory job, and keep the output logs in your terminal.&lt;br /&gt;
&lt;br /&gt;
 srun -c 64 --mem=80G --time=00:30:00 make -j64&lt;br /&gt;
&lt;br /&gt;
This will leave your vg binary at '''~/workspace/vg/bin/vg'''.&lt;br /&gt;
&lt;br /&gt;
==Misc Tips==&lt;br /&gt;
&lt;br /&gt;
* If you want an interactive session with appreciable resources, you can schedule one with '''srun'''. For example, to get 16 cores and 120G memory all for you, run:&lt;br /&gt;
&lt;br /&gt;
 srun -c 16 --mem 120G --time=08:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
* To send out a job without making a script file for it, use '''sbatch --wrap &amp;quot;your command here&amp;quot;'''.&lt;br /&gt;
&lt;br /&gt;
* You can use arguments from SBATCH lines on the command line!&lt;br /&gt;
&lt;br /&gt;
* You can use [https://github.com/CLIP-HPC/SlurmCommander#readme Slurm Commander] to watch the state of the cluster with the '''scom''' command.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Queues_(Partitions)_and_Resource_Management&amp;diff=454</id>
		<title>Slurm Queues (Partitions) and Resource Management</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Queues_(Partitions)_and_Resource_Management&amp;diff=454"/>
		<updated>2024-01-05T15:12:56Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Partitions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Partitions ==&lt;br /&gt;
&lt;br /&gt;
Due to heterogeneous workloads and different batch requirements, we have implemented partitions in slurm, which are similar to queues.&lt;br /&gt;
&lt;br /&gt;
Each partition has different default and maximum walltime limits (aka &amp;quot;runtime&amp;quot; limits).  You will need to select a partition to launch your jobs in based on what kind of jobs they are and how long they are expected to run.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; &lt;br /&gt;
|- style=&amp;quot;font-weight:bold;&amp;quot;&lt;br /&gt;
! Partition Name&lt;br /&gt;
! Default Walltime Limit&lt;br /&gt;
! Maximum Walltime Limit&lt;br /&gt;
! style=&amp;quot;border-color:inherit;&amp;quot; | Default Partition?&lt;br /&gt;
! Job Priority&lt;br /&gt;
! Maximum Nodes Utilized&lt;br /&gt;
|-&lt;br /&gt;
| short&lt;br /&gt;
| 10 minutes&lt;br /&gt;
| 1 hour&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | Yes&lt;br /&gt;
| Normal&lt;br /&gt;
| All&lt;br /&gt;
|-&lt;br /&gt;
| medium&lt;br /&gt;
| 1 hour&lt;br /&gt;
| 12 hours&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | No&lt;br /&gt;
| Normal&lt;br /&gt;
| 15&lt;br /&gt;
|-&lt;br /&gt;
| long&lt;br /&gt;
| 12 hours&lt;br /&gt;
| 7 days&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | No&lt;br /&gt;
| Normal&lt;br /&gt;
| 10&lt;br /&gt;
|-&lt;br /&gt;
| high_priority&lt;br /&gt;
| 10 minutes&lt;br /&gt;
| 7 days&lt;br /&gt;
| style=&amp;quot;border-color:inherit;&amp;quot; | No&lt;br /&gt;
| High&lt;br /&gt;
| All&amp;lt;br /&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| gpu&lt;br /&gt;
| 10 minutes&lt;br /&gt;
| 7 days&lt;br /&gt;
| No&lt;br /&gt;
| Normal&lt;br /&gt;
| 6&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If you do not specify a partition to run your job in (with e.g. &amp;lt;code&amp;gt;--partition=medium&amp;lt;/code&amp;gt;), it will automatically be assigned the &amp;quot;short&amp;quot; partition by default.  If you do not specify a walltime value in your job submission script (with e.g. &amp;lt;code&amp;gt;--time=00:30:00&amp;lt;/code&amp;gt;), it will inherit the &amp;quot;Default Walltime Limit&amp;quot; of the partition it is assigned.  Therefore, it is a very good idea to specify which partition your job will go in, and you should also specify a walltime limit, otherwise your jobs will inherit the default walltime limit in the chart above.&lt;br /&gt;
&lt;br /&gt;
This all means that it is very important to '''TEST''' your jobs before running many of them!  Submit one job and note how much resources it takes (RAM, CPU) and how long it takes to run.  Then when you submit many of those jobs, you can correctly specify the number of CPU cores your job needs, how much RAM it needs (pad it by about 20% just in case), and how much time it needs to run (pad it by 40% to account for environmental variables like disk IO load and CPU context switching load).&lt;br /&gt;
&lt;br /&gt;
You can test your jobs by running one job via '''srun''' with fairly high CPU, RAM and walltime limits (just so it isn't killed due to default limits), then noting how much in resources it consumed while running (after it finishes).&lt;br /&gt;
&lt;br /&gt;
'''Example'''&lt;br /&gt;
&lt;br /&gt;
 seff 769059&lt;br /&gt;
&lt;br /&gt;
'''Output'''&lt;br /&gt;
&lt;br /&gt;
 Job ID: 769059&lt;br /&gt;
 Cluster: phoenix&lt;br /&gt;
 User/Group: &amp;lt;user-name&amp;gt;/&amp;lt;group-name&amp;gt;&lt;br /&gt;
 State: COMPLETED (exit code 0)&lt;br /&gt;
 Nodes: 1&lt;br /&gt;
 Cores per node: 16&lt;br /&gt;
 CPU Utilized: 00:00:01&lt;br /&gt;
 CPU Efficiency: 0.11% of 00:15:28 core-walltime&lt;br /&gt;
 Job Wall-clock time: 00:00:58&lt;br /&gt;
 Memory Utilized: 4.79 MB&lt;br /&gt;
 Memory Efficiency: 4.79% of 100.00 MB&lt;br /&gt;
&lt;br /&gt;
So if I needed to run like 1000 of these jobs, and they were all similar, I would select the &amp;quot;short&amp;quot; partition, 1 CPU core, maybe specify 8MB RAM, and maybe 90 seconds walltime limit.  Note how I padded the RAM and walltime a bit to account for unexpected variable cluster conditions.&lt;br /&gt;
&lt;br /&gt;
== '''high_priority''' Partition Notes ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;high_priority&amp;quot; partition is special in that it will have the highest priority of all jobs on the cluster and will push all other jobs aside in an effort to finish jobs in that partition as fast as possible.  This is only available for emergency or mission critical batches that need to be completed in an unexpectedly critically fast way.  Access to this partition is only granted on a per request basis, and is temporary until your batch finishes.  Email '''cluster-admin@soe.ucsc.edu''' if you need to access the high_priority queue and make your case why it is necessary.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_vg&amp;diff=453</id>
		<title>Slurm Tips for vg</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_vg&amp;diff=453"/>
		<updated>2024-01-05T15:11:59Z</updated>

		<summary type="html">&lt;p&gt;Anovak: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page explains how to set up a development environment for [https://github.com/vgteam/vg vg] on the Phoenix cluster.&lt;br /&gt;
&lt;br /&gt;
==Setting Up==&lt;br /&gt;
&lt;br /&gt;
1. After connecting to the VPN, connect to the cluster head node:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
This node is relatively small, so you shouldn't run real work on it, but it is the place you need to be to submit Slurm jobs.&lt;br /&gt;
&lt;br /&gt;
2. Make yourself a user directory under '''/private/groups''', which is where large data must be stored. For example, if you are in the Paten lab:&lt;br /&gt;
&lt;br /&gt;
 mkdir /private/groups/patenlab/$USER&lt;br /&gt;
&lt;br /&gt;
3. (Optional) Link it over to your home directory, so it is easy to use storage there to store your repos. The '''/private/groups''' storage may be faster than the home directory storage.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p /private/groups/patenlab/$USER/workspace&lt;br /&gt;
 ln -s /private/groups/patenlab/$USER/workspace ~/workspace&lt;br /&gt;
&lt;br /&gt;
4. Make sure you have SSH keys created and add them to Github.&lt;br /&gt;
&lt;br /&gt;
 cat ~/.ssh/id_ed25519.pub || (ssh-keygen -t dsa &amp;amp;&amp;amp; cat  ~/.ssh/id_ed25519.pub)&lt;br /&gt;
 # Paste into https://github.com/settings/ssh/new&lt;br /&gt;
&lt;br /&gt;
5. Make a place to put your clone, and clone vg:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p ~/workspace&lt;br /&gt;
 cd ~/workspace&lt;br /&gt;
 git clone --recursive git@github.com:vgteam/vg.git&lt;br /&gt;
 cd vg&lt;br /&gt;
&lt;br /&gt;
6. vg's dependencies should already be installed on the cluster nodes. If any of them seem to be missing, tell cluster-admin@soe.ucsc.edu to install them.&lt;br /&gt;
&lt;br /&gt;
7. Build vg as a Slurm job. This will send the build out to the cluster as a 64-core, 80G memory job, and keep the output logs in your terminal.&lt;br /&gt;
&lt;br /&gt;
 srun -c 64 --mem=80G --time=00:30:00 make -j64&lt;br /&gt;
&lt;br /&gt;
This will leave your vg binary at '''~/workspace/vg/bin/vg'''.&lt;br /&gt;
&lt;br /&gt;
==Misc Tips==&lt;br /&gt;
&lt;br /&gt;
* If you want an interactive session with appreciable resources, you can schedule one with '''srun'''. For example, to get 16 cores and 120G memory all for you, run:&lt;br /&gt;
&lt;br /&gt;
 srun -c 16 --mem 120G --time=08:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
* To send out a job without making a script file for it, use '''sbatch --wrap &amp;quot;your command here&amp;quot;'''.&lt;br /&gt;
&lt;br /&gt;
* You can use arguments from SBATCH lines on the command line!&lt;br /&gt;
&lt;br /&gt;
* You can use [https://github.com/CLIP-HPC/SlurmCommander#readme Slurm Commander] to watch the state of the cluster with the '''scom''' command.&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=452</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=452"/>
		<updated>2023-12-07T23:00:48Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Show the safer export syntax&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/public/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /public/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; environment variable to tell Toil how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 export TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot;&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=449</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=449"/>
		<updated>2023-12-01T16:14:21Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Testing at small scale single-machine */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/public/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /public/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; to tell Toiul how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot; toil-wdl-runner --jobStore ./big_store --batchSystem slurm --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot; toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=448</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=448"/>
		<updated>2023-12-01T16:10:49Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Show using the partitions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/public/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /public/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem --time=02:00:00 --partition=medium --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell that can run for 2 hours; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
Additionally, since [https://github.com/DataBiosphere/toil/issues/4686 Toil can't manage Slurm partitions itself], we will use the &amp;lt;code&amp;gt;TOIL_SLURM_ARGS&amp;lt;/code&amp;gt; to tell Toiul how long jobs should be allowed to run for (2 hours) and what [[Slurm Queues (Partitions) and Resource Management | partition]] they should go in.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot; toil-wdl-runner --jobStore ./big_store --batchSystem slurm --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 TOIL_SLURM_ARGS=&amp;quot;--time=02:00:00 --partition=medium&amp;quot; toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=447</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=447"/>
		<updated>2023-12-01T16:02:52Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Configuring Toil for Phoenix */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/public/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /public/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=441</id>
		<title>Slurm Tips for Toil</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Slurm_Tips_for_Toil&amp;diff=441"/>
		<updated>2023-11-20T21:54:12Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Show how to install with extras and a branch&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Here are some tips for running Toil workflows on the Phoenix Slurm cluster. Mostly you might want to run WDL workflows, but you can use some of these for other workflows like Cactus. You can also consult [https://github.com/DataBiosphere/toil/blob/master/docs/running/wdl.rst#running-wdl-with-toil the Toil documentation on WDL workflows].&lt;br /&gt;
&lt;br /&gt;
* Install Toil with WDL support with:&lt;br /&gt;
&lt;br /&gt;
 pip3 install --upgrade toil[wdl]&lt;br /&gt;
&lt;br /&gt;
To use a development version of Toil, you can install from source instead:&lt;br /&gt;
&lt;br /&gt;
 pip3 install git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl]&lt;br /&gt;
&lt;br /&gt;
Or for a particular branch:&lt;br /&gt;
&lt;br /&gt;
 pip3 install git+https://github.com/DataBiosphere/toil.git@issues/123-abc#egg=toil[wdl]&lt;br /&gt;
&lt;br /&gt;
* You will then need to make sure your '''~/.local/bin''' directory is on your PATH. Open up your '''~/.bashrc''' file and add:&lt;br /&gt;
&lt;br /&gt;
 export PATH=$PATH:$HOME/.local/bin&lt;br /&gt;
&lt;br /&gt;
Then make sure to log out and back in again.&lt;br /&gt;
&lt;br /&gt;
* For Toil options, you will want '''--batchSystem slurm''' to make it use Slurm and '''--batchLogsDir ./logs''' (or some other location on a shared filesystem) for the Slurm logs to not get lost.&lt;br /&gt;
&lt;br /&gt;
* You may be able to speed up your workflow with '''--caching true''', to cache data on nodes to be shared among multiple simultaneous tasks.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you might want to add '''--jobStore ./jobStore''' to make sure the job store is in a defined, shared location so that you can use '''--restart''' later.&lt;br /&gt;
&lt;br /&gt;
* If using '''toil-wdl-runner''', you will want to set the '''SINGULARITY_CACHEDIR''' and '''MINIWDL__SINGULARITY__IMAGE_CACHE''' environment variables for your workflow to locations on shared storage, and possibly to the default cache locations in your home directory. Otherwise Toil will set them to node-local directories for each node, and thus re-download images for each workflow run, and for each cluster node. To avoid this, you could, for example, before your run or in your '''~/.bashrc''' you could:&lt;br /&gt;
&lt;br /&gt;
 export SINGULARITY_CACHEDIR=$HOME/.singularity/cache&lt;br /&gt;
 export MINIWDL__SINGULARITY__IMAGE_CACHE=$HOME/.cache/miniwdl&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=434</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=434"/>
		<updated>2023-11-09T22:12:25Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Configuring Toil for Phoenix */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/public/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then, after logging out and in again, use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /public/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=433</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=433"/>
		<updated>2023-10-27T22:32:00Z</updated>

		<summary type="html">&lt;p&gt;Anovak: /* Configuring Toil for Phoenix */ Complain about file locking&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/public/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day].&lt;br /&gt;
&lt;br /&gt;
'''If you get errors about mutexes, lock files, or other weird problems with Singularity''', try moving these directories to inside &amp;lt;code&amp;gt;/data/tmp&amp;lt;/code&amp;gt; on the individual nodes, or unsetting them and letting Toil use its defaults (and exhaust our Docker pull limits). [https://github.com/DataBiosphere/toil/issues/4654 It is not clear that &amp;lt;code&amp;gt;/private/groups&amp;lt;/code&amp;gt; actually implements the necessary file locking correctly.]&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /public/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=426</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=426"/>
		<updated>2023-10-23T20:24:46Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Cache Singularity stuff outside the size-limited home directories.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Remember this path; we will need it later.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. However, since these files can be large, and the home directory quota is only 30 GB, we can't keep these in your home directory. We will need to use the &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt; directory you created earlier.&lt;br /&gt;
&lt;br /&gt;
Make sure that that directory is available in your &amp;lt;code&amp;gt;~/.bashrc&amp;lt;/code&amp;gt; file by editing and running this command:&lt;br /&gt;
&lt;br /&gt;
 echo 'BIG_DATA_DIR=/public/groups/YOURGROUPNAME/YOURUSERNAME' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
Then use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${BIG_DATA_DIR}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${BIG_DATA_DIR}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day], so it is important not to skip this step.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /public/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
	<entry>
		<id>http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=425</id>
		<title>Phoenix WDL Tutorial</title>
		<link rel="alternate" type="text/html" href="http://giwiki.gi.ucsc.edu/index.php?title=Phoenix_WDL_Tutorial&amp;diff=425"/>
		<updated>2023-10-20T21:21:46Z</updated>

		<summary type="html">&lt;p&gt;Anovak: Remind people where the data needs to live.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Tutorial: Getting Started with WDL Workflows on Phoenix'''&lt;br /&gt;
&lt;br /&gt;
Instead of giant shell scripts that only work on one grad student's laptop, modern, reusable bioinformatics experiments should be written as workflows, in a language like Workflow Description language (WDL). Workflows succinctly describe their own execution requirements, and which pieces depend on which other pieces, making your analyses reproducible by people other than you.&lt;br /&gt;
&lt;br /&gt;
Workflows are also easily scaled up and down: you can develop and test your workflow on a small test data set on one machine, and then run it on real data on the cluster without having to worry about whether the right tasks will run in the right order.&lt;br /&gt;
&lt;br /&gt;
This tutorial will help you get started writing and running workflows. The '''Phoenix Cluster Setup''' section is specifically for the UC Santa Cruz Genomics Institute's Phoenix Slurm cluster. The other sections are broadly applicable to other environments. By the end, you will be able to run workflows on Slurm with [https://toil.readthedocs.io/en/latest/ Toil], write your own workflows in WDL, and debug workflows when something goes wrong.&lt;br /&gt;
&lt;br /&gt;
=Phoenix Cluster Setup=&lt;br /&gt;
&lt;br /&gt;
Before we begin, you will need a computer to work at, which you are able to install software on, and the ability to connect to other machines over SSH.&lt;br /&gt;
&lt;br /&gt;
==Getting VPN access==&lt;br /&gt;
&lt;br /&gt;
We are going to work on the Phoenix cluster, but this cluster is kept behind the Prism firewall, where all of our controlled-access data lives. So, to get access to the cluster, you need to get access to the VPN (Virtual Private Network) system that we use to allow people through the firewall.&lt;br /&gt;
&lt;br /&gt;
To get VPN access, follow the instructions at https://giwiki.gi.ucsc.edu/index.php/Requirement_for_users_to_get_GI_VPN_access. Note that this process involves making a one-on-one appointment with one of our admins to help you set up your VPN client, so make sure to do it in advance of when you need to use the cluster.&lt;br /&gt;
&lt;br /&gt;
==Connecting to Phoenix==&lt;br /&gt;
&lt;br /&gt;
Once you have VPN access, you can connect to the &amp;quot;head node&amp;quot; of the Phoenix cluster. This node is where everyone logs in, but you should *not* run actual work on this node; it exists only to give you access to the files on the cluster and to the commands to control cluster jobs.&lt;br /&gt;
&lt;br /&gt;
To connect to the head node:&lt;br /&gt;
&lt;br /&gt;
1. Connect to the VPN.&lt;br /&gt;
2. SSH to &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;. At the command line, run:&lt;br /&gt;
&lt;br /&gt;
 ssh phoenix.prism&lt;br /&gt;
&lt;br /&gt;
If your username on the cluster (say, &amp;lt;code&amp;gt;flastname&amp;lt;/code&amp;gt;) is different than your username on your computer (which might be &amp;lt;code&amp;gt;firstname&amp;lt;/code&amp;gt;), you might instead have to run:&lt;br /&gt;
&lt;br /&gt;
 ssh flastname@phoenix.prism&lt;br /&gt;
&lt;br /&gt;
The first time you connect, you will see a message like:&lt;br /&gt;
&lt;br /&gt;
 The authenticity of host 'phoenix.prism (10.50.1.66)' can't be established.&lt;br /&gt;
 ED25519 key fingerprint is SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI.&lt;br /&gt;
 This key is not known by any other names&lt;br /&gt;
 Are you sure you want to continue connecting (yes/no/[fingerprint])?&lt;br /&gt;
&lt;br /&gt;
This is your computer asking you to help it decide if it is talking to the genuine &amp;lt;code&amp;gt;phoenix.prism&amp;lt;/code&amp;gt;, and not an imposter. You will want to make sure that the &amp;quot;key fingerprint&amp;quot; is indeed &amp;lt;code&amp;gt;SHA256:SUgdBXgsWwUJXxAz/BpGzlGFLOsFtZzeqQ3kzdl3iuI&amp;lt;/code&amp;gt;. If it is not, someone (probably the GI sysadmins, but possibly a cabal of hackers) has replaced the head node, and you should verify that this was supposed to happen. If the fingerprints do match, type &amp;lt;code&amp;gt;yes&amp;lt;/code&amp;gt; to accept and remember that the server is who it says it is.&lt;br /&gt;
&lt;br /&gt;
==Installing Toil with WDL support==&lt;br /&gt;
&lt;br /&gt;
Once you are on the head node, you can install Toil, a program for running workflows. When installing, you need to specify that you want WDL support. To do this, you can run:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl]'&lt;br /&gt;
&lt;br /&gt;
If you also want to use AWS S3 &amp;lt;code&amp;gt;s3://&amp;lt;/code&amp;gt; and/or Google &amp;lt;code&amp;gt;gs://&amp;lt;/code&amp;gt; URLs for data, you will need to also install Toil with the &amp;lt;code&amp;gt;aws&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;google&amp;lt;/code&amp;gt; extras, respectively:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
This will install Toil in the &amp;lt;code&amp;gt;.local&amp;lt;/code&amp;gt; directory inside your home directory, which we write as &amp;lt;code&amp;gt;~/.local&amp;lt;/code&amp;gt;. The program to run WDL workflows, &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, will be at &amp;lt;code&amp;gt;~/.local/bin/toil-wdl-runner&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
By default, the command interpreter *will not* look there, so if you type &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt;, it will complain that the command is not found. To fix this, you need to configure the command interpreter (bash) to look where Toil is installed. To do this, run:&lt;br /&gt;
&lt;br /&gt;
 echo 'export PATH=&amp;quot;${HOME}/.local/bin:${PATH}&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, **log out and log back in**, to restart bash and pick up the change.&lt;br /&gt;
&lt;br /&gt;
To make sure it worked, you can run:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner --help&lt;br /&gt;
&lt;br /&gt;
If everything worked correctly, it will print a long list of the various option flags that the &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; command supports.&lt;br /&gt;
&lt;br /&gt;
If you ever want to upgrade Toil to a new release, you can repeat the &amp;lt;code&amp;gt;pip&amp;lt;/code&amp;gt; command above.&lt;br /&gt;
&lt;br /&gt;
==Configuring Toil for Phoenix==&lt;br /&gt;
&lt;br /&gt;
Toil is set up to work in a large number of different environments, and doesn't necessarily rely on the existence of things like a shared cluster filesystem. However, on the Phoenix cluster, we have a shared filesystem, and so we should configure Toil to use it for caching the Docker container images used for running workflow steps. So, use these commands to make sure that Toil knows where it ought to put its caches:&lt;br /&gt;
&lt;br /&gt;
 echo 'export SINGULARITY_CACHEDIR=&amp;quot;${HOME}/.singularity/cache&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
 echo 'export MINIWDL__SINGULARITY__IMAGE_CACHE=&amp;quot;${HOME}/.cache/miniwdl&amp;quot;' &amp;gt;&amp;gt;~/.bashrc&lt;br /&gt;
&lt;br /&gt;
After that, '''log out and log back in again''', to apply the changes.&lt;br /&gt;
&lt;br /&gt;
If you don't do this, Toil will re-download each container image, on each node, for each run of each workflow. That wastes a lot of time, and can exhaust the [https://docs.docker.com/docker-hub/download-rate-limit/#whats-the-download-rate-limit-on-docker-hub limits on how many containers you are allowed to download each day], so it is important not to skip this step.&lt;br /&gt;
&lt;br /&gt;
==Configuring your Phoenix Environment==&lt;br /&gt;
&lt;br /&gt;
'''Do not try and store data in your home directory on Phoenix!''' The home directories are meant for code and programs. Any data worth running a workflow on should be in a directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. You will probably need to email the admins to get added to a group so you can create a directory to work in somewhere under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;. Usually you would end up with &amp;lt;code&amp;gt;/public/groups/YOURGROUPNAME/YOURUSERNAME&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=Running an existing workflow=&lt;br /&gt;
&lt;br /&gt;
First, let's use &amp;lt;code&amp;gt;toil-wdl-runner&amp;lt;/code&amp;gt; to run an existing demonstration workflow. We're going to use the MiniWDL self-test workflow, from the [https://github.com/chanzuckerberg/miniwdl#readme MiniWDL project].&lt;br /&gt;
&lt;br /&gt;
First, go to your user directory under &amp;lt;code&amp;gt;/public/groups&amp;lt;/code&amp;gt;, and make a directory to work in.&lt;br /&gt;
&lt;br /&gt;
 cd /public/groups/YOURGROUPNAME/YOURUSERNAME&lt;br /&gt;
 mkdir workflow-test&lt;br /&gt;
 cd workflow-test&lt;br /&gt;
&lt;br /&gt;
Next, download the workflow. While Toil can run workflows directly from a URL, your commands will be shorter if the workflow is available locally.&lt;br /&gt;
&lt;br /&gt;
 wget https://raw.githubusercontent.com/DataBiosphere/toil/d686daca091849e681d2f3f3a349001ca83d2e3e/src/toil/test/wdl/miniwdl_self_test/self_test.wdl&lt;br /&gt;
&lt;br /&gt;
==Preparing an input file==&lt;br /&gt;
&lt;br /&gt;
Near the top of the WDL file, there's a section like this:&lt;br /&gt;
&lt;br /&gt;
 workflow hello_caller {&lt;br /&gt;
     input {&lt;br /&gt;
         File who&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
This means that there is a workflow named &amp;lt;code&amp;gt;hello_caller&amp;lt;/code&amp;gt; in this file, and it takes as input a file variable named &amp;lt;code&amp;gt;who&amp;lt;/code&amp;gt;. For this particular workflow, the file is supposed to have a list of names, one per line, and the workflow is going to greet each one.&lt;br /&gt;
&lt;br /&gt;
So first, we have to make that list of names. Let's make it in &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 echo &amp;quot;Mridula Resurrección&amp;quot; &amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Gershom Šarlota&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
 echo &amp;quot;Ritchie Ravi&amp;quot; &amp;gt;&amp;gt;names.txt&lt;br /&gt;
&lt;br /&gt;
Then, we need to create an *inputs file*, which is a JSON (JavaScript Object Notation) file describing what value to use for each input when running the workflow. (You can also reach down into the workflow and override individual task settings, but for now we'll just set the inputs.) So, make another file next to &amp;lt;code&amp;gt;names.txt&amp;lt;/code&amp;gt; that references it by relative path, like this:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./names.txt&amp;quot;}' &amp;gt;inputs.json&lt;br /&gt;
&lt;br /&gt;
Note that, for a key, we're using the workflow name, a dot, and then the input name. For a value, we're using a quoted string of the filename, relative to the location of the inputs file. Absolute paths and URLs will also work for files; more information on the input file syntax is in [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md#json-input-format the JSON Input Format section of the WDL specification].&lt;br /&gt;
&lt;br /&gt;
==Testing at small scale single-machine==&lt;br /&gt;
&lt;br /&gt;
We are now ready to run the workflow!&lt;br /&gt;
&lt;br /&gt;
You don't want to run workflows on the head node. So, use Slurm to get an interactive session on one of the cluster's worker nodes, by running:&lt;br /&gt;
&lt;br /&gt;
 srun -c 2 --mem 8G --pty bash -i&lt;br /&gt;
&lt;br /&gt;
This will start a new shell; to leave it and go back to the head node you can use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In your new shell, run this Toil command:&lt;br /&gt;
&lt;br /&gt;
 toil-wdl-runner self_test.wdl inputs.json -o local_run&lt;br /&gt;
&lt;br /&gt;
This will, by default, use the &amp;lt;code&amp;gt;single_machine&amp;lt;/code&amp;gt; Toil &amp;quot;batch system&amp;quot; to run all of the workflow's tasks locally. Output will be sent to a new directory named &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This will print a lot of logging to standard error, and to standard output it will print:&lt;br /&gt;
&lt;br /&gt;
 {&amp;quot;hello_caller.message_files&amp;quot;: [&amp;quot;local_run/Mridula Resurrecci\u00f3n.txt&amp;quot;, &amp;quot;local_run/Gershom \u0160arlota.txt&amp;quot;, &amp;quot;local_run/Ritchie Ravi.txt&amp;quot;], &amp;quot;hello_caller.messages&amp;quot;: [&amp;quot;Hello, Mridula Resurrecci\u00f3n!&amp;quot;, &amp;quot;Hello, Gershom \u0160arlota!&amp;quot;, &amp;quot;Hello, Ritchie Ravi!&amp;quot;]}&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;local_run&amp;lt;/code&amp;gt; directory will contain the described text files (with Unicode escape sequences like &amp;lt;code&amp;gt;\u00f3&amp;lt;/code&amp;gt; replaced by their corresponding characters), each containing a greeting for the corresponding person.&lt;br /&gt;
&lt;br /&gt;
To leave your interactive Slurm session and return to the head node, use &amp;lt;code&amp;gt;exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Running at larger scale==&lt;br /&gt;
&lt;br /&gt;
Back on the head node, let's prepare to run a larger run. Greeting 3 people isn't cool, let's greet one hundred people!&lt;br /&gt;
&lt;br /&gt;
Go get this handy list of people and cut it to length:&lt;br /&gt;
&lt;br /&gt;
 wget https://gist.githubusercontent.com/smsohan/ae142977b5099dba03f6e0d909108e97/raw/f6e319b1a0f6a0f87f93f73b3acd24795361aeba/1000_names.txt&lt;br /&gt;
 head -n100 1000_names.txt &amp;gt;100_names.txt&lt;br /&gt;
&lt;br /&gt;
And make a new inputs file:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;hello_caller.who&amp;quot;: &amp;quot;./100_names.txt&amp;quot;}' &amp;gt;inputs_big.json&lt;br /&gt;
&lt;br /&gt;
Now, we will run the same workflow, but with the new inputs, and against the Slurm cluster.&lt;br /&gt;
&lt;br /&gt;
To run against the Slurm cluster, we need to use the &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; option to point Toil to a shared directory it can create where it can store information that the cluster nodes can read. We will add the &amp;lt;code&amp;gt;--batchLogsDir&amp;lt;/code&amp;gt; option to tell Toil to store the logs from the individual Slurm jobs in a folder on the shared filesystem. We'll also use the &amp;lt;code&amp;gt;-m&amp;lt;/code&amp;gt; option to save the output JSON to a file instead of printing it.&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./big_store --batchSystem slurm --batchLogsDir ./logs self_test.wdl inputs_big.json -o slurm_run -m slurm_run.json&lt;br /&gt;
&lt;br /&gt;
This will tick for a while, but eventually you should end up with 100 greeting files in the &amp;lt;code&amp;gt;slurm_run&amp;lt;/code&amp;gt; directory.&lt;br /&gt;
&lt;br /&gt;
=Writing your own workflow=&lt;br /&gt;
&lt;br /&gt;
In addition to running existing workflows, you probably want to be able to write your own. This part of the tutorial will walk you through writing a workflow. We're going to write a workflow for [https://en.wikipedia.org/wiki/Fizz_buzz Fizz Buzz]. &lt;br /&gt;
&lt;br /&gt;
==Writing the file==&lt;br /&gt;
&lt;br /&gt;
===Version===&lt;br /&gt;
&lt;br /&gt;
All WDL files need to start with a &amp;lt;code&amp;gt;version&amp;lt;/code&amp;gt; statement (unless they are very old &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; files). Toil supports &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt;, WDL 1.0, and WDL 1.1, while Cromwell (another popular WDL runner used on Terra) supports only &amp;lt;code&amp;gt;draft-2&amp;lt;/code&amp;gt; and 1.0.&lt;br /&gt;
&lt;br /&gt;
So let's start a new WDL 1.0 workflow. Open up a file named &amp;lt;code&amp;gt;fizzbuzz.wdl&amp;lt;/code&amp;gt; and start with a version statement:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
&lt;br /&gt;
===Workflow Block===&lt;br /&gt;
&lt;br /&gt;
Then, add an empty &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; named &amp;lt;code&amp;gt;FizzBuzz&amp;lt;/code&amp;gt;.&lt;br /&gt;
 &lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Input Block===&lt;br /&gt;
&lt;br /&gt;
Workflows usually need some kind of user input, so let's give our workflow an &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; section.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Notice that each input has a type, a name, and an optional default value. If the type ends in &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;, the value is optional, and it may be &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. If an input is ''not'' optional, and there is no default value, then the user's inputs file ''must'' specify a value for it in order for the workflow to run.&lt;br /&gt;
&lt;br /&gt;
===Body===&lt;br /&gt;
&lt;br /&gt;
Now we'll start on the body of the workflow, to be inserted just after the inputs section. &lt;br /&gt;
&lt;br /&gt;
The first thing we're going to need to do is create an array of all the numbers up to the &amp;lt;code&amp;gt;item_count&amp;lt;/code&amp;gt;. We can do this by calling the WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; function, and assigning the result to an &amp;lt;code&amp;gt;Array[Int]&amp;lt;/code&amp;gt; variable.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
&lt;br /&gt;
WDL 1.0 has [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#standard-library a wide variety of functions in its standard library], and WDL 1.1 has even more.&lt;br /&gt;
&lt;br /&gt;
===Scattering===&lt;br /&gt;
&lt;br /&gt;
Once we create an array of all the numbers, we can use a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt; to operate on each. WDL does not have loops; instead it has scatters, which work a bit like a &amp;lt;code&amp;gt;map()&amp;lt;/code&amp;gt; in Python. The body of the scatter runs for each value in the input array, all in parallel. We're going to increment all the numbers, since FizzBuzz starts at 1 but WDL &amp;lt;code&amp;gt;range()&amp;lt;/code&amp;gt; starts at 0.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
     Int one_based = i + 1&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Conditionals===&lt;br /&gt;
&lt;br /&gt;
Inside the body of the scatter, we are going to put some conditionals to determine if we should produce &amp;lt;code&amp;gt;&amp;quot;Fizz&amp;quot;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;quot;Buzz&amp;quot;&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;&amp;quot;FizzBuzz&amp;quot;&amp;lt;/code&amp;gt;. To support our &amp;lt;code&amp;gt;fizzbuzz_override&amp;lt;/code&amp;gt;, we use an array of it and a default value, and use the WDL &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; function to find the first non-null value in that array.&lt;br /&gt;
&lt;br /&gt;
Each execution of a scatter is allowed to declare variables, and outside the scatter those variables are combined into arrays of all the results. But each variable can be declared only ''once'' in the scatter, even with conditionals. So we're going to use &amp;lt;code&amp;gt;select_first()&amp;lt;/code&amp;gt; at the end and take advantage of variables from un-executed conditionals being &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Note that WDL supports conditional ''expressions'' with a &amp;lt;code&amp;gt;then&amp;lt;/code&amp;gt; and an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt;, but conditional ''statements'' only have a body, not an &amp;lt;code&amp;gt;else&amp;lt;/code&amp;gt; branch. If you need an else you will have to check the negated condition.&lt;br /&gt;
&lt;br /&gt;
So first, let's handle the special cases.&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
    }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Calling Tasks===&lt;br /&gt;
&lt;br /&gt;
Now for the normal numbers, we need to convert our number into a string. In WDL 1.1, and in WDL 1.0 on Cromwell, you can use a &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt; substitution syntax in quoted strings anywhere, not just in command line commands. Toil technically will support this too, but it's not in the spec, and the tutorial needs an excuse for you to call a task. So we're going to insert a call to a &amp;lt;code&amp;gt;stringify_number&amp;lt;/code&amp;gt; task, to be written later.&lt;br /&gt;
&lt;br /&gt;
To call a task (or another workflow), we use a &amp;lt;code&amp;gt;call&amp;lt;/code&amp;gt; statement and give it some inputs. Then we can fish the output values out of the task with &amp;lt;/code&amp;gt;.&amp;lt;/code&amp;gt; access, only if we don't make a noise instead.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 Array[Int] numbers = range(item_count)&lt;br /&gt;
 scatter (i in numbers) {&lt;br /&gt;
    Int one_based = i + 1&lt;br /&gt;
    if (one_based % to_fizz == 0) {&lt;br /&gt;
        String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
        if (one_based % to_buzz == 0) {&lt;br /&gt;
            String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_buzz == 0) {&lt;br /&gt;
        String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
        # Just a normal number.&lt;br /&gt;
        call stringify_number {&lt;br /&gt;
            input:&lt;br /&gt;
                the_number = one_based&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string])&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We can put the code into the workflow now, and set about writing the task.&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         &lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Writing Tasks===&lt;br /&gt;
&lt;br /&gt;
Our task should go after the workflow in the file. It looks a lot like a workflow except it uses &amp;lt;code&amp;gt;task&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're going to want it to take in an integer &amp;lt;code&amp;gt;the_number&amp;lt;/code&amp;gt;, and we're going to want it to output a string &amp;lt;code&amp;gt;the_string&amp;lt;/code&amp;gt;. So let's fill that in in &amp;lt;code&amp;gt;input&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     # ???&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now, unlike workflows, tasks can have a &amp;lt;code&amp;gt;command&amp;lt;/code&amp;gt; section, which gives a command to run. This section is now usually set off with triple angle brackets, and inside it you can use &amp;lt;code&amp;gt;~{}&amp;lt;/code&amp;gt;, that is, Bash-like substitution but with a tilde, to place WDL variables into your command script. So let's add a command that will echo back the number so we can see it as a string.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string # = ???&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Now we need to capture the result of the command script. The WDL &amp;lt;code&amp;gt;stdout()&amp;lt;/code&amp;gt; returns a WDL &amp;lt;code&amp;gt;File&amp;lt;/code&amp;gt; containing the standard output printed by the task's command. We want to read that back into a string, which we can do with the WDL &amp;lt;code&amp;gt;read_string()&amp;lt;/code&amp;gt; function (which also [https://github.com/openwdl/wdl/blob/main/versions/1.0/SPEC.md#string-read_stringstringfile removes trailing newlines]).&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
        # This is a Bash script.&lt;br /&gt;
        # So we should do good Bash script things like stop on errors&lt;br /&gt;
        set -e&lt;br /&gt;
        # Now print our number as a string&lt;br /&gt;
        echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
We're also going to want to add a &amp;lt;code&amp;gt;runtime&amp;lt;/code&amp;gt; section to our task, to specify resource requirements. We're also going to tell it to run in a Docker container, to make sure that absolutely nothing can go wrong with our delicate &amp;lt;code&amp;gt;echo&amp;lt;/code&amp;gt; command. In a real workflow, you probably want to set up optiopnal inputs for all the tasks to let you control the resource requirements, but here we will just hardcode them.&lt;br /&gt;
&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;disks&amp;lt;/code&amp;gt; section is a little weird; it isn't in the WDL spec, but Toil supports Cromwell-style strings that ask for a &amp;lt;code&amp;gt;local-disk&amp;lt;/code&amp;gt; of a certain number of gigabytes, which may suggest that it be &amp;lt;code&amp;gt;SSD&amp;lt;/code&amp;gt; storage.&lt;br /&gt;
&lt;br /&gt;
Then we can put our task into our WDL file:&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
===Output Block===&lt;br /&gt;
&lt;br /&gt;
Now the only thing missing is a workflow-level &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section. Technically, in WDL 1.0 you aren't supposed to need this, but you do need it in 1.1 and Toil doesn't actually send your outputs anywhere yet if you don't have one, so we're going to make one. We need to collect together all the strings that came out of the different tasks in our scatter into an &amp;lt;code&amp;gt;Array[String]&amp;lt;/code&amp;gt;. We'll add the &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; section at the end of the &amp;lt;code&amp;gt;workflow&amp;lt;/code&amp;gt; section, above the task.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 version 1.0&lt;br /&gt;
 workflow FizzBuzz {&lt;br /&gt;
     input {&lt;br /&gt;
         # How many FizzBuzz numbers do we want to make?&lt;br /&gt;
         Int item_count&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Fizz&amp;quot;&lt;br /&gt;
         Int to_fizz = 3&lt;br /&gt;
         # Every multiple of this number, we produce &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         Int to_buzz = 5&lt;br /&gt;
         # Optional replacement for the string to print when a multiple of both&lt;br /&gt;
         String? fizzbuzz_override&lt;br /&gt;
     }&lt;br /&gt;
     Array[Int] numbers = range(item_count)&lt;br /&gt;
     scatter (i in numbers) {&lt;br /&gt;
         Int one_based = i + 1&lt;br /&gt;
         if (one_based % to_fizz == 0) {&lt;br /&gt;
             String fizz = &amp;quot;Fizz&amp;quot;&lt;br /&gt;
             if (one_based % to_buzz == 0) {&lt;br /&gt;
                 String fizzbuzz = select_first([fizzbuzz_override, &amp;quot;FizzBuzz&amp;quot;])&lt;br /&gt;
             }&lt;br /&gt;
          }&lt;br /&gt;
         if (one_based % to_buzz == 0) {&lt;br /&gt;
             String buzz = &amp;quot;Buzz&amp;quot;&lt;br /&gt;
         }&lt;br /&gt;
         if (one_based % to_fizz != 0 &amp;amp;&amp;amp; one_based % to_buzz != 0) {&lt;br /&gt;
             # Just a normal number.&lt;br /&gt;
             call stringify_number {&lt;br /&gt;
                 input:&lt;br /&gt;
                     the_number = one_based&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
         String result = select_first([fizzbuzz, fizz, buzz, stringify_number.the_string]&lt;br /&gt;
     }&lt;br /&gt;
     output {&lt;br /&gt;
        Array[String] fizzbuzz_results = result&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 task stringify_number {&lt;br /&gt;
     input {&lt;br /&gt;
         Int the_number&lt;br /&gt;
     }&lt;br /&gt;
     command &amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
         # This is a Bash script.&lt;br /&gt;
         # So we should do good Bash script things like stop on errors&lt;br /&gt;
         set -e&lt;br /&gt;
         # Now print our number as a string&lt;br /&gt;
         echo ~{the_number}&lt;br /&gt;
     &amp;gt;&amp;gt;&amp;gt;&lt;br /&gt;
     output {&lt;br /&gt;
         String the_string = read_string(stdout())&lt;br /&gt;
     }&lt;br /&gt;
     runtime {&lt;br /&gt;
         cpu: 1&lt;br /&gt;
         memory: &amp;quot;0.5 GB&amp;quot;&lt;br /&gt;
         disks: &amp;quot;local-disk 1 SSD&amp;quot;&lt;br /&gt;
         docker: &amp;quot;ubuntu:22.04&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Because the &amp;lt;code&amp;gt;result&amp;lt;/code&amp;gt; variable is defined inside a &amp;lt;code&amp;gt;scatter&amp;lt;/code&amp;gt;, when we reference it outside the scatter we see it as being an array.&lt;br /&gt;
&lt;br /&gt;
==Running the Workflow==&lt;br /&gt;
&lt;br /&gt;
Now all that remains is to run the workflow! As before, make an inputs file to specify the workflow inputs:&lt;br /&gt;
&lt;br /&gt;
 echo '{&amp;quot;FizzBuzz.item_count&amp;quot;: 20}' &amp;gt;fizzbuzz.json&lt;br /&gt;
&lt;br /&gt;
Then run it on the cluster with Toil:&lt;br /&gt;
&lt;br /&gt;
 mkdir -p logs&lt;br /&gt;
 toil-wdl-runner --jobStore ./fizzbuzz_store --batchSystem slurm --batchLogsDir ./logs fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
Or locally:&lt;br /&gt;
&lt;br /&gt;
    toil-wdl-runner fizzbuzz.wdl fizzbuzz.json -o fizzbuzz_out -m fizzbuzz_out.json&lt;br /&gt;
&lt;br /&gt;
=Debugging Workflows=&lt;br /&gt;
Sometimes, your workflow won't work. Try these ideas for figuring out what is going wrong.&lt;br /&gt;
&lt;br /&gt;
==Debugging Options==&lt;br /&gt;
When debugging a workflow, make sure to run the workflow with &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt;, to set the log level to &amp;lt;code&amp;gt;DEBUG&amp;lt;/code&amp;gt;, and with &amp;lt;code&amp;gt;--jobStore /some/path/to/a/shared/directory/it/can/create&amp;lt;/code&amp;gt; so that the stored files shipped between jobs are in a place you can access them.&lt;br /&gt;
&lt;br /&gt;
When debug logging is on, the log from every Toil job is inserted in the main Toil log between these markers:&lt;br /&gt;
&lt;br /&gt;
 =========&amp;gt;&lt;br /&gt;
         Toil job log is here&lt;br /&gt;
 &amp;lt;=========&lt;br /&gt;
&lt;br /&gt;
Normally, only the logs of failing jobs are reproduced like this.&lt;br /&gt;
&lt;br /&gt;
==Reading the Log==&lt;br /&gt;
&lt;br /&gt;
When a WDL workflow fails, you are likely to see a message like this:&lt;br /&gt;
&lt;br /&gt;
 WDL.runtime.error.CommandFailed: task command failed with exit status 1&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [E] [toil.worker] Exiting the worker because of a failed job on host phoenix-15.prism&lt;br /&gt;
&lt;br /&gt;
This means that the command line command specified by one of your WDL tasks exited with a failing (i.e. nonzero) exit code, which will happen when either the command line command is written wrong, or when the error detection code in the tool you are trying to run detects and reports an error.&lt;br /&gt;
&lt;br /&gt;
Go up higher in the log until you find lines that look like:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard error at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stderr.txt:&lt;br /&gt;
&lt;br /&gt;
And&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [I] [toil.wdl.wdltoil] Standard output at /data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/stdout.txt:&lt;br /&gt;
&lt;br /&gt;
These will be followed by the standard error and standard output log data from the task's command. There may be useful information (such as an error message from the underlying tool) in there.&lt;br /&gt;
&lt;br /&gt;
==Reproducing Problems==&lt;br /&gt;
&lt;br /&gt;
When trying to fix a failing step, it is useful to be able to run a command outside of Toil or WDL that might reproduce the problem. In addition to getting the standard output and standard error logs as described above, you may also need input files for your tool in order to do this. In the log of your failing Toil taks, look for likes like this:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Failed job accessed files:&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/no-job/file-4f886176ab8344baaf17dc72fc445445/toplog.sh' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmprwhi6h3q/toplog.sh'&lt;br /&gt;
 [2023-07-16T16:23:54-0700] [MainThread] [W] [toil.fileStores.abstractFileStore] Downloaded file 'files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam' to path '/data/tmp/c3d51c0611b9511da167528976fef714/9b0e/467f/tmpjyksfoko/Sample.bam'&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&amp;lt;/code&amp;gt; part is a Toil file ID, and it is a relative path from your &amp;lt;code&amp;gt;--jobStore&amp;lt;/code&amp;gt; value to where the file is stored on disk. So if you ran the workflow with &amp;lt;code&amp;gt;--jobStore /private/groups/patenlab/anovak/jobstore&amp;lt;/code&amp;gt;, you would look for this file at:&lt;br /&gt;
&lt;br /&gt;
 /private/groups/patenlab/anovak/jobstore/files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-1bb5d92ae8f3413eb82fe8ef88686bf6/Sample.bam&lt;br /&gt;
&lt;br /&gt;
==More Ways of Finding Files==&lt;br /&gt;
&lt;br /&gt;
Sometimes, a step might not fail, but you still might want to see the files it is using as input. If you have the job store path, you can use the &amp;lt;code&amp;gt;find&amp;lt;/code&amp;gt; command to try and find the files by name. For example, if you want to look at &amp;lt;code&amp;gt;Sample.bam&amp;lt;/code&amp;gt;, you can look for it like this:&lt;br /&gt;
&lt;br /&gt;
 find /path/to/the/jobstore -name &amp;quot;Sample.bam&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If you want to find files that were ''uploaded'' from a job, look for lines like this in the job's log:&lt;br /&gt;
&lt;br /&gt;
 [2023-07-16T15:58:39-0700] [MainThread] [D] [toil.wdl.wdltoil] Virtualized /data/tmp/2846b6012e3e5535add03b363950dd78/cb23/197c/work/bamPerChrs/Sample.chr14.bam as WDL file toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
You can take the &amp;lt;code&amp;gt;toilfile:2703483274%3A0%3Afiles%2Ffor-job%2Fkind-WDLTaskJob%2Finstance-b4c5x6hq%2Ffile-c4e4f1b16ddf4c2ab92c2868421f3351%2FSample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt; URI and URL-decode it with, for example, [https://www.urldecoder.io/], getting this:&lt;br /&gt;
&lt;br /&gt;
 toilfile:2703483274:0:files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&lt;br /&gt;
&lt;br /&gt;
Then you can take the part after the last colon, &amp;lt;code&amp;gt;files/for-job/kind-WDLTaskJob/instance-b4c5x6hq/file-c4e4f1b16ddf4c2ab92c2868421f3351/Sample.chr14.bam/Sample.chr14.bam&amp;lt;/code&amp;gt;, and that is the path relative to the job store where this file can be found.&lt;br /&gt;
&lt;br /&gt;
==Using Development Versions of Toil==&lt;br /&gt;
&lt;br /&gt;
Sometimes, bugs will be fixed in the development version of Toil, but not released yet. To try the current development version of Toil, you can install it like this:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
If you want to use a particular branch or commit, like &amp;lt;code&amp;gt;aaa451b320fc115b3563ced25cb501301cf86f90&amp;lt;/code&amp;gt;, you can do:&lt;br /&gt;
&lt;br /&gt;
 pip install --upgrade --user 'git+https://github.com/DataBiosphere/toil.git@aaa451b320fc115b3563ced25cb501301cf86f90#egg=toil[wdl,aws,google]'&lt;br /&gt;
&lt;br /&gt;
==Frequently Asked Questions==&lt;br /&gt;
&lt;br /&gt;
===I am getting warnings about &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt;===&lt;br /&gt;
You may be seeing warnings that &amp;lt;code&amp;gt;XDG_RUNTIME_DIR is set to nonexistent directory /run/user/$UID; your environment may be out of spec!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This happens because Slurm is providing Toil with an &amp;lt;code&amp;gt;XDG_RUNTIME_DIR&amp;lt;/code&amp;gt; environment variable that points to a directory that doesn't exist, which the XDG spec says it shouldn't be doing. This is a known bug in the GI Slurm configuration, and Toil is letting you know that it is working around it.&lt;br /&gt;
&lt;br /&gt;
===Toil said it was &amp;lt;code&amp;gt;Redirecting logging&amp;lt;/code&amp;gt; somewhere, but I can't find that file!===&lt;br /&gt;
The Toil worker process for each job will say that it is &amp;lt;code&amp;gt;Redirecting logging to /data/tmp/somewhere/worker_log.txt&amp;lt;/code&amp;gt;, and when running in single machine mode these messages go to the main Toil log.&lt;br /&gt;
&lt;br /&gt;
The Toil worker logs are automatically cleaned up when the worker finishes. If you want to see the individual worker logs in the Toil log, use the &amp;lt;code&amp;gt;--logDebug&amp;lt;/code&amp;gt; option to Toil.&lt;br /&gt;
&lt;br /&gt;
If you are looking for the log for a worker process that did not finish (i.e. that crashed), make sure to look on the machine that the worker actually ran on, not on the head node.&lt;br /&gt;
&lt;br /&gt;
=Additional WDL resources=&lt;br /&gt;
&lt;br /&gt;
For more information on writing and running WDL workflows, see:&lt;br /&gt;
&lt;br /&gt;
* [https://docs.openwdl.org/en/stable/ The WDL dcoumentation]&lt;br /&gt;
* [https://www.youtube.com/playlist?list=PL4Q4HssKcxYv5syJKUKRrD8Fbd-_CnxTM The &amp;quot;Learn WDL&amp;quot; video course on YouTube]&lt;br /&gt;
* [https://github.com/openwdl/wdl/blob/main/versions/1.1/SPEC.md The WDL 1.1 language specification]&lt;/div&gt;</summary>
		<author><name>Anovak</name></author>
	</entry>
</feed>