Skip to content

Commit de35681

Browse files
committed
Add workflows of workflows side quest
1 parent 99df8a2 commit de35681

File tree

11 files changed

+453
-1
lines changed

11 files changed

+453
-1
lines changed
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
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)

main.nf

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
1-
// This main.nf script has intentionally been left blank to run on Seqera Platform
1+
include { GREETING_WORKFLOW } from './workflows/greeting'
2+
3+
workflow {
4+
Channel.from('Alice', 'Bob', 'Charlie') | GREETING_WORKFLOW
5+
6+
GREETING_WORKFLOW.out.greetings_ch.view { "Original: $it" }
7+
GREETING_WORKFLOW.out.timestamped_ch.view { "Timestamped: $it" }
8+
}

mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ nav:
2020
- hello_nextflow/06_hello_config.md
2121
- Side Quests:
2222
- side_quests/nf-test.md
23+
- side_quests/workflows_of_workflows.md
2324
- hello_nextflow/survey.md
2425
- hello_nextflow/next_steps.md
2526
- Nextflow for Genomics:
@@ -188,6 +189,7 @@ plugins:
188189
- advanced/index.md
189190
- advanced/orientation.md
190191
- side_quests/nf-test.md
192+
- side_quests/workflows_of_workflows.md
191193
- nf4_science/genomics/03_modules.md
192194

193195
- i18n:
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
include { GREETING_WORKFLOW } from './workflows/greeting'
2+
include { TRANSFORM_WORKFLOW } from './workflows/transform'
3+
4+
workflow {
5+
names = Channel.from('Alice', 'Bob', 'Charlie')
6+
7+
// Run the greeting workflow
8+
GREETING_WORKFLOW(names)
9+
10+
// Run the transform workflow
11+
TRANSFORM_WORKFLOW(GREETING_WORKFLOW.out.timestamped)
12+
13+
// View results
14+
TRANSFORM_WORKFLOW.out.upper.view { "Uppercase: $it" }
15+
TRANSFORM_WORKFLOW.out.reversed.view { "Reversed: $it" }
16+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
include { VALIDATE_NAME } from '../modules/validate_name'
2+
include { SAY_HELLO } from '../modules/say_hello'
3+
include { TIMESTAMP_GREETING } from '../modules/timestamp_greeting'
4+
5+
workflow GREETING_WORKFLOW {
6+
take:
7+
names_ch // Input channel with names
8+
9+
main:
10+
// Chain processes: validate -> create greeting -> add timestamp
11+
validated_ch = VALIDATE_NAME(names_ch)
12+
greetings_ch = SAY_HELLO(validated_ch)
13+
timestamped_ch = TIMESTAMP_GREETING(greetings_ch)
14+
15+
emit:
16+
greetings = greetings_ch // Original greetings
17+
timestamped = timestamped_ch // Timestamped greetings
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
include { SAY_HELLO_UPPER } from '../modules/say_hello_upper'
2+
include { REVERSE_TEXT } from '../modules/reverse_text'
3+
4+
workflow TRANSFORM_WORKFLOW {
5+
take:
6+
input_ch // Input channel with messages
7+
8+
main:
9+
// Apply transformations in sequence
10+
upper_ch = SAY_HELLO_UPPER(input_ch)
11+
reversed_ch = REVERSE_TEXT(upper_ch)
12+
13+
emit:
14+
upper = upper_ch // Uppercase greetings
15+
reversed = reversed_ch // Reversed uppercase greetings
16+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Use a text manipulation tool to reverse the text in a file
3+
*/
4+
process REVERSE_TEXT {
5+
publishDir 'results', mode: 'copy'
6+
7+
tag "reversing ${input_file}"
8+
9+
input:
10+
path input_file
11+
12+
output:
13+
path "REVERSED-${input_file}"
14+
15+
script:
16+
"""
17+
cat '${input_file}' | rev > 'REVERSED-${input_file}'
18+
"""
19+
}

0 commit comments

Comments
 (0)