Part 4: Hello Modules¶
This section covers how to organize your workflow code to make development and maintenance of your pipeline more efficient and sustainable. Specifically, we are going to demonstrate how to use modules.
In Nextflow, a module is a standalone code file, often encapsulating a single process definition.
To use a module in a workflow, you just add a single-line include statement to your workflow code file; then you can integrate the process into the workflow the same way you normally would.
That makes it possible to reuse process definitions in multiple workflows without producing multiple copies of the code.
When we started developing our workflow, we wrote everything in one single code file. Now we're going to move the processes out into individual modules.
This will make our code more shareable, flexible and maintainable.
How to begin from this section
This section of the course assumes you have completed Parts 1-3 of the Hello Nextflow course, but if you are comfortable with the basics covered in those sections, you can start from here without doing anything special.
0. Warmup: Run hello-modules.nf¶
We're going to use the workflow script hello-modules.nf as a starting point.
It is equivalent to the script produced by working through Part 3 of this training course, except we've changed the output destinations:
| hello-modules.nf | |
|---|---|
Just to make sure everything is working, run the script once before making any changes:
Command output
As previously, you will find the output files in the directory specified in the output block (here, results/hello_modules/).
Directory contents
If that worked for you, you're ready to learn how to modularize your workflow code.
1. Create a directory to store modules¶
It is best practice to store your modules in a specific directory.
You can call that directory anything you want, but the convention is to call it modules/.
2. Create a module for sayHello()¶
In its simplest form, turning an existing process into a module is little more than a copy-paste operation. We're going to create a file stub for the module, copy the relevant code over then delete it from the main workflow file.
Then all we'll need to do is add an include statement so that Nextflow will know to pull in the relevant code at runtime.
2.1. Create a file stub for the new module¶
Let's create an empty file for the module called sayHello.nf.
This gives us a place to put the process code.
2.2. Move the sayHello process code to the module file¶
Copy the whole process definition over from the workflow file to the module file.
| modules/sayHello.nf | |
|---|---|
Once that is done, delete the process definition from the workflow file.
2.3. Add an include declaration before the workflow block¶
The syntax for including a process from a module is fairly straightforward:
Let's insert that above the params block and fill it out appropriately.
You see we've filled in the process name, sayHello, and the path to the file containing the module code, ./modules/sayHello.nf.
2.4. Run the workflow¶
We're running the workflow with essentially the same code and inputs as before, so let's run with the -resume flag and see what happens.
Command output
This should run quickly very quickly because everything is cached. Feel free to check the published outputs.
Nextflow recognized that it's still all the same work to be done, even if the code is split up into multiple files.
Takeaway¶
You know how to extract a process into a local module and you know doing this doesn't break the resumability of the workflow.
What's next?¶
Practice making more modules. Once you've done one, you can do a million more... But let's just do two more for now.
3. Modularize the convertToUpper() process¶
3.1. Create a file stub for the new module¶
Create an empty file for the module called convertToUpper.nf.
3.2. Move the convertToUpper process code to the module file¶
Copy the whole process definition over from the workflow file to the module file.
| modules/convertToUpper.nf | |
|---|---|
Once that is done, delete the process definition from the workflow file.
3.3. Add an include declaration before the params block¶
Insert the include declaration above the params block and fill it out appropriately.
This should start to look very familiar.
3.4. Run the workflow again¶
Run this with the -resume flag.
Command output
This should still produce the same output as previously.
Two done, one more to go!
4. Modularize the collectGreetings() process¶
4.1. Create a file stub for the new module¶
Create an empty file for the module called collectGreetings.nf.
4.2. Move the collectGreetings process code to the module file¶
Copy the whole process definition over from the workflow file to the module file.
Once that is done, delete the process definition from the workflow file.
4.3. Add an include declaration before the params block¶
Insert the include declaration above the params block and fill it out appropriately.
| hello-modules.nf | |
|---|---|
Last one!
4.4. Run the workflow¶
Run this with the -resume flag.
Command output
This should still produce the same output as previously.
Takeaway¶
You know how to modularize multiple processes in a workflow.
Congratulations, you've done all this work and absolutely nothing has changed to how the pipeline works!
Jokes aside, now your code is more modular, and if you decide to write another pipeline that calls on one of those processes, you just need to type one short include statement to use the relevant module.
This is better than copy-pasting the code, because if later you decide to improve the module, all your pipelines will inherit the improvements.
What's next?¶
Take a short break if you feel like it.
When you're ready, move on to Part 5: Hello Containers to learn how to use containers to manage software dependencies more conveniently and reproducibly.
Quiz¶
What is a module in Nextflow?
What convention is typically used for storing module files?
What is the correct syntax to use a module?
What happens to the -resume functionality when using modules?
What are the benefits of using modules? (Select all that apply)