Chapter 2: Nextflow DSL2 Core Syntax, Channels & Reactive Flow
Tested Stacks:
GATK 4.6.1
Samtools 1.21
Nextflow 24.10
Seurat v5.1
Python 3.12
Prerequisites: Basic Linux CLI & Sequencing Formats
📚 Chapter 2 of 13
0% Course Completed
Chapter 2: Nextflow DSL2 Core Syntax, Channels & Reactive Flow
Nextflow DSL2 (Domain Specific Language 2) enforces clean modularity, separating process definitions from workflow logic. At the heart of Nextflow lies the concept of Channels: asynchronous streams that transport data items between processes.
Queue Channels vs. Value Channels
- Queue Channel (Asynchronous FIFO): Consumed upon read. Once an item is pulled by a process execution, it disappears from the channel. Created by factories like
Channel.fromPath()orChannel.of(). - Value Channel (Broadcast / Bound): Bound to a single data value or file. It can be read multiple times without being consumed, making it ideal for reference genomes, index files, or global parameters.
1. Core Channel Factories for Bioinformatics
// 1. Reading single files into a queue channel
ch_reads = Channel.fromPath( 'data/*.fastq.gz' )
// 2. Reading Paired-End FASTQ files with automatic regex grouping
// Emits: [ sample_id, [ read1.fastq.gz, read2.fastq.gz ] ]
ch_paired_reads = Channel.fromFilePairs( 'data/*_{1,2}.fastq.gz' )
// 3. Creating a broadcast Value Channel for reference genome
ch_genome = Channel.value( file('/ref/GRCh38.fasta') )
// 4. Emitting a channel from a sample sheet CSV
ch_samplesheet = Channel.fromPath('samplesheet.csv')
| splitCsv(header: true)
| map { row -> tuple(row.sample_id, file(row.fastq_1), file(row.fastq_2)) }2. Essential Channel Operators
Channel operators transform, filter, and combine data streams using pipe syntax (|).
// Operator 1: map (Transform elements)
ch_paired_reads
| map { meta, fastqs -> tuple( meta, fastqs[0] ) } // Extract only R1
| view { "Sample: ${it[0]}, R1: ${it[1]}" }
// Operator 2: filter (Select elements matching condition)
ch_samplesheet
| filter { sample_id, r1, r2 -> sample_id.startsWith('TUMOR') }
// Operator 3: combine (Cartesian product of channels)
// Combines every sample with every bed interval
ch_samples.combine( ch_intervals )
// Operator 4: join (Inner join based on matching key)
// Joins BAM channel and VCF channel having the same sample_id
ch_bams.join( ch_vcfs )3. The Branch Operator: Splitting Data Streams
The branch operator directs channel elements into distinct output sub-channels based on logical criteria (e.g. single-end vs paired-end sequencing, or control vs tumor samples).
ch_reads.branch {
single_end: it[1] instanceof Path
paired_end: it[1] instanceof List
}.set { ch_branched }
ch_branched.single_end.view { "Single-end: $it" }
ch_branched.paired_end.view { "Paired-end: $it" }
⚠️
Common Pitfalls & Troubleshooting Checklist
- Java Heap Exhaustion (GATK / Picard): If processes terminate with
java.lang.OutOfMemoryError, set your environment variable explicitly:export _JAVA_OPTIONS="-Xmx16g -Djava.io.tmpdir=/tmp". - Index Timestamp Mismatch: If tools complain that a BAM index is missing or out of date, regenerate with
samtools index -@ 4 sample.bam. - Conda Channel Conflicts: Avoid ABI incompatibilities between bioconda and conda-forge by setting strict priority:
conda config --set channel_priority strict. - Chromosome Naming Contamination: Always verify whether reference FASTA headers use
chr1or1; divergent naming causes silent alignment drops.
Was this guide helpful to your research?
Help us maintain 100% accurate, peer-reviewed bioinformatics standards.