Add more background to lesson 1.
[rnaseq-cwl-training.git] / lesson5 / lesson5.md
1 # Dynamic Workflow behavior with expressions
2
3 ### 1. Expressions on step inputs
4
5 You might have noticed that the output bam files are all named
6 `Aligned.sortedByCoord.out.bam`.  This happens because because when we
7 call STAR, it gives the output a default file name.
8
9 During workflow execution, this is usually not a problem.  The
10 workflow runner is smart enough to know that these files are different
11 and keep them separate.  This can even make development easier by not
12 having to worry about assigning unique file names to every file.
13
14 However, it is a problem for humans interpreting the output.  We can
15 fix this by setting the parameter `OutFileNamePrefix` on STAR.  We
16 want the output filename to be based on the input filename.
17
18 In `alignment.cwl`, we can use `valueFrom` on the `OutFileNamePrefix`
19 input parameter to construct the output prefix from the input
20 filename.
21
22 ```
23 requirements:
24   StepInputExpressionRequirement: {}
25
26 steps:
27   ...
28   STAR:
29     ...
30     in:
31       ForwardReads: fq
32       ...
33       OutFileNamePrefix: {valueFrom: "$(inputs.ForwardReads.nameroot)."}
34 ```
35
36 The code between `$(...)` is called an "expression".  It is evaluated
37 when setting up the step to run, and the expression is replaced by the
38 result to get the parameter value.
39
40 An expression can refer to other inputs to the step that are either
41 directly connected to another value, or have a default value.  Here,
42 we refer to the input parameter ForwardReads, which is our fastq input
43 file.
44
45 ForwardReads is a File object, not a plain file name, so it has some
46 fields of its own.  The file object has a number of fields that we can
47 use.  These include `basename` (the name of the file, without a
48 directory), `size` (file size, in bytes), `nameext` (the last file
49 extension) and `nameroot` (the name with `nameext` removed).  Using
50
51 Finally, our expression is embedded in a string, so after replacing
52 the expression with the value of `inputs.ForwardReads.nameroot`, it
53 adds the remainder of the string, which just is a dot `.`.  This is to
54 separate the leading part of our filename from the "Aligned.bam"
55 extension that will be added by STAR.
56
57 ### 2. Organizing output files into Directories
58
59 You probably noticed that all the output files appear in the same
60 directory.  You might prefer that each file appears in its own
61 directory.  This will show you how to do that.
62
63 Unlike shell scripts, in CWL you cannot call `mkdir` and `mv` to
64 organize files into directories.  This is because the output files, at
65 this point, do not actually exist together in one directory.  They may
66 exist on different nodes, in different directories, or different cloud
67 buckets.  In CWL, instead of moving files around directly, you tell
68 the runner you want your directory to look like, and it will create it
69 for you.
70
71 We can use an "expression" to create a `Directory` object describing
72 each of our directories.  An expression is a piece of Javascript code
73 which is executed by the workflow runner to produce values that affect
74 the workflow.  These can be a simple as substituting the value of an
75 input variable, or as complex as en entire function that generates new
76 objects.
77
78 Javscript code must be bracketed inside `$(...)` or `${...}`. The
79 difference comes down to syntax.  The `$()` form is more compact but
80 can only include code that can appear on the right side of an
81 assignment (`=`), which cannot include control blocks like `if` or
82 `for`.  The `${}` form is a Javascript function, which can include
83 control blocks, and must end in a `return` statement.
84
85 Dxpressions can both appear in `valueFrom` fields as well as some
86 other fields, or in an `ExpressionTool` which, like `Workflow` or
87 `CommandLineTool` has explicitly defined `inputs` and `outputs`
88 sections.
89
90 The approach here is to define an expression tool which takes a
91
92 The `Directory` object has two fields, `basename` and `listing`.  The
93 `basename` is the name of the directory, and the `listing` is the
94 contents, which consists of other File and Directory objects.
95
96 Create `subdirs.cwl`:
97
98 ```
99 cwlVersion: v1.2
100 class: ExpressionTool
101 requirements:
102   InlineJavascriptRequirement: {}
103 inputs:
104   fq: File[]
105   bams: File[]
106   qc: File[]
107 outputs:
108   dirs: Directory[]
109 expression: |-
110   ${
111   var dirs = [];
112   for (var i = 0; i < inputs.bams.length; i++) {
113     dirs.push({
114       "class": "Directory",
115       "basename": inputs.fq[i].nameroot,
116       "listing": [inputs.bams[i], inputs.qc[i]]
117     });
118   }
119   return {"dirs": dirs};
120   }
121 ```
122
123 Then change `main.cwl`:
124
125 ```
126 steps:
127   ...
128   output-subdirs:
129     run: subdirs.cwl
130     in:
131       fq: fq
132       bams: alignment/bam_sorted_indexed
133       qc: alignment/qc_html
134     out: [dirs]
135 outputs:
136   dirs:
137     type: Directory[]
138     outputSource: output-subdirs/dirs
139   featurecounts:
140     type: File
141     outputSource: featureCounts/featurecounts
142 ```