Merge branch 'crunch-job_finds_newer_docker_hashes' of https://github.com/tmooney...
[arvados.git] / doc / user / cwl / cwl-style.html.textile.liquid
1 ---
2 layout: default
3 navsection: userguide
4 title: Best Practices for writing CWL
5 ...
6
7 * Build a reusable library of components.  Share tool wrappers and subworkflows between projects.  Make use of and contribute to "community maintained workflows and tools":https://github.com/common-workflow-language/workflows and tool registries such as "Dockstore":http://dockstore.org .
8
9 * When combining a parameter value with a string, such as adding a filename extension, write @$(inputs.file.basename).ext@ instead of @$(inputs.file.basename + 'ext')@.  The first form is evaluated as a simple text substitution, the second form (using the @+@ operator) is evaluated as an arbitrary Javascript expression and requires that you declare @InlineJavascriptRequirement@.
10
11 * Avoid declaring @InlineJavascriptRequirement@ or @ShellCommandRequirement@ unless you specifically need them.  Don't include them "just in case" because they change the default behavior and may imply extra overhead.
12
13 * Don't write CWL scripts that access the Arvados SDK.  This is non-portable; a script that access Arvados directly won't work with @cwltool@ or crunch v2.
14
15 * CommandLineTools wrapping custom scripts should represent the script as an input parameter with the script file as a default value.  Use @secondaryFiles@ for scripts that consist of multiple files.  For example:
16
17 <pre>
18 cwlVersion: v1.0
19 class: CommandLineTool
20 baseCommand: python
21 inputs:
22   script:
23     type: File
24     inputBinding: {position: 1}
25     default:
26       class: File
27       location: bclfastq.py
28       secondaryFiles:
29         - class: File
30           location: helper1.py
31         - class: File
32           location: helper2.py
33   inputfile:
34     type: File
35     inputBinding: {position: 2}
36 outputs:
37   out:
38     type: File
39     outputBinding:
40       glob: "*.fastq"
41 </pre>
42
43 * You can get the designated temporary directory using @$(runtime.tmpdir)@ in your CWL file, or from the @$TMPDIR@ environment variable in your script.
44
45 * Similarly, you can get the designated output directory using $(runtime.outdir), or from the @HOME@ environment variable in your script.
46
47 * Use @ExpressionTool@ to efficiently rearrange input files between steps of a Workflow.  For example, the following expression accepts a directory containing files paired by @_R1_@ and @_R2_@ and produces an array of Directories containing each pair.
48
49 <pre>
50 class: ExpressionTool
51 cwlVersion: v1.0
52 inputs:
53   inputdir: Directory
54 outputs:
55   out: Directory[]
56 requirements:
57   InlineJavascriptRequirement: {}
58 expression: |
59   ${
60     var samples = {};
61     for (var i = 0; i < inputs.inputdir.listing.length; i++) {
62       var file = inputs.inputdir.listing[i];
63       var groups = file.basename.match(/^(.+)(_R[12]_)(.+)$/);
64       if (groups) {
65         if (!samples[groups[1]]) {
66           samples[groups[1]] = [];
67         }
68         samples[groups[1]].push(file);
69       }
70     }
71     var dirs = [];
72     for (var key in samples) {
73       dirs.push({"class": "Directory",
74                  "basename": key,
75                  "listing": [samples[key]]});
76     }
77     return {"out": dirs};
78   }
79 </pre>
80
81 * Avoid specifying resource requirements in CommandLineTool.  Prefer to specify them in the workflow.  You can provide a default resource requirement in the top level @hints@ section, and individual steps can override it with their own resource requirement.
82
83 <pre>
84 cwlVersion: v1.0
85 class: Workflow
86 inputs:
87   inp: File
88 hints:
89   ResourceRequirement:
90     ramMin: 1000
91     coresMin: 1
92     tmpdirMin: 45000
93 steps:
94   step1:
95     in: {inp: inp}
96     out: [out]
97     run: tool1.cwl
98   step2:
99     in: {inp: step1/inp}
100     out: [out]
101     run: tool2.cwl
102     hints:
103       ResourceRequirement:
104         ramMin: 2000
105         coresMin: 2
106         tmpdirMin: 90000
107 </pre>
108
109 * Instead of scattering separate steps, prefer to scatter over a subworkflow.
110
111 With the following pattern, @step1@ has to wait for all samples to complete before @step2@ can start computing on any samples.  This means a single long-running sample can prevent the rest of the workflow from moving on:
112
113 <pre>
114 cwlVersion: v1.0
115 class: Workflow
116 inputs:
117   inp: File
118 steps:
119   step1:
120     in: {inp: inp}
121     scatter: inp
122     out: [out]
123     run: tool1.cwl
124   step2:
125     in: {inp: step1/inp}
126     scatter: inp
127     out: [out]
128     run: tool2.cwl
129   step3:
130     in: {inp: step2/inp}
131     scatter: inp
132     out: [out]
133     run: tool3.cwl
134 </pre>
135
136 Instead, scatter over a subworkflow.  In this pattern, a sample can proceed to @step2@ as soon as @step1@ is done, independently of any other samples.
137 Example: (note, the subworkflow can also be put in a separate file)
138
139 <pre>
140 cwlVersion: v1.0
141 class: Workflow
142 steps:
143   step1:
144     in: {inp: inp}
145     scatter: inp
146     out: [out]
147     run:
148       class: Workflow
149       inputs:
150         inp: File
151       outputs:
152         out:
153           type: File
154           outputSource: step3/out
155       steps:
156         step1:
157           in: {inp: inp}
158           out: [out]
159           run: tool1.cwl
160         step2:
161           in: {inp: step1/inp}
162           out: [out]
163           run: tool2.cwl
164         step3:
165           in: {inp: step2/inp}
166           out: [out]
167           run: tool3.cwl
168 </pre>