|
| 1 | +# Side Quest: Workflows of Workflows |
| 2 | + |
| 3 | +One of the most powerful features of Nextflow is its ability to compose complex pipelines from smaller, reusable workflow modules. This modular approach makes pipelines easier to develop, test, and maintain. |
| 4 | + |
| 5 | +Let's explore why workflow composition is so important. When you're developing a pipeline, you often find yourself creating similar sequences of processes for different data types or analysis steps. Without workflow composition, you might end up copying and pasting these process sequences, leading to duplicated code that's hard to maintain. Or you might create one massive workflow that's difficult to understand and modify. |
| 6 | + |
| 7 | +With workflow composition, you can: |
| 8 | +- Break down complex pipelines into logical, reusable units |
| 9 | +- Test each workflow module independently |
| 10 | +- Mix and match workflows to create new pipelines |
| 11 | +- Share common workflow modules across different pipelines |
| 12 | +- Make your code more maintainable and easier to understand |
| 13 | + |
| 14 | +In this side quest, we'll create a pipeline that demonstrates these benefits by: |
| 15 | +1. Creating independent workflow modules that can be tested and used separately |
| 16 | +2. Composing these modules into a larger pipeline |
| 17 | +3. Using Nextflow's workflow composition features to manage data flow between modules |
| 18 | + |
| 19 | +--- |
| 20 | +## 0. Warmup |
| 21 | + |
| 22 | +### 0.1 Prerequisites |
| 23 | + |
| 24 | +Before taking on this side quest you should: |
| 25 | + |
| 26 | +- Complete the [Hello Nextflow](../hello_nextflow/README.md) tutorial |
| 27 | +- Understand basic Nextflow concepts (processes, channels, operators) |
| 28 | + |
| 29 | +### 0.1. Starting Point |
| 30 | + |
| 31 | +Let's move into the project directory. |
| 32 | + |
| 33 | +```bash |
| 34 | +cd side-quests/workflows_of_workflows |
| 35 | +``` |
| 36 | + |
| 37 | +You'll find a `modules` directory containing several process definitions that build upon what you learned in 'Hello Nextflow': |
| 38 | + |
| 39 | +```console title="Directory contents" |
| 40 | +modules/ |
| 41 | +├── say_hello.nf # Creates a greeting (from Hello Nextflow) |
| 42 | +├── say_hello_upper.nf # Converts to uppercase (from Hello Nextflow) |
| 43 | +├── timestamp_greeting.nf # Adds timestamps to greetings |
| 44 | +├── validate_name.nf # Validates input names |
| 45 | +└── reverse_text.nf # Reverses text content |
| 46 | +``` |
| 47 | + |
| 48 | +We going to compose these modules into two separate workflows that we will then compose into a main workflow. |
| 49 | + |
| 50 | +--- |
| 51 | + |
| 52 | +## 1. Create the Greeting Workflow |
| 53 | + |
| 54 | +Let's start by creating a workflow that validates names and generates timestamped greetings. |
| 55 | + |
| 56 | +### 1.1. Create the workflow structure |
| 57 | + |
| 58 | +```bash title="Create workflow directory and file" |
| 59 | +mkdir -p workflows |
| 60 | +touch workflows/greeting.nf |
| 61 | +``` |
| 62 | + |
| 63 | +### 1.2. Add the first (sub)workflow code |
| 64 | + |
| 65 | +Add this code to `workflows/greeting.nf`: |
| 66 | + |
| 67 | +```groovy title="workflows/greeting.nf" linenums="1" |
| 68 | +include { VALIDATE_NAME } from '../modules/validate_name' |
| 69 | +include { SAY_HELLO } from '../modules/say_hello' |
| 70 | +include { TIMESTAMP_GREETING } from '../modules/timestamp_greeting' |
| 71 | +
|
| 72 | +workflow GREETING_WORKFLOW { |
| 73 | + take: |
| 74 | + names_ch // Input channel with names |
| 75 | +
|
| 76 | + main: |
| 77 | + // Chain processes: validate -> create greeting -> add timestamp |
| 78 | + validated_ch = VALIDATE_NAME(names_ch) |
| 79 | + greetings_ch = SAY_HELLO(validated_ch) |
| 80 | + timestamped_ch = TIMESTAMP_GREETING(greetings_ch) |
| 81 | +
|
| 82 | + emit: |
| 83 | + greetings = greetings_ch // Original greetings |
| 84 | + timestamped = timestamped_ch // Timestamped greetings |
| 85 | +} |
| 86 | +``` |
| 87 | + |
| 88 | +This is already a complete workflow that we can test independently. You might notice that it's very similar to the `hello` workflow from the 'Hello Nextflow' tutorial, but with some additional syntax to allow it to receive input ('take:') and produce output ('emit:'). These are the connections we will use to compose a higher level workflow. |
| 89 | + |
| 90 | +### 1.3. Create and test the main workflow |
| 91 | + |
| 92 | +Now we will create a main workflow that imports and uses the `greeting` workflow. |
| 93 | + |
| 94 | +Create `main.nf`: |
| 95 | + |
| 96 | +```groovy title="main.nf" linenums="1" |
| 97 | +include { GREETING_WORKFLOW } from './workflows/greeting' |
| 98 | +
|
| 99 | +workflow { |
| 100 | + names = Channel.from('Alice', 'Bob', 'Charlie') |
| 101 | + GREETING_WORKFLOW(names) |
| 102 | +
|
| 103 | + GREETING_WORKFLOW.out.greetings.view { "Original: $it" } |
| 104 | + GREETING_WORKFLOW.out.timestamped.view { "Timestamped: $it" } |
| 105 | +} |
| 106 | +
|
| 107 | +``` |
| 108 | + |
| 109 | +Run this and see the output: |
| 110 | +```bash title="Run the workflow" |
| 111 | +nextflow run main.nf |
| 112 | +``` |
| 113 | + |
| 114 | +```console title="Expected output" |
| 115 | +N E X T F L O W ~ version 23.10.1 |
| 116 | +Launching `main.nf` [goofy_mayer] DSL2 - revision: 543f8742fe |
| 117 | +executor > local (9) |
| 118 | +[05/3cc752] process > GREETING_WORKFLOW:VALIDATE_NAME (validating Char... [100%] 3 of 3 ✔ |
| 119 | +[b1/b56ecf] process > GREETING_WORKFLOW:SAY_HELLO (greeting Charlie) [100%] 3 of 3 ✔ |
| 120 | +[ea/342168] process > GREETING_WORKFLOW:TIMESTAMP_GREETING (adding tim... [100%] 3 of 3 ✔ |
| 121 | +Original: /workspaces/training/side_quests/workflows_of_workflows/work/bb/c8aff3df0ebc15a4d7d35f736db44c/Alice-output.txt |
| 122 | +Original: /workspaces/training/side_quests/workflows_of_workflows/work/fb/fa877776e8a5d90b537b1bcd3b6f5b/Bob-output.txt |
| 123 | +Original: /workspaces/training/side_quests/workflows_of_workflows/work/b1/b56ecf938fda8bcbec211847c8f0be/Charlie-output.txt |
| 124 | +Timestamped: /workspaces/training/side_quests/workflows_of_workflows/work/06/877bc909f140bbf8223343450cea36/timestamped_Alice-output.txt |
| 125 | +Timestamped: /workspaces/training/side_quests/workflows_of_workflows/work/aa/bd31b71cdb745b7c155ca7f8837b8a/timestamped_Bob-output.txt |
| 126 | +Timestamped: /workspaces/training/side_quests/workflows_of_workflows/work/ea/342168d4ba04cc899a89c56cbfd9b0/timestamped_Charlie-output.txt |
| 127 | +``` |
| 128 | + |
| 129 | +### Takeaway |
| 130 | + |
| 131 | +You should now have a working greeting workflow that: |
| 132 | +- Takes a channel of names as input |
| 133 | +- Validates each name |
| 134 | +- Creates a greeting for each valid name |
| 135 | +- Adds timestamps to the greetings |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## 2. Add the Transform Workflow |
| 140 | + |
| 141 | +Now let's create a workflow that applies text transformations to the greetings. |
| 142 | + |
| 143 | +### 2.1. Create the workflow file |
| 144 | + |
| 145 | +```bash title="Create transform workflow" |
| 146 | +touch workflows/transform.nf |
| 147 | +``` |
| 148 | + |
| 149 | +### 2.2. Add the workflow code |
| 150 | + |
| 151 | +Add this code to `workflows/transform.nf`: |
| 152 | + |
| 153 | +```groovy title="workflows/transform.nf" linenums="1" |
| 154 | +include { SAY_HELLO_UPPER } from '../modules/say_hello_upper' |
| 155 | +include { REVERSE_TEXT } from '../modules/reverse_text' |
| 156 | +
|
| 157 | +workflow TRANSFORM_WORKFLOW { |
| 158 | + take: |
| 159 | + input_ch // Input channel with messages |
| 160 | +
|
| 161 | + main: |
| 162 | + // Apply transformations in sequence |
| 163 | + upper_ch = SAY_HELLO_UPPER(input_ch) |
| 164 | + reversed_ch = REVERSE_TEXT(upper_ch) |
| 165 | +
|
| 166 | + emit: |
| 167 | + upper = upper_ch // Uppercase greetings |
| 168 | + reversed = reversed_ch // Reversed uppercase greetings |
| 169 | +} |
| 170 | +``` |
| 171 | + |
| 172 | +### 2.3. Update the main workflow |
| 173 | + |
| 174 | +Update `main.nf` to use both workflows: |
| 175 | + |
| 176 | +```groovy title="main.nf" linenums="1" |
| 177 | +include { SAY_HELLO_UPPER } from '../modules/say_hello_upper' |
| 178 | +include { REVERSE_TEXT } from '../modules/reverse_text' |
| 179 | +
|
| 180 | +workflow TRANSFORM_WORKFLOW { |
| 181 | + take: |
| 182 | + input_ch // Input channel with messages |
| 183 | +
|
| 184 | + main: |
| 185 | + // Apply transformations in sequence |
| 186 | + upper_ch = SAY_HELLO_UPPER(input_ch) |
| 187 | + reversed_ch = REVERSE_TEXT(upper_ch) |
| 188 | +
|
| 189 | + emit: |
| 190 | + upper = upper_ch // Uppercase greetings |
| 191 | + reversed = reversed_ch // Reversed uppercase greetings |
| 192 | +} |
| 193 | +``` |
| 194 | + |
| 195 | +Run the complete pipeline: |
| 196 | +```bash title="Run the workflow" |
| 197 | +nextflow run main.nf |
| 198 | +``` |
| 199 | + |
| 200 | +```console title="Expected output" |
| 201 | +N E X T F L O W ~ version 23.10.1 |
| 202 | +Launching `main.nf` [sick_kimura] DSL2 - revision: 8dc45fc6a8 |
| 203 | +executor > local (13) |
| 204 | +executor > local (15) |
| 205 | +[83/1b51f4] process > GREETING_WORKFLOW:VALIDATE_NAME (validating Alice) [100%] 3 of 3 ✔ |
| 206 | +[68/556150] process > GREETING_WORKFLOW:SAY_HELLO (greeting Alice) [100%] 3 of 3 ✔ |
| 207 | +[de/511abd] process > GREETING_WORKFLOW:TIMESTAMP_GREETING (adding tim... [100%] 3 of 3 ✔ |
| 208 | +[cd/e6a7e0] process > TRANSFORM_WORKFLOW:SAY_HELLO_UPPER (converting t... [100%] 3 of 3 ✔ |
| 209 | +[f0/74ba4a] process > TRANSFORM_WORKFLOW:REVERSE_TEXT (reversing UPPER... [100%] 3 of 3 ✔ |
| 210 | +Uppercase: /workspaces/training/side_quests/workflows_of_workflows/work/a0/d4f5df4d6344604498fa47a6084a11/UPPER-timestamped_Bob-output.txt |
| 211 | +Uppercase: /workspaces/training/side_quests/workflows_of_workflows/work/69/b5e37f6c79c2fd38adb75d0eca8f87/UPPER-timestamped_Charlie-output.txt |
| 212 | +Uppercase: /workspaces/training/side_quests/workflows_of_workflows/work/cd/e6a7e0b17e7d5a2f71bb8123cd53a7/UPPER-timestamped_Alice-output.txt |
| 213 | +Reversed: /workspaces/training/side_quests/workflows_of_workflows/work/7a/7a222f7957b35d1d121338566a24ac/REVERSED-UPPER-timestamped_Bob-output.txt |
| 214 | +Reversed: /workspaces/training/side_quests/workflows_of_workflows/work/46/8d19af62e33a5a6417c773496e0f90/REVERSED-UPPER-timestamped_Charlie-output.txt |
| 215 | +Reversed: /workspaces/training/side_quests/workflows_of_workflows/work/f0/74ba4a10d9ef5c82f829d1c154d0f6/REVERSED-UPPER-timestamped_Alice-output.txt |
| 216 | +``` |
| 217 | + |
| 218 | +If you take a look at one of those reversed files, you'll see that it's the uppercase version of the greeting reversed: |
| 219 | + |
| 220 | +```bash title="Check a final output file" |
| 221 | +cat /workspaces/training/side_quests/workflows_of_workflows/work/f0/74ba4a10d9ef5c82f829d1c154d0f6/REVERSED-UPPER-timestamped_Alice-output.txt``` |
| 222 | +
|
| 223 | +```console title="Reversed file content" |
| 224 | +!ECILA ,OLLEH ]04:50:71 60-30-5202[ |
| 225 | +``` |
| 226 | +
|
| 227 | +### Takeaway |
| 228 | +
|
| 229 | +You should now have a complete pipeline that: |
| 230 | +- Processes names through the greeting workflow |
| 231 | +- Feeds the timestamped greetings into the transform workflow |
| 232 | +- Produces both uppercase and reversed versions of the greetings |
| 233 | +
|
| 234 | +## Summary |
| 235 | +
|
| 236 | +In this side quest, we've explored the powerful concept of workflow composition in Nextflow, which allows us to build complex pipelines from smaller, reusable components. Here's what we've accomplished: |
| 237 | +
|
| 238 | +1. **Created Modular Workflows**: We built two independent workflow modules: |
| 239 | + - A `GREETING_WORKFLOW` that validates names, creates greetings, and adds timestamps |
| 240 | + - A `TRANSFORM_WORKFLOW` that converts text to uppercase and reverses it |
| 241 | +
|
| 242 | +2. **Composed Workflows Together**: We connected these workflows in a main pipeline, demonstrating how data can flow from one workflow to another. |
| 243 | +
|
| 244 | +3. **Used Workflow Interfaces**: We defined clear inputs and outputs for each workflow using the `take:` and `emit:` syntax, creating well-defined interfaces between components. |
| 245 | +
|
| 246 | +4. **Managed Data Flow**: We learned how to access workflow outputs using the namespace notation (`WORKFLOW_NAME.out.channel_name`) and pass them to other workflows. |
| 247 | +
|
| 248 | +5. **Practiced Modular Design**: We experienced firsthand how breaking a pipeline into logical components makes the code more maintainable and easier to understand. |
| 249 | +
|
| 250 | +This modular approach offers several advantages over monolithic pipelines: |
| 251 | +- Each workflow can be developed, tested, and debugged independently |
| 252 | +- Workflows can be reused across different pipelines |
| 253 | +- The overall pipeline structure becomes more readable and maintainable |
| 254 | +- Changes to one workflow don't necessarily affect others if the interfaces remain consistent |
| 255 | +
|
| 256 | +It's important to note that while calling workflows is a bit like calling processes, it's not the same. You can't, for example, run a workflow n times by calling it with a channel of size n- you would need to pass a channel of size n to the workflow and iterate internally. |
| 257 | +
|
| 258 | +By mastering workflow composition, you're now equipped to build more sophisticated Nextflow pipelines that can handle complex bioinformatics tasks while remaining maintainable and scalable. |
| 259 | +
|
| 260 | +### Key Concepts |
| 261 | +
|
| 262 | +1. **Workflow Inclusion** |
| 263 | + ```nextflow |
| 264 | + // Include a single workflow |
| 265 | + include { WORKFLOW_NAME } from './path/to/workflow' |
| 266 | +
|
| 267 | + // Include multiple workflows |
| 268 | + include { WORKFLOW_A; WORKFLOW_B } from './path/to/workflows' |
| 269 | +
|
| 270 | + // Include with alias to avoid name conflicts |
| 271 | + include { WORKFLOW_A as WORKFLOW_A_ALIAS } from './path/to/workflow' |
| 272 | + ``` |
| 273 | +
|
| 274 | +2. **Workflow Inputs and Outputs** |
| 275 | + ```nextflow |
| 276 | + workflow EXAMPLE { |
| 277 | + take: |
| 278 | + input_ch // Declare inputs |
| 279 | + emit: |
| 280 | + output_ch // Declare outputs |
| 281 | + } |
| 282 | + ``` |
| 283 | +
|
| 284 | +3. **Workflow Composition** |
| 285 | + ```nextflow |
| 286 | + // Using explicit connections |
| 287 | + WORKFLOW_A(input_ch) |
| 288 | + WORKFLOW_B(WORKFLOW_A.out.some_channel) |
| 289 | + ``` |
| 290 | +
|
| 291 | +## Resources |
| 292 | +
|
| 293 | +- [Nextflow Workflow Documentation](https://www.nextflow.io/docs/latest/workflow.html) |
| 294 | +- [Channel Operators Reference](https://www.nextflow.io/docs/latest/operator.html) |
| 295 | +- [Error Strategy Documentation](https://www.nextflow.io/docs/latest/process.html#errorstrategy) |
0 commit comments