Part 4: Hello Modules¶
See the whole playlist on the Nextflow YouTube channel.
The video transcript is available here.
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 single process definition that is encapsulated by itself in a standalone code file. To use a module in a workflow, you just add a single-line import 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/.
Tip
Here we are showing you how to use local modules, meaning modules stored locally in the same repository as the rest of the workflow code, in contrast to remote modules, which are stored in other (remote) repositories. For more information about remote modules, see the documentation.
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 import 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, making sure to copy over the #!/usr/bin/env nextflow shebang too.
| modules/sayHello.nf | |
|---|---|
Once that is done, delete the process definition from the workflow file, but make sure to leave the shebang in place.
2.3. Add an import declaration before the workflow block¶
The syntax for importing a local module is fairly straightforward:
Let's insert that above the params block and fill it out appropriately.
You see we've filled in the module 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, making sure to copy over the #!/usr/bin/env nextflow shebang too.
| modules/convertToUpper.nf | |
|---|---|
Once that is done, delete the process definition from the workflow file, but make sure to leave the shebang in place.
3.3. Add an import declaration before the params block¶
Insert the import 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, making sure to copy over the #!/usr/bin/env nextflow shebang too.
Once that is done, delete the process definition from the workflow file, but make sure to leave the shebang in place.
4.3. Add an import declaration before the params block¶
Insert the import 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 import 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.