From de563f1463f65f62a8f7b555bb07b42acddf59e0 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Wed, 20 May 2026 11:37:12 +0200 Subject: [PATCH 01/19] variant calling workflow --- .gitignore | 2 + conf/hpc.config | 2 + conf/modules.config | 36 ++ modules.json | 30 ++ modules/local/gtf2bed/environment.yml | 7 + modules/local/gtf2bed/main.nf | 41 +++ modules/local/gtf2bed/tests/main.nf.test | 48 +++ modules/local/gtf2bed/tests/main.nf.test.snap | 54 +++ .../gatk4/baserecalibrator/environment.yml | 10 + .../nf-core/gatk4/baserecalibrator/main.nf | 55 +++ .../nf-core/gatk4/baserecalibrator/meta.yml | 131 +++++++ .../gatk4/baserecalibrator/tests/main.nf.test | 250 +++++++++++++ .../baserecalibrator/tests/main.nf.test.snap | 330 ++++++++++++++++++ .../gatk4/bedtointervallist/environment.yml | 10 + .../nf-core/gatk4/bedtointervallist/main.nf | 47 +++ .../nf-core/gatk4/bedtointervallist/meta.yml | 78 +++++ .../bedtointervallist/tests/main.nf.test | 61 ++++ .../bedtointervallist/tests/main.nf.test.snap | 84 +++++ .../createsequencedictionary/environment.yml | 10 + .../gatk4/createsequencedictionary/main.nf | 43 +++ .../gatk4/createsequencedictionary/meta.yml | 68 ++++ .../tests/main.nf.test | 53 +++ .../tests/main.nf.test.snap | 84 +++++ .../gatk4/haplotypecaller/environment.yml | 10 + modules/nf-core/gatk4/haplotypecaller/main.nf | 68 ++++ .../nf-core/gatk4/haplotypecaller/meta.yml | 153 ++++++++ .../gatk4/haplotypecaller/tests/main.nf.test | 145 ++++++++ .../haplotypecaller/tests/main.nf.test.snap | 82 +++++ .../gatk4/intervallisttools/environment.yml | 10 + .../nf-core/gatk4/intervallisttools/main.nf | 64 ++++ .../nf-core/gatk4/intervallisttools/meta.yml | 66 ++++ .../intervallisttools/tests/main.nf.test | 69 ++++ .../intervallisttools/tests/main.nf.test.snap | 104 ++++++ .../intervallisttools/tests/nextflow.config | 5 + .../gatk4/splitncigarreads/environment.yml | 10 + .../nf-core/gatk4/splitncigarreads/main.nf | 52 +++ .../nf-core/gatk4/splitncigarreads/meta.yml | 102 ++++++ .../gatk4/splitncigarreads/tests/main.nf.test | 88 +++++ .../splitncigarreads/tests/main.nf.test.snap | 59 ++++ nextflow.config | 8 +- .../local/bam_variant_calling/main.nf | 114 ++++++ workflows/dxnextflowrna.nf | 31 +- 42 files changed, 2766 insertions(+), 8 deletions(-) create mode 100644 modules/local/gtf2bed/environment.yml create mode 100755 modules/local/gtf2bed/main.nf create mode 100644 modules/local/gtf2bed/tests/main.nf.test create mode 100644 modules/local/gtf2bed/tests/main.nf.test.snap create mode 100644 modules/nf-core/gatk4/baserecalibrator/environment.yml create mode 100644 modules/nf-core/gatk4/baserecalibrator/main.nf create mode 100644 modules/nf-core/gatk4/baserecalibrator/meta.yml create mode 100644 modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test create mode 100644 modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test.snap create mode 100644 modules/nf-core/gatk4/bedtointervallist/environment.yml create mode 100644 modules/nf-core/gatk4/bedtointervallist/main.nf create mode 100644 modules/nf-core/gatk4/bedtointervallist/meta.yml create mode 100644 modules/nf-core/gatk4/bedtointervallist/tests/main.nf.test create mode 100644 modules/nf-core/gatk4/bedtointervallist/tests/main.nf.test.snap create mode 100644 modules/nf-core/gatk4/createsequencedictionary/environment.yml create mode 100644 modules/nf-core/gatk4/createsequencedictionary/main.nf create mode 100644 modules/nf-core/gatk4/createsequencedictionary/meta.yml create mode 100644 modules/nf-core/gatk4/createsequencedictionary/tests/main.nf.test create mode 100644 modules/nf-core/gatk4/createsequencedictionary/tests/main.nf.test.snap create mode 100644 modules/nf-core/gatk4/haplotypecaller/environment.yml create mode 100644 modules/nf-core/gatk4/haplotypecaller/main.nf create mode 100644 modules/nf-core/gatk4/haplotypecaller/meta.yml create mode 100644 modules/nf-core/gatk4/haplotypecaller/tests/main.nf.test create mode 100644 modules/nf-core/gatk4/haplotypecaller/tests/main.nf.test.snap create mode 100644 modules/nf-core/gatk4/intervallisttools/environment.yml create mode 100644 modules/nf-core/gatk4/intervallisttools/main.nf create mode 100644 modules/nf-core/gatk4/intervallisttools/meta.yml create mode 100644 modules/nf-core/gatk4/intervallisttools/tests/main.nf.test create mode 100644 modules/nf-core/gatk4/intervallisttools/tests/main.nf.test.snap create mode 100644 modules/nf-core/gatk4/intervallisttools/tests/nextflow.config create mode 100644 modules/nf-core/gatk4/splitncigarreads/environment.yml create mode 100644 modules/nf-core/gatk4/splitncigarreads/main.nf create mode 100644 modules/nf-core/gatk4/splitncigarreads/meta.yml create mode 100644 modules/nf-core/gatk4/splitncigarreads/tests/main.nf.test create mode 100644 modules/nf-core/gatk4/splitncigarreads/tests/main.nf.test.snap create mode 100644 subworkflows/local/bam_variant_calling/main.nf diff --git a/.gitignore b/.gitignore index e5289c5..3621fb1 100644 --- a/.gitignore +++ b/.gitignore @@ -334,3 +334,5 @@ fabric.properties .nf-test/ .nf-test/* .nf-test* + +null/ diff --git a/conf/hpc.config b/conf/hpc.config index 83b92a4..985aeed 100644 --- a/conf/hpc.config +++ b/conf/hpc.config @@ -18,6 +18,8 @@ params { ref_flat = "${params.dx_tracks_path}/rna/${params.gencode_version_name}.ref_annot.gtf.refflat" rrna_intervals = "${params.dx_tracks_path}/rna/GRCh38_rRNA_genbank.interval_list" + // dbSNP + dbSNP = "/hpc/diaggen/data/databases/GATK_resource_bundle/hg38/dbsnp_146.hg38.vcf.gz" // Fusion config starfusion_ref = params.genome_base diff --git a/conf/modules.config b/conf/modules.config index 95aca41..e718b82 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -78,6 +78,42 @@ process { scratch = true } + withName: GATK4_BEDTOINTERVALLIST { + ext.args = '--DROP_MISSING_CONTIGS TRUE' + } + + withName: GATK4_INTERVALLISTTOOLS { + ext.args = { [ + "--SUBDIVISION_MODE BALANCING_WITHOUT_INTERVAL_SUBDIVISION_WITH_OVERFLOW", + "--UNIQUE true", + "--SORT true", + "--SCATTER_COUNT ${params.scatter_size}" + ].join(' ').trim() } + } + + withName: GATK4_HAPLOTYPECALLER { + cpus = 2 + memory = { 8.GB * task.attempt } + time = { 4.h * task.attempt } + array = params.scatter_size + + ext.args = { [ + "--dont-use-soft-clipped-bases", + "--standard-min-confidence-threshold-for-calling ${params.min_confidence_threshold}", + "--emit-ref-confidence GVCF" + ].join(' ').trim() } + } + + withName: GATK4_SPLITNCIGARREADS { + cpus = 2 + memory = { 16.GB * task.attempt } + time = { 4.h * task.attempt } + array = params.scatter_size + + ext.args = '--create-output-bam-index false' + ext.prefix = { "${meta.id}.splitncigarreads" } + } + withName: HGNC_DOWNLOAD { cpus = 1 memory = { 1.GB } diff --git a/modules.json b/modules.json index d231cd1..f1ad12c 100644 --- a/modules.json +++ b/modules.json @@ -25,6 +25,36 @@ "git_sha": "e753770db613ce014b3c4bc94f6cba443427b726", "installed_by": ["modules"] }, + "gatk4/baserecalibrator": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, + "gatk4/bedtointervallist": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, + "gatk4/createsequencedictionary": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, + "gatk4/haplotypecaller": { + "branch": "master", + "git_sha": "b73338eca19d798eb0ef6ed112ef4d27a30d2871", + "installed_by": ["modules"] + }, + "gatk4/intervallisttools": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, + "gatk4/splitncigarreads": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, "multiqc": { "branch": "master", "git_sha": "cf17ca47590cc578dfb47db1c2a44ef86f89976d", diff --git a/modules/local/gtf2bed/environment.yml b/modules/local/gtf2bed/environment.yml new file mode 100644 index 0000000..4e537c7 --- /dev/null +++ b/modules/local/gtf2bed/environment.yml @@ -0,0 +1,7 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + - conda-forge::r-base=3.5.1 diff --git a/modules/local/gtf2bed/main.nf b/modules/local/gtf2bed/main.nf new file mode 100755 index 0000000..03bb2b2 --- /dev/null +++ b/modules/local/gtf2bed/main.nf @@ -0,0 +1,41 @@ +process GTF2BED { + tag "${gtf}" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container + ? 'https://depot.galaxyproject.org/singularity/r-base:3.5.1' + : 'biocontainers/r-base:3.5.1'}" + + input: + tuple val(meta), path(gtf) + val feature_type + + output: + tuple val(meta), path('*.bed'), emit: bed + tuple val("${task.process}"), val('Rscript'), eval("Rscript --version 2>&1 | sed 's/R scripting front-end version //'"), topic: versions, emit: versions_rscript + + when: + task.ext.when == null || task.ext.when + + script: + def allowed_type = ["exon", "transcript", "gene"] + if (feature_type) { + feature_type = allowed_type.contains(feature_type) ? feature_type : "exon" + } + """ + Rscript --no-save -<<'RCODE' + gtf = read.table("${gtf}", sep="\t") + gtf = subset(gtf, V3 == "${feature_type}") + write.table(data.frame(chrom=gtf[,'V1'], start=gtf[,'V4'], end=gtf[,'V5']), "tmp.exome.bed", quote = F, sep="\t", col.names = F, row.names = F) + RCODE + + awk '{print \$1 "\t" (\$2 - 1) "\t" \$3}' tmp.exome.bed > exome.bed + rm -rf tmp.exome.bed + """ + + stub: + """ + touch exome.bed + """ +} diff --git a/modules/local/gtf2bed/tests/main.nf.test b/modules/local/gtf2bed/tests/main.nf.test new file mode 100644 index 0000000..424c4ee --- /dev/null +++ b/modules/local/gtf2bed/tests/main.nf.test @@ -0,0 +1,48 @@ +nextflow_process { + + name "Test Process GTF2BED" + script "../main.nf" + process "GTF2BED" + + test("gtf") { + + when { + process { + """ + input[0] = Channel.value([ + [id:'gtf'], + file(params.pipelines_testdata_base_path + '/genomics/homo_sapiens/genome/genome.gtf', checkIfExists: true) + ]) + input[1] = "exon" + """ + } + } + + then { + assert process.success + assert snapshot(sanitizeOutput(process.out)).match() + } + } + + test("gtf - stub") { + + options "-stub" + + when { + process { + """ + input[0] = Channel.value([ + [id:'gtf'], + file(params.pipelines_testdata_base_path + '/genomics/homo_sapiens/genome/genome.gtf', checkIfExists: true) + ]) + input[1] = "exon" + """ + } + } + + then { + assert process.success + assert snapshot(sanitizeOutput(process.out)).match() + } + } +} diff --git a/modules/local/gtf2bed/tests/main.nf.test.snap b/modules/local/gtf2bed/tests/main.nf.test.snap new file mode 100644 index 0000000..7b1287a --- /dev/null +++ b/modules/local/gtf2bed/tests/main.nf.test.snap @@ -0,0 +1,54 @@ +{ + "gtf": { + "content": [ + { + "bed": [ + [ + { + "id": "gtf" + }, + "exome.bed:md5,17192418c24c524881e7d970f94a4a26" + ] + ], + "versions_rscript": [ + [ + "GTF2BED", + "Rscript", + "3.5.1 (2018-07-02)" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.04.8" + }, + "timestamp": "2025-10-16T15:25:07.682657227" + }, + "gtf - stub": { + "content": [ + { + "bed": [ + [ + { + "id": "gtf" + }, + "exome.bed:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_rscript": [ + [ + "GTF2BED", + "Rscript", + "3.5.1 (2018-07-02)" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.04.8" + }, + "timestamp": "2025-10-16T15:27:28.797666996" + } +} \ No newline at end of file diff --git a/modules/nf-core/gatk4/baserecalibrator/environment.yml b/modules/nf-core/gatk4/baserecalibrator/environment.yml new file mode 100644 index 0000000..67e0eb8 --- /dev/null +++ b/modules/nf-core/gatk4/baserecalibrator/environment.yml @@ -0,0 +1,10 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + # renovate: datasource=conda depName=bioconda/gatk4 + - bioconda::gatk4=4.6.2.0 + # renovate: datasource=conda depName=bioconda/gcnvkernel + - bioconda::gcnvkernel=0.9 diff --git a/modules/nf-core/gatk4/baserecalibrator/main.nf b/modules/nf-core/gatk4/baserecalibrator/main.nf new file mode 100644 index 0000000..d7c5c65 --- /dev/null +++ b/modules/nf-core/gatk4/baserecalibrator/main.nf @@ -0,0 +1,55 @@ +process GATK4_BASERECALIBRATOR { + tag "${meta.id}" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/ce/ced519873646379e287bc28738bdf88e975edd39a92e7bc6a34bccd37153d9d0/data' + : 'community.wave.seqera.io/library/gatk4_gcnvkernel:edb12e4f0bf02cd3'}" + + input: + tuple val(meta), path(input), path(input_index), path(intervals) + tuple val(meta2), path(fasta) + tuple val(meta3), path(fai) + tuple val(meta4), path(dict) + tuple val(meta5), path(known_sites) + tuple val(meta6), path(known_sites_tbi) + + output: + tuple val(meta), path("*.table"), emit: table + tuple val("${task.process}"), val('gatk4'), eval("gatk --version | sed -n '/GATK.*v/s/.*v//p'"), topic: versions, emit: versions_gatk4 + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def interval_command = intervals ? "--intervals ${intervals}" : "" + def sites_command = known_sites.collect { vcf -> "--known-sites ${vcf}" }.join(' ') + + def avail_mem = 3072 + if (!task.memory) { + log.info('[GATK BaseRecalibrator] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.') + } + else { + avail_mem = (task.memory.mega * 0.8).intValue() + } + """ + gatk --java-options "-Xmx${avail_mem}M -XX:-UsePerfData" \\ + BaseRecalibrator \\ + --input ${input} \\ + --output ${prefix}.table \\ + --reference ${fasta} \\ + ${interval_command} \\ + ${sites_command} \\ + --tmp-dir . \\ + ${args} + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.table + """ +} diff --git a/modules/nf-core/gatk4/baserecalibrator/meta.yml b/modules/nf-core/gatk4/baserecalibrator/meta.yml new file mode 100644 index 0000000..af53133 --- /dev/null +++ b/modules/nf-core/gatk4/baserecalibrator/meta.yml @@ -0,0 +1,131 @@ +name: gatk4_baserecalibrator +description: Generate recalibration table for Base Quality Score Recalibration (BQSR) +keywords: + - base quality score recalibration + - table + - bqsr + - gatk4 + - sort +tools: + - gatk4: + description: | + Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools + with a primary focus on variant discovery and genotyping. Its powerful processing engine + and high-performance computing features make it capable of taking on projects of any size. + homepage: https://gatk.broadinstitute.org/hc/en-us + documentation: https://gatk.broadinstitute.org/hc/en-us/categories/360002369672s + doi: 10.1158/1538-7445.AM2017-3590 + licence: ["Apache-2.0"] + identifier: "" +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - input: + type: file + description: BAM/CRAM file from alignment + pattern: "*.{bam,cram}" + ontologies: [] + - input_index: + type: file + description: BAI/CRAI file from alignment + pattern: "*.{bai,crai}" + ontologies: [] + - intervals: + type: file + description: Bed file with the genomic regions included in the library (optional) + ontologies: [] + - - meta2: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome'] + - fasta: + type: file + description: The reference fasta file + pattern: "*.fasta" + ontologies: [] + - - meta3: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome'] + - fai: + type: file + description: Index of reference fasta file + pattern: "*.fasta.fai" + ontologies: [] + - - meta4: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome'] + - dict: + type: file + description: GATK sequence dictionary + pattern: "*.dict" + ontologies: [] + - - meta5: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome'] + - known_sites: + type: file + description: VCF files with known sites for indels / snps + pattern: "*.vcf.gz" + ontologies: + - edam: http://edamontology.org/format_3989 # GZIP format + - - meta6: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome'] + - known_sites_tbi: + type: file + description: Tabix index of the known_sites + pattern: "*.vcf.gz.tbi" + ontologies: [] +output: + table: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.table": + type: file + description: Recalibration table from BaseRecalibrator + pattern: "*.{table}" + ontologies: [] + versions_gatk4: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@yocra3" + - "@FriederikeHanssen" + - "@maxulysse" +maintainers: + - "@yocra3" + - "@FriederikeHanssen" + - "@maxulysse" diff --git a/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test b/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test new file mode 100644 index 0000000..0167404 --- /dev/null +++ b/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test @@ -0,0 +1,250 @@ +nextflow_process { + + name "Test Process GATK4_BASERECALIBRATOR" + script "../main.nf" + process "GATK4_BASERECALIBRATOR" + + tag "modules" + tag "modules_nfcore" + tag "gatk4" + tag "gatk4/baserecalibrator" + + test("sarscov2 - bam") { + when { + process { + """ + input[0] = Channel.of([ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), + [] + ]) + input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) + input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) + input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) + input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true)]) + input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true)]) + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } + + test("sarscov2 - bam - intervals") { + when { + process { + """ + input[0] = Channel.of([ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/bed/test.bed', checkIfExists: true) + ]) + input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) + input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) + input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) + input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true)]) + input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true)]) + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + + } + + test("sarscov2 - bam - multiple sites") { + when { + process { + """ + input[0] = Channel.of([ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), + [] + ]) + input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) + input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) + input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) + input[4] = Channel.of([ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test2.vcf.gz', checkIfExists: true) + ]) + input[5] = Channel.of([ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test2.vcf.gz.tbi', checkIfExists: true) + ]) + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } + + test("homo_sapiens - cram") { + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram.crai', checkIfExists: true), + [] + ]) + input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true)]) + input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true)]) + input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true)]) + input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz', checkIfExists: true)]) + input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz.tbi', checkIfExists: true)]) + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } + + test("sarscov2 - bam - stub") { + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), + [] + ]) + input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) + input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) + input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) + input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true)]) + input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true)]) + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } + + test("sarscov2 - bam - intervals - stub") { + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/bed/test.bed', checkIfExists: true) + ]) + input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) + input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) + input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) + input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true)]) + input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true)]) + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + + } + + test("sarscov2 - bam - multiple sites - stub") { + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), + [] + ]) + input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) + input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) + input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) + input[4] = Channel.of([ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test2.vcf.gz', checkIfExists: true) + ]) + input[5] = Channel.of([ + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test2.vcf.gz.tbi', checkIfExists: true) + ]) + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } + + test("homo_sapiens - cram - stub") { + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram.crai', checkIfExists: true), + [] + ]) + input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true)]) + input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true)]) + input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true)]) + input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz', checkIfExists: true)]) + input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz.tbi', checkIfExists: true)]) + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test.snap b/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test.snap new file mode 100644 index 0000000..3f38dd8 --- /dev/null +++ b/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test.snap @@ -0,0 +1,330 @@ +{ + "homo_sapiens - cram": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.table:md5,35d89a3811aa31711fc9815b6b80e6ec" + ] + ], + "1": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ], + "table": [ + [ + { + "id": "test" + }, + "test.table:md5,35d89a3811aa31711fc9815b6b80e6ec" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T15:30:03.162339291" + }, + "sarscov2 - bam - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ], + "table": [ + [ + { + "id": "test" + }, + "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T15:30:16.189687912" + }, + "sarscov2 - bam - intervals": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.table:md5,9ecb5f00a2229291705addc09c0ec231" + ] + ], + "1": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ], + "table": [ + [ + { + "id": "test" + }, + "test.table:md5,9ecb5f00a2229291705addc09c0ec231" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T15:29:29.326290321" + }, + "sarscov2 - bam - multiple sites": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.table:md5,e2e43abdc0c943c1a54dae816d0b9ea7" + ] + ], + "1": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ], + "table": [ + [ + { + "id": "test" + }, + "test.table:md5,e2e43abdc0c943c1a54dae816d0b9ea7" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T15:29:45.46009485" + }, + "homo_sapiens - cram - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ], + "table": [ + [ + { + "id": "test" + }, + "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T15:30:54.713622275" + }, + "sarscov2 - bam": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.table:md5,e2e43abdc0c943c1a54dae816d0b9ea7" + ] + ], + "1": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ], + "table": [ + [ + { + "id": "test" + }, + "test.table:md5,e2e43abdc0c943c1a54dae816d0b9ea7" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T15:29:13.31883295" + }, + "sarscov2 - bam - intervals - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ], + "table": [ + [ + { + "id": "test" + }, + "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T15:30:29.349106158" + }, + "sarscov2 - bam - multiple sites - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ], + "table": [ + [ + { + "id": "test" + }, + "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BASERECALIBRATOR", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T15:30:41.454826856" + } +} \ No newline at end of file diff --git a/modules/nf-core/gatk4/bedtointervallist/environment.yml b/modules/nf-core/gatk4/bedtointervallist/environment.yml new file mode 100644 index 0000000..67e0eb8 --- /dev/null +++ b/modules/nf-core/gatk4/bedtointervallist/environment.yml @@ -0,0 +1,10 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + # renovate: datasource=conda depName=bioconda/gatk4 + - bioconda::gatk4=4.6.2.0 + # renovate: datasource=conda depName=bioconda/gcnvkernel + - bioconda::gcnvkernel=0.9 diff --git a/modules/nf-core/gatk4/bedtointervallist/main.nf b/modules/nf-core/gatk4/bedtointervallist/main.nf new file mode 100644 index 0000000..8a86bf7 --- /dev/null +++ b/modules/nf-core/gatk4/bedtointervallist/main.nf @@ -0,0 +1,47 @@ +process GATK4_BEDTOINTERVALLIST { + tag "${meta.id}" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/ce/ced519873646379e287bc28738bdf88e975edd39a92e7bc6a34bccd37153d9d0/data' + : 'community.wave.seqera.io/library/gatk4_gcnvkernel:edb12e4f0bf02cd3'}" + + input: + tuple val(meta), path(bed) + tuple val(meta2), path(dict) + + output: + tuple val(meta), path('*.interval_list'), emit: interval_list + tuple val("${task.process}"), val('gatk4'), eval("gatk --version | sed -n '/GATK.*v/s/.*v//p'"), topic: versions, emit: versions_gatk4 + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + def avail_mem = 3072 + if (!task.memory) { + log.info('[GATK BedToIntervalList] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.') + } + else { + avail_mem = (task.memory.mega * 0.8).intValue() + } + """ + gatk --java-options "-Xmx${avail_mem}M -XX:-UsePerfData" \\ + BedToIntervalList \\ + --INPUT ${bed} \\ + --OUTPUT ${prefix}.interval_list \\ + --SEQUENCE_DICTIONARY ${dict} \\ + --TMP_DIR . \\ + ${args} + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.interval_list + """ +} diff --git a/modules/nf-core/gatk4/bedtointervallist/meta.yml b/modules/nf-core/gatk4/bedtointervallist/meta.yml new file mode 100644 index 0000000..a3e2f23 --- /dev/null +++ b/modules/nf-core/gatk4/bedtointervallist/meta.yml @@ -0,0 +1,78 @@ +name: gatk4_bedtointervallist +description: Creates an interval list from a bed file and a reference dict +keywords: + - bed + - bedtointervallist + - gatk4 + - interval list +tools: + - gatk4: + description: | + Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools + with a primary focus on variant discovery and genotyping. Its powerful processing engine + and high-performance computing features make it capable of taking on projects of any size. + homepage: https://gatk.broadinstitute.org/hc/en-us + documentation: https://gatk.broadinstitute.org/hc/en-us/categories/360002369672s + doi: 10.1158/1538-7445.AM2017-3590 + licence: ["Apache-2.0"] + identifier: "" +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - bed: + type: file + description: Input bed file + pattern: "*.bed" + ontologies: [] + - - meta2: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome' ] + - dict: + type: file + description: Sequence dictionary + pattern: "*.dict" + ontologies: [] +output: + interval_list: + - - meta: + type: file + description: gatk interval list file + pattern: "*.interval_list" + ontologies: [] + - "*.interval_list": + type: file + description: gatk interval list file + pattern: "*.interval_list" + ontologies: [] + versions_gatk4: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@kevinmenden" + - "@ramprasadn" +maintainers: + - "@kevinmenden" + - "@ramprasadn" diff --git a/modules/nf-core/gatk4/bedtointervallist/tests/main.nf.test b/modules/nf-core/gatk4/bedtointervallist/tests/main.nf.test new file mode 100644 index 0000000..8d318e9 --- /dev/null +++ b/modules/nf-core/gatk4/bedtointervallist/tests/main.nf.test @@ -0,0 +1,61 @@ +nextflow_process { + + name "Test Process GATK4_BEDTOINTERVALLIST" + script "../main.nf" + process "GATK4_BEDTOINTERVALLIST" + + tag "modules" + tag "modules_nfcore" + tag "gatk4" + tag "gatk4/bedtointervallist" + + test("test_gatk4_bedtointervallist") { + + when { + process { + """ + input[0] = [ [ id:'test' ], // meta map + [file(params.modules_testdata_base_path + + 'genomics/sarscov2/genome/bed/test.bed', checkIfExists: true) ] + ] + input[1] = [ [ id:'dict' ], // meta map + [file(params.modules_testdata_base_path + + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true) ] + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } + + test("test_gatk4_bedtointervallist - stub") { + options "-stub" + when { + process { + """ + input[0] = [ [ id:'test' ], // meta map + [file(params.modules_testdata_base_path + + 'genomics/sarscov2/genome/bed/test.bed', checkIfExists: true) ] + ] + input[1] = [ [ id:'dict' ], // meta map + [file(params.modules_testdata_base_path + + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true) ] + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/gatk4/bedtointervallist/tests/main.nf.test.snap b/modules/nf-core/gatk4/bedtointervallist/tests/main.nf.test.snap new file mode 100644 index 0000000..5544250 --- /dev/null +++ b/modules/nf-core/gatk4/bedtointervallist/tests/main.nf.test.snap @@ -0,0 +1,84 @@ +{ + "test_gatk4_bedtointervallist - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.interval_list:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "GATK4_BEDTOINTERVALLIST", + "gatk4", + "4.6.2.0" + ] + ], + "interval_list": [ + [ + { + "id": "test" + }, + "test.interval_list:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BEDTOINTERVALLIST", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-22T09:52:55.698969373" + }, + "test_gatk4_bedtointervallist": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.interval_list:md5,e51101c9357fb2d59fd30e370eefa39c" + ] + ], + "1": [ + [ + "GATK4_BEDTOINTERVALLIST", + "gatk4", + "4.6.2.0" + ] + ], + "interval_list": [ + [ + { + "id": "test" + }, + "test.interval_list:md5,e51101c9357fb2d59fd30e370eefa39c" + ] + ], + "versions_gatk4": [ + [ + "GATK4_BEDTOINTERVALLIST", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-22T09:52:46.629308651" + } +} \ No newline at end of file diff --git a/modules/nf-core/gatk4/createsequencedictionary/environment.yml b/modules/nf-core/gatk4/createsequencedictionary/environment.yml new file mode 100644 index 0000000..67e0eb8 --- /dev/null +++ b/modules/nf-core/gatk4/createsequencedictionary/environment.yml @@ -0,0 +1,10 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + # renovate: datasource=conda depName=bioconda/gatk4 + - bioconda::gatk4=4.6.2.0 + # renovate: datasource=conda depName=bioconda/gcnvkernel + - bioconda::gcnvkernel=0.9 diff --git a/modules/nf-core/gatk4/createsequencedictionary/main.nf b/modules/nf-core/gatk4/createsequencedictionary/main.nf new file mode 100644 index 0000000..98ad30c --- /dev/null +++ b/modules/nf-core/gatk4/createsequencedictionary/main.nf @@ -0,0 +1,43 @@ +process GATK4_CREATESEQUENCEDICTIONARY { + tag "${fasta}" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/ce/ced519873646379e287bc28738bdf88e975edd39a92e7bc6a34bccd37153d9d0/data' + : 'community.wave.seqera.io/library/gatk4_gcnvkernel:edb12e4f0bf02cd3'}" + + input: + tuple val(meta), path(fasta) + + output: + tuple val(meta), path('*.dict'), emit: dict + tuple val("${task.process}"), val('gatk4'), eval("gatk --version | sed -n '/GATK.*v/s/.*v//p'"), topic: versions, emit: versions_gatk4 + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + + def avail_mem = 6144 + if (!task.memory) { + log.info('[GATK CreateSequenceDictionary] Available memory not known - defaulting to 6GB. Specify process memory requirements to change this.') + } + else { + avail_mem = (task.memory.mega * 0.8).intValue() + } + """ + gatk --java-options "-Xmx${avail_mem}M -XX:-UsePerfData" \\ + CreateSequenceDictionary \\ + --REFERENCE ${fasta} \\ + --URI ${fasta} \\ + --TMP_DIR . \\ + ${args} + """ + + stub: + """ + touch ${fasta.baseName}.dict + """ +} diff --git a/modules/nf-core/gatk4/createsequencedictionary/meta.yml b/modules/nf-core/gatk4/createsequencedictionary/meta.yml new file mode 100644 index 0000000..5fdfcc2 --- /dev/null +++ b/modules/nf-core/gatk4/createsequencedictionary/meta.yml @@ -0,0 +1,68 @@ +name: gatk4_createsequencedictionary +description: Creates a sequence dictionary for a reference sequence +keywords: + - createsequencedictionary + - dictionary + - fasta + - gatk4 +tools: + - gatk: + description: | + Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools + with a primary focus on variant discovery and genotyping. Its powerful processing engine + and high-performance computing features make it capable of taking on projects of any size. + homepage: https://gatk.broadinstitute.org/hc/en-us + documentation: https://gatk.broadinstitute.org/hc/en-us/categories/360002369672s + doi: 10.1158/1538-7445.AM2017-3590 + licence: ["Apache-2.0"] + identifier: "" +input: + - - meta: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome' ] + - fasta: + type: file + description: Input fasta file + pattern: "*.{fasta,fa}" + ontologies: [] +output: + dict: + - - meta: + type: file + description: gatk dictionary file + pattern: "*.{dict}" + ontologies: [] + - "*.dict": + type: file + description: gatk dictionary file + pattern: "*.{dict}" + ontologies: [] + versions_gatk4: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@maxulysse" + - "@ramprasadn" +maintainers: + - "@maxulysse" + - "@ramprasadn" diff --git a/modules/nf-core/gatk4/createsequencedictionary/tests/main.nf.test b/modules/nf-core/gatk4/createsequencedictionary/tests/main.nf.test new file mode 100644 index 0000000..076eb0d --- /dev/null +++ b/modules/nf-core/gatk4/createsequencedictionary/tests/main.nf.test @@ -0,0 +1,53 @@ +nextflow_process { + + name "Test Process GATK4_CREATESEQUENCEDICTIONARY" + script "../main.nf" + process "GATK4_CREATESEQUENCEDICTIONARY" + + tag "modules" + tag "modules_nfcore" + tag "gatk4" + tag "gatk4/createsequencedictionary" + + test("sarscov2 - fasta") { + + when { + process { + """ + input[0] = [ [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } + + test("sarscov2 - fasta - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/gatk4/createsequencedictionary/tests/main.nf.test.snap b/modules/nf-core/gatk4/createsequencedictionary/tests/main.nf.test.snap new file mode 100644 index 0000000..357cd58 --- /dev/null +++ b/modules/nf-core/gatk4/createsequencedictionary/tests/main.nf.test.snap @@ -0,0 +1,84 @@ +{ + "sarscov2 - fasta - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "genome.dict:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "GATK4_CREATESEQUENCEDICTIONARY", + "gatk4", + "4.6.2.0" + ] + ], + "dict": [ + [ + { + "id": "test" + }, + "genome.dict:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_gatk4": [ + [ + "GATK4_CREATESEQUENCEDICTIONARY", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T16:01:50.146061464" + }, + "sarscov2 - fasta": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "genome.dict:md5,7362679f176e0f52add03c08f457f646" + ] + ], + "1": [ + [ + "GATK4_CREATESEQUENCEDICTIONARY", + "gatk4", + "4.6.2.0" + ] + ], + "dict": [ + [ + { + "id": "test" + }, + "genome.dict:md5,7362679f176e0f52add03c08f457f646" + ] + ], + "versions_gatk4": [ + [ + "GATK4_CREATESEQUENCEDICTIONARY", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-01-21T16:01:38.694508582" + } +} \ No newline at end of file diff --git a/modules/nf-core/gatk4/haplotypecaller/environment.yml b/modules/nf-core/gatk4/haplotypecaller/environment.yml new file mode 100644 index 0000000..67e0eb8 --- /dev/null +++ b/modules/nf-core/gatk4/haplotypecaller/environment.yml @@ -0,0 +1,10 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + # renovate: datasource=conda depName=bioconda/gatk4 + - bioconda::gatk4=4.6.2.0 + # renovate: datasource=conda depName=bioconda/gcnvkernel + - bioconda::gcnvkernel=0.9 diff --git a/modules/nf-core/gatk4/haplotypecaller/main.nf b/modules/nf-core/gatk4/haplotypecaller/main.nf new file mode 100644 index 0000000..ff69f6c --- /dev/null +++ b/modules/nf-core/gatk4/haplotypecaller/main.nf @@ -0,0 +1,68 @@ +process GATK4_HAPLOTYPECALLER { + tag "${meta.id}" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/ce/ced519873646379e287bc28738bdf88e975edd39a92e7bc6a34bccd37153d9d0/data' + : 'community.wave.seqera.io/library/gatk4_gcnvkernel:edb12e4f0bf02cd3'}" + + input: + tuple val(meta), path(input), path(input_index), path(intervals), path(dragstr_model) + tuple val(meta2), path(fasta) + tuple val(meta3), path(fai) + tuple val(meta4), path(dict) + tuple val(meta5), path(dbsnp) + tuple val(meta6), path(dbsnp_tbi) + + output: + tuple val(meta), path("*.vcf.gz"), emit: vcf + tuple val(meta), path("*.tbi"), emit: tbi, optional: true + tuple val(meta), path("*.realigned.bam"), emit: bam, optional: true + tuple val("${task.process}"), val('gatk4'), eval("gatk --version | sed -n '/GATK.*v/s/.*v//p'"), topic: versions, emit: versions_gatk4 + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def dbsnp_command = dbsnp ? "--dbsnp ${dbsnp}" : "" + def interval_command = intervals ? "--intervals ${intervals}" : "" + def dragstr_command = dragstr_model ? "--dragstr-params-path ${dragstr_model}" : "" + def bamout_command = args.contains("--bam-writer-type") ? "--bam-output ${prefix.replaceAll('.g\\s*$', '')}.realigned.bam" : "" + + def avail_mem = 3072 + if (!task.memory) { + log.info('[GATK HaplotypeCaller] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.') + } + else { + avail_mem = (task.memory.mega * 0.8).intValue() + } + """ + gatk --java-options "-Xmx${avail_mem}M -XX:-UsePerfData" \\ + HaplotypeCaller \\ + --input ${input} \\ + --output ${prefix}.vcf.gz \\ + --reference ${fasta} \\ + --native-pair-hmm-threads ${task.cpus} \\ + ${dbsnp_command} \\ + ${interval_command} \\ + ${dragstr_command} \\ + ${bamout_command} \\ + --tmp-dir . \\ + ${args} + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def bamout_command = args.contains("--bam-writer-type") ? "--bam-output ${prefix.replaceAll('.g\\s*$', '')}.realigned.bam" : "" + + def stub_realigned_bam = bamout_command ? "touch ${prefix.replaceAll('.g\\s*$', '')}.realigned.bam" : "" + """ + echo | gzip > ${prefix}.vcf.gz + touch ${prefix}.vcf.gz.tbi + ${stub_realigned_bam} + """ +} diff --git a/modules/nf-core/gatk4/haplotypecaller/meta.yml b/modules/nf-core/gatk4/haplotypecaller/meta.yml new file mode 100644 index 0000000..29a29ba --- /dev/null +++ b/modules/nf-core/gatk4/haplotypecaller/meta.yml @@ -0,0 +1,153 @@ +name: gatk4_haplotypecaller +description: Call germline SNPs and indels via local re-assembly of haplotypes +keywords: + - gatk4 + - haplotype + - haplotypecaller +tools: + - gatk4: + description: | + Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools + with a primary focus on variant discovery and genotyping. Its powerful processing engine + and high-performance computing features make it capable of taking on projects of any size. + homepage: https://gatk.broadinstitute.org/hc/en-us + documentation: https://gatk.broadinstitute.org/hc/en-us/categories/360002369672s + doi: 10.1158/1538-7445.AM2017-3590 + licence: ["Apache-2.0"] + identifier: "" +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - input: + type: file + description: BAM/CRAM file from alignment + pattern: "*.{bam,cram}" + ontologies: [] + - input_index: + type: file + description: BAI/CRAI file from alignment + pattern: "*.{bai,crai}" + ontologies: [] + - intervals: + type: file + description: Bed file with the genomic regions included in the library (optional) + ontologies: [] + - dragstr_model: + type: file + description: Text file containing the DragSTR model of the used BAM/CRAM file + (optional) + pattern: "*.txt" + ontologies: [] + - - meta2: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'test_reference' ] + - fasta: + type: file + description: The reference fasta file + pattern: "*.fasta" + ontologies: [] + - - meta3: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'test_reference' ] + - fai: + type: file + description: Index of reference fasta file + pattern: "fasta.fai" + ontologies: [] + - - meta4: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'test_reference' ] + - dict: + type: file + description: GATK sequence dictionary + pattern: "*.dict" + ontologies: [] + - - meta5: + type: map + description: | + Groovy Map containing dbsnp information + e.g. [ id:'test_dbsnp' ] + - dbsnp: + type: file + description: VCF file containing known sites (optional) + ontologies: [] + - - meta6: + type: map + description: | + Groovy Map containing dbsnp information + e.g. [ id:'test_dbsnp' ] + - dbsnp_tbi: + type: file + description: VCF index of dbsnp (optional) + ontologies: [] +output: + vcf: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.vcf.gz": + type: file + description: Compressed VCF file + pattern: "*.vcf.gz" + ontologies: + - edam: http://edamontology.org/format_3989 # GZIP format + tbi: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.tbi": + type: file + description: Index of VCF file + pattern: "*.vcf.gz.tbi" + ontologies: [] + bam: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.realigned.bam": + type: file + description: Assembled haplotypes and locally realigned reads + pattern: "*.realigned.bam" + ontologies: [] + versions_gatk4: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@suzannejin" + - "@FriederikeHanssen" +maintainers: + - "@suzannejin" + - "@FriederikeHanssen" diff --git a/modules/nf-core/gatk4/haplotypecaller/tests/main.nf.test b/modules/nf-core/gatk4/haplotypecaller/tests/main.nf.test new file mode 100644 index 0000000..32b106e --- /dev/null +++ b/modules/nf-core/gatk4/haplotypecaller/tests/main.nf.test @@ -0,0 +1,145 @@ +// nf-core modules test gatk4/haplotypecaller +nextflow_process { + + name "Test Process GATK4_HAPLOTYPECALLER" + script "../main.nf" + process "GATK4_HAPLOTYPECALLER" + + tag "modules" + tag "modules_nfcore" + tag "gatk4" + tag "gatk4/haplotypecaller" + + test("homo_sapiens - [bam, bai] - fasta - fai - dict") { + + when { + process { + """ + input[0] = [ + [ id:'test_bam' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), + [], + [] + ] + input[1] = [ [ id:'test_fa' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true) ] + input[2] = [ [ id:'test_fai' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) ] + input[3] = [ [ id:'test_dict' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true) ] + input[4] = [ [], [] ] + input[5] = [ [], [] ] + + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.vcf[0][1]).name, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() } + ) + } + } + + test("homo_sapiens - [cram, crai] - fasta - fai - dict") { + + when { + process { + """ + input[0] = [ + [ id:'test_cram' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram.crai', checkIfExists: true), + [], + [] + ] + input[1] = [ [ id:'test_fa' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true) ] + input[2] = [ [ id:'test_fai' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) ] + input[3] = [ [ id:'test_dict' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true) ] + input[4] = [ [], [] ] + input[5] = [ [], [] ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.vcf[0][1]).name, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() } + ) + } + } + + test("homo_sapiens - [cram, crai] - fasta - fai - dict - sites - sites_tbi") { + + when { + process { + """ + input[0] = [ + [ id:'test_cram_sites' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram.crai', checkIfExists: true), + [], + [] + ] + input[1] = [ [ id:'test_fa' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true) ] + input[2] = [ [ id:'test_fai' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) ] + input[3] = [ [ id:'test_dict' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true) ] + input[4] = [ [ id:'test_sites' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz', checkIfExists: true) ] + input[5] = [ [ id:'test_sites_tbi' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz.tbi', checkIfExists: true) ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.vcf[0][1]).name, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() } + ) + } + } + + test("homo_sapiens - [cram, crai, dragstr_model] - fasta - fai - dict - sites - sites_tbi") { + + when { + process { + """ + input[0] = [ + [ id:'test_cram_sites_dragstr' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram.crai', checkIfExists: true), + [], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/gatk/test_paired_end_sorted_dragstrmodel.txt', checkIfExists: true) + ] + input[1] = [ [ id:'test_fa' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true) ] + input[2] = [ [ id:'test_fai' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) ] + input[3] = [ [ id:'test_dict' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true) ] + input[4] = [ [ id:'test_sites' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz', checkIfExists: true) ] + input[5] = [ [ id:'test_sites_tbi' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz.tbi', checkIfExists: true) ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.vcf[0][1]).name, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() } + ) + } + } +} diff --git a/modules/nf-core/gatk4/haplotypecaller/tests/main.nf.test.snap b/modules/nf-core/gatk4/haplotypecaller/tests/main.nf.test.snap new file mode 100644 index 0000000..180ad2b --- /dev/null +++ b/modules/nf-core/gatk4/haplotypecaller/tests/main.nf.test.snap @@ -0,0 +1,82 @@ +{ + "homo_sapiens - [cram, crai] - fasta - fai - dict": { + "content": [ + "test_cram.vcf.gz", + "test_cram.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_HAPLOTYPECALLER", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T16:28:30.201888121" + }, + "homo_sapiens - [cram, crai] - fasta - fai - dict - sites - sites_tbi": { + "content": [ + "test_cram_sites.vcf.gz", + "test_cram_sites.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_HAPLOTYPECALLER", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T16:28:39.948711806" + }, + "homo_sapiens - [bam, bai] - fasta - fai - dict": { + "content": [ + "test_bam.vcf.gz", + "test_bam.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_HAPLOTYPECALLER", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T16:28:20.2518415" + }, + "homo_sapiens - [cram, crai, dragstr_model] - fasta - fai - dict - sites - sites_tbi": { + "content": [ + "test_cram_sites_dragstr.vcf.gz", + "test_cram_sites_dragstr.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_HAPLOTYPECALLER", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T16:28:49.357679232" + } +} \ No newline at end of file diff --git a/modules/nf-core/gatk4/intervallisttools/environment.yml b/modules/nf-core/gatk4/intervallisttools/environment.yml new file mode 100644 index 0000000..67e0eb8 --- /dev/null +++ b/modules/nf-core/gatk4/intervallisttools/environment.yml @@ -0,0 +1,10 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + # renovate: datasource=conda depName=bioconda/gatk4 + - bioconda::gatk4=4.6.2.0 + # renovate: datasource=conda depName=bioconda/gcnvkernel + - bioconda::gcnvkernel=0.9 diff --git a/modules/nf-core/gatk4/intervallisttools/main.nf b/modules/nf-core/gatk4/intervallisttools/main.nf new file mode 100644 index 0000000..289964f --- /dev/null +++ b/modules/nf-core/gatk4/intervallisttools/main.nf @@ -0,0 +1,64 @@ +process GATK4_INTERVALLISTTOOLS { + tag "${meta.id}" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/ce/ced519873646379e287bc28738bdf88e975edd39a92e7bc6a34bccd37153d9d0/data' + : 'community.wave.seqera.io/library/gatk4_gcnvkernel:edb12e4f0bf02cd3'}" + + input: + tuple val(meta), path(intervals) + + output: + tuple val(meta), path("*_split/*/*.interval_list"), emit: interval_list + tuple val("${task.process}"), val('gatk4'), eval("gatk --version | sed -n '/GATK.*v/s/.*v//p'"), topic: versions, emit: versions_gatk4 + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + def avail_mem = 3072 + if (!task.memory) { + log.info('[GATK IntervalListTools] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.') + } + else { + avail_mem = (task.memory.mega * 0.8).intValue() + } + """ + mkdir ${prefix}_split + + gatk --java-options "-Xmx${avail_mem}M -XX:-UsePerfData" \\ + IntervalListTools \\ + --INPUT ${intervals} \\ + --OUTPUT ${prefix}_split \\ + --TMP_DIR . \\ + ${args} + + python3 < key.startsWith("versions") } + ).match() + } + ) + } + } + + test("test_gatk4_splitncigarreads_intervals") { + + when { + process { + """ + input[0] = [ [ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/bed/test.bed', checkIfExists: true)] + input[1] = [ [ id:'reference' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) ] + input[2] = [ [ id:'reference' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true) ] + input[3] = [ [ id:'reference' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true) ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.bam[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() + } + ) + } + } + + test("test_gatk4_splitncigarreads_stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ [ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.bam', checkIfExists: true), [], []] + input[1] = [ [ id:'reference' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true) ] + input[2] = [ [ id:'reference' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true) ] + input[3] = [ [ id:'reference' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true) ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.bam[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() + } + ) + } + } +} diff --git a/modules/nf-core/gatk4/splitncigarreads/tests/main.nf.test.snap b/modules/nf-core/gatk4/splitncigarreads/tests/main.nf.test.snap new file mode 100644 index 0000000..2281192 --- /dev/null +++ b/modules/nf-core/gatk4/splitncigarreads/tests/main.nf.test.snap @@ -0,0 +1,59 @@ +{ + "test_gatk4_splitncigarreads_intervals": { + "content": [ + "test.bam", + { + "versions_gatk4": [ + [ + "GATK4_SPLITNCIGARREADS", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T18:01:00.303332906" + }, + "test_gatk4_splitncigarreads_stub": { + "content": [ + "test.bam", + { + "versions_gatk4": [ + [ + "GATK4_SPLITNCIGARREADS", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T18:01:06.852378451" + }, + "test_gatk4_splitncigarreads": { + "content": [ + "test.bam", + { + "versions_gatk4": [ + [ + "GATK4_SPLITNCIGARREADS", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T18:00:52.006171566" + } +} \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index f69d66c..f6d94d5 100644 --- a/nextflow.config +++ b/nextflow.config @@ -28,7 +28,11 @@ params { gtf = null star_index = null - + // Variant Calling options + dbsnp = null + scatter_size = 40 + min_confidence_threshold = 20 + starfusion_ref = null arriba_base = null arriba_blacklist = null @@ -36,7 +40,7 @@ params { arriba_protein = null arriba_known_fusions = null fusion_tools_cutoff = 1 - + // Dx_tracks references dx_tracks_path = null diff --git a/subworkflows/local/bam_variant_calling/main.nf b/subworkflows/local/bam_variant_calling/main.nf new file mode 100644 index 0000000..dbe866b --- /dev/null +++ b/subworkflows/local/bam_variant_calling/main.nf @@ -0,0 +1,114 @@ +include { GATK4_BEDTOINTERVALLIST } from '../../../modules/nf-core/gatk4/bedtointervallist/main' +include { GATK4_CREATESEQUENCEDICTIONARY } from '../../../modules/nf-core/gatk4/createsequencedictionary/main' +include { GATK4_HAPLOTYPECALLER } from '../../../modules/nf-core/gatk4/haplotypecaller/main' +include { GATK4_INTERVALLISTTOOLS } from '../../../modules/nf-core/gatk4/intervallisttools/main' +include { GATK4_SPLITNCIGARREADS } from '../../../modules/nf-core/gatk4/splitncigarreads/main' +include { GTF2BED } from '../../../modules/local/gtf2bed/main' +include { SAMTOOLS_MERGE } from '../../../modules/nf-core/samtools/merge/main' +include { SAMTOOLS_INDEX } from '../../../modules/nf-core/samtools/index/main' + +workflow BAM_VARIANT_CALLING { + take: + ch_bam_bai + ch_fasta_fai + ch_gtf + ch_dbsnp + ch_dbsnp_tbi + + + main: + ch_fasta = ch_fasta_fai.map { meta, fasta, _fai -> [meta, fasta] } + ch_fai = ch_fasta_fai.map { meta, _fasta, fai -> [meta, fai] } + + + GATK4_CREATESEQUENCEDICTIONARY(ch_fasta_fai.map { meta, fasta, _fai -> [meta, fasta] }) + def ch_dict = GATK4_CREATESEQUENCEDICTIONARY.out.dict + + GTF2BED(ch_gtf, 'exon') + + GATK4_BEDTOINTERVALLIST(GTF2BED.out.bed.collect(), ch_dict) + def interval_list = GATK4_BEDTOINTERVALLIST.out.interval_list + + // MODULE: Scatter one interval-list into many interval-files using GATK4 IntervalListTools + GATK4_INTERVALLISTTOOLS(interval_list) + + def interval_list_split = GATK4_INTERVALLISTTOOLS.out.interval_list.map { _meta, bed -> [bed] }.collect() + + + def bam_interval = ch_bam_bai + .combine(interval_list_split) + .map { meta, bam, bai, intervals -> + def new_meta = meta + [interval_count: intervals.size()] + [new_meta, bam, bai, intervals] + } + .transpose(by: 3) + .map { meta, bam, bai, interval -> + [meta + [id: "${meta.id}_${interval.baseName}", sample: meta.id], bam, bai, interval] + } + + GATK4_SPLITNCIGARREADS( + bam_interval, + ch_fasta, + ch_fai, + ch_dict, + ) + + def bam_splitncigar = GATK4_SPLITNCIGARREADS.out.bam + + def bam_splitncigar_interval = bam_splitncigar + .map { meta, bam_ -> + def new_meta = meta + [id: meta.sample] - meta.subMap('sample') - meta.subMap('interval_count') + [groupKey(new_meta, meta.interval_count), bam_] + } + .groupTuple() + + SAMTOOLS_MERGE( + bam_splitncigar_interval, + ch_fasta, + ch_fai + ) + + def splitncigar_bam = SAMTOOLS_MERGE.out.bam + + SAMTOOLS_INDEX(splitncigar_bam) + + def splitncigar_bam_indices = SAMTOOLS_INDEX.out.bai + .mix(SAMTOOLS_INDEX.out.csi) + .mix(SAMTOOLS_INDEX.out.crai) + + def splitncigar_bam_bai = splitncigar_bam.join(splitncigar_bam_indices, failOnDuplicate: true, failOnMismatch: true) + + def haplotypecaller_interval_bam = splitncigar_bam_bai + .combine(interval_list_split) + .map { meta, bam, bai, interval_lists -> + [meta + [interval_count: interval_lists.size()], bam, bai, interval_lists] + } + .transpose(by: 3) + .map { meta, bam, bai, interval_list_ -> + [meta + [id: meta.id + "_" + interval_list_.baseName, sample: meta.id, variantcaller: 'haplotypecaller'], bam, bai, interval_list_, []] + } + + // MODULE: HaplotypeCaller from GATK4 + // Calls germline SNPs and indels via local re-assembly of haplotypes. + + GATK4_HAPLOTYPECALLER( + haplotypecaller_interval_bam, + ch_fasta, + ch_fai, + ch_dict, + ch_dbsnp, + ch_dbsnp_tbi, + ) + + def haplotypecaller_merged = GATK4_HAPLOTYPECALLER.out.vcf + .join(GATK4_HAPLOTYPECALLER.out.tbi, failOnMismatch: true, failOnDuplicate: true) + .map { meta, vcf, tbi -> + [groupKey(meta + [id: meta.sample] - meta.subMap('sample', "interval_count"), meta.interval_count), vcf, tbi] + } + .groupTuple() + + + emit: + ch_vcf_tbi = haplotypecaller_merged + +} diff --git a/workflows/dxnextflowrna.nf b/workflows/dxnextflowrna.nf index a9cfb25..0fd7f8d 100644 --- a/workflows/dxnextflowrna.nf +++ b/workflows/dxnextflowrna.nf @@ -8,6 +8,7 @@ include { MULTIQC } from '../modules/nf-core/multiqc/main' // SUBWORKFLOWS include { BAM_QUANTIFICATION_FEATURECOUNTS } from '../subworkflows/local/bam_quantification_featurecounts' +include { BAM_VARIANT_CALLING } from '../subworkflows/local/bam_variant_calling/main' include { FASTQ_BAM_QC } from '../subworkflows/local/fastq_bam_qc' include { FASTQ_TRIM_FILTER_ALIGN_DEDUP } from '../subworkflows/local/fastq_trim_filter_align_dedup/main' include { GENE_EXON_OUTRIDER } from '../subworkflows/local/gene_exon_outrider/main' @@ -60,6 +61,15 @@ workflow DXNEXTFLOWRNA { .map(createMetaWithIdSimpleName) .first() + ch_dbsnp = channel + .fromPath(params.dbSNP) + .map(createMetaWithIdSimpleName) + .first() + ch_dbsnp_tbi = channel + .fromPath("${paramsb.dbSNP}.tbi") + .map(createMetaWithIdSimpleName) + .first() + // Input channel ch_fastq = Channel .fromFilePairs("${params.input}/*_R{1,2}_001.fastq.gz") @@ -140,7 +150,7 @@ workflow DXNEXTFLOWRNA { // SUBWORKFLOW: Run bam_quantification_featurecounts // - + BAM_QUANTIFICATION_FEATURECOUNTS( FASTQ_TRIM_FILTER_ALIGN_DEDUP.out.ch_bam_bai, ch_gtf @@ -157,8 +167,8 @@ workflow DXNEXTFLOWRNA { if (params.run_gene_fusion){ ch_starfusion_ref = Channel.fromPath(params.starfusion_ref).collect() - - + + BAM_GENE_FUSION( FASTQ_TRIM_FILTER_ALIGN_DEDUP.out.star_align_junction, ch_starfusion_ref, @@ -168,11 +178,11 @@ workflow DXNEXTFLOWRNA { params.arriba_blacklist, params.arriba_known_fusions, params.arriba_cytobands, - params.arriba_protein + params.arriba_protein ) } - - + + // // SUBWORKFLOW: Run bam_outrider for genes and exons // @@ -188,6 +198,15 @@ workflow DXNEXTFLOWRNA { } + if (params.run_variant_calling) { + BAM_VARIANT_CALLING( + FASTQ_TRIM_FILTER_ALIGN_DEDUP.out.ch_bam_bai, + ch_fasta_fai, + ch_gtf, + ch_dbsnp, + ch_dbsnp_tbi + ) + } // // Collate and save software versions From 15f176775a262b4322906d73c9d577264520f8ba Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Wed, 20 May 2026 17:17:54 +0200 Subject: [PATCH 02/19] add unit test --- nextflow.config | 1 + nf-test.config | 1 + .../bam_variant_calling/tests/main.nf.test | 45 +++++++++++++++++++ .../tests/main.nf.test.snap | 30 +++++++++++++ tests/nextflow.config | 6 ++- workflows/dxnextflowrna.nf | 2 +- 6 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 subworkflows/local/bam_variant_calling/tests/main.nf.test create mode 100644 subworkflows/local/bam_variant_calling/tests/main.nf.test.snap diff --git a/nextflow.config b/nextflow.config index f6d94d5..f561c7a 100644 --- a/nextflow.config +++ b/nextflow.config @@ -47,6 +47,7 @@ params { // Run options run_outrider = true run_umitools_dedup = true + run_variant_calling = true // Custom reference files GRCh38 gene_bed = null diff --git a/nf-test.config b/nf-test.config index 1727ee7..db8dfdf 100644 --- a/nf-test.config +++ b/nf-test.config @@ -13,5 +13,6 @@ config { plugins { load "nft-csv@0.1.0" + load "nft-vcf@1.0.7" } } diff --git a/subworkflows/local/bam_variant_calling/tests/main.nf.test b/subworkflows/local/bam_variant_calling/tests/main.nf.test new file mode 100644 index 0000000..54c662f --- /dev/null +++ b/subworkflows/local/bam_variant_calling/tests/main.nf.test @@ -0,0 +1,45 @@ +nextflow_workflow { + name "Test Subworkflow BAM_VARIANT_CALING" + script "../main.nf" + workflow "BAM_VARIANT_CALLING" + + tag "subworkflows/local" + tag "subworkflows" + tag "local" + tag "variant_calling" + + test ("Test scatter and merge") { + when { + workflow { + """ + input[0] = channel.of([ + [ id:'sampleA' ], + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/illumina/bam/NA12878.chr21_22.bam'), + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/illumina/bam/NA12878.chr21_22.bam.bai') + ]) + input[1] = channel.of([ + [ id:'minigenome_fasta' ], + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta'), + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta.fai') + ]).first() + input[2] = channel.of([ + [ id:'minigenome_gtf' ], + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/chr21_gencode.gtf') + ]).first() + input[3] = [[:],[]] + input[4] = [[:],[]] + """ + } + } + then { + assertAll( + { assert workflow.success }, + { assert snapshot( + workflow.out.ch_vcf_tbi.collect() {meta, vcf, tbi -> + [meta, path(vcf).vcf.variantsMD5, path(tbi).name] + } + ).match() } + ) + } + } +} diff --git a/subworkflows/local/bam_variant_calling/tests/main.nf.test.snap b/subworkflows/local/bam_variant_calling/tests/main.nf.test.snap new file mode 100644 index 0000000..320daea --- /dev/null +++ b/subworkflows/local/bam_variant_calling/tests/main.nf.test.snap @@ -0,0 +1,30 @@ +{ + "Test scatter and merge": { + "content": [ + [ + [ + { + "groupSize": 2, + "groupTarget": { + "id": "sampleA", + "variantcaller": "haplotypecaller" + } + }, + [ + "sampleA_2scattered.vcf.gz:md5,211e2365caeccd0cf38188c5667bbf18", + "sampleA_1scattered.vcf.gz:md5,321d393676e98d5175f9bd0b99f0cf10" + ], + [ + "sampleA_2scattered.vcf.gz.tbi:md5,cd55f12b595e999c30908d8901254b26", + "sampleA_1scattered.vcf.gz.tbi:md5,1549b6bac8da745c73cf37bba629e029" + ] + ] + ] + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-05-20T13:44:47.782141" + } +} \ No newline at end of file diff --git a/tests/nextflow.config b/tests/nextflow.config index afced7c..4fe36df 100644 --- a/tests/nextflow.config +++ b/tests/nextflow.config @@ -9,6 +9,8 @@ params { config_profile_name = "Test Profile" config_profile_description = "Minimal pipeline test" + scatter_size = 2 + seq_platform = "Illumina" seq_center = "UMCU Genetics" @@ -25,6 +27,7 @@ params { run_outrider = true run_gene_fusion = true + run_variant_calling = true arriba_base = "./" arriba_blacklist = [] @@ -32,7 +35,7 @@ params { arriba_protein = [] arriba_known_fusions = [] - + outrider_reference_base = "./" outrider_refs = null conditions = ['chx'] @@ -47,6 +50,7 @@ process { queue = 'cpu' withName: ".*" { + array = null cpus = 1 memory = 3.GB time = 10.m diff --git a/workflows/dxnextflowrna.nf b/workflows/dxnextflowrna.nf index 0fd7f8d..06ed7fa 100644 --- a/workflows/dxnextflowrna.nf +++ b/workflows/dxnextflowrna.nf @@ -66,7 +66,7 @@ workflow DXNEXTFLOWRNA { .map(createMetaWithIdSimpleName) .first() ch_dbsnp_tbi = channel - .fromPath("${paramsb.dbSNP}.tbi") + .fromPath("${params.dbsnp}.{tbi,csi}") .map(createMetaWithIdSimpleName) .first() From bbd79da71b5c21e88c372d791968fea2bcd669e1 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Wed, 20 May 2026 17:19:00 +0200 Subject: [PATCH 03/19] fix params typo --- conf/hpc.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/hpc.config b/conf/hpc.config index 985aeed..ee896fb 100644 --- a/conf/hpc.config +++ b/conf/hpc.config @@ -19,7 +19,7 @@ params { rrna_intervals = "${params.dx_tracks_path}/rna/GRCh38_rRNA_genbank.interval_list" // dbSNP - dbSNP = "/hpc/diaggen/data/databases/GATK_resource_bundle/hg38/dbsnp_146.hg38.vcf.gz" + dbsnp = "/hpc/diaggen/data/databases/GATK_resource_bundle/hg38/dbsnp_146.hg38.vcf.gz" // Fusion config starfusion_ref = params.genome_base From d223783a4b5961f9783fb50e3f774e1166f4b5e1 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Wed, 20 May 2026 17:22:06 +0200 Subject: [PATCH 04/19] fix params typo --- workflows/dxnextflowrna.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/dxnextflowrna.nf b/workflows/dxnextflowrna.nf index 06ed7fa..ef4e97b 100644 --- a/workflows/dxnextflowrna.nf +++ b/workflows/dxnextflowrna.nf @@ -62,7 +62,7 @@ workflow DXNEXTFLOWRNA { .first() ch_dbsnp = channel - .fromPath(params.dbSNP) + .fromPath(params.dbsnp) .map(createMetaWithIdSimpleName) .first() ch_dbsnp_tbi = channel From 2b0e464cbfeac0d6c609ac668a9a1c23bde74b96 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Fri, 22 May 2026 11:02:18 +0200 Subject: [PATCH 05/19] variant filtration step --- conf/modules.config | 18 +++ modules.json | 5 + .../gatk4/variantfiltration/environment.yml | 10 ++ .../nf-core/gatk4/variantfiltration/main.nf | 64 ++++++++ .../nf-core/gatk4/variantfiltration/meta.yml | 126 ++++++++++++++++ .../variantfiltration/tests/main.nf.test | 137 ++++++++++++++++++ .../variantfiltration/tests/main.nf.test.snap | 62 ++++++++ nextflow.config | 4 + .../local/bam_variant_calling/main.nf | 21 ++- 9 files changed, 442 insertions(+), 5 deletions(-) create mode 100644 modules/nf-core/gatk4/variantfiltration/environment.yml create mode 100644 modules/nf-core/gatk4/variantfiltration/main.nf create mode 100644 modules/nf-core/gatk4/variantfiltration/meta.yml create mode 100644 modules/nf-core/gatk4/variantfiltration/tests/main.nf.test create mode 100644 modules/nf-core/gatk4/variantfiltration/tests/main.nf.test.snap diff --git a/conf/modules.config b/conf/modules.config index e718b82..0dddd99 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -114,6 +114,24 @@ process { ext.prefix = { "${meta.id}.splitncigarreads" } } + withName: GATK4_VARIANTFILTRATION { + cpus = 1 + memory = { 6.GB * task.attempt } + time = { 2.h * task.attempt } + + ext.prefix = { "${meta.id}.hc.filtered" } + ext.args = { [ + params.gatk_vf_window_size ? "--cluster-window-size ${params.gatk_vf_window_size}" : '', + params.gatk_vf_cluster_size ? "--cluster-size ${params.gatk_vf_cluster_size}" : '', + params.gatk_vf_fs_filter ? "--filter-name \"FS\" --filter-expression \"FS > ${params.gatk_vf_fs_filter}\" " : '', + params.gatk_vf_qd_filter ? "--filter-name \"QD\" --filter-expression \"QD < ${params.gatk_vf_qd_filter}\" " : '', + ].join(' ').trim() } + publishDir = [ + path: { "${params.outdir}/variant_calling/" }, + mode: params.publish_dir_mode + ] + } + withName: HGNC_DOWNLOAD { cpus = 1 memory = { 1.GB } diff --git a/modules.json b/modules.json index f1ad12c..9f9a0ec 100644 --- a/modules.json +++ b/modules.json @@ -55,6 +55,11 @@ "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", "installed_by": ["modules"] }, + "gatk4/variantfiltration": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, "multiqc": { "branch": "master", "git_sha": "cf17ca47590cc578dfb47db1c2a44ef86f89976d", diff --git a/modules/nf-core/gatk4/variantfiltration/environment.yml b/modules/nf-core/gatk4/variantfiltration/environment.yml new file mode 100644 index 0000000..67e0eb8 --- /dev/null +++ b/modules/nf-core/gatk4/variantfiltration/environment.yml @@ -0,0 +1,10 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + # renovate: datasource=conda depName=bioconda/gatk4 + - bioconda::gatk4=4.6.2.0 + # renovate: datasource=conda depName=bioconda/gcnvkernel + - bioconda::gcnvkernel=0.9 diff --git a/modules/nf-core/gatk4/variantfiltration/main.nf b/modules/nf-core/gatk4/variantfiltration/main.nf new file mode 100644 index 0000000..58283b1 --- /dev/null +++ b/modules/nf-core/gatk4/variantfiltration/main.nf @@ -0,0 +1,64 @@ +process GATK4_VARIANTFILTRATION { + tag "${meta.id}" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/ce/ced519873646379e287bc28738bdf88e975edd39a92e7bc6a34bccd37153d9d0/data' + : 'community.wave.seqera.io/library/gatk4_gcnvkernel:edb12e4f0bf02cd3'}" + + input: + tuple val(meta), path(vcf), path(tbi) + tuple val(meta2), path(fasta) + tuple val(meta3), path(fai) + tuple val(meta4), path(dict) + tuple val(meta5), path(gzi) + + output: + tuple val(meta), path("*.vcf.gz"), emit: vcf + tuple val(meta), path("*.tbi"), emit: tbi + tuple val("${task.process}"), val('gatk4'), eval("gatk --version | sed -n '/GATK.*v/s/.*v//p'"), topic: versions, emit: versions_gatk4 + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + // Infer .dict file name that gatk expects, even if ends in .fasta.gz + def dict_name = fasta.getBaseName() + if (dict_name ==~ /^.*\.(fasta|fna|fa)$/) { + dict_name = dict_name.replaceAll(/\.(fasta|fna|fa)$/, "") + } + dict_name = "${dict_name}.dict" + + def avail_mem = 3072 + if (!task.memory) { + log.info('[GATK VariantFiltration] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.') + } + else { + avail_mem = (task.memory.mega * 0.8).intValue() + } + """ + # Make sure the .dict file is named correctly + if [[ ${dict_name} != ${dict} ]]; then + ln -s ${dict} ${dict_name} + fi + + gatk --java-options "-Xmx${avail_mem}M -XX:-UsePerfData" \\ + VariantFiltration \\ + --variant ${vcf} \\ + --output ${prefix}.vcf.gz \\ + --reference ${fasta} \\ + --tmp-dir . \\ + ${args} + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + """ + echo "" | gzip > ${prefix}.vcf.gz + touch ${prefix}.vcf.gz.tbi + """ +} diff --git a/modules/nf-core/gatk4/variantfiltration/meta.yml b/modules/nf-core/gatk4/variantfiltration/meta.yml new file mode 100644 index 0000000..4177a70 --- /dev/null +++ b/modules/nf-core/gatk4/variantfiltration/meta.yml @@ -0,0 +1,126 @@ +name: gatk4_variantfiltration +description: Filter variants +keywords: + - filter + - gatk4 + - variantfiltration + - vcf +tools: + - gatk4: + description: | + Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools + with a primary focus on variant discovery and genotyping. Its powerful processing engine + and high-performance computing features make it capable of taking on projects of any size. + homepage: https://gatk.broadinstitute.org/hc/en-us + documentation: https://gatk.broadinstitute.org/hc/en-us/categories/360002369672s + doi: 10.1158/1538-7445.AM2017-3590 + licence: ["Apache-2.0"] + identifier: "" +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - vcf: + type: list + description: List of VCF(.gz) files + pattern: "*.{vcf,vcf.gz}" + - tbi: + type: list + description: List of VCF file indexes + pattern: "*.{tbi}" + - - meta2: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome' ] + - fasta: + type: file + description: Fasta file of reference genome + pattern: "*.fasta" + ontologies: [] + - - meta3: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome' ] + - fai: + type: file + description: Index of fasta file + pattern: "*.fasta.fai" + ontologies: [] + - - meta4: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome' ] + - dict: + type: file + description: Sequence dictionary of fastea file + pattern: "*.dict" + ontologies: [] + - - meta5: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome' ] + - gzi: + type: file + description: Genome index file only needed when the genome file was compressed + with the BGZF algorithm. + pattern: "*.gzi" + ontologies: [] +output: + vcf: + - - meta: + type: file + description: Compressed VCF file + pattern: "*.vcf.gz" + ontologies: + - edam: http://edamontology.org/format_3989 # GZIP format + - "*.vcf.gz": + type: file + description: Compressed VCF file + pattern: "*.vcf.gz" + ontologies: + - edam: http://edamontology.org/format_3989 # GZIP format + tbi: + - - meta: + type: file + description: Compressed VCF file + pattern: "*.vcf.gz" + ontologies: + - edam: http://edamontology.org/format_3989 # GZIP format + - "*.tbi": + type: file + description: Index of VCF file + pattern: "*.vcf.gz.tbi" + ontologies: [] + versions_gatk4: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@kevinmenden" + - "@ramprasadn" +maintainers: + - "@kevinmenden" + - "@ramprasadn" diff --git a/modules/nf-core/gatk4/variantfiltration/tests/main.nf.test b/modules/nf-core/gatk4/variantfiltration/tests/main.nf.test new file mode 100644 index 0000000..cbb647c --- /dev/null +++ b/modules/nf-core/gatk4/variantfiltration/tests/main.nf.test @@ -0,0 +1,137 @@ +nextflow_process { + + name "Test Process GATK4_VARIANTFILTRATION" + script "../main.nf" + process "GATK4_VARIANTFILTRATION" + + tag "modules" + tag "modules_nfcore" + tag "gatk4" + tag "gatk4/variantfiltration" + + test("gatk4_variantfiltration - human - vcf - fasta") { + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/gvcf/test.genome.vcf', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/gvcf/test.genome.vcf.idx', checkIfExists: true) + ] + input[1] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true) + ] + input[2] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ] + input[3] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true) + ] + input[4] = [ + [ id:'genome' ], // meta map + [] + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + path(process.out.vcf[0][1]).vcf.variantsMD5, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() } + ) + } + } + + test("gatk4_variantfiltration - human - vcf - fasta.gz") { + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/gvcf/test.genome.vcf', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/gvcf/test.genome.vcf.idx', checkIfExists: true) + ] + input[1] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.gz', checkIfExists: true) + ] + input[2] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.gz.fai', checkIfExists: true) + ] + input[3] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true) + ] + input[4] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.gz.gzi', checkIfExists: true) + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + path(process.out.vcf[0][1]).vcf.variantsMD5, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() } + ) + } + } + + test("gatk4_variantfiltration - human - vcf.gz - fasta") { + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/gvcf/test.genome.vcf.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/gvcf/test.genome.vcf.gz.tbi', checkIfExists: true) + ] + input[1] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true) + ] + input[2] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true) + ] + input[3] = [ + [ id:'genome' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true) + ] + input[4] = [ + [ id:'genome' ], // meta map + [] + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + path(process.out.vcf[0][1]).vcf.variantsMD5, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() } + ) + } + } +} diff --git a/modules/nf-core/gatk4/variantfiltration/tests/main.nf.test.snap b/modules/nf-core/gatk4/variantfiltration/tests/main.nf.test.snap new file mode 100644 index 0000000..e1a3c78 --- /dev/null +++ b/modules/nf-core/gatk4/variantfiltration/tests/main.nf.test.snap @@ -0,0 +1,62 @@ +{ + "gatk4_variantfiltration - human - vcf - fasta.gz": { + "content": [ + "cf0477cc14953b16caacbf5be507b88", + "test.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_VARIANTFILTRATION", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T18:03:01.467326216" + }, + "gatk4_variantfiltration - human - vcf.gz - fasta": { + "content": [ + "cf0477cc14953b16caacbf5be507b88", + "test.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_VARIANTFILTRATION", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T18:03:09.049097033" + }, + "gatk4_variantfiltration - human - vcf - fasta": { + "content": [ + "cf0477cc14953b16caacbf5be507b88", + "test.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_VARIANTFILTRATION", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T18:02:52.913618285" + } +} \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index f561c7a..6cdbf1a 100644 --- a/nextflow.config +++ b/nextflow.config @@ -32,6 +32,10 @@ params { dbsnp = null scatter_size = 40 min_confidence_threshold = 20 + gatk_vf_window_size = 35 + gatk_vf_cluster_size = 3 + gatk_vf_fs_filter = 30.0 + gatk_vf_qd_filter = 2.0 starfusion_ref = null arriba_base = null diff --git a/subworkflows/local/bam_variant_calling/main.nf b/subworkflows/local/bam_variant_calling/main.nf index dbe866b..48ac387 100644 --- a/subworkflows/local/bam_variant_calling/main.nf +++ b/subworkflows/local/bam_variant_calling/main.nf @@ -3,10 +3,13 @@ include { GATK4_CREATESEQUENCEDICTIONARY } from '../../../modules/nf-core/gatk4/ include { GATK4_HAPLOTYPECALLER } from '../../../modules/nf-core/gatk4/haplotypecaller/main' include { GATK4_INTERVALLISTTOOLS } from '../../../modules/nf-core/gatk4/intervallisttools/main' include { GATK4_SPLITNCIGARREADS } from '../../../modules/nf-core/gatk4/splitncigarreads/main' +include { GATK4_VARIANTFILTRATION } from '../../../modules/nf-core/gatk4/variantfiltration/main' include { GTF2BED } from '../../../modules/local/gtf2bed/main' include { SAMTOOLS_MERGE } from '../../../modules/nf-core/samtools/merge/main' include { SAMTOOLS_INDEX } from '../../../modules/nf-core/samtools/index/main' + +// workflow adapted from github.com/nf-core/rnavar workflow BAM_VARIANT_CALLING { take: ch_bam_bai @@ -88,9 +91,6 @@ workflow BAM_VARIANT_CALLING { [meta + [id: meta.id + "_" + interval_list_.baseName, sample: meta.id, variantcaller: 'haplotypecaller'], bam, bai, interval_list_, []] } - // MODULE: HaplotypeCaller from GATK4 - // Calls germline SNPs and indels via local re-assembly of haplotypes. - GATK4_HAPLOTYPECALLER( haplotypecaller_interval_bam, ch_fasta, @@ -100,15 +100,26 @@ workflow BAM_VARIANT_CALLING { ch_dbsnp_tbi, ) - def haplotypecaller_merged = GATK4_HAPLOTYPECALLER.out.vcf + def ch_vcf_tbi = GATK4_HAPLOTYPECALLER.out.vcf .join(GATK4_HAPLOTYPECALLER.out.tbi, failOnMismatch: true, failOnDuplicate: true) .map { meta, vcf, tbi -> [groupKey(meta + [id: meta.sample] - meta.subMap('sample', "interval_count"), meta.interval_count), vcf, tbi] } .groupTuple() + GATK4_VARIANTFILTRATION( + ch_vcf_tbi, + ch_fasta, + ch_fai, + ch_dict, + [[:],[]] + ) + + emit: - ch_vcf_tbi = haplotypecaller_merged + ch_vcf_tbi_unfiltered = ch_vcf_tbi + ch_vcf_filtered = GATK4_VARIANTFILTRATION.out.vcf + ch_tbi_filtered = GATK4_VARIANTFILTRATION.out.tbi } From 4dd914d3d2fb3f830712385b34337cd84750678a Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Fri, 22 May 2026 11:04:45 +0200 Subject: [PATCH 06/19] add missing run flag param --- nextflow.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 6cdbf1a..3ccf109 100644 --- a/nextflow.config +++ b/nextflow.config @@ -49,8 +49,9 @@ params { dx_tracks_path = null // Run options - run_outrider = true + run_outrider = false run_umitools_dedup = true + run_gene_fusion = true run_variant_calling = true // Custom reference files GRCh38 From e2b7edab8543c9682702cf070d8de925fddf15d1 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Tue, 26 May 2026 11:46:21 +0200 Subject: [PATCH 07/19] fix config syntax errors --- conf/modules.config | 4 ++-- tests/nextflow.config | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index c6927a8..891585f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -136,7 +136,7 @@ process { time = { 15.m * task.attempt } ext.args = { params.multiqc_title ? "--title \"${params.multiqc_title}\"" : "--title \"${params.analysis_id}\"" } - ext.prefix = { $ { params.analysis_id } } + ext.prefix = { params.analysis_id } publishDir = [ [ path: "${params.outdir}/QC", @@ -365,7 +365,7 @@ process { path: "${params.outdir}/QC/star_align", mode: params.publish_dir_mode, saveAs: { filename -> - filename.endsWith('.bam') || filename.endsWith(".fastq.gz") : null ? filename + filename.endsWith('.bam') || filename.endsWith(".fastq.gz") ? null : filename } ] ] diff --git a/tests/nextflow.config b/tests/nextflow.config index 4fe36df..55e4810 100644 --- a/tests/nextflow.config +++ b/tests/nextflow.config @@ -23,6 +23,7 @@ params { gene_bed = "gencode.ref_annot.gtf.bed" ref_flat = "gencode.ref_annot.gtf.refflat" rrna_intervals = "GRCh38_rRNA_genbank.interval_list" + dbsnp = "dbsnp.vcf.gz" run_outrider = true @@ -50,7 +51,7 @@ process { queue = 'cpu' withName: ".*" { - array = null + array = 0 cpus = 1 memory = 3.GB time = 10.m From fa8f34a5c9809d846ad5c7dd1f15f11048e351c8 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Tue, 26 May 2026 16:10:28 +0200 Subject: [PATCH 08/19] merge gatk scatters after haplotypecaller --- modules.json | 15 +- .../nf-core/bcftools/index/environment.yml | 10 + modules/nf-core/bcftools/index/main.nf | 40 +++ modules/nf-core/bcftools/index/meta.yml | 82 +++++ .../nf-core/bcftools/index/tests/main.nf.test | 108 ++++++ .../bcftools/index/tests/main.nf.test.snap | 148 ++++++++ .../bcftools/index/tests/nextflow.config | 3 + .../nf-core/gatk4/baserecalibrator/meta.yml | 131 ------- .../gatk4/baserecalibrator/tests/main.nf.test | 250 ------------- .../baserecalibrator/tests/main.nf.test.snap | 330 ------------------ .../environment.yml | 0 .../{baserecalibrator => mergevcfs}/main.nf | 34 +- modules/nf-core/gatk4/mergevcfs/meta.yml | 88 +++++ .../gatk4/mergevcfs/tests/main.nf.test | 80 +++++ .../gatk4/mergevcfs/tests/main.nf.test.snap | 62 ++++ .../local/bam_variant_calling/main.nf | 18 +- .../bam_variant_calling/tests/main.nf.test | 4 +- .../tests/main.nf.test.snap | 20 +- 18 files changed, 664 insertions(+), 759 deletions(-) create mode 100644 modules/nf-core/bcftools/index/environment.yml create mode 100644 modules/nf-core/bcftools/index/main.nf create mode 100644 modules/nf-core/bcftools/index/meta.yml create mode 100644 modules/nf-core/bcftools/index/tests/main.nf.test create mode 100644 modules/nf-core/bcftools/index/tests/main.nf.test.snap create mode 100644 modules/nf-core/bcftools/index/tests/nextflow.config delete mode 100644 modules/nf-core/gatk4/baserecalibrator/meta.yml delete mode 100644 modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test delete mode 100644 modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test.snap rename modules/nf-core/gatk4/{baserecalibrator => mergevcfs}/environment.yml (100%) rename modules/nf-core/gatk4/{baserecalibrator => mergevcfs}/main.nf (54%) create mode 100644 modules/nf-core/gatk4/mergevcfs/meta.yml create mode 100644 modules/nf-core/gatk4/mergevcfs/tests/main.nf.test create mode 100644 modules/nf-core/gatk4/mergevcfs/tests/main.nf.test.snap diff --git a/modules.json b/modules.json index 9f9a0ec..c2bbeed 100644 --- a/modules.json +++ b/modules.json @@ -10,6 +10,11 @@ "git_sha": "025a6f75e1f72e4ab60abb4bd65b3f289d4ad910", "installed_by": ["modules"] }, + "bcftools/index": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, "fastqc": { "branch": "master", "git_sha": "dc94b6ee04a05ddb9f7ae050712ff30a13149164", @@ -25,11 +30,6 @@ "git_sha": "e753770db613ce014b3c4bc94f6cba443427b726", "installed_by": ["modules"] }, - "gatk4/baserecalibrator": { - "branch": "master", - "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", - "installed_by": ["modules"] - }, "gatk4/bedtointervallist": { "branch": "master", "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", @@ -50,6 +50,11 @@ "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", "installed_by": ["modules"] }, + "gatk4/mergevcfs": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, "gatk4/splitncigarreads": { "branch": "master", "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", diff --git a/modules/nf-core/bcftools/index/environment.yml b/modules/nf-core/bcftools/index/environment.yml new file mode 100644 index 0000000..01193fd --- /dev/null +++ b/modules/nf-core/bcftools/index/environment.yml @@ -0,0 +1,10 @@ +--- +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/environment-schema.json +channels: + - conda-forge + - bioconda +dependencies: + # renovate: datasource=conda depName=bioconda/htslib + - bioconda::htslib=1.23.1 + # renovate: datasource=conda depName=bioconda/bcftools + - bioconda::bcftools=1.23.1 diff --git a/modules/nf-core/bcftools/index/main.nf b/modules/nf-core/bcftools/index/main.nf new file mode 100644 index 0000000..757fa7c --- /dev/null +++ b/modules/nf-core/bcftools/index/main.nf @@ -0,0 +1,40 @@ +process BCFTOOLS_INDEX { + tag "${meta.id}" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/0b/0b4d52ca9a56d07be3f78a12af654e5116f5112908dba277e6796fd9dfb83fe5/data' + : 'community.wave.seqera.io/library/bcftools_htslib:1.23.1--9f08ec665533d64a'}" + + input: + tuple val(meta), path(vcf) + + output: + tuple val(meta), path("*.csi"), emit: csi, optional: true + tuple val(meta), path("*.tbi"), emit: tbi, optional: true + tuple val("${task.process}"), val('bcftools'), eval("bcftools --version | sed '1!d; s/^.*bcftools //'"), topic: versions, emit: versions_bcftools + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + + """ + bcftools \\ + index \\ + ${args} \\ + --threads ${task.cpus} \\ + ${vcf} + """ + + stub: + def args = task.ext.args ?: '' + def extension = args.contains("--tbi") || args.contains("-t") + ? "tbi" + : "csi" + """ + touch ${vcf}.${extension} + """ +} diff --git a/modules/nf-core/bcftools/index/meta.yml b/modules/nf-core/bcftools/index/meta.yml new file mode 100644 index 0000000..4e4bdc8 --- /dev/null +++ b/modules/nf-core/bcftools/index/meta.yml @@ -0,0 +1,82 @@ +name: bcftools_index +description: Index VCF tools +keywords: + - vcf + - index + - bcftools + - csi + - tbi +tools: + - bcftools: + description: BCFtools is a set of utilities that manipulate variant calls in the + Variant Call Format (VCF) and its binary counterpart BCF. All commands work + transparently with both VCFs and BCFs, both uncompressed and BGZF-compressed. Most + commands accept VCF, bgzipped VCF and BCF with filetype detected automatically + even when streaming from a pipe. Indexed VCF and BCF will work in all situations. + Un-indexed VCF and BCF and streams will work in most, but not all situations. + homepage: https://samtools.github.io/bcftools/ + documentation: https://samtools.github.io/bcftools/howtos/index.html + tool_dev_url: https://github.com/samtools/bcftools + doi: "10.1093/gigascience/giab008" + licence: ["MIT", "GPL-3.0-or-later"] + identifier: biotools:bcftools +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - vcf: + type: file + description: VCF file (optionally GZIPPED) + pattern: "*.{vcf,vcf.gz}" + ontologies: [] +output: + csi: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.csi": + type: file + description: Default VCF file index file + pattern: "*.csi" + ontologies: [] + tbi: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.tbi": + type: file + description: Alternative VCF file index file for larger files (activated with + -t parameter) + pattern: "*.tbi" + ontologies: [] + versions_bcftools: + - - ${task.process}: + type: string + description: The process the versions were collected from + - bcftools: + type: string + description: The tool name + - "bcftools --version | sed '1!d; s/^.*bcftools //'": + type: string + description: The command used to generate the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The process the versions were collected from + - bcftools: + type: string + description: The tool name + - "bcftools --version | sed '1!d; s/^.*bcftools //'": + type: string + description: The command used to generate the version of the tool +authors: + - "@jfy133" +maintainers: + - "@jfy133" diff --git a/modules/nf-core/bcftools/index/tests/main.nf.test b/modules/nf-core/bcftools/index/tests/main.nf.test new file mode 100644 index 0000000..b38c6ad --- /dev/null +++ b/modules/nf-core/bcftools/index/tests/main.nf.test @@ -0,0 +1,108 @@ +nextflow_process { + + name "Test Process BCFTOOLS_INDEX" + script "../main.nf" + process "BCFTOOLS_INDEX" + + tag "modules" + tag "modules_nfcore" + tag "bcftools" + tag "bcftools/index" + + test("sarscov2 - vcf - csi") { + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true) + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.csi.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.findAll { key, val -> key.startsWith("versions") }).match() + } + ) + } + } + + test("sarscov2 - vcf - tbi") { + + config "./nextflow.config" + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true) + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.tbi.collect { it.collect { it instanceof Map ? it : file(it).name } }, + process.out.findAll { key, val -> key.startsWith("versions") }).match() + } + ) + } + } + + test("sarscov2 - vcf - csi - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true) + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("sarscov2 - vcf - tbi - stub") { + + config "./nextflow.config" + options "-stub" + + when { + process { + """ + input[0] = [ + [ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true) + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/bcftools/index/tests/main.nf.test.snap b/modules/nf-core/bcftools/index/tests/main.nf.test.snap new file mode 100644 index 0000000..cce1c79 --- /dev/null +++ b/modules/nf-core/bcftools/index/tests/main.nf.test.snap @@ -0,0 +1,148 @@ +{ + "sarscov2 - vcf - csi - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.vcf.gz.csi:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + + ], + "2": [ + [ + "BCFTOOLS_INDEX", + "bcftools", + "1.23.1" + ] + ], + "csi": [ + [ + { + "id": "test" + }, + "test.vcf.gz.csi:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "tbi": [ + + ], + "versions_bcftools": [ + [ + "BCFTOOLS_INDEX", + "bcftools", + "1.23.1" + ] + ] + } + ], + "timestamp": "2026-03-20T18:02:30.863066697", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.2" + } + }, + "sarscov2 - vcf - tbi": { + "content": [ + [ + [ + { + "id": "test" + }, + "test.vcf.gz.tbi" + ] + ], + { + "versions_bcftools": [ + [ + "BCFTOOLS_INDEX", + "bcftools", + "1.23.1" + ] + ] + } + ], + "timestamp": "2026-03-20T18:02:23.639599735", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.2" + } + }, + "sarscov2 - vcf - tbi - stub": { + "content": [ + { + "0": [ + + ], + "1": [ + [ + { + "id": "test" + }, + "test.vcf.gz.tbi:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + [ + "BCFTOOLS_INDEX", + "bcftools", + "1.23.1" + ] + ], + "csi": [ + + ], + "tbi": [ + [ + { + "id": "test" + }, + "test.vcf.gz.tbi:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_bcftools": [ + [ + "BCFTOOLS_INDEX", + "bcftools", + "1.23.1" + ] + ] + } + ], + "timestamp": "2026-03-20T18:02:38.300964104", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.2" + } + }, + "sarscov2 - vcf - csi": { + "content": [ + [ + [ + { + "id": "test" + }, + "test.vcf.gz.csi" + ] + ], + { + "versions_bcftools": [ + [ + "BCFTOOLS_INDEX", + "bcftools", + "1.23.1" + ] + ] + } + ], + "timestamp": "2026-03-20T18:02:16.523585018", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.2" + } + } +} \ No newline at end of file diff --git a/modules/nf-core/bcftools/index/tests/nextflow.config b/modules/nf-core/bcftools/index/tests/nextflow.config new file mode 100644 index 0000000..db83f7e --- /dev/null +++ b/modules/nf-core/bcftools/index/tests/nextflow.config @@ -0,0 +1,3 @@ +process { + ext.args = '--tbi' +} diff --git a/modules/nf-core/gatk4/baserecalibrator/meta.yml b/modules/nf-core/gatk4/baserecalibrator/meta.yml deleted file mode 100644 index af53133..0000000 --- a/modules/nf-core/gatk4/baserecalibrator/meta.yml +++ /dev/null @@ -1,131 +0,0 @@ -name: gatk4_baserecalibrator -description: Generate recalibration table for Base Quality Score Recalibration (BQSR) -keywords: - - base quality score recalibration - - table - - bqsr - - gatk4 - - sort -tools: - - gatk4: - description: | - Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools - with a primary focus on variant discovery and genotyping. Its powerful processing engine - and high-performance computing features make it capable of taking on projects of any size. - homepage: https://gatk.broadinstitute.org/hc/en-us - documentation: https://gatk.broadinstitute.org/hc/en-us/categories/360002369672s - doi: 10.1158/1538-7445.AM2017-3590 - licence: ["Apache-2.0"] - identifier: "" -input: - - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - input: - type: file - description: BAM/CRAM file from alignment - pattern: "*.{bam,cram}" - ontologies: [] - - input_index: - type: file - description: BAI/CRAI file from alignment - pattern: "*.{bai,crai}" - ontologies: [] - - intervals: - type: file - description: Bed file with the genomic regions included in the library (optional) - ontologies: [] - - - meta2: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'genome'] - - fasta: - type: file - description: The reference fasta file - pattern: "*.fasta" - ontologies: [] - - - meta3: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'genome'] - - fai: - type: file - description: Index of reference fasta file - pattern: "*.fasta.fai" - ontologies: [] - - - meta4: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'genome'] - - dict: - type: file - description: GATK sequence dictionary - pattern: "*.dict" - ontologies: [] - - - meta5: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'genome'] - - known_sites: - type: file - description: VCF files with known sites for indels / snps - pattern: "*.vcf.gz" - ontologies: - - edam: http://edamontology.org/format_3989 # GZIP format - - - meta6: - type: map - description: | - Groovy Map containing reference information - e.g. [ id:'genome'] - - known_sites_tbi: - type: file - description: Tabix index of the known_sites - pattern: "*.vcf.gz.tbi" - ontologies: [] -output: - table: - - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - "*.table": - type: file - description: Recalibration table from BaseRecalibrator - pattern: "*.{table}" - ontologies: [] - versions_gatk4: - - - ${task.process}: - type: string - description: The name of the process - - gatk4: - type: string - description: The name of the tool - - gatk --version | sed -n '/GATK.*v/s/.*v//p': - type: eval - description: The expression to obtain the version of the tool -topics: - versions: - - - ${task.process}: - type: string - description: The name of the process - - gatk4: - type: string - description: The name of the tool - - gatk --version | sed -n '/GATK.*v/s/.*v//p': - type: eval - description: The expression to obtain the version of the tool -authors: - - "@yocra3" - - "@FriederikeHanssen" - - "@maxulysse" -maintainers: - - "@yocra3" - - "@FriederikeHanssen" - - "@maxulysse" diff --git a/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test b/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test deleted file mode 100644 index 0167404..0000000 --- a/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test +++ /dev/null @@ -1,250 +0,0 @@ -nextflow_process { - - name "Test Process GATK4_BASERECALIBRATOR" - script "../main.nf" - process "GATK4_BASERECALIBRATOR" - - tag "modules" - tag "modules_nfcore" - tag "gatk4" - tag "gatk4/baserecalibrator" - - test("sarscov2 - bam") { - when { - process { - """ - input[0] = Channel.of([ - [ id:'test' ], // meta map - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), - [] - ]) - input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) - input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) - input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) - input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true)]) - input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true)]) - """ - } - } - - then { - assert process.success - assertAll( - { assert snapshot(process.out).match() } - ) - } - } - - test("sarscov2 - bam - intervals") { - when { - process { - """ - input[0] = Channel.of([ - [ id:'test' ], // meta map - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/bed/test.bed', checkIfExists: true) - ]) - input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) - input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) - input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) - input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true)]) - input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true)]) - """ - } - } - - then { - assert process.success - assertAll( - { assert snapshot(process.out).match() } - ) - } - - } - - test("sarscov2 - bam - multiple sites") { - when { - process { - """ - input[0] = Channel.of([ - [ id:'test' ], // meta map - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), - [] - ]) - input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) - input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) - input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) - input[4] = Channel.of([ - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test2.vcf.gz', checkIfExists: true) - ]) - input[5] = Channel.of([ - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test2.vcf.gz.tbi', checkIfExists: true) - ]) - """ - } - } - - then { - assert process.success - assertAll( - { assert snapshot(process.out).match() } - ) - } - } - - test("homo_sapiens - cram") { - - when { - process { - """ - input[0] = Channel.of([ - [ id:'test' ], // meta map - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram.crai', checkIfExists: true), - [] - ]) - input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true)]) - input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true)]) - input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true)]) - input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz', checkIfExists: true)]) - input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz.tbi', checkIfExists: true)]) - """ - } - } - - then { - assert process.success - assertAll( - { assert snapshot(process.out).match() } - ) - } - } - - test("sarscov2 - bam - stub") { - options "-stub" - - when { - process { - """ - input[0] = Channel.of([ - [ id:'test' ], // meta map - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), - [] - ]) - input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) - input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) - input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) - input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true)]) - input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true)]) - """ - } - } - - then { - assert process.success - assertAll( - { assert snapshot(process.out).match() } - ) - } - } - - test("sarscov2 - bam - intervals - stub") { - options "-stub" - - when { - process { - """ - input[0] = Channel.of([ - [ id:'test' ], // meta map - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/bed/test.bed', checkIfExists: true) - ]) - input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) - input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) - input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) - input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true)]) - input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true)]) - """ - } - } - - then { - assert process.success - assertAll( - { assert snapshot(process.out).match() } - ) - } - - } - - test("sarscov2 - bam - multiple sites - stub") { - options "-stub" - - when { - process { - """ - input[0] = Channel.of([ - [ id:'test' ], // meta map - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam.bai', checkIfExists: true), - [] - ]) - input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta', checkIfExists: true)]) - input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.fasta.fai', checkIfExists: true)]) - input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/sarscov2/genome/genome.dict', checkIfExists: true)]) - input[4] = Channel.of([ - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test2.vcf.gz', checkIfExists: true) - ]) - input[5] = Channel.of([ - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test.vcf.gz.tbi', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/vcf/test2.vcf.gz.tbi', checkIfExists: true) - ]) - """ - } - } - - then { - assert process.success - assertAll( - { assert snapshot(process.out).match() } - ) - } - } - - test("homo_sapiens - cram - stub") { - options "-stub" - - when { - process { - """ - input[0] = Channel.of([ - [ id:'test' ], // meta map - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/cram/test.paired_end.sorted.cram.crai', checkIfExists: true), - [] - ]) - input[1] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta', checkIfExists: true)]) - input[2] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.fasta.fai', checkIfExists: true)]) - input[3] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true)]) - input[4] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz', checkIfExists: true)]) - input[5] = Channel.of([[ id:'test' ], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz.tbi', checkIfExists: true)]) - """ - } - } - - then { - assert process.success - assertAll( - { assert snapshot(process.out).match() } - ) - } - } -} diff --git a/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test.snap b/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test.snap deleted file mode 100644 index 3f38dd8..0000000 --- a/modules/nf-core/gatk4/baserecalibrator/tests/main.nf.test.snap +++ /dev/null @@ -1,330 +0,0 @@ -{ - "homo_sapiens - cram": { - "content": [ - { - "0": [ - [ - { - "id": "test" - }, - "test.table:md5,35d89a3811aa31711fc9815b6b80e6ec" - ] - ], - "1": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ], - "table": [ - [ - { - "id": "test" - }, - "test.table:md5,35d89a3811aa31711fc9815b6b80e6ec" - ] - ], - "versions_gatk4": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.10.2" - }, - "timestamp": "2026-01-21T15:30:03.162339291" - }, - "sarscov2 - bam - stub": { - "content": [ - { - "0": [ - [ - { - "id": "test" - }, - "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "1": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ], - "table": [ - [ - { - "id": "test" - }, - "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "versions_gatk4": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.10.2" - }, - "timestamp": "2026-01-21T15:30:16.189687912" - }, - "sarscov2 - bam - intervals": { - "content": [ - { - "0": [ - [ - { - "id": "test" - }, - "test.table:md5,9ecb5f00a2229291705addc09c0ec231" - ] - ], - "1": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ], - "table": [ - [ - { - "id": "test" - }, - "test.table:md5,9ecb5f00a2229291705addc09c0ec231" - ] - ], - "versions_gatk4": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.10.2" - }, - "timestamp": "2026-01-21T15:29:29.326290321" - }, - "sarscov2 - bam - multiple sites": { - "content": [ - { - "0": [ - [ - { - "id": "test" - }, - "test.table:md5,e2e43abdc0c943c1a54dae816d0b9ea7" - ] - ], - "1": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ], - "table": [ - [ - { - "id": "test" - }, - "test.table:md5,e2e43abdc0c943c1a54dae816d0b9ea7" - ] - ], - "versions_gatk4": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.10.2" - }, - "timestamp": "2026-01-21T15:29:45.46009485" - }, - "homo_sapiens - cram - stub": { - "content": [ - { - "0": [ - [ - { - "id": "test" - }, - "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "1": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ], - "table": [ - [ - { - "id": "test" - }, - "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "versions_gatk4": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.10.2" - }, - "timestamp": "2026-01-21T15:30:54.713622275" - }, - "sarscov2 - bam": { - "content": [ - { - "0": [ - [ - { - "id": "test" - }, - "test.table:md5,e2e43abdc0c943c1a54dae816d0b9ea7" - ] - ], - "1": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ], - "table": [ - [ - { - "id": "test" - }, - "test.table:md5,e2e43abdc0c943c1a54dae816d0b9ea7" - ] - ], - "versions_gatk4": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.10.2" - }, - "timestamp": "2026-01-21T15:29:13.31883295" - }, - "sarscov2 - bam - intervals - stub": { - "content": [ - { - "0": [ - [ - { - "id": "test" - }, - "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "1": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ], - "table": [ - [ - { - "id": "test" - }, - "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "versions_gatk4": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.10.2" - }, - "timestamp": "2026-01-21T15:30:29.349106158" - }, - "sarscov2 - bam - multiple sites - stub": { - "content": [ - { - "0": [ - [ - { - "id": "test" - }, - "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "1": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ], - "table": [ - [ - { - "id": "test" - }, - "test.table:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "versions_gatk4": [ - [ - "GATK4_BASERECALIBRATOR", - "gatk4", - "4.6.2.0" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.10.2" - }, - "timestamp": "2026-01-21T15:30:41.454826856" - } -} \ No newline at end of file diff --git a/modules/nf-core/gatk4/baserecalibrator/environment.yml b/modules/nf-core/gatk4/mergevcfs/environment.yml similarity index 100% rename from modules/nf-core/gatk4/baserecalibrator/environment.yml rename to modules/nf-core/gatk4/mergevcfs/environment.yml diff --git a/modules/nf-core/gatk4/baserecalibrator/main.nf b/modules/nf-core/gatk4/mergevcfs/main.nf similarity index 54% rename from modules/nf-core/gatk4/baserecalibrator/main.nf rename to modules/nf-core/gatk4/mergevcfs/main.nf index d7c5c65..d7837d2 100644 --- a/modules/nf-core/gatk4/baserecalibrator/main.nf +++ b/modules/nf-core/gatk4/mergevcfs/main.nf @@ -1,4 +1,4 @@ -process GATK4_BASERECALIBRATOR { +process GATK4_MERGEVCFS { tag "${meta.id}" label 'process_low' @@ -8,15 +8,12 @@ process GATK4_BASERECALIBRATOR { : 'community.wave.seqera.io/library/gatk4_gcnvkernel:edb12e4f0bf02cd3'}" input: - tuple val(meta), path(input), path(input_index), path(intervals) - tuple val(meta2), path(fasta) - tuple val(meta3), path(fai) - tuple val(meta4), path(dict) - tuple val(meta5), path(known_sites) - tuple val(meta6), path(known_sites_tbi) + tuple val(meta), path(vcf) + tuple val(meta2), path(dict) output: - tuple val(meta), path("*.table"), emit: table + tuple val(meta), path('*.vcf.gz'), emit: vcf + tuple val(meta), path("*.tbi"), emit: tbi tuple val("${task.process}"), val('gatk4'), eval("gatk --version | sed -n '/GATK.*v/s/.*v//p'"), topic: versions, emit: versions_gatk4 when: @@ -25,31 +22,30 @@ process GATK4_BASERECALIBRATOR { script: def args = task.ext.args ?: '' def prefix = task.ext.prefix ?: "${meta.id}" - def interval_command = intervals ? "--intervals ${intervals}" : "" - def sites_command = known_sites.collect { vcf -> "--known-sites ${vcf}" }.join(' ') + def input_list = vcf.collect { vcf_ -> "--INPUT ${vcf_}" }.join(' ') + def reference_command = dict ? "--SEQUENCE_DICTIONARY ${dict}" : "" def avail_mem = 3072 if (!task.memory) { - log.info('[GATK BaseRecalibrator] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.') + log.info('[GATK MergeVcfs] Available memory not known - defaulting to 3GB. Specify process memory requirements to change this.') } else { avail_mem = (task.memory.mega * 0.8).intValue() } """ gatk --java-options "-Xmx${avail_mem}M -XX:-UsePerfData" \\ - BaseRecalibrator \\ - --input ${input} \\ - --output ${prefix}.table \\ - --reference ${fasta} \\ - ${interval_command} \\ - ${sites_command} \\ - --tmp-dir . \\ + MergeVcfs \\ + ${input_list} \\ + --OUTPUT ${prefix}.vcf.gz \\ + ${reference_command} \\ + --TMP_DIR . \\ ${args} """ stub: def prefix = task.ext.prefix ?: "${meta.id}" """ - touch ${prefix}.table + echo "" | gzip > ${prefix}.vcf.gz + touch ${prefix}.vcf.gz.tbi """ } diff --git a/modules/nf-core/gatk4/mergevcfs/meta.yml b/modules/nf-core/gatk4/mergevcfs/meta.yml new file mode 100644 index 0000000..146eaf8 --- /dev/null +++ b/modules/nf-core/gatk4/mergevcfs/meta.yml @@ -0,0 +1,88 @@ +name: gatk4_mergevcfs +description: Merges several vcf files +keywords: + - gatk4 + - merge + - vcf +tools: + - gatk4: + description: | + Developed in the Data Sciences Platform at the Broad Institute, the toolkit offers a wide variety of tools + with a primary focus on variant discovery and genotyping. Its powerful processing engine + and high-performance computing features make it capable of taking on projects of any size. + homepage: https://gatk.broadinstitute.org/hc/en-us + documentation: https://gatk.broadinstitute.org/hc/en-us/categories/360002369672s + doi: 10.1158/1538-7445.AM2017-3590 + licence: ["Apache-2.0"] + identifier: "" +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - vcf: + type: list + description: Two or more VCF files + pattern: "*.{vcf,vcf.gz}" + - - meta2: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'genome'] + - dict: + type: file + description: Optional Sequence Dictionary as input + pattern: "*.dict" + ontologies: [] +output: + vcf: + - - meta: + type: file + description: merged vcf file + pattern: "*.vcf.gz" + ontologies: + - edam: http://edamontology.org/format_3989 # GZIP format + - "*.vcf.gz": + type: file + description: merged vcf file + pattern: "*.vcf.gz" + ontologies: + - edam: http://edamontology.org/format_3989 # GZIP format + tbi: + - - meta: + type: file + description: merged vcf file + pattern: "*.vcf.gz" + ontologies: + - edam: http://edamontology.org/format_3989 # GZIP format + - "*.tbi": + type: file + description: index files for the merged vcf files + pattern: "*.tbi" + ontologies: [] + versions_gatk4: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - gatk4: + type: string + description: The name of the tool + - gatk --version | sed -n '/GATK.*v/s/.*v//p': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@kevinmenden" +maintainers: + - "@kevinmenden" diff --git a/modules/nf-core/gatk4/mergevcfs/tests/main.nf.test b/modules/nf-core/gatk4/mergevcfs/tests/main.nf.test new file mode 100644 index 0000000..fd9972b --- /dev/null +++ b/modules/nf-core/gatk4/mergevcfs/tests/main.nf.test @@ -0,0 +1,80 @@ +nextflow_process { + + name "Test Process GATK4_MERGEVCFS" + script "../main.nf" + process "GATK4_MERGEVCFS" + + tag "modules" + tag "modules_nfcore" + tag "gatk4" + tag "gatk4/mergevcfs" + + test("test_gatk4_mergevcfs") { + when { + process { + """ + input[0] = [ [ id:'test' ], [ file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz', checkIfExists: true), file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/gnomAD.r2.1.1.vcf.gz', checkIfExists: true) ]] + input[1] = [ [], file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.dict', checkIfExists: true)] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.vcf[0][1]).name, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() }, + ) + } + } + + test("test_gatk4_mergevcfs_no_dict") { + when { + process { + """ + input[0] = [ [ id:'test' ], [ file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz', checkIfExists: true), file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/gnomAD.r2.1.1.vcf.gz', checkIfExists: true) ]] + input[1] = [ [],[]] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.vcf[0][1]).name, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() }, + ) + } + } + + test("test_gatk4_mergevcfs_no_dict_stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ [ id:'test' ], [ file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/dbsnp_146.hg38.vcf.gz', checkIfExists: true), file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/vcf/gnomAD.r2.1.1.vcf.gz', checkIfExists: true) ]] + input[1] = [ [],[]] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + file(process.out.vcf[0][1]).name, + file(process.out.tbi[0][1]).name, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() }, + ) + } + } +} diff --git a/modules/nf-core/gatk4/mergevcfs/tests/main.nf.test.snap b/modules/nf-core/gatk4/mergevcfs/tests/main.nf.test.snap new file mode 100644 index 0000000..38e5555 --- /dev/null +++ b/modules/nf-core/gatk4/mergevcfs/tests/main.nf.test.snap @@ -0,0 +1,62 @@ +{ + "test_gatk4_mergevcfs_no_dict_stub": { + "content": [ + "test.vcf.gz", + "test.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_MERGEVCFS", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T16:40:17.349813218" + }, + "test_gatk4_mergevcfs": { + "content": [ + "test.vcf.gz", + "test.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_MERGEVCFS", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T16:40:03.274024419" + }, + "test_gatk4_mergevcfs_no_dict": { + "content": [ + "test.vcf.gz", + "test.vcf.gz.tbi", + { + "versions_gatk4": [ + [ + "GATK4_MERGEVCFS", + "gatk4", + "4.6.2.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-02-05T16:40:10.948156303" + } +} \ No newline at end of file diff --git a/subworkflows/local/bam_variant_calling/main.nf b/subworkflows/local/bam_variant_calling/main.nf index 48ac387..0b0a6dc 100644 --- a/subworkflows/local/bam_variant_calling/main.nf +++ b/subworkflows/local/bam_variant_calling/main.nf @@ -1,7 +1,9 @@ +include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index/main' include { GATK4_BEDTOINTERVALLIST } from '../../../modules/nf-core/gatk4/bedtointervallist/main' include { GATK4_CREATESEQUENCEDICTIONARY } from '../../../modules/nf-core/gatk4/createsequencedictionary/main' include { GATK4_HAPLOTYPECALLER } from '../../../modules/nf-core/gatk4/haplotypecaller/main' include { GATK4_INTERVALLISTTOOLS } from '../../../modules/nf-core/gatk4/intervallisttools/main' +include { GATK4_MERGEVCFS } from '../../../modules/nf-core/gatk4/mergevcfs/main' include { GATK4_SPLITNCIGARREADS } from '../../../modules/nf-core/gatk4/splitncigarreads/main' include { GATK4_VARIANTFILTRATION } from '../../../modules/nf-core/gatk4/variantfiltration/main' include { GTF2BED } from '../../../modules/local/gtf2bed/main' @@ -105,18 +107,28 @@ workflow BAM_VARIANT_CALLING { .map { meta, vcf, tbi -> [groupKey(meta + [id: meta.sample] - meta.subMap('sample', "interval_count"), meta.interval_count), vcf, tbi] } - .groupTuple() + .groupTuple() + + + def haplotypecaller_raw = ch_vcf_tbi.map { meta, vcfs, _tbis -> [meta, vcfs] } + GATK4_MERGEVCFS( + haplotypecaller_raw, + ch_dict, + ) + + BCFTOOLS_INDEX(GATK4_MERGEVCFS.out.vcf) GATK4_VARIANTFILTRATION( - ch_vcf_tbi, + GATK4_MERGEVCFS.out.vcf.join(BCFTOOLS_INDEX.out.tbi), ch_fasta, ch_fai, ch_dict, - [[:],[]] + [[],[]] ) + emit: ch_vcf_tbi_unfiltered = ch_vcf_tbi ch_vcf_filtered = GATK4_VARIANTFILTRATION.out.vcf diff --git a/subworkflows/local/bam_variant_calling/tests/main.nf.test b/subworkflows/local/bam_variant_calling/tests/main.nf.test index 54c662f..7806e9f 100644 --- a/subworkflows/local/bam_variant_calling/tests/main.nf.test +++ b/subworkflows/local/bam_variant_calling/tests/main.nf.test @@ -35,9 +35,7 @@ nextflow_workflow { assertAll( { assert workflow.success }, { assert snapshot( - workflow.out.ch_vcf_tbi.collect() {meta, vcf, tbi -> - [meta, path(vcf).vcf.variantsMD5, path(tbi).name] - } + workflow.out.ch_vcf_filtered ).match() } ) } diff --git a/subworkflows/local/bam_variant_calling/tests/main.nf.test.snap b/subworkflows/local/bam_variant_calling/tests/main.nf.test.snap index 320daea..b4722bf 100644 --- a/subworkflows/local/bam_variant_calling/tests/main.nf.test.snap +++ b/subworkflows/local/bam_variant_calling/tests/main.nf.test.snap @@ -2,29 +2,13 @@ "Test scatter and merge": { "content": [ [ - [ - { - "groupSize": 2, - "groupTarget": { - "id": "sampleA", - "variantcaller": "haplotypecaller" - } - }, - [ - "sampleA_2scattered.vcf.gz:md5,211e2365caeccd0cf38188c5667bbf18", - "sampleA_1scattered.vcf.gz:md5,321d393676e98d5175f9bd0b99f0cf10" - ], - [ - "sampleA_2scattered.vcf.gz.tbi:md5,cd55f12b595e999c30908d8901254b26", - "sampleA_1scattered.vcf.gz.tbi:md5,1549b6bac8da745c73cf37bba629e029" - ] - ] + ] ], "meta": { "nf-test": "0.9.3", "nextflow": "25.10.4" }, - "timestamp": "2026-05-20T13:44:47.782141" + "timestamp": "2026-05-26T14:31:27.300922" } } \ No newline at end of file From 571149f0b88220bea07f36f346aafef662456c7c Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Tue, 26 May 2026 16:28:06 +0200 Subject: [PATCH 09/19] mqc outdir conf fix --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index 891585f..3129569 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -139,7 +139,7 @@ process { ext.prefix = { params.analysis_id } publishDir = [ [ - path: "${params.outdir}/QC", + path: { "${params.outdir}/QC" }, mode: params.publish_dir_mode, ] ] From c4aadd6e981ab51c5d497b256f64acf8abc9140e Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Tue, 26 May 2026 16:37:24 +0200 Subject: [PATCH 10/19] update step count --- tests/main.nf.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/main.nf.test b/tests/main.nf.test index 36d1db9..7b58df6 100644 --- a/tests/main.nf.test +++ b/tests/main.nf.test @@ -30,7 +30,7 @@ nextflow_pipeline { } - + when { params { input = "${launchDir}/input/" @@ -45,7 +45,7 @@ nextflow_pipeline { } then { - assert workflow.trace.succeeded().size() == 27 // check if all steps are executed + assert workflow.trace.succeeded().size() == 37 // check if all steps are executed assert workflow.success } } From eb0c600101574d6eb26135fdfcdd4d331924ccd0 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Tue, 2 Jun 2026 09:16:56 +0200 Subject: [PATCH 11/19] add caching for repeated jobs --- conf/hpc.config | 2 ++ conf/modules.config | 10 ++++++++++ nextflow.config | 2 ++ 3 files changed, 14 insertions(+) diff --git a/conf/hpc.config b/conf/hpc.config index ee896fb..07b335f 100644 --- a/conf/hpc.config +++ b/conf/hpc.config @@ -68,4 +68,6 @@ params { ] ] + cache_dir = "/hpc/diaggen/data/databases/Nextflow_cache/DxNextflowRNA/" + } diff --git a/conf/modules.config b/conf/modules.config index 3129569..4b8ae7b 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -72,6 +72,11 @@ process { withName: GATK4_BEDTOINTERVALLIST { ext.args = '--DROP_MISSING_CONTIGS TRUE' + storeDir = "${params.cache_dir}/gatk4/bedtointervallist/" + } + + withName: GATK4_CREATESEQUENCEDICTIONARY { + storeDir = "${params.cache_dir}/gatk4/createsequencedictionary/" } withName: GATK4_INTERVALLISTTOOLS { @@ -81,6 +86,7 @@ process { "--SORT true", "--SCATTER_COUNT ${params.scatter_size}" ].join(' ').trim() } + storeDir = "${params.cache_dir}/gatk4/intervallisttools/" } withName: GATK4_HAPLOTYPECALLER { @@ -124,6 +130,10 @@ process { ] } + withName: GTF2BED { + storeDir = "${params.cache_dir}/gtf2bed/" + } + withName: HGNC_DOWNLOAD { cpus = 1 memory = { 1.GB } diff --git a/nextflow.config b/nextflow.config index 7a84921..f32b3a7 100644 --- a/nextflow.config +++ b/nextflow.config @@ -102,6 +102,8 @@ params { validate_params = true cluster_options = "--mail-user ${params.email} --mail-type FAIL --account=diaggen" + // Nextflow process cache dir + cache_dir = null } // Load base.config by default for all pipelines From 85e04fe667ec232fe4be58db6533eb5cc3213e71 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Tue, 2 Jun 2026 09:55:10 +0200 Subject: [PATCH 12/19] fix styling --- subworkflows/local/bam_variant_calling/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/bam_variant_calling/main.nf b/subworkflows/local/bam_variant_calling/main.nf index 0b0a6dc..e25a017 100644 --- a/subworkflows/local/bam_variant_calling/main.nf +++ b/subworkflows/local/bam_variant_calling/main.nf @@ -61,7 +61,7 @@ workflow BAM_VARIANT_CALLING { def bam_splitncigar = GATK4_SPLITNCIGARREADS.out.bam def bam_splitncigar_interval = bam_splitncigar - .map { meta, bam_ -> + .map { meta, _bam -> def new_meta = meta + [id: meta.sample] - meta.subMap('sample') - meta.subMap('interval_count') [groupKey(new_meta, meta.interval_count), bam_] } From c007b790ec8dc35d9590620e78ece834e5af7fba Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Tue, 2 Jun 2026 09:57:20 +0200 Subject: [PATCH 13/19] remove random underscore --- subworkflows/local/bam_variant_calling/main.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/bam_variant_calling/main.nf b/subworkflows/local/bam_variant_calling/main.nf index e25a017..81211dd 100644 --- a/subworkflows/local/bam_variant_calling/main.nf +++ b/subworkflows/local/bam_variant_calling/main.nf @@ -61,9 +61,9 @@ workflow BAM_VARIANT_CALLING { def bam_splitncigar = GATK4_SPLITNCIGARREADS.out.bam def bam_splitncigar_interval = bam_splitncigar - .map { meta, _bam -> + .map { meta, bam -> def new_meta = meta + [id: meta.sample] - meta.subMap('sample') - meta.subMap('interval_count') - [groupKey(new_meta, meta.interval_count), bam_] + [groupKey(new_meta, meta.interval_count), bam] } .groupTuple() From 7091486c3e7b49aeb3d7866f3423d75f4d3318b1 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Tue, 2 Jun 2026 10:53:27 +0200 Subject: [PATCH 14/19] improve readability --- subworkflows/local/bam_variant_calling/main.nf | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/subworkflows/local/bam_variant_calling/main.nf b/subworkflows/local/bam_variant_calling/main.nf index 81211dd..7984d4a 100644 --- a/subworkflows/local/bam_variant_calling/main.nf +++ b/subworkflows/local/bam_variant_calling/main.nf @@ -58,9 +58,7 @@ workflow BAM_VARIANT_CALLING { ch_dict, ) - def bam_splitncigar = GATK4_SPLITNCIGARREADS.out.bam - - def bam_splitncigar_interval = bam_splitncigar + def bam_splitncigar_interval = GATK4_SPLITNCIGARREADS.out.bam .map { meta, bam -> def new_meta = meta + [id: meta.sample] - meta.subMap('sample') - meta.subMap('interval_count') [groupKey(new_meta, meta.interval_count), bam] @@ -73,17 +71,17 @@ workflow BAM_VARIANT_CALLING { ch_fai ) - def splitncigar_bam = SAMTOOLS_MERGE.out.bam + def splitncigar_merged_bam = SAMTOOLS_MERGE.out.bam - SAMTOOLS_INDEX(splitncigar_bam) + SAMTOOLS_INDEX(splitncigar_merged_bam) - def splitncigar_bam_indices = SAMTOOLS_INDEX.out.bai + def splitncigar_merged_bam_indices = SAMTOOLS_INDEX.out.bai .mix(SAMTOOLS_INDEX.out.csi) .mix(SAMTOOLS_INDEX.out.crai) - def splitncigar_bam_bai = splitncigar_bam.join(splitncigar_bam_indices, failOnDuplicate: true, failOnMismatch: true) + def splitncigar_merged_bam_bai = splitncigar_merged_bam.join(splitncigar_merged_bam_indices, failOnDuplicate: true, failOnMismatch: true) - def haplotypecaller_interval_bam = splitncigar_bam_bai + def haplotypecaller_interval_bam = splitncigar_merged_bam_bai .combine(interval_list_split) .map { meta, bam, bai, interval_lists -> [meta + [interval_count: interval_lists.size()], bam, bai, interval_lists] @@ -109,10 +107,8 @@ workflow BAM_VARIANT_CALLING { } .groupTuple() - - def haplotypecaller_raw = ch_vcf_tbi.map { meta, vcfs, _tbis -> [meta, vcfs] } GATK4_MERGEVCFS( - haplotypecaller_raw, + ch_vcf_tbi.map { meta, vcfs, _tbis -> [meta, vcfs] }, ch_dict, ) From 5d956771809d62878604db7a5c0adb6730ff5597 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Wed, 3 Jun 2026 09:58:08 +0200 Subject: [PATCH 15/19] prepare genomes subflow and bedtops/gtf2bed --- conf/modules.config | 10 +-- modules.json | 5 ++ modules/local/gtf2bed/main.nf | 41 --------- modules/local/gtf2bed/tests/main.nf.test | 48 ----------- modules/local/gtf2bed/tests/main.nf.test.snap | 54 ------------ .../bedops}/gtf2bed/environment.yml | 2 +- modules/nf-core/bedops/gtf2bed/main.nf | 40 +++++++++ modules/nf-core/bedops/gtf2bed/meta.yml | 64 ++++++++++++++ .../nf-core/bedops/gtf2bed/tests/main.nf.test | 56 +++++++++++++ .../bedops/gtf2bed/tests/main.nf.test.snap | 84 +++++++++++++++++++ .../local/bam_variant_calling/main.nf | 59 +++++-------- .../bam_variant_calling/tests/main.nf.test | 30 +++++-- subworkflows/local/prepare_references/main.nf | 30 +++++++ .../prepare_references/tests/main.nf.test | 41 +++++++++ .../tests/main.nf.test.snap | 43 ++++++++++ workflows/dxnextflowrna.nf | 10 ++- 16 files changed, 422 insertions(+), 195 deletions(-) delete mode 100755 modules/local/gtf2bed/main.nf delete mode 100644 modules/local/gtf2bed/tests/main.nf.test delete mode 100644 modules/local/gtf2bed/tests/main.nf.test.snap rename modules/{local => nf-core/bedops}/gtf2bed/environment.yml (85%) create mode 100644 modules/nf-core/bedops/gtf2bed/main.nf create mode 100644 modules/nf-core/bedops/gtf2bed/meta.yml create mode 100644 modules/nf-core/bedops/gtf2bed/tests/main.nf.test create mode 100644 modules/nf-core/bedops/gtf2bed/tests/main.nf.test.snap create mode 100644 subworkflows/local/prepare_references/main.nf create mode 100644 subworkflows/local/prepare_references/tests/main.nf.test create mode 100644 subworkflows/local/prepare_references/tests/main.nf.test.snap diff --git a/conf/modules.config b/conf/modules.config index 4b8ae7b..6088c20 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -72,11 +72,11 @@ process { withName: GATK4_BEDTOINTERVALLIST { ext.args = '--DROP_MISSING_CONTIGS TRUE' - storeDir = "${params.cache_dir}/gatk4/bedtointervallist/" + storeDir = params.cache_dir ? "${params.cache_dir}/gatk4/bedtointervallist" : null } withName: GATK4_CREATESEQUENCEDICTIONARY { - storeDir = "${params.cache_dir}/gatk4/createsequencedictionary/" + storeDir = params.cache_dir ? "${params.cache_dir}/gatk4/createsequencedictionary" : null } withName: GATK4_INTERVALLISTTOOLS { @@ -86,7 +86,7 @@ process { "--SORT true", "--SCATTER_COUNT ${params.scatter_size}" ].join(' ').trim() } - storeDir = "${params.cache_dir}/gatk4/intervallisttools/" + storeDir = params.cache_dir ? "${params.cache_dir}/gatk4/intervallisttools" : null } withName: GATK4_HAPLOTYPECALLER { @@ -130,8 +130,8 @@ process { ] } - withName: GTF2BED { - storeDir = "${params.cache_dir}/gtf2bed/" + withName: BEDOPS_GTF2BED { + storeDir = params.cache_dir ? "${params.cache_dir}/gtf2bed" : null } withName: HGNC_DOWNLOAD { diff --git a/modules.json b/modules.json index c2bbeed..cc857e8 100644 --- a/modules.json +++ b/modules.json @@ -15,6 +15,11 @@ "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", "installed_by": ["modules"] }, + "bedops/gtf2bed": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, "fastqc": { "branch": "master", "git_sha": "dc94b6ee04a05ddb9f7ae050712ff30a13149164", diff --git a/modules/local/gtf2bed/main.nf b/modules/local/gtf2bed/main.nf deleted file mode 100755 index 03bb2b2..0000000 --- a/modules/local/gtf2bed/main.nf +++ /dev/null @@ -1,41 +0,0 @@ -process GTF2BED { - tag "${gtf}" - label 'process_low' - - conda "${moduleDir}/environment.yml" - container "${workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container - ? 'https://depot.galaxyproject.org/singularity/r-base:3.5.1' - : 'biocontainers/r-base:3.5.1'}" - - input: - tuple val(meta), path(gtf) - val feature_type - - output: - tuple val(meta), path('*.bed'), emit: bed - tuple val("${task.process}"), val('Rscript'), eval("Rscript --version 2>&1 | sed 's/R scripting front-end version //'"), topic: versions, emit: versions_rscript - - when: - task.ext.when == null || task.ext.when - - script: - def allowed_type = ["exon", "transcript", "gene"] - if (feature_type) { - feature_type = allowed_type.contains(feature_type) ? feature_type : "exon" - } - """ - Rscript --no-save -<<'RCODE' - gtf = read.table("${gtf}", sep="\t") - gtf = subset(gtf, V3 == "${feature_type}") - write.table(data.frame(chrom=gtf[,'V1'], start=gtf[,'V4'], end=gtf[,'V5']), "tmp.exome.bed", quote = F, sep="\t", col.names = F, row.names = F) - RCODE - - awk '{print \$1 "\t" (\$2 - 1) "\t" \$3}' tmp.exome.bed > exome.bed - rm -rf tmp.exome.bed - """ - - stub: - """ - touch exome.bed - """ -} diff --git a/modules/local/gtf2bed/tests/main.nf.test b/modules/local/gtf2bed/tests/main.nf.test deleted file mode 100644 index 424c4ee..0000000 --- a/modules/local/gtf2bed/tests/main.nf.test +++ /dev/null @@ -1,48 +0,0 @@ -nextflow_process { - - name "Test Process GTF2BED" - script "../main.nf" - process "GTF2BED" - - test("gtf") { - - when { - process { - """ - input[0] = Channel.value([ - [id:'gtf'], - file(params.pipelines_testdata_base_path + '/genomics/homo_sapiens/genome/genome.gtf', checkIfExists: true) - ]) - input[1] = "exon" - """ - } - } - - then { - assert process.success - assert snapshot(sanitizeOutput(process.out)).match() - } - } - - test("gtf - stub") { - - options "-stub" - - when { - process { - """ - input[0] = Channel.value([ - [id:'gtf'], - file(params.pipelines_testdata_base_path + '/genomics/homo_sapiens/genome/genome.gtf', checkIfExists: true) - ]) - input[1] = "exon" - """ - } - } - - then { - assert process.success - assert snapshot(sanitizeOutput(process.out)).match() - } - } -} diff --git a/modules/local/gtf2bed/tests/main.nf.test.snap b/modules/local/gtf2bed/tests/main.nf.test.snap deleted file mode 100644 index 7b1287a..0000000 --- a/modules/local/gtf2bed/tests/main.nf.test.snap +++ /dev/null @@ -1,54 +0,0 @@ -{ - "gtf": { - "content": [ - { - "bed": [ - [ - { - "id": "gtf" - }, - "exome.bed:md5,17192418c24c524881e7d970f94a4a26" - ] - ], - "versions_rscript": [ - [ - "GTF2BED", - "Rscript", - "3.5.1 (2018-07-02)" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.04.8" - }, - "timestamp": "2025-10-16T15:25:07.682657227" - }, - "gtf - stub": { - "content": [ - { - "bed": [ - [ - { - "id": "gtf" - }, - "exome.bed:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "versions_rscript": [ - [ - "GTF2BED", - "Rscript", - "3.5.1 (2018-07-02)" - ] - ] - } - ], - "meta": { - "nf-test": "0.9.3", - "nextflow": "25.04.8" - }, - "timestamp": "2025-10-16T15:27:28.797666996" - } -} \ No newline at end of file diff --git a/modules/local/gtf2bed/environment.yml b/modules/nf-core/bedops/gtf2bed/environment.yml similarity index 85% rename from modules/local/gtf2bed/environment.yml rename to modules/nf-core/bedops/gtf2bed/environment.yml index 4e537c7..79a9bce 100644 --- a/modules/local/gtf2bed/environment.yml +++ b/modules/nf-core/bedops/gtf2bed/environment.yml @@ -4,4 +4,4 @@ channels: - conda-forge - bioconda dependencies: - - conda-forge::r-base=3.5.1 + - bioconda::bedops=2.4.42 diff --git a/modules/nf-core/bedops/gtf2bed/main.nf b/modules/nf-core/bedops/gtf2bed/main.nf new file mode 100644 index 0000000..07880dd --- /dev/null +++ b/modules/nf-core/bedops/gtf2bed/main.nf @@ -0,0 +1,40 @@ +process BEDOPS_GTF2BED { + tag "$gtf" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/bedops:2.4.42--h9948957_0': + 'quay.io/biocontainers/bedops:2.4.42--h9948957_0' }" + + input: + tuple val(meta), path(gtf) + + output: + tuple val(meta), path('*.bed'), emit: bed + tuple val("${task.process}"), val("bedops"), eval('bedops --version | sed -n "s/.*version: *\\([^ ]*\\).*/\\1/p"'), emit: versions_bedops, topic: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${gtf.baseName}" + + """ + cat \\ + $gtf \\ + | gtf2bed \\ + $args \\ + --attribute-key=exon_id \\ + | awk -F'\t' '\$8 == "exon" ' \\ + > ${prefix}.bed + """ + + stub: + def prefix = task.ext.prefix ?: "${gtf.baseName}" + """ + touch ${prefix}.bed + """ + +} diff --git a/modules/nf-core/bedops/gtf2bed/meta.yml b/modules/nf-core/bedops/gtf2bed/meta.yml new file mode 100644 index 0000000..eb9c509 --- /dev/null +++ b/modules/nf-core/bedops/gtf2bed/meta.yml @@ -0,0 +1,64 @@ +name: "bedops_gtf2bed" +description: Convert gtf format to bed format +keywords: + - gtf + - bed + - conversion +tools: + - gtf2bed: + description: The gtf2bed script converts 1-based, closed [start, end] Gene + Transfer Format v2.2 (GTF2.2) to sorted, 0-based, half-open [start-1, end) + extended BED-formatted data. + homepage: https://bedops.readthedocs.io/en/latest/content/reference/file-management/conversion/gtf2bed.html + documentation: https://bedops.readthedocs.io/en/latest/content/reference/file-management/conversion/gtf2bed.html + tool_dev_url: https://github.com/bedops/bedops + doi: 10.1093/bioinformatics/bts277 + licence: + - "GPL v2" + identifier: biotools:bedops +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1', single_end:false ]` + - gtf: + type: file + description: A reference file in GTF format + pattern: "*.{gtf,gtf.gz}" + ontologies: [] +output: + bed: + - - meta: + type: file + description: A reference file in BED format + pattern: "*.{bed}" + ontologies: [] + - "*.bed": + type: file + description: A reference file in BED format + pattern: "*.{bed}" + ontologies: [] + versions_bedops: + - - ${task.process}: + type: string + description: "The name of the process" + - bedops: + type: string + description: The name of the tool + - 'bedops --version | sed -n "s/.*version: *\([^ ]*\).*/\1/p"': + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: "The name of the process" + - bedops: + type: string + description: The name of the tool + - 'bedops --version | sed -n "s/.*version: *\([^ ]*\).*/\1/p"': + type: eval + description: The expression to obtain the version of the tool +authors: + - "@davidecarlson" diff --git a/modules/nf-core/bedops/gtf2bed/tests/main.nf.test b/modules/nf-core/bedops/gtf2bed/tests/main.nf.test new file mode 100644 index 0000000..c6f4ed2 --- /dev/null +++ b/modules/nf-core/bedops/gtf2bed/tests/main.nf.test @@ -0,0 +1,56 @@ +nextflow_process { + + name "Test Process BEDOPS_GTF2BED" + script "../main.nf" + process "BEDOPS_GTF2BED" + + tag "modules" + tag "modules_nfcore" + tag "bedops" + tag "bedops/gtf2bed" + + test("homo sapiens") { + + when { + process { + """ + input[0] = [[ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.gtf', checkIfExists: true) + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + + test("homo sapiens - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [[ id:'test' ], // meta map + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.gtf', checkIfExists: true) + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + +} diff --git a/modules/nf-core/bedops/gtf2bed/tests/main.nf.test.snap b/modules/nf-core/bedops/gtf2bed/tests/main.nf.test.snap new file mode 100644 index 0000000..b86b437 --- /dev/null +++ b/modules/nf-core/bedops/gtf2bed/tests/main.nf.test.snap @@ -0,0 +1,84 @@ +{ + "homo sapiens": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "genome.bed:md5,783257789b0c59a1a5391f7f2b9ac08a" + ] + ], + "1": [ + [ + "BEDOPS_GTF2BED", + "bedops", + "2.4.42" + ] + ], + "bed": [ + [ + { + "id": "test" + }, + "genome.bed:md5,783257789b0c59a1a5391f7f2b9ac08a" + ] + ], + "versions_bedops": [ + [ + "BEDOPS_GTF2BED", + "bedops", + "2.4.42" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.04.3" + }, + "timestamp": "2026-02-17T10:50:08.652816984" + }, + "homo sapiens - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "genome.bed:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "BEDOPS_GTF2BED", + "bedops", + "2.4.42" + ] + ], + "bed": [ + [ + { + "id": "test" + }, + "genome.bed:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_bedops": [ + [ + "BEDOPS_GTF2BED", + "bedops", + "2.4.42" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.04.3" + }, + "timestamp": "2026-02-17T10:50:16.813347554" + } +} \ No newline at end of file diff --git a/subworkflows/local/bam_variant_calling/main.nf b/subworkflows/local/bam_variant_calling/main.nf index 7984d4a..6d0fa8b 100644 --- a/subworkflows/local/bam_variant_calling/main.nf +++ b/subworkflows/local/bam_variant_calling/main.nf @@ -1,12 +1,8 @@ include { BCFTOOLS_INDEX } from '../../../modules/nf-core/bcftools/index/main' -include { GATK4_BEDTOINTERVALLIST } from '../../../modules/nf-core/gatk4/bedtointervallist/main' -include { GATK4_CREATESEQUENCEDICTIONARY } from '../../../modules/nf-core/gatk4/createsequencedictionary/main' include { GATK4_HAPLOTYPECALLER } from '../../../modules/nf-core/gatk4/haplotypecaller/main' -include { GATK4_INTERVALLISTTOOLS } from '../../../modules/nf-core/gatk4/intervallisttools/main' include { GATK4_MERGEVCFS } from '../../../modules/nf-core/gatk4/mergevcfs/main' include { GATK4_SPLITNCIGARREADS } from '../../../modules/nf-core/gatk4/splitncigarreads/main' include { GATK4_VARIANTFILTRATION } from '../../../modules/nf-core/gatk4/variantfiltration/main' -include { GTF2BED } from '../../../modules/local/gtf2bed/main' include { SAMTOOLS_MERGE } from '../../../modules/nf-core/samtools/merge/main' include { SAMTOOLS_INDEX } from '../../../modules/nf-core/samtools/index/main' @@ -16,7 +12,8 @@ workflow BAM_VARIANT_CALLING { take: ch_bam_bai ch_fasta_fai - ch_gtf + ch_dict + interval_list_split ch_dbsnp ch_dbsnp_tbi @@ -26,20 +23,6 @@ workflow BAM_VARIANT_CALLING { ch_fai = ch_fasta_fai.map { meta, _fasta, fai -> [meta, fai] } - GATK4_CREATESEQUENCEDICTIONARY(ch_fasta_fai.map { meta, fasta, _fai -> [meta, fasta] }) - def ch_dict = GATK4_CREATESEQUENCEDICTIONARY.out.dict - - GTF2BED(ch_gtf, 'exon') - - GATK4_BEDTOINTERVALLIST(GTF2BED.out.bed.collect(), ch_dict) - def interval_list = GATK4_BEDTOINTERVALLIST.out.interval_list - - // MODULE: Scatter one interval-list into many interval-files using GATK4 IntervalListTools - GATK4_INTERVALLISTTOOLS(interval_list) - - def interval_list_split = GATK4_INTERVALLISTTOOLS.out.interval_list.map { _meta, bed -> [bed] }.collect() - - def bam_interval = ch_bam_bai .combine(interval_list_split) .map { meta, bam, bai, intervals -> @@ -91,26 +74,26 @@ workflow BAM_VARIANT_CALLING { [meta + [id: meta.id + "_" + interval_list_.baseName, sample: meta.id, variantcaller: 'haplotypecaller'], bam, bai, interval_list_, []] } - GATK4_HAPLOTYPECALLER( - haplotypecaller_interval_bam, - ch_fasta, - ch_fai, - ch_dict, - ch_dbsnp, - ch_dbsnp_tbi, - ) - - def ch_vcf_tbi = GATK4_HAPLOTYPECALLER.out.vcf - .join(GATK4_HAPLOTYPECALLER.out.tbi, failOnMismatch: true, failOnDuplicate: true) - .map { meta, vcf, tbi -> - [groupKey(meta + [id: meta.sample] - meta.subMap('sample', "interval_count"), meta.interval_count), vcf, tbi] - } + GATK4_HAPLOTYPECALLER( + haplotypecaller_interval_bam, + ch_fasta, + ch_fai, + ch_dict, + ch_dbsnp, + ch_dbsnp_tbi, + ) + + def ch_vcf_tbi = GATK4_HAPLOTYPECALLER.out.vcf + .join(GATK4_HAPLOTYPECALLER.out.tbi, failOnMismatch: true, failOnDuplicate: true) + .map { meta, vcf, tbi -> + [groupKey(meta + [id: meta.sample] - meta.subMap('sample', "interval_count"), meta.interval_count), vcf, tbi] + } .groupTuple() - GATK4_MERGEVCFS( - ch_vcf_tbi.map { meta, vcfs, _tbis -> [meta, vcfs] }, - ch_dict, - ) + GATK4_MERGEVCFS( + ch_vcf_tbi.map { meta, vcfs, _tbis -> [meta, vcfs] }, + ch_dict, + ) BCFTOOLS_INDEX(GATK4_MERGEVCFS.out.vcf) @@ -123,8 +106,6 @@ workflow BAM_VARIANT_CALLING { ) - - emit: ch_vcf_tbi_unfiltered = ch_vcf_tbi ch_vcf_filtered = GATK4_VARIANTFILTRATION.out.vcf diff --git a/subworkflows/local/bam_variant_calling/tests/main.nf.test b/subworkflows/local/bam_variant_calling/tests/main.nf.test index 7806e9f..b3ac9a1 100644 --- a/subworkflows/local/bam_variant_calling/tests/main.nf.test +++ b/subworkflows/local/bam_variant_calling/tests/main.nf.test @@ -1,5 +1,5 @@ nextflow_workflow { - name "Test Subworkflow BAM_VARIANT_CALING" + name "Test Subworkflow BAM_VARIANT_CALLING" script "../main.nf" workflow "BAM_VARIANT_CALLING" @@ -9,6 +9,26 @@ nextflow_workflow { tag "variant_calling" test ("Test scatter and merge") { + + setup { + run("PREPARE_REFERENCES") { + script "../../prepare_references/main.nf" + workflow { + """ + input[0] = channel.of([ + [ id:'minigenome_fasta' ], + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta'), + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta.fai') + ]).first() + input[1] = channel.of([ + [ id:'minigenome_gtf' ], + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/chr21_gencode.gtf') + ]).first() + """ + } + } + } + when { workflow { """ @@ -22,12 +42,10 @@ nextflow_workflow { file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta'), file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta.fai') ]).first() - input[2] = channel.of([ - [ id:'minigenome_gtf' ], - file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/chr21_gencode.gtf') - ]).first() - input[3] = [[:],[]] + input[2] = PREPARE_REFERENCES.out.dict + input[3] = PREPARE_REFERENCES.out.interval_list_split input[4] = [[:],[]] + input[5] = [[:],[]] """ } } diff --git a/subworkflows/local/prepare_references/main.nf b/subworkflows/local/prepare_references/main.nf new file mode 100644 index 0000000..b0e2ff0 --- /dev/null +++ b/subworkflows/local/prepare_references/main.nf @@ -0,0 +1,30 @@ +include { GATK4_BEDTOINTERVALLIST } from '../../../modules/nf-core/gatk4/bedtointervallist/main' +include { GATK4_CREATESEQUENCEDICTIONARY } from '../../../modules/nf-core/gatk4/createsequencedictionary/main' +include { GATK4_INTERVALLISTTOOLS } from '../../../modules/nf-core/gatk4/intervallisttools/main' +include { BEDOPS_GTF2BED } from '../../../modules/nf-core/bedops/gtf2bed/main' + +workflow PREPARE_REFERENCES { + take: + ch_fasta_fai + ch_gtf + + main: + + GATK4_CREATESEQUENCEDICTIONARY(ch_fasta_fai.map { meta, fasta, _fai -> [meta, fasta] }) + def ch_dict = GATK4_CREATESEQUENCEDICTIONARY.out.dict + + BEDOPS_GTF2BED(ch_gtf) + GATK4_BEDTOINTERVALLIST(BEDOPS_GTF2BED.out.bed.collect(), ch_dict) + + // Scatter one interval-list into many interval-files using GATK4 IntervalListTools + GATK4_INTERVALLISTTOOLS(GATK4_BEDTOINTERVALLIST.out.interval_list) + + + + emit: + dict = ch_dict + bed = BEDOPS_GTF2BED.out.bed.collect() + interval_list = GATK4_BEDTOINTERVALLIST.out.interval_list + interval_list_split = GATK4_INTERVALLISTTOOLS.out.interval_list.map { _meta, bed -> [bed] }.collect() + +} diff --git a/subworkflows/local/prepare_references/tests/main.nf.test b/subworkflows/local/prepare_references/tests/main.nf.test new file mode 100644 index 0000000..ec4c776 --- /dev/null +++ b/subworkflows/local/prepare_references/tests/main.nf.test @@ -0,0 +1,41 @@ +nextflow_workflow { + name "Test Subworkflow PREPARE_REFERENCES" + script "../main.nf" + workflow "PREPARE_REFERENCES" + + tag "subworkflows/local" + tag "subworkflows" + tag "local" + tag "prepare_references" + + test ("Test intervals and scatters") { + + when { + workflow { + """ + input[0] = channel.of([ + [ id:'minigenome_fasta' ], + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta'), + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/genome.fasta.fai') + ]).first() + input[1] = channel.of([ + [ id:'minigenome_gtf' ], + file(params.nfcore_modules_testdata + 'genomics/homo_sapiens/genome/chr21/sequence/chr21_gencode.gtf') + ]).first() + """ + } + + } + then { + assertAll( + { assert workflow.success }, + { assert snapshot( + workflow.out.dict, + workflow.out.bed, + workflow.out.interval_list, + workflow.out.interval_list_split + ).match() } + ) + } + } +} diff --git a/subworkflows/local/prepare_references/tests/main.nf.test.snap b/subworkflows/local/prepare_references/tests/main.nf.test.snap new file mode 100644 index 0000000..40e803d --- /dev/null +++ b/subworkflows/local/prepare_references/tests/main.nf.test.snap @@ -0,0 +1,43 @@ +{ + "Test intervals and scatters": { + "content": [ + [ + [ + { + "id": "minigenome_fasta" + }, + "genome.dict:md5,de4ff13287d28c1679a11ae989a3a980" + ] + ], + [ + [ + { + "id": "minigenome_gtf" + }, + "exome.bed:md5,f95c84914555fd4c0aa1bd9541ab5e0f" + ] + ], + [ + [ + { + "id": "minigenome_gtf" + }, + "minigenome_gtf.interval_list:md5,0266e4cef2ca29605c6f05ab687fd1cd" + ] + ], + [ + [ + [ + "1scattered.interval_list:md5,4ef1d2d1b8d522193037e657fd664263", + "2scattered.interval_list:md5,8847ce9646a754980f7dc3bb830574f1" + ] + ] + ] + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-06-03T08:41:56.743202" + } +} \ No newline at end of file diff --git a/workflows/dxnextflowrna.nf b/workflows/dxnextflowrna.nf index ef4e97b..53b730b 100644 --- a/workflows/dxnextflowrna.nf +++ b/workflows/dxnextflowrna.nf @@ -13,6 +13,7 @@ include { FASTQ_BAM_QC } from '../subworkflows/local/fastq_b include { FASTQ_TRIM_FILTER_ALIGN_DEDUP } from '../subworkflows/local/fastq_trim_filter_align_dedup/main' include { GENE_EXON_OUTRIDER } from '../subworkflows/local/gene_exon_outrider/main' include { BAM_GENE_FUSION } from '../subworkflows/local/bam_gene_fusion/main' +include { PREPARE_REFERENCES } from '../subworkflows/local/prepare_references/main' // FUNCTIONS include { methodsDescriptionText } from '../subworkflows/local/utils_umcugenetics_dxnextflowrna_pipeline' include { paramsSummaryMap } from 'plugin/nf-schema' @@ -70,6 +71,12 @@ workflow DXNEXTFLOWRNA { .map(createMetaWithIdSimpleName) .first() + + PREPARE_REFERENCES( + ch_fasta_fai, + ch_gtf + ) + // Input channel ch_fastq = Channel .fromFilePairs("${params.input}/*_R{1,2}_001.fastq.gz") @@ -202,7 +209,8 @@ workflow DXNEXTFLOWRNA { BAM_VARIANT_CALLING( FASTQ_TRIM_FILTER_ALIGN_DEDUP.out.ch_bam_bai, ch_fasta_fai, - ch_gtf, + PREPARE_REFERENCES.out.ch_dict, + PREPARE_REFERENCES.out.interval_list_split, ch_dbsnp, ch_dbsnp_tbi ) From 100366526fb95d2a101b2fd8853160b49a49b803 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Wed, 3 Jun 2026 10:53:50 +0200 Subject: [PATCH 16/19] default dbSNP version to 156 --- conf/hpc.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/hpc.config b/conf/hpc.config index 07b335f..e03d604 100644 --- a/conf/hpc.config +++ b/conf/hpc.config @@ -19,7 +19,7 @@ params { rrna_intervals = "${params.dx_tracks_path}/rna/GRCh38_rRNA_genbank.interval_list" // dbSNP - dbsnp = "/hpc/diaggen/data/databases/GATK_resource_bundle/hg38/dbsnp_146.hg38.vcf.gz" + dbsnp = "/hpc/diaggen/data/databases/dbSNP/dbSNP_v156.vcf.gz" // Fusion config starfusion_ref = params.genome_base From 27d523570c4dd04b724343e781f1172c4bd4ae57 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Wed, 3 Jun 2026 14:07:19 +0200 Subject: [PATCH 17/19] update snapshot after gtf2bed change --- .../local/prepare_references/tests/main.nf.test.snap | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/subworkflows/local/prepare_references/tests/main.nf.test.snap b/subworkflows/local/prepare_references/tests/main.nf.test.snap index 40e803d..f2d45b3 100644 --- a/subworkflows/local/prepare_references/tests/main.nf.test.snap +++ b/subworkflows/local/prepare_references/tests/main.nf.test.snap @@ -14,7 +14,7 @@ { "id": "minigenome_gtf" }, - "exome.bed:md5,f95c84914555fd4c0aa1bd9541ab5e0f" + "chr21_gencode.bed:md5,6018f980c6ee0e8da905b50d6838ea40" ] ], [ @@ -22,14 +22,14 @@ { "id": "minigenome_gtf" }, - "minigenome_gtf.interval_list:md5,0266e4cef2ca29605c6f05ab687fd1cd" + "minigenome_gtf.interval_list:md5,f49045d215e5fc1dfcd4f6f79a8da0b5" ] ], [ [ [ - "1scattered.interval_list:md5,4ef1d2d1b8d522193037e657fd664263", - "2scattered.interval_list:md5,8847ce9646a754980f7dc3bb830574f1" + "1scattered.interval_list:md5,7ade4bf4b27da132228553b59323db6e", + "2scattered.interval_list:md5,0f3322aa6497f446b43b74144e6fbada" ] ] ] @@ -38,6 +38,6 @@ "nf-test": "0.9.3", "nextflow": "25.10.4" }, - "timestamp": "2026-06-03T08:41:56.743202" + "timestamp": "2026-06-03T13:58:43.674297" } } \ No newline at end of file From 85d253581db6e3e3657a9857378feeb3f8d03230 Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Wed, 3 Jun 2026 14:51:10 +0200 Subject: [PATCH 18/19] fixed typo in variable name --- subworkflows/local/prepare_references/main.nf | 2 +- workflows/dxnextflowrna.nf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/prepare_references/main.nf b/subworkflows/local/prepare_references/main.nf index b0e2ff0..dfe9c8f 100644 --- a/subworkflows/local/prepare_references/main.nf +++ b/subworkflows/local/prepare_references/main.nf @@ -10,6 +10,7 @@ workflow PREPARE_REFERENCES { main: + GATK4_CREATESEQUENCEDICTIONARY(ch_fasta_fai.map { meta, fasta, _fai -> [meta, fasta] }) def ch_dict = GATK4_CREATESEQUENCEDICTIONARY.out.dict @@ -20,7 +21,6 @@ workflow PREPARE_REFERENCES { GATK4_INTERVALLISTTOOLS(GATK4_BEDTOINTERVALLIST.out.interval_list) - emit: dict = ch_dict bed = BEDOPS_GTF2BED.out.bed.collect() diff --git a/workflows/dxnextflowrna.nf b/workflows/dxnextflowrna.nf index 53b730b..87ba3d7 100644 --- a/workflows/dxnextflowrna.nf +++ b/workflows/dxnextflowrna.nf @@ -209,7 +209,7 @@ workflow DXNEXTFLOWRNA { BAM_VARIANT_CALLING( FASTQ_TRIM_FILTER_ALIGN_DEDUP.out.ch_bam_bai, ch_fasta_fai, - PREPARE_REFERENCES.out.ch_dict, + PREPARE_REFERENCES.out.dict, PREPARE_REFERENCES.out.interval_list_split, ch_dbsnp, ch_dbsnp_tbi From 8cdb784e9b625e965c18a3f467a4e797c3beacda Mon Sep 17 00:00:00 2001 From: Jorisvansteenbrugge <7196110+Jorisvansteenbrugge@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:39:14 +0200 Subject: [PATCH 19/19] generate bedops/gtf2bed patch file --- modules.json | 3 ++- .../nf-core/bedops/gtf2bed/bedops-gtf2bed.diff | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 modules/nf-core/bedops/gtf2bed/bedops-gtf2bed.diff diff --git a/modules.json b/modules.json index cc857e8..3f89f4b 100644 --- a/modules.json +++ b/modules.json @@ -18,7 +18,8 @@ "bedops/gtf2bed": { "branch": "master", "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", - "installed_by": ["modules"] + "installed_by": ["modules"], + "patch": "modules/nf-core/bedops/gtf2bed/bedops-gtf2bed.diff" }, "fastqc": { "branch": "master", diff --git a/modules/nf-core/bedops/gtf2bed/bedops-gtf2bed.diff b/modules/nf-core/bedops/gtf2bed/bedops-gtf2bed.diff new file mode 100644 index 0000000..b086e75 --- /dev/null +++ b/modules/nf-core/bedops/gtf2bed/bedops-gtf2bed.diff @@ -0,0 +1,18 @@ +Changes in component 'nf-core/bedops/gtf2bed' +'modules/nf-core/bedops/gtf2bed/environment.yml' is unchanged +'modules/nf-core/bedops/gtf2bed/meta.yml' is unchanged +Changes in 'bedops/gtf2bed/main.nf': +--- modules/nf-core/bedops/gtf2bed/main.nf ++++ modules/nf-core/bedops/gtf2bed/main.nf +@@ -27,6 +27,7 @@ + | gtf2bed \\ + $args \\ + --attribute-key=exon_id \\ ++ | awk -F'\t' '\$8 == "exon" ' \\ + > ${prefix}.bed + """ + + +'modules/nf-core/bedops/gtf2bed/tests/main.nf.test.snap' is unchanged +'modules/nf-core/bedops/gtf2bed/tests/main.nf.test' is unchanged +************************************************************