diff --git a/subworkflows/nf-core/fastq_remove_adapters_and_merge/main.nf b/subworkflows/nf-core/fastq_remove_adapters_and_merge/main.nf new file mode 100644 index 000000000000..f13df3bef4af --- /dev/null +++ b/subworkflows/nf-core/fastq_remove_adapters_and_merge/main.nf @@ -0,0 +1,124 @@ +// ok for single end adapter removal +include { TRIMMOMATIC } from '../../../modules/nf-core/trimmomatic/main' +include { CUTADAPT } from '../../../modules/nf-core/cutadapt/main' +include { TRIMGALORE } from '../../../modules/nf-core/trimgalore/main' +include { BBMAP_BBDUK } from '../../../modules/nf-core/bbmap/bbduk/main' + +// allows merging of paired end reads, but will work for single end reads as well +include { FASTP } from '../../../modules/nf-core/fastp/main' +include { ADAPTERREMOVAL} from '../../../modules/nf-core/adapterremoval/main' +include { LEEHOM } from '../../../modules/nf-core/leehom/main' + +// requirs paired end becouse of merging +include { NGMERGE } from '../../../modules/nf-core/ngmerge/main' + +workflow FASTQ_REMOVE_ADAPTERS_AND_MERGE { + + take: + reads // (meta, reads) - SE or PE + skip_trimmomatic // boolean + skip_cutadapt + skip_trimgalore + skip_bbduk + skip_fastp + skip_adapterremoval + skip_leehom + skip_ngmerge + do_merge + adapters_contaminants + main: + + ch_current = reads + ch_versions = Channel.empty() + + if (!skip_trimmomatic) { + // TRIMMOMATIC.ext.adapters = adapters_contaminants + TRIMMOMATIC( ch_current ) + ch_current = TRIMMOMATIC.out.trimmed_reads.map { meta, r -> [meta, r] } + ch_versions = ch_versions.mix(TRIMMOMATIC.out.versions) + } + + if (!skip_cutadapt) { + CUTADAPT( ch_current ) + ch_current = CUTADAPT.out.reads.map { meta, r -> [meta, r] } + ch_versions = ch_versions.mix(CUTADAPT.out.versions) + } + + if (!skip_trimgalore) { + TRIMGALORE( ch_current ) + ch_current = TRIMGALORE.out.reads.map { meta, r -> [meta, r] } + ch_versions = ch_versions.mix(TRIMGALORE.out.versions) + } + + if (!skip_bbduk) { + ch_current.view { "DEBUG: BEFORE BBMAP_BBDUK → $it" } + BBMAP_BBDUK( ch_current, adapters_contaminants ) + ch_current = BBMAP_BBDUK.out.reads.map { meta, r -> [meta, r] } + ch_versions = ch_versions.mix(BBMAP_BBDUK.out.versions) + ch_current.view { "DEBUG: AFTER BBMAP_BBDUK → $it" } + } + + if (!skip_fastp && !do_merge) { + ch_current.view { "DEBUG: BEFORE FASTP → $it" } + FASTP( ch_current.map { meta, r -> tuple(meta, r, adapters_contaminants)}, + false, + true, + do_merge) + ch_current = FASTP.out.reads.map { meta, r -> [meta, r] } + ch_versions = ch_versions.mix(FASTP.out.versions) + ch_current.view { "DEBUG: AFTER FASTP → $it" } + } + + if (!skip_adapterremoval && !do_merge) { + ADAPTERREMOVAL( ch_current, adapters_contaminants ) + ch_current.view { "DEBUG: BEFORE ADAPTERREMOVAL → $it" } + ch_current = ADAPTERREMOVAL.out.paired_truncated + .mix(ADAPTERREMOVAL.out.singles_truncated) + .map { tup -> + def meta = tup[0] + def r = tup[1] + [meta, r] + } + ch_versions = ch_versions.mix(ADAPTERREMOVAL.out.versions) + ch_current.view { "DEBUG: AFTER ADAPTERREMOVAL → $it" } + } + + if (!skip_leehom && !do_merge) { + ch_current.view { "DEBUG: BEFORE LEEHOM → $it" } + ch_leehom_input = ch_current.map { meta, r -> + if (meta.single_end) + return [meta, r] + else + return [meta, [reads[0], reads[1]]] + } + + LEEHOM(ch_leehom_input) + + ch_leehom_pe = + LEEHOM.out.unmerged_r1_fq_pass + .combine(LEEHOM.out.unmerged_r2_fq_pass) + .map { left, right -> + def meta = left[0] + def r1 = left[1] + def r2 = right[1] + [meta, [r1, r2]] + } + .filter { meta, r -> !meta.single_end } // only PE + +// ---- SE OUTPUT ---- +ch_leehom_se = + LEEHOM.out.fq_pass + .filter { meta, r -> meta.single_end } + .map { meta, r -> [meta, r] } + +// ---- MERGE CHANNELS ---- +ch_current = ch_leehom_pe.mix(ch_leehom_se) + + ch_versions = ch_versions.mix(LEEHOM.out.versions) + } + + emit: + + trimmed_reads = ch_current // channel: [ meta, trimmed_reads ] + versions = ch_versions // channel: [ versions.yml ] +} diff --git a/subworkflows/nf-core/fastq_remove_adapters_and_merge/meta.yml b/subworkflows/nf-core/fastq_remove_adapters_and_merge/meta.yml new file mode 100644 index 000000000000..0390e17f75c6 --- /dev/null +++ b/subworkflows/nf-core/fastq_remove_adapters_and_merge/meta.yml @@ -0,0 +1,51 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/subworkflows/yaml-schema.json +name: "fastq_remove_adapters_and_merge" +## TODO nf-core: Add a description of the subworkflow and list keywords +description: Sort SAM/BAM/CRAM file +keywords: + - sort + - bam + - sam + - cram +## TODO nf-core: Add a list of the modules and/or subworkflows used in the subworkflow +components: + - samtools/sort + - samtools/index +## TODO nf-core: List all of the channels used as input with a description and their structure +input: + - ch_bam: + type: file + description: | + The input channel containing the BAM/CRAM/SAM files + Structure: [ val(meta), path(bam) ] + pattern: "*.{bam/cram/sam}" +## TODO nf-core: List all of the channels used as output with a descriptions and their structure +output: + - bam: + type: file + description: | + Channel containing BAM files + Structure: [ val(meta), path(bam) ] + pattern: "*.bam" + - bai: + type: file + description: | + Channel containing indexed BAM (BAI) files + Structure: [ val(meta), path(bai) ] + pattern: "*.bai" + - csi: + type: file + description: | + Channel containing CSI files + Structure: [ val(meta), path(csi) ] + pattern: "*.csi" + - versions: + type: file + description: | + File containing software versions + Structure: [ path(versions.yml) ] + pattern: "versions.yml" +authors: + - "@kornkv" +maintainers: + - "@kornkv" diff --git a/subworkflows/nf-core/fastq_remove_adapters_and_merge/tests/main.nf.test b/subworkflows/nf-core/fastq_remove_adapters_and_merge/tests/main.nf.test new file mode 100644 index 000000000000..3ba4c0236b64 --- /dev/null +++ b/subworkflows/nf-core/fastq_remove_adapters_and_merge/tests/main.nf.test @@ -0,0 +1,95 @@ +nextflow_workflow { + + name "Test Subworkflow FASTQ_REMOVE_ADAPTERS_AND_MERGE" + script "../main.nf" + workflow "FASTQ_REMOVE_ADAPTERS_AND_MERGE" + + tag "subworkflows" + tag "trimmomatic" + tag "cutadapt" + tag "trimgalore" + tag "bbmap/bbduk" + tag "fastp" + tag "adapterremoval" + tag "leehom" + tag "ngmerge" + + /************************************************************** + * TEST 1 — SINGLE END + **************************************************************/ + test("sarscov2 - fastq - single-end") { + config "./nextflow.config" + when { + workflow { + """ + input[0] = [ + [ id:'test', single_end:true ], + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) + ] + + input[1] = false // skip_trimmomatic + input[2] = false // skip_cutadapt + input[3] = false // skip_trimgalore + input[4] = false // skip_bbduk + input[5] = false // skip_fastp + input[6] = false // skip_adapterremoval + input[7] = false // skip_leehom + input[8] = true // skip_ngmerge + input[9] = false // do_merge + input[10] = [] + """ + } + } + + then { + assert workflow.success + + assert snapshot( + workflow.out.trimmed_reads.sort(), + workflow.out.versions.collect { path(it).yaml }.sort() + ).match() + } + } + + + /************************************************************** + * TEST 2 — PAIRED END + **************************************************************/ + test("sarscov2 - fastq - paired-end, no merging") { + config "./nextflow.config" + when { params { + samtools_arg = '' + } + workflow { + """ + input[0] = [ + [ id:'test_paired', single_end:false ], + [ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) + ] + ] + + input[1] = false // skip_trimmomatic + input[2] = false // skip_cutadapt + input[3] = false // skip_trimgalore + input[4] = false // skip_bbduk + input[5] = false // skip_fastp + input[6] = false // skip_adapterremoval + input[7] = false // skip_leehom + input[8] = true // skip_ngmerge + input[9] = false // do_merge + input[10] = [] + """ + } + } + + then { + assertAll( + { assert workflow.success }, + { assert snapshot(workflow.out).match() } + ) + } + } + +} diff --git a/subworkflows/nf-core/fastq_remove_adapters_and_merge/tests/main.nf.test.snap b/subworkflows/nf-core/fastq_remove_adapters_and_merge/tests/main.nf.test.snap new file mode 100644 index 000000000000..d66a72a5622e --- /dev/null +++ b/subworkflows/nf-core/fastq_remove_adapters_and_merge/tests/main.nf.test.snap @@ -0,0 +1,92 @@ +{ + "sarscov2 - fastq - paired-end, no merging": { + "content": [ + { + "0": [ + + ], + "1": [ + "versions.yml:md5,2d964cf60309a142f1c9958da875eb54", + "versions.yml:md5,90401b38cbb7e43510ca1a7cbcea3145", + "versions.yml:md5,95732be10dd312717626b93c727886ef", + "versions.yml:md5,aa5ed9a565951378f5d671bfa6b88370", + "versions.yml:md5,b9c8e5d6936d4d2dbc08084d03ee57c8", + "versions.yml:md5,de9c9c39a848b9543456f693e4ca6ad3" + ], + "trimmed_reads": [ + + ], + "versions": [ + "versions.yml:md5,2d964cf60309a142f1c9958da875eb54", + "versions.yml:md5,90401b38cbb7e43510ca1a7cbcea3145", + "versions.yml:md5,95732be10dd312717626b93c727886ef", + "versions.yml:md5,aa5ed9a565951378f5d671bfa6b88370", + "versions.yml:md5,b9c8e5d6936d4d2dbc08084d03ee57c8", + "versions.yml:md5,de9c9c39a848b9543456f693e4ca6ad3" + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-12-08T10:34:07.314632994" + }, + "sarscov2 - fastq - single-end": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.fq.gz:md5,6dcea64429b2bcf5e31751f9c8041914" + ] + ], + [ + { + "FASTQ_REMOVE_ADAPTERS_AND_MERGE:FASTP": { + "fastp": "1.0.1" + } + }, + { + "FASTQ_REMOVE_ADAPTERS_AND_MERGE:TRIMGALORE": { + "trimgalore": "0.6.10", + "cutadapt": 4.9, + "pigz": 2.8 + } + }, + { + "FASTQ_REMOVE_ADAPTERS_AND_MERGE:LEEHOM": { + "leehom": "1.2.15" + } + }, + { + "FASTQ_REMOVE_ADAPTERS_AND_MERGE:ADAPTERREMOVAL": { + "adapterremoval": "2.3.2" + } + }, + { + "FASTQ_REMOVE_ADAPTERS_AND_MERGE:TRIMMOMATIC": { + "trimmomatic": 0.39 + } + }, + { + "FASTQ_REMOVE_ADAPTERS_AND_MERGE:BBMAP_BBDUK": { + "bbmap": 39.18 + } + }, + { + "FASTQ_REMOVE_ADAPTERS_AND_MERGE:CUTADAPT": { + "cutadapt": 5.0 + } + } + ] + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.0" + }, + "timestamp": "2025-12-08T10:33:53.838298621" + } +} \ No newline at end of file diff --git a/subworkflows/nf-core/fastq_remove_adapters_and_merge/tests/nextflow.config b/subworkflows/nf-core/fastq_remove_adapters_and_merge/tests/nextflow.config new file mode 100644 index 000000000000..6b7b780d8765 --- /dev/null +++ b/subworkflows/nf-core/fastq_remove_adapters_and_merge/tests/nextflow.config @@ -0,0 +1,17 @@ +process { + + withName: TRIMMOMATIC { + ext.args = { + def adapters = "TruSeq3-SE.fa" + "ILLUMINACLIP:${adapters}:2:30:10 LEADING:3 TRAILING:3 SLIDINGWINDOW:4:15 MINLEN:36" + } + } + + withName: CUTADAPT { + ext.args = '-q 25' + } + withName: BBMAP_BBDUK { + ext.args = 'trimq=10 qtrim=r' + ext.prefix = { "${meta.id}.trim" } + } + }