Custom Pipelines (nf-template & nf-modules)¶
When standard community pipelines do not cover your analysis requirements (e.g., custom GWAS, quality control, or specific cohort merging), the lab builds custom Nextflow pipelines.
To maintain consistency and reuse code across the lab, all custom pipelines are built using nf-template as the boilerplate and nf-modules as a shared library of tool processes.
The Starter Boilerplate: nf-template¶
The starter template is hosted at SysMedBio/nf-template. It implements several design patterns that are standard in our lab:
Core Design Patterns¶
- Meta-tuple Pattern
Every process receives a[meta, files]tuple, wheremetais a Groovy map containing at least anidkey (e.g., cohort or sample name). Thisidis used to prefix all output filenames, ensuring that downstream results are traceable back to their origin. - Per-Cohort Configurations
All cohort-specific paths and identifiers live inconf/cohorts/*.configfiles. The main pipeline files (main.nf) remain generic and cohort-agnostic. - Two Entrypoint Modes
- Single-cohort (
main.nf): Designed to run a single cohort configuration. Driven by adding a-c conf/cohorts/<COHORT>.configflag. - Batch worksheet (
main_worksheet.nf): Reads a CSV file detailing paths for multiple cohorts, executing them in parallel. Driven by passing--worksheet worksheets/my_cohorts.csv. - Predefined Resource Labels
Processes are annotated with resource labels rather than hardcoded CPU/memory values. The underlying scheduling is defined inconf/compute.config:
| Label | CPUs | Memory | Time Limit | SLURM Queue |
|---|---|---|---|---|
process_single |
1 | 4 GB | 1 h | standard |
process_low |
2 | 12 GB | 4 h | standard |
process_medium |
6 | 36 GB | 8 h | standard |
process_high |
12 | 72 GB | 16 h | standard |
process_long |
— | — | 7 d | long |
process_high_memory |
— | 200 GB | — | standard |
process_high_cpu |
64 | — | — | standard |
Bootstrapping a New Pipeline¶
Step 1: Create the Repository¶
Always create your pipeline under the SysMedBio GitHub organization and use the -nf suffix.
- Option A (GitHub Interface - Recommended):
- Navigate to SysMedBio/nf-template.
- Click Use this template -> Create a new repository.
-
Select the
SysMedBioowner, set your repository name (e.g.gwas-qc-nf), and clone it onto the cluster:git clone git@github.com:SysMedBio/your-pipeline-name-nf.git cd your-pipeline-name-nf -
Option B (Manual Detached Clone): If you are working strictly from the terminal on the cluster, you can clone and detach:
git clone --depth 1 git@github.com:SysMedBio/nf-template.git your-pipeline-name-nf cd your-pipeline-name-nf rm -rf .git git init git add . git commit -m "Initial commit from nf-template" # Push to your newly created repo git remote add origin git@github.com:SysMedBio/your-pipeline-name-nf.git git push -u origin main
Step 2: Configure the Pipeline¶
- Edit the header blocks in
main.nfandmain_worksheet.nfto fill in the author, creation date, and purpose. - Add pipeline-specific parameters (e.g., thresholds, filtering options) to
conf/pipeline.config. - Add any cohorts you want to analyze by creating config files in
conf/cohorts/.
Reusing Processes: nf-modules¶
Instead of writing custom process blocks for standard tools (like PLINK2 or BCFtools) from scratch, you should pull existing modules from the SysMedBio/nf-modules repository.
Installing a Module¶
Ensure you have the wrapper scripts in your PATH (see the Container Wrappers guide), then run:
nf-modules <tool>/<operation>
Examples:¶
# Fetch PLINK2 PCA module
nf-modules plink2/pca
# Fetch BCFtools Merge module
nf-modules bcftools/merge
This utility automatically checks out the module from the nf-modules repository and places it under modules/<tool>/<operation>/ in your pipeline directory, ready for import.
Importing a Module in main.nf¶
At the top of your main.nf, include the process using Nextflow DSL2 syntax:
include { PLINK2_PCA } from './modules/plink2/pca/main.nf'
workflow {
// Channel initialization ...
pca_results_ch = PLINK2_PCA(samples_ch, 10) // passes meta-tuple and num components
}
Module Development Style Guide¶
If you write a new module, or update an existing one, you must adhere to the style guide in nf-modules to ensure it is clean and reusable across the lab.
1. Process Names & Directories¶
- File path:
modules/<tool>/<operation>/main.nf. - Process name:
<TOOL>_<OPERATION>inSCREAMING_SNAKE_CASE(e.g.process PLINK2_PCA).
2. Tags, Labels, and Container Setup¶
Configure the process directives as follows:
process TOOL_OPERATION {
tag "TOOL_OPERATION_${meta.id}"
label 'process_low'
container 'docker://coreygiles/gwas-suite:main'
// ... inputs, outputs, script
}
tag: Always include a unique tag starting with the process name and suffixing the meta ID:"TOOL_OPERATION_${meta.id}".label: Assign a resource label (process_low,process_medium, etc.) rather than hardcoding compute requirements.container: Reference a shared lab container (e.g.,docker://coreygiles/gwas-suite:mainor another official image from the cache) so the process does not depend on local host tools.
3. Staging Inputs & Genotype Formats¶
Declare input channels using the meta-tuple pattern:
input:
tuple val(meta), path(input_files, stageAs: "input/*", arity: 1..3)
val(args) // extra arguments
- Use
stageAs: "input/*"to stage files into a subfolder. This avoids file collisions when processes consume files with identical names from different directories. - Use
arity: 1..3to support single files (VCFs) and multi-file sets (PLINK binary/pgen sets). - Pass tool parameters as input values (
val(args)) rather than referencing global parameters.
4. The Genotype Helper¶
If the module works on generic genotype formats (VCF, BED/BIM/FAM, or PGEN/PVAR/PSAM), import and run the Genotype helper class:
import Genotype
// ... in script block:
def genotype_input = Genotype.detect(input_files)
The helper exposes properties to build command arguments dynamically:
genotype_input.cmd: Formatted input command flag (e.g.--pfile input/sampleor--bfile input/sampleor--vcf input/sample.vcf).genotype_input.make: Format-preserving output flag (e.g.--make-pgenor--make-bed).
5. Outputs & Logs¶
- Every output must use named emits (e.g.
emit: geno_files). - Log files must be emitted separately:
output: tuple val(meta), path("${meta.id}.{pgen,pvar,psam}"), emit: geno_files path("*.log"), emit: logs
6. Command Conventions¶
Pass resource allocations directly from Nextflow variables to ensure the process respects cluster scheduler boundaries:
plink2 \
--threads ${task.cpus} \
--memory ${task.memory.toMega()} \
${genotype_input.cmd} \
${args} \
--out "${meta.id}"
Use \ to split long command lines, and include comments to document specific flags.