9932: Add CWL best practices guide
[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 resources such as https://github.com/common-workflow-language/workflows and 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 including @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 * 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.
46
47 <pre>
48 class: ExpressionTool
49 cwlVersion: v1.0
50 inputs:
51   inputdir: Directory
52 outputs:
53   out: Directory[]
54 requirements:
55   InlineJavascriptRequirement: {}
56 expression: |
57   ${
58     var samples = {};
59     for (var i = 0; i < inputs.inputdir.listing.length; i++) {
60       var file = inputs.inputdir.listing[i];
61       var groups = file.basename.match(/^(.+)(_R[12]_)(.+)$/);
62       if (groups) {
63         if (!samples[groups[1]]) {
64           samples[groups[1]] = [];
65         }
66         samples[groups[1]].push(file);
67       }
68     }
69     var dirs = [];
70     for (var key in samples) {
71       dirs.push({"class": "Directory",
72                  "basename": key,
73                  "listing": [samples[key]]});
74     }
75     return {"out": dirs};
76   }
77 </pre>
78
79 * Don't 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.
80
81 <pre>
82 cwlVersion: v1.0
83 class: Workflow
84 inputs:
85   inp: File
86 hints:
87   ResourceRequirement:
88     ramMin: 1000
89     coresMin: 1
90     tmpdirMin: 45000
91 steps:
92   step1:
93     in: {inp: inp}
94     out: [out]
95     run: tool1.cwl
96   step2:
97     in: {inp: step1/inp}
98     out: [out]
99     run: tool2.cwl
100     hints:
101       ResourceRequirement:
102         ramMin: 2000
103         coresMin: 2
104         tmpdirMin: 90000
105 </pre>
106
107 * Instead of scattering separate steps, prefer to scatter over a subworkflow.
108
109 With the following pattern, @step1@ has to complete for all samples to complete before @step2@ can start on any samples.  This means a single long-running sample can prevent the rest of the workflow from moving on:
110
111 <pre>
112 cwlVersion: v1.0
113 class: Workflow
114 inputs:
115   inp: File
116 steps:
117   step1:
118     in: {inp: inp}
119     scatter: inp
120     out: [out]
121     run: tool1.cwl
122   step2:
123     in: {inp: step1/inp}
124     scatter: inp
125     out: [out]
126     run: tool2.cwl
127   step3:
128     in: {inp: step2/inp}
129     scatter: inp
130     out: [out]
131     run: tool3.cwl
132 </pre>
133
134 Instead, scatter over a subworkflow.  In this pattern, a sample can proceed from to @step2@ as soon as @step1@ is done, independently of any other samples.
135 Example: (note, the subworkflow can also be put in a separate file)
136
137 <pre>
138 cwlVersion: v1.0
139 class: Workflow
140 steps:
141   step1:
142     in: {inp: inp}
143     scatter: inp
144     out: [out]
145     run:
146       class: Workflow
147       inputs:
148         inp: File
149       outputs:
150         out:
151           type: File
152           outputSource: step3/out
153       steps:
154         step1:
155           in: {inp: inp}
156           out: [out]
157           run: tool1.cwl
158         step2:
159           in: {inp: step1/inp}
160           out: [out]
161           run: tool2.cwl
162         step3:
163           in: {inp: step2/inp}
164           out: [out]
165           run: tool3.cwl
166 </pre>