diff --git a/CITATIONS.md b/CITATIONS.md index 02b84e877..c55c138b2 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -82,6 +82,10 @@ > Chen X, Schulz-Trieglaff O, Shaw R, et al. Manta: rapid detection of structural variants and indels for germline and cancer sequencing applications. Bioinformatics. 2016;32(8):1220-1222. doi:10.1093/bioinformatics/btv710 +- [Mitosalt](https://sourceforge.net/projects/mitosalt/) + + > Basu S, Xie X, Uhler JP, et al. Accurate mapping of mitochondrial DNA deletions and duplications using deep sequencing. PLoS Genet. 2020;16(12):e1009242. doi: 10.1371/journal.pgen.1009242 + - [Mosdepth](https://academic.oup.com/bioinformatics/article/34/5/867/4583630?login=true) > Pedersen BS, Quinlan AR. Mosdepth: quick coverage calculation for genomes and exomes. Hancock J, ed. Bioinformatics. 2018;34(5):867-868. doi:10.1093/bioinformatics/btx699 @@ -112,6 +116,8 @@ > John G. Cleary, Ross Braithwaite, Kurt Gaastra, Brian S. Hilbush, Stuart Inglis, Sean A. Irvine, Alan Jackson, Richard Littin, Mehul Rathod, David Ware, Justin M. Zook, Len Trigg, and Francisco M. De La Vega. "Comparing Variant Call Files for Performance Benchmarking of Next-Generation Sequencing Variant Calling Pipelines." bioRxiv, 2015. doi:10.1101/023754. +- [saltshaker](https://pypi.org/project/saltshaker/) + - [Sambamba](https://academic.oup.com/bioinformatics/article/31/12/2032/213831) > Tarasov A, Vilella AJ, Cuppen E, Nijman IJ, Prins P. Sambamba: fast processing of NGS alignment formats. Bioinformatics. 2015;31(12):2032-2034. doi:10.1093/bioinformatics/btv098 diff --git a/conf/modules/call_sv_MT.config b/conf/modules/call_sv_MT.config index be140cabe..321f23add 100644 --- a/conf/modules/call_sv_MT.config +++ b/conf/modules/call_sv_MT.config @@ -22,4 +22,19 @@ process { ext.prefix = { "${meta.id}_mitochondria_deletions" } } + withName: '.*CALL_SV_MT:SALTSHAKER_CALL' { + ext.args = '--blacklist' + } + + withName: '.*CALL_SV_MT:SALTSHAKER_CLASSIFY' { + ext.args = { "--blacklist --vcf --dominant-fraction ${params.saltshaker_dominant_fraction} \ + --radius ${params.saltshaker_group_radius} --high-het ${params.saltshaker_high_heteroplasmy} \ + --multiple-threshold ${params.saltshaker_multiple_threshold} --noise ${params.saltshaker_noise_threshold}" + } + } + + withName: '.*CALL_SV_MT:SALTSHAKER_PLOT' { + ext.args = '--blacklist --genes' + } + } diff --git a/conf/test.config b/conf/test.config index ee863b84e..f22ee64b0 100644 --- a/conf/test.config +++ b/conf/test.config @@ -22,8 +22,6 @@ env { MPLCONFIGDIR="." } -singularity.pullTimeout='2h' - params { config_profile_name = 'Test profile - default' config_profile_description = 'Minimal test dataset to check pipeline function' diff --git a/modules.json b/modules.json index 52b5ec8b0..26986ceb5 100644 --- a/modules.json +++ b/modules.json @@ -396,6 +396,21 @@ "git_sha": "0f04646a9282049b16b3eb35b042e94dfb3340a0", "installed_by": ["modules"] }, + "saltshaker/call": { + "branch": "master", + "git_sha": "9d051963759dda1a424374e23f4f22aaa2b0bd60", + "installed_by": ["modules"] + }, + "saltshaker/classify": { + "branch": "master", + "git_sha": "ff5f2ad4481a4a1e1769a1fad922681e7f7fd176", + "installed_by": ["modules"] + }, + "saltshaker/plot": { + "branch": "master", + "git_sha": "ff5f2ad4481a4a1e1769a1fad922681e7f7fd176", + "installed_by": ["modules"] + }, "sambamba/depth": { "branch": "master", "git_sha": "ca6da11b05740de461b1e2714037345c0f856201", diff --git a/modules/local/mitosalt/main.nf b/modules/local/mitosalt/main.nf index caf939c44..3eee65aa3 100644 --- a/modules/local/mitosalt/main.nf +++ b/modules/local/mitosalt/main.nf @@ -4,9 +4,8 @@ process MITOSALT { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/dc/dc2631dac526622d28ea1109b0f714d536606d0e5b3b85fe24407c8206e7e6b6/data': - 'community.wave.seqera.io/library/mitosalt:1.1.1--5fd87ac48a683358' }" - + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/13/130779a0dd5a8d86441f262a16a5fb1dfe562125edf93646b53c893d982b5519/data': + 'community.wave.seqera.io/library/bbmap_bedtools_bioconductor-biostrings_bioconductor-pwalign_pruned:856c05081cbd8239' }" input: tuple val(meta), path(reads) @@ -28,7 +27,7 @@ process MITOSALT { """ cat $msconfig | sed "s/threads = 1/threads = ${task.cpus}/" > new-${msconfig} mkdir -p log indel bam tab bw plot - mitosalt new-${msconfig} $reads $prefix + MitoSAlt1.1.1.pl new-${msconfig} $reads $prefix mv indel/*.breakpoint ${prefix}.breakpoint mv indel/*.cluster ${prefix}.cluster """ @@ -38,7 +37,7 @@ process MITOSALT { """ cat $msconfig | sed "s/threads = 1/threads = ${task.cpus}/" > new-${msconfig} touch ${prefix}.breakpoint - touch ${prefix}.cluster + echo 'cluster' > ${prefix}.cluster """ } diff --git a/modules/local/mitosalt/resources/usr/bin/MitoSAlt1.1.1.pl b/modules/local/mitosalt/resources/usr/bin/MitoSAlt1.1.1.pl index 5c6739daf..bbaf37d9b 100755 --- a/modules/local/mitosalt/resources/usr/bin/MitoSAlt1.1.1.pl +++ b/modules/local/mitosalt/resources/usr/bin/MitoSAlt1.1.1.pl @@ -10,14 +10,6 @@ my $p2 = $ARGV[2]; my $tag = $ARGV[3]; -#CREATE DIRECTORIES -- added by ID -mkdir "log" unless -d "log"; -mkdir "indel" unless -d "indel"; -mkdir "bam" unless -d "bam"; -mkdir "tab" unless -d "tab"; -mkdir "bw" unless -d "bw"; -mkdir "plot" unless -d "plot"; - #LOG open (STDOUT, "| tee -ai log/$tag.log"); @@ -158,7 +150,7 @@ if($nu_mt eq 'no' && $o_mt eq 'yes' && $enriched eq 'yes'){ #REMAP ON MT GENOME print scalar(localtime).": Map to MT genome\n"; - system("$reformat in=$p1 in2=$p2 out=tmp_$tag.fq overwrite=true addslash=t trimreaddescription=t spaceslash=f -Xmx100g"); #2>> log/$tag.log"); + system("$reformat in=$p1 in2=$p2 out=tmp_$tag.fq overwrite=true addslash=t trimreaddescription=t spaceslash=f -Xmx100g 2>> log/$tag.log"); system("$lastal -Q1 -e80 -P$threads $lastindex tmp_$tag.fq|$lastsp > tmp_$tag.maf"); system("$mfcv sam -d tmp_$tag.maf|$samtools view -@ $threads -bt $mtfaindex -|$samtools sort -@ $threads -o bam/$tag.bam -"); system("$samtools index bam/$tag.bam"); diff --git a/modules/local/prep_mitosalt/main.nf b/modules/local/prep_mitosalt/main.nf index 41b0caa4f..83455fd3c 100644 --- a/modules/local/prep_mitosalt/main.nf +++ b/modules/local/prep_mitosalt/main.nf @@ -53,6 +53,7 @@ process PREP_MITOSALT { echo "MT_fasta = ${mtfasta}" >> mitosalt_config.txt echo "threads = 1" >> mitosalt_config.txt echo "refchr = ${mito_name}" >> mitosalt_config.txt + echo "msize = 16569" >> mitosalt_config.txt echo "exclude = ${exclude}" >> mitosalt_config.txt echo "orihs = 16081" >> mitosalt_config.txt echo "orihe = 407" >> mitosalt_config.txt diff --git a/modules/nf-core/saltshaker/call/environment.yml b/modules/nf-core/saltshaker/call/environment.yml new file mode 100644 index 000000000..f76c4bf5a --- /dev/null +++ b/modules/nf-core/saltshaker/call/environment.yml @@ -0,0 +1,7 @@ +channels: + - conda-forge + - bioconda +dependencies: + - pip==26.0.1 + - pip: + - saltshaker==1.0.0 diff --git a/modules/nf-core/saltshaker/call/main.nf b/modules/nf-core/saltshaker/call/main.nf new file mode 100644 index 000000000..7565ce603 --- /dev/null +++ b/modules/nf-core/saltshaker/call/main.nf @@ -0,0 +1,58 @@ +process SALTSHAKER_CALL { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/e9/e93d703b195dd27cd920cee46669d3f51043216c12fd05168c937e93adf170e8/data': + 'community.wave.seqera.io/library/pip_saltshaker:e08e38a6d45f8f32' }" + + input: + tuple val(meta), path(breakpoint), path(cluster) + tuple val(meta2), path(mtfasta) + val flank + val heteroplasmy_limit + val mito_length + val heavy_strand_origin_start + val heavy_strand_origin_end + val light_strand_origin_start + val light_strand_origin_end + + output: + tuple val(meta), path("*_call_metadata.tsv"), emit: call + tuple val("${task.process}"), val('saltshaker'), val("1.0.0"), topic: versions, emit: versions_saltshaker + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + saltshaker call \\ + --prefix $prefix \\ + --output-dir . \\ + --reference $mtfasta \\ + --cluster $cluster \\ + --breakpoint $breakpoint \\ + --flank-size $flank \\ + --het-limit $heteroplasmy_limit \\ + --genome-length $mito_length \\ + --ori-h-start $heavy_strand_origin_start \\ + --ori-h-end $heavy_strand_origin_end \\ + --ori-l-start $light_strand_origin_start \\ + --ori-l-end $light_strand_origin_end \\ + $args + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + echo $args + + touch ${prefix}.saltshaker_call_metadata.tsv + """ +} diff --git a/modules/nf-core/saltshaker/call/meta.yml b/modules/nf-core/saltshaker/call/meta.yml new file mode 100644 index 000000000..51be6c452 --- /dev/null +++ b/modules/nf-core/saltshaker/call/meta.yml @@ -0,0 +1,99 @@ +name: "saltshaker_call" +description: mtDNA deletion and duplication calling downstream of mitosalt +keywords: + - saltshaker + - mitosalt + - mtDNA + - structural-variant calling +tools: + - "saltshaker": + description: "A Python package for classifying and visualizing mitochondrial structural variants from MitoSAlt pipeline output." + homepage: "https://github.com/aksenia/saltshaker" + documentation: "https://github.com/aksenia/saltshaker/tree/main/saltshaker/docs" + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - breakpoint: + type: file + description: breakpoint file from mitosalt + pattern: "*.{breakpoint}" + ontologies: [] + - cluster: + type: file + description: cluster file from mitosalt + pattern: "*.{cluster}" + ontologies: [] + - - meta2: + type: map + description: | + Groovy Map containing reference information + e.g. [ id:'fasta' ] + - mtfasta: + type: file + description: Reference mitochondrial genome in FASTA format + pattern: "*.{fasta,fa}" + ontologies: [] + - flank: + type: integer + description: Basepairs flanking a deletion + - heteroplasmy_limit: + type: float + description: Minimum heteroplasmy level to report a deletion/duplication + - mito_length: + type: integer + description: Length of the mitochondrial genome + - heavy_strand_origin_start: + type: integer + description: Start position of the heavy strand origin of replication + - heavy_strand_origin_end: + type: integer + description: End position of the heavy strand origin of replication + - light_strand_origin_start: + type: integer + description: Start position of the light strand origin of replication + - light_strand_origin_end: + type: integer + description: End position of the light strand origin of replication + +output: + call: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*_call_metadata.tsv": + type: file + description: tsv with variant call metadata to be used in saltshaker_classify + pattern: "*_call_metadata.tsv" + ontologies: [] + versions_saltshaker: + - - "${task.process}": + type: string + description: The name of the process + - "saltshaker": + type: string + description: The name of the tool + - "1.0.0": + type: string + description: Hardcoded version of saltshaker used in the module + +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - saltshaker: + type: string + description: The name of the tool + - 1.0.0: + type: string + description: Hardcoded version of saltshaker used in the module +authors: + - "@ieduba" +maintainers: + - "@ieduba" diff --git a/modules/nf-core/saltshaker/call/tests/main.nf.test b/modules/nf-core/saltshaker/call/tests/main.nf.test new file mode 100644 index 000000000..8bb37c3b9 --- /dev/null +++ b/modules/nf-core/saltshaker/call/tests/main.nf.test @@ -0,0 +1,94 @@ +nextflow_process { + + name "Test Process SALTSHAKER_CALL" + script "../main.nf" + process "SALTSHAKER_CALL" + config "./nextflow.config" + + tag "modules" + tag "modules_nfcore" + tag "saltshaker" + tag "saltshaker/call" + + test("call - tsv") { + + when { + process { + """ + + input[0] = [ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/mitosalt/test_mitosalt.breakpoint', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/mitosalt/test_mitosalt.cluster', checkIfExists: true), + ] + + input[1] = [ + [ id:'hg38' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/human_mt_rCRS.fasta', checkIfExists: true), + ] + + input[2] = 15 + input[3] = 0.01 + input[4] = 16569 + input[5] = 16081 + input[6] = 407 + input[7] = 5730 + input[8] = 5763 + + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + sanitizeOutput(process.out) + ).match() } + ) + } + + } + + test("call - tsv - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/mitosalt/test_mitosalt.breakpoint', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/mitosalt/test_mitosalt.cluster', checkIfExists: true), + ] + + input[1] = [ + [ id:'hg38' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/human_mt_rCRS.fasta', checkIfExists: true), + ] + + input[2] = 15 + input[3] = 0.01 + input[4] = 16569 + input[5] = 16081 + input[6] = 407 + input[7] = 5730 + input[8] = 5763 + + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + sanitizeOutput(process.out) + ).match() } + ) + } + + } + +} diff --git a/modules/nf-core/saltshaker/call/tests/main.nf.test.snap b/modules/nf-core/saltshaker/call/tests/main.nf.test.snap new file mode 100644 index 000000000..d377c6626 --- /dev/null +++ b/modules/nf-core/saltshaker/call/tests/main.nf.test.snap @@ -0,0 +1,54 @@ +{ + "call - tsv": { + "content": [ + { + "call": [ + [ + { + "id": "test" + }, + "test.saltshaker_call_metadata.tsv:md5,f0e21ea8c12afecbae37cd9b55f8f8c2" + ] + ], + "versions_saltshaker": [ + [ + "SALTSHAKER_CALL", + "saltshaker", + "1.0.0" + ] + ] + } + ], + "timestamp": "2026-03-06T15:18:19.555305", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.0" + } + }, + "call - tsv - stub": { + "content": [ + { + "call": [ + [ + { + "id": "test" + }, + "test.saltshaker_call_metadata.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_saltshaker": [ + [ + "SALTSHAKER_CALL", + "saltshaker", + "1.0.0" + ] + ] + } + ], + "timestamp": "2026-03-05T09:54:13.739643", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.0" + } + } +} \ No newline at end of file diff --git a/modules/nf-core/saltshaker/call/tests/nextflow.config b/modules/nf-core/saltshaker/call/tests/nextflow.config new file mode 100644 index 000000000..ba448353c --- /dev/null +++ b/modules/nf-core/saltshaker/call/tests/nextflow.config @@ -0,0 +1,5 @@ +process { + withName: 'SALTSHAKER_CALL' { + ext.args = '--blacklist' + } +} diff --git a/modules/nf-core/saltshaker/classify/environment.yml b/modules/nf-core/saltshaker/classify/environment.yml new file mode 100644 index 000000000..3672c7873 --- /dev/null +++ b/modules/nf-core/saltshaker/classify/environment.yml @@ -0,0 +1,7 @@ +channels: + - conda-forge + - bioconda +dependencies: + - pip==26.0.1 + - pip: + - saltshaker==1.0.1 diff --git a/modules/nf-core/saltshaker/classify/main.nf b/modules/nf-core/saltshaker/classify/main.nf new file mode 100644 index 000000000..72fa27c16 --- /dev/null +++ b/modules/nf-core/saltshaker/classify/main.nf @@ -0,0 +1,48 @@ +process SALTSHAKER_CLASSIFY { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/0c/0c955cc086622ef50876a10e58a1e6711e42b70a0e4cbbc377142b62b0ad4f47/data': + 'community.wave.seqera.io/library/pip_saltshaker:ef543ea5ca09afbe' }" + + input: + tuple val(meta), path(call) + val mito_name + + output: + tuple val(meta), path("*_classify_metadata.tsv"), emit: classify + tuple val(meta), path("*_classify.txt") , emit: txt + tuple val(meta), path("*saltshaker.vcf") , emit: vcf, optional: true + tuple val("${task.process}"), val('saltshaker'), val("1.0.1"), topic: versions, emit: versions_saltshaker + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + saltshaker classify \\ + --prefix $prefix \\ + --input-dir . \\ + --chr-format $mito_name \\ + $args + + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def touch_vcf = args.contains('--vcf') ? "touch ${prefix}.saltshaker.vcf" : '' + + """ + echo $args + + $touch_vcf + touch ${prefix}.saltshaker_classify.txt + touch ${prefix}.saltshaker_classify_metadata.tsv + """ +} diff --git a/modules/nf-core/saltshaker/classify/meta.yml b/modules/nf-core/saltshaker/classify/meta.yml new file mode 100644 index 000000000..be07a8946 --- /dev/null +++ b/modules/nf-core/saltshaker/classify/meta.yml @@ -0,0 +1,94 @@ +name: "saltshaker_classify" +description: mtDNA deletion and duplication classification downstream of mitosalt +keywords: + - saltshaker + - mitosalt + - mtDNA + - structural-variant calling +tools: + - "saltshaker": + description: "A Python package for classifying and visualizing mitochondrial structural variants from MitoSAlt pipeline output." + homepage: "https://github.com/aksenia/saltshaker" + documentation: "https://github.com/aksenia/saltshaker/tree/main/saltshaker/docs" + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - call: + type: file + description: call metadata file from saltshaker_call + pattern: "*_call_metadata.tsv" + ontologies: + - edam: http://edamontology.org/operation_3227 #variant calling + - edam: http://edamontology.org/format_3475 #tsv + - mito_name: + type: string + description: Name of the mitochondrial chromosome +output: + classify: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*_classify_metadata.tsv": + type: file + description: tsv with classified call metadata to be used in saltshaker_plot + pattern: "*_classify_metadata.tsv" + ontologies: + - edam: http://edamontology.org/operation_3225 #classification + - edam: http://edamontology.org/format_3475 #tsv + txt: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*_classify.txt": + type: file + description: txt file with case classification + pattern: "*_classify.txt" + ontologies: + - edam: http://edamontology.org/operation_3225 #classification + - edam: http://edamontology.org/format_2330 #txt + vcf: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*saltshaker.vcf": + type: file + description: vcf file with classified calls + pattern: "*saltshaker.vcf" + ontologies: + - edam: http://edamontology.org/operation_3225 #classification + - edam: http://edamontology.org/format_3016 #vcf + versions_saltshaker: + - - ${task.process}: + type: string + description: The name of the process + - saltshaker: + type: string + description: The name of the tool + - 1.0.1: + type: string + description: Hardcoded version of saltshaker used in the module +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - saltshaker: + type: string + description: The name of the tool + - 1.0.1: + type: string + description: Hardcoded version of saltshaker used in the module +authors: + - "@ieduba" +maintainers: + - "@ieduba" diff --git a/modules/nf-core/saltshaker/classify/tests/main.nf.test b/modules/nf-core/saltshaker/classify/tests/main.nf.test new file mode 100644 index 000000000..3830b1832 --- /dev/null +++ b/modules/nf-core/saltshaker/classify/tests/main.nf.test @@ -0,0 +1,148 @@ +nextflow_process { + + name "Test Process SALTSHAKER_CLASSIFY" + script "../main.nf" + process "SALTSHAKER_CLASSIFY" + config "./nextflow.config" + + tag "modules" + tag "modules_nfcore" + tag "saltshaker" + tag "saltshaker/call" + tag "saltshaker/classify" + + setup { + run("SALTSHAKER_CALL") { + script "../../call/main.nf" + process { + """ + input[0] = [ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/mitosalt/test_mitosalt.breakpoint', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/mitosalt/test_mitosalt.cluster', checkIfExists: true), + ] + + input[1] = [ + [ id:'hg38' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/human_mt_rCRS.fasta', checkIfExists: true), + ] + + input[2] = 15 + input[3] = 0.01 + input[4] = 16569 + input[5] = 16081 + input[6] = 407 + input[7] = 5730 + input[8] = 5763 + """ + } + } + } + + test("classify - vcf") { + + when { + params { + module_args = '--blacklist --vcf --dominant-fraction 0.5 --radius 600 --high-het 10 --multiple-threshold 5 --noise 0.3' + } + process { + """ + input[0] = SALTSHAKER_CALL.out.call + input[1] = "MT" + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + sanitizeOutput(process.out), + ).match() } + + ) + } + + } + + test("classify - vcf - stub") { + + options "-stub" + + when { + params { + module_args = '--blacklist --vcf --dominant-fraction 0.5 --radius 600 --high-het 10 --multiple-threshold 5 --noise 0.3' + } + process { + """ + input[0] = SALTSHAKER_CALL.out.call + input[1] = "MT" + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + sanitizeOutput(process.out), + ).match() } + ) + } + + } + + test("classify - defaults") { + + when { + params { + module_args = '' + } + process { + """ + input[0] = SALTSHAKER_CALL.out.call + input[1] = "MT" + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + sanitizeOutput(process.out), + ).match() } + + ) + } + + } + + test("classify - defaults - stub") { + + options "-stub" + + when { + params { + module_args = '' + } + process { + """ + input[0] = SALTSHAKER_CALL.out.call + input[1] = "MT" + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + sanitizeOutput(process.out), + ).match() } + ) + } + + } + +} diff --git a/modules/nf-core/saltshaker/classify/tests/main.nf.test.snap b/modules/nf-core/saltshaker/classify/tests/main.nf.test.snap new file mode 100644 index 000000000..507105e8c --- /dev/null +++ b/modules/nf-core/saltshaker/classify/tests/main.nf.test.snap @@ -0,0 +1,160 @@ +{ + "classify - defaults - stub": { + "content": [ + { + "classify": [ + [ + { + "id": "test" + }, + "test.saltshaker_classify_metadata.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "txt": [ + [ + { + "id": "test" + }, + "test.saltshaker_classify.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "vcf": [ + + ], + "versions_saltshaker": [ + [ + "SALTSHAKER_CLASSIFY", + "saltshaker", + "1.0.1" + ] + ] + } + ], + "timestamp": "2026-03-20T12:29:31.591954", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.0" + } + }, + "classify - defaults": { + "content": [ + { + "classify": [ + [ + { + "id": "test" + }, + "test.saltshaker_classify_metadata.tsv:md5,0c86c16ae38a5dc7070ad76e764aee6d" + ] + ], + "txt": [ + [ + { + "id": "test" + }, + "test.saltshaker_classify.txt:md5,8e11ad30539d0b5e0ab9a42b4d68b208" + ] + ], + "vcf": [ + + ], + "versions_saltshaker": [ + [ + "SALTSHAKER_CLASSIFY", + "saltshaker", + "1.0.1" + ] + ] + } + ], + "timestamp": "2026-03-20T12:29:26.57794", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.0" + } + }, + "classify - vcf - stub": { + "content": [ + { + "classify": [ + [ + { + "id": "test" + }, + "test.saltshaker_classify_metadata.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "txt": [ + [ + { + "id": "test" + }, + "test.saltshaker_classify.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "vcf": [ + [ + { + "id": "test" + }, + "test.saltshaker.vcf:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_saltshaker": [ + [ + "SALTSHAKER_CLASSIFY", + "saltshaker", + "1.0.1" + ] + ] + } + ], + "timestamp": "2026-03-20T11:05:52.842015", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.0" + } + }, + "classify - vcf": { + "content": [ + { + "classify": [ + [ + { + "id": "test" + }, + "test.saltshaker_classify_metadata.tsv:md5,0c86c16ae38a5dc7070ad76e764aee6d" + ] + ], + "txt": [ + [ + { + "id": "test" + }, + "test.saltshaker_classify.txt:md5,8e11ad30539d0b5e0ab9a42b4d68b208" + ] + ], + "vcf": [ + [ + { + "id": "test" + }, + "test.saltshaker.vcf:md5,0c59728289ee8bcb19d44aea4d3155ab" + ] + ], + "versions_saltshaker": [ + [ + "SALTSHAKER_CLASSIFY", + "saltshaker", + "1.0.1" + ] + ] + } + ], + "timestamp": "2026-03-20T11:05:45.058275", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.0" + } + } +} \ No newline at end of file diff --git a/modules/nf-core/saltshaker/classify/tests/nextflow.config b/modules/nf-core/saltshaker/classify/tests/nextflow.config new file mode 100644 index 000000000..97d25b454 --- /dev/null +++ b/modules/nf-core/saltshaker/classify/tests/nextflow.config @@ -0,0 +1,5 @@ +process { + withName: 'SALTSHAKER_CLASSIFY' { + ext.args = params.module_args + } +} diff --git a/modules/nf-core/saltshaker/plot/environment.yml b/modules/nf-core/saltshaker/plot/environment.yml new file mode 100644 index 000000000..f76c4bf5a --- /dev/null +++ b/modules/nf-core/saltshaker/plot/environment.yml @@ -0,0 +1,7 @@ +channels: + - conda-forge + - bioconda +dependencies: + - pip==26.0.1 + - pip: + - saltshaker==1.0.0 diff --git a/modules/nf-core/saltshaker/plot/main.nf b/modules/nf-core/saltshaker/plot/main.nf new file mode 100644 index 000000000..56e7b9a3e --- /dev/null +++ b/modules/nf-core/saltshaker/plot/main.nf @@ -0,0 +1,41 @@ +process SALTSHAKER_PLOT { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/e9/e93d703b195dd27cd920cee46669d3f51043216c12fd05168c937e93adf170e8/data': + 'community.wave.seqera.io/library/pip_saltshaker:e08e38a6d45f8f32' }" + + input: + tuple val(meta), path(classify) + + output: + tuple val(meta), path("*saltshaker.png"), emit: plot + tuple val("${task.process}"), val('saltshaker'), val("1.0.0"), topic: versions, emit: versions_saltshaker + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + saltshaker plot \\ + --prefix $prefix \\ + --input-dir . \\ + $args + + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + + """ + echo $args + + touch ${prefix}.saltshaker.png + """ +} diff --git a/modules/nf-core/saltshaker/plot/meta.yml b/modules/nf-core/saltshaker/plot/meta.yml new file mode 100644 index 000000000..b95deb3f5 --- /dev/null +++ b/modules/nf-core/saltshaker/plot/meta.yml @@ -0,0 +1,67 @@ +name: "saltshaker_plot" +description: mtDNA deletion and duplication plotting downstream of mitosalt +keywords: + - saltshaker + - mitosalt + - mtDNA + - structural-variant calling +tools: + - "saltshaker": + description: "A Python package for classifying and visualizing mitochondrial structural variants from MitoSAlt pipeline output." + homepage: "https://github.com/aksenia/saltshaker" + documentation: "https://github.com/aksenia/saltshaker/tree/main/saltshaker/docs" + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - classify: + type: file + description: classify metadata file from saltshaker_classify + pattern: "*_classify_metadata.tsv" + ontologies: + - edam: http://edamontology.org/operation_3225 #classification + - edam: http://edamontology.org/format_3475 #tsv + +output: + plot: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*saltshaker.png": + type: file + description: png file with saltshaker plot + pattern: "*saltshaker.png" + ontologies: + - edam: http://edamontology.org/operation_0337 #visualization + - edam: http://edamontology.org/format_3603 #png + versions_saltshaker: + - - "${task.process}": + type: string + description: The name of the process + - "saltshaker": + type: string + description: The name of the tool + - "1.0.0": + type: string + description: Hardcoded version of saltshaker used in the module + +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - saltshaker: + type: string + description: The name of the tool + - 1.0.0: + type: string + description: Hardcoded version of saltshaker used in the module +authors: + - "@ieduba" +maintainers: + - "@ieduba" diff --git a/modules/nf-core/saltshaker/plot/tests/main.nf.test b/modules/nf-core/saltshaker/plot/tests/main.nf.test new file mode 100644 index 000000000..ec18cc241 --- /dev/null +++ b/modules/nf-core/saltshaker/plot/tests/main.nf.test @@ -0,0 +1,104 @@ +nextflow_process { + + name "Test Process SALTSHAKER_PLOT" + script "../main.nf" + process "SALTSHAKER_PLOT" + config "./nextflow.config" + + tag "modules" + tag "modules_nfcore" + tag "saltshaker" + tag "saltshaker/call" + tag "saltshaker/classify" + tag "saltshaker/plot" + + setup { + run("SALTSHAKER_CALL") { + script "../../call/main.nf" + process { + """ + input[0] = [ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/mitosalt/test_mitosalt.breakpoint', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/mitosalt/test_mitosalt.cluster', checkIfExists: true), + ] + + input[1] = [ + [ id:'hg38' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/human_mt_rCRS.fasta', checkIfExists: true), + ] + + input[2] = 15 + input[3] = 0.01 + input[4] = 16569 + input[5] = 16081 + input[6] = 407 + input[7] = 5730 + input[8] = 5763 + """ + } + } + + run("SALTSHAKER_CLASSIFY") { + script "../../classify/main.nf" + process { + """ + input[0] = SALTSHAKER_CALL.out.call + input[1] = "MT" + """ + } + } + } + + + test("plot - png") { + + when { + params { + module_args = '--blacklist' + } + process { + """ + input[0] = SALTSHAKER_CLASSIFY.out.classify + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + sanitizeOutput(process.out), + ).match() } + ) + } + + } + + test("plot - png - stub") { + + options "-stub" + + when { + params { + module_args = '--blacklist' + } + process { + """ + input[0] = SALTSHAKER_CLASSIFY.out.classify + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + sanitizeOutput(process.out), + ).match() } + ) + } + + } + +} diff --git a/modules/nf-core/saltshaker/plot/tests/main.nf.test.snap b/modules/nf-core/saltshaker/plot/tests/main.nf.test.snap new file mode 100644 index 000000000..ea7f4d94c --- /dev/null +++ b/modules/nf-core/saltshaker/plot/tests/main.nf.test.snap @@ -0,0 +1,54 @@ +{ + "plot - png - stub": { + "content": [ + { + "plot": [ + [ + { + "id": "test" + }, + "test.saltshaker.png:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_saltshaker": [ + [ + "SALTSHAKER_PLOT", + "saltshaker", + "1.0.0" + ] + ] + } + ], + "timestamp": "2026-03-17T14:57:52.937691", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.0" + } + }, + "plot - png": { + "content": [ + { + "plot": [ + [ + { + "id": "test" + }, + "test.saltshaker.png:md5,c5c8f7b96daab112b53dfe42194855af" + ] + ], + "versions_saltshaker": [ + [ + "SALTSHAKER_PLOT", + "saltshaker", + "1.0.0" + ] + ] + } + ], + "timestamp": "2026-03-17T14:57:46.77575", + "meta": { + "nf-test": "0.9.4", + "nextflow": "25.10.0" + } + } +} \ No newline at end of file diff --git a/modules/nf-core/saltshaker/plot/tests/nextflow.config b/modules/nf-core/saltshaker/plot/tests/nextflow.config new file mode 100644 index 000000000..51393788b --- /dev/null +++ b/modules/nf-core/saltshaker/plot/tests/nextflow.config @@ -0,0 +1,5 @@ +process { + withName: 'SALTSHAKER_PLOT' { + ext.args = params.module_args + } +} diff --git a/nextflow.config b/nextflow.config index a067e918f..a5b0cc24a 100644 --- a/nextflow.config +++ b/nextflow.config @@ -14,11 +14,16 @@ params { // References genome = 'GRCh38' + heavy_strand_origin_start = 16081 + heavy_strand_origin_end = 407 igenomes_base = 's3://ngi-igenomes/igenomes/' igenomes_ignore = false + light_strand_origin_start = 5730 + light_strand_origin_end = 5763 local_genomes = null save_reference = false mito_name = 'chrM' + mito_length = 16569 // Main options analysis_type = 'wgs' @@ -122,22 +127,27 @@ params { sentieon_dnascope_pcr_indel_model = 'CONSERVATIVE' variant_type = 'snp,indel' - // Mitosalt/saltshaker options - breakspan = 15 - breakthreshold = 2 - cluster_threshold = 2 - deletion_threshold_max = 30000 - deletion_threshold_min = 30 - evalue_threshold = 0.00001 - exclude = 5 - flank = 15 - hplimit = 0.01 - mitosalt_depth = 10000000 - paired_distance = 1000 - score_threshold = 80 - sizelimit = 10000 - split_distance_threshold = 5 - split_length = 15 + // Mitosalt and saltshaker options + mitosalt_breakspan = 15 + mitosalt_breakthreshold = 2 + mitosalt_cluster_threshold = 5 + mitosalt_deletion_threshold_max = 30000 + mitosalt_deletion_threshold_min = 30 + mitosalt_evalue_threshold = 0.00001 + mitosalt_exclude = 5 + mitosalt_flank = 15 + mitosalt_heteroplasmy_limit = 0.01 + mitosalt_depth = 10000000 + mitosalt_paired_distance = 1000 + mitosalt_score_threshold = 80 + mitosalt_sizelimit = 10000 + mitosalt_split_distance_threshold = 5 + mitosalt_split_length = 15 + saltshaker_dominant_fraction = 0.5 + saltshaker_group_radius = 600 + saltshaker_high_heteroplasmy = 10 + saltshaker_multiple_threshold = 5 + saltshaker_noise_threshold = 0.3 // MultiQC options multiqc_config = null @@ -334,6 +344,7 @@ env { R_PROFILE_USER = "/.Rprofile" R_ENVIRON_USER = "/.Renviron" JULIA_DEPOT_PATH = "/usr/local/share/julia" + NXF_SINGULARITY_NEW_PID_NAMESPACE = false } // Set bash options diff --git a/nextflow_schema.json b/nextflow_schema.json index c4113ff6c..c9d140bac 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -756,47 +756,62 @@ "hisat2": { "type": "string" }, - "breakspan": { + "mitosalt_breakspan": { "type": "integer", "default": 15, "description": "minimum number of bases a non-split read must span either side of a breakpoint to be considered in the heteroplasmy count" }, - "breakthreshold": { + "mitosalt_breakthreshold": { "type": "integer", "default": 2, "description": "the maximum deviation a given set of split read breakpoints can have to be considered within the same cluster" }, - "cluster_threshold": { + "mitosalt_cluster_threshold": { "type": "integer", - "default": 2, + "default": 5, "description": "minimum number of reads supporting a cluster" }, - "deletion_threshold_max": { + "mitosalt_deletion_threshold_max": { "type": "integer", "default": 30000, "description": "the maximum size of the gap between fragments of a split read for the split read to be considered as potentially spanning a deletion" }, - "deletion_threshold_min": { + "mitosalt_deletion_threshold_min": { "type": "integer", "default": 30, - "description": "the minimum size of the gap between fragemtns of a split read for the split read to be considered as potentially spanning a deletion" + "description": "the minimum size of the gap between fragments of a split read for the split read to be considered as potentially spanning a deletion" + }, + "saltshaker_dominant_fraction": { + "type": "number", + "default": 0.5, + "description": "fraction for dominant fraction for saltshaker classification" }, - "evalue_threshold": { + "mitosalt_evalue_threshold": { "type": "number", "default": 1e-5, "description": "alignment e-value cutoff" }, - "exclude": { + "mitosalt_exclude": { "type": "integer", "default": 5, "description": "filter split reads if both fragments fall within the regions at the start and end to avoid pseudo-gapped alignments due to a circular genome" }, - "flank": { + "mitosalt_flank": { "type": "integer", "default": 15, "description": "basepairs flanking a deletion" }, - "hplimit": { + "saltshaker_group_radius": { + "type": "integer", + "default": 600, + "description": "spatial clustering radius for saltshaker grouping" + }, + "saltshaker_high_heteroplasmy": { + "type": "integer", + "default": 10, + "description": "high heteroplasmy threshold for saltshaker classification" + }, + "mitosalt_heteroplasmy_limit": { "type": "number", "default": 0.01, "description": "detected heteroplasmy threshold" @@ -806,27 +821,62 @@ "default": 10000000, "description": "depth to subsample fastq files to before running mitosalt" }, - "paired_distance": { + "mito_length": { + "type": "integer", + "default": 16569, + "description": "length of mitochondrial genome" + }, + "saltshaker_multiple_threshold": { + "type": "integer", + "default": 5, + "description": "threshold for multiple saltshaker classification" + }, + "saltshaker_noise_threshold": { + "type": "number", + "default": 0.3, + "description": "heteroplasmy threshold for background noise" + }, + "heavy_strand_origin_start": { + "type": "integer", + "default": 16081, + "description": "start of heavy strand origin" + }, + "heavy_strand_origin_end": { + "type": "integer", + "default": 407, + "description": "end of heavy strand origin" + }, + "light_strand_origin_start": { + "type": "integer", + "default": 5730, + "description": "start of light strand origin" + }, + "light_strand_origin_end": { + "type": "integer", + "default": 5763, + "description": "end of light strand origin" + }, + "mitosalt_paired_distance": { "type": "integer", "default": 1000, "description": "the maximum distance of a paired read from its split counterpart for paired support to be considered positive" }, - "score_threshold": { + "mitosalt_score_threshold": { "type": "integer", "default": 80, - "description": "alignemnt score cutoff" + "description": "alignment score cutoff" }, - "sizelimit": { + "mitosalt_sizelimit": { "type": "integer", "default": 10000, "description": "maximum size of deletions tolerated, beyond which they are reclassified as potential duplications in the reverse orientation" }, - "split_distance_threshold": { + "mitosalt_split_distance_threshold": { "type": "integer", "default": 5, "description": "the maximum length of unmapped distance between two fragments of a split read" }, - "split_length": { + "mitosalt_split_length": { "type": "integer", "default": 15, "description": "minimum number of bases in a split read fragment for the read to be considered as potentially spanning a deletion" diff --git a/subworkflows/local/annotate_genome_snvs/main.nf b/subworkflows/local/annotate_genome_snvs/main.nf index 3f9f7e37f..184733124 100644 --- a/subworkflows/local/annotate_genome_snvs/main.nf +++ b/subworkflows/local/annotate_genome_snvs/main.nf @@ -13,7 +13,6 @@ include { CHROMOGRAPH as CHROMOGRAPH_SITES } from '../../../modules/nf-core include { CHROMOGRAPH as CHROMOGRAPH_REGIONS } from '../../../modules/nf-core/chromograph/main' include { ENSEMBLVEP_VEP as ENSEMBLVEP_SNV } from '../../../modules/nf-core/ensemblvep/vep/main' include { TABIX_BGZIPTABIX as ZIP_TABIX_ROHCALL } from '../../../modules/nf-core/tabix/bgziptabix/main' -include { TABIX_BGZIPTABIX as ZIP_TABIX_VCFANNO } from '../../../modules/nf-core/tabix/bgziptabix/main' include { GATK4_SELECTVARIANTS } from '../../../modules/nf-core/gatk4/selectvariants/main' include { ANNOTATE_CADD } from '../annotate_cadd' include { ANNOTATE_RHOCALLVIZ } from '../annotate_rhocallviz' @@ -98,9 +97,11 @@ workflow ANNOTATE_GENOME_SNVS { VCFANNO (ch_vcfanno_in, ch_vcfanno_toml, ch_vcfanno_lua, ch_vcfanno_resources) - ZIP_TABIX_VCFANNO (VCFANNO.out.vcf) + VCFANNO.out.vcf + .join(VCFANNO.out.tbi) + .set { ch_vcfanno_out } - BCFTOOLS_VIEW(ZIP_TABIX_VCFANNO.out.gz_index, [], [], []) // filter on frequencies + BCFTOOLS_VIEW(ch_vcfanno_out, [], [], []) // filter on frequencies // Annotating with CADD if (!val_cadd_resources.equals(null)) { diff --git a/subworkflows/local/annotate_mt_snvs/main.nf b/subworkflows/local/annotate_mt_snvs/main.nf index 82972aa71..fc02ec005 100644 --- a/subworkflows/local/annotate_mt_snvs/main.nf +++ b/subworkflows/local/annotate_mt_snvs/main.nf @@ -9,7 +9,6 @@ include { ENSEMBLVEP_VEP as ENSEMBLVEP_MT } from '../../../module include { HAPLOGREP3_CLASSIFY as HAPLOGREP3_CLASSIFY_MT } from '../../../modules/nf-core/haplogrep3/classify/main' include { VCFANNO as VCFANNO_MT } from '../../../modules/nf-core/vcfanno/main' include { ANNOTATE_CADD } from '../annotate_cadd' -include { TABIX_BGZIPTABIX as ZIP_TABIX_VCFANNO_MT } from '../../../modules/nf-core/tabix/bgziptabix/main' workflow ANNOTATE_MT_SNVS { take: @@ -40,9 +39,8 @@ workflow ANNOTATE_MT_SNVS { .set { ch_in_vcfanno } VCFANNO_MT(ch_in_vcfanno, ch_vcfanno_toml, ch_vcfanno_lua, ch_vcfanno_resources) - ZIP_TABIX_VCFANNO_MT(VCFANNO_MT.out.vcf) - ch_vcfanno_vcf = ZIP_TABIX_VCFANNO_MT.out.gz_index.map{meta, vcf, _tbi -> return [meta, vcf]} + ch_vcfanno_vcf = VCFANNO_MT.out.vcf // Annotating with CADD if (!val_cadd_resources.equals(null)) { @@ -50,7 +48,7 @@ workflow ANNOTATE_MT_SNVS { ch_cadd_resources, ch_fai, ch_cadd_header, - ZIP_TABIX_VCFANNO_MT.out.gz_index, + VCFANNO_MT.out.tbi, val_genome ) ch_cadd_vcf = ANNOTATE_CADD.out.vcf diff --git a/subworkflows/local/call_structural_variants/main.nf b/subworkflows/local/call_structural_variants/main.nf index f9ef01c2a..1e1b4c7c0 100644 --- a/subworkflows/local/call_structural_variants/main.nf +++ b/subworkflows/local/call_structural_variants/main.nf @@ -4,6 +4,7 @@ include { CALL_SV_MANTA } from '../call_sv_manta' include { CALL_SV_TIDDIT } from '../call_sv_tiddit' +include { CALL_SV_MT } from '../call_sv_MT' include { SVDB_MERGE } from '../../../modules/nf-core/svdb/merge/main' include { CALL_SV_GERMLINECNVCALLER } from '../call_sv_germlinecnvcaller' include { CALL_SV_CNVNATOR } from '../call_sv_cnvnator' @@ -12,21 +13,50 @@ include { TABIX_TABIX } from '../../../modules/nf-core/tabix/ workflow CALL_STRUCTURAL_VARIANTS { take: - ch_genome_bam // channel: [mandatory] [ val(meta), path(bam) ] - ch_genome_bai // channel: [mandatory] [ val(meta), path(bai) ] - ch_genome_bam_bai // channel: [mandatory] [ val(meta), path(bam), path(bai) ] - ch_bwa_index // channel: [mandatory] [ val(meta), path(index)] - ch_genome_fasta // channel: [mandatory] [ val(meta), path(fasta) ] - ch_genome_fai // channel: [mandatory] [ val(meta), path(fai) ] - ch_case_info // channel: [mandatory] [ val(case_info) ] - ch_target_bed // channel: [mandatory for WES] [ val(meta), path(bed), path(tbi) ] - ch_genome_dictionary // channel: [optional; used by mandatory for GATK's cnvcaller][ val(meta), path(dict) ] - ch_svcaller_priority // channel: [mandatory] [ val(["var caller tag 1", ...]) ] - ch_readcount_intervals // channel: [optional; used by mandatory for GATK's cnvcaller][ path(intervals) ] - ch_ploidy_model // channel: [optional; used by mandatory for GATK's cnvcaller][ path(ploidy_model) ] - ch_gcnvcaller_model // channel: [optional; used by mandatory for GATK's cnvcaller][ path(gcnvcaller_model) ] - val_analysis_type // string: "wes", "wgs", or "mito" - skip_germlinecnvcaller // boolean + ch_genome_bam // channel: [mandatory] [ val(meta), path(bam) ] + ch_genome_bai // channel: [mandatory] [ val(meta), path(bai) ] + ch_genome_bam_bai // channel: [mandatory] [ val(meta), path(bam), path(bai) ] + ch_bwa_index // channel: [mandatory] [ val(meta), path(index)] + ch_genome_fasta // channel: [mandatory] [ val(meta), path(fasta) ] + ch_genome_fai // channel: [mandatory] [ val(meta), path(fai) ] + ch_case_info // channel: [mandatory] [ val(case_info) ] + ch_target_bed // channel: [mandatory for WES] [ val(meta), path(bed), path(tbi) ] + ch_genome_dictionary // channel: [optional; used by mandatory for GATK's cnvcaller][ val(meta), path(dict) ] + ch_svcaller_priority // channel: [mandatory] [ val(["var caller tag 1", ...]) ] + ch_readcount_intervals // channel: [optional; used by mandatory for GATK's cnvcaller][ path(intervals) ] + ch_ploidy_model // channel: [optional; used by mandatory for GATK's cnvcaller][ path(ploidy_model) ] + ch_gcnvcaller_model // channel: [optional; used by mandatory for GATK's cnvcaller][ path(gcnvcaller_model) ] + val_analysis_type // string: "wes", "wgs", or "mito" + skip_germlinecnvcaller // boolean + ch_mt_bam_bai // channel: [mandatory] [ val(meta), path(bam), path(bai) ] + ch_genome_chrsizes // channel: [mandatory] [ path(chrsizes) ] + ch_genome_hisat2index // channel: [mandatory] [ val(meta), path(hisat2index) ] + ch_mt_fai // channel: [mandatory] [ val(meta), path(mtfai) ] + ch_mt_fasta // channel: [mandatory] [ val(meta), path(mtfasta) ] + ch_mt_lastdb // channel: [mandatory] [ val(meta), path(lastindex) ] + ch_reads // channel: [mandatory] [ val(meta), [path(reads)] ] + ch_subdepth // channel: [mandatory] [ val(mitosalt_depth) ] + val_heavy_strand_origin_start // string: [mandatory] mitochondira_heavy_strand_origin_start + val_heavy_strand_origin_end // string: [mandatory] mitochondira_heavy_strand_origin_end + val_light_strand_origin_start // string: [mandatory] mitochondira_light_strand_origin_start + val_light_strand_origin_end // string: [mandatory] mitochondira_light_strand_origin_end + val_mito_length // string: [mandatory] mito_length + val_mito_name // string: [mandatory] mito_name + val_mitosalt_breakspan // string: [mandatory] mitosalt_breakspan + val_mitosalt_breakthreshold // string: [mandatory] mitosalt_breakthreshold + val_mitosalt_cluster_threshold // string: [mandatory] mitosalt_cluster_threshold + val_mitosalt_deletion_threshold_max // string: [mandatory] mitosalt_deletion_threshold_max + val_mitosalt_deletion_threshold_min // string: [mandatory] mitosalt_deletion_threshold_min + val_mitosalt_evalue_threshold // string: [mandatory] mitosalt_evalue_threshold + val_mitosalt_exclude // string: [mandatory] mitosalt_exclude + val_mitosalt_flank // string: [mandatory] mitosalt_flank + val_mitosalt_heteroplasmy_limit // string: [mandatory] mitosalt_heteroplasmy_limit + val_mitosalt_paired_distance // string: [mandatory] mitosalt_paired_distance + val_mitosalt_score_threshold // string: [mandatory] mitosalt_score_threshold + val_mitosalt_sizelimit // string: [mandatory] mitosalt_sizelimit + val_mitosalt_split_distance_threshold // string: [mandatory] mitosalt_split_distance_threshold + val_mitosalt_split_length // string: [mandatory] mitosalt_split_length + val_run_mt_for_wes // boolean: [mandatory] run_mt_for_wes main: ch_merged_svs = channel.empty() @@ -59,51 +89,99 @@ workflow CALL_STRUCTURAL_VARIANTS { } + if (val_analysis_type.matches("wgs|mito") || val_run_mt_for_wes) { + CALL_SV_MT( + ch_mt_bam_bai, + ch_genome_chrsizes, + ch_genome_fai, + ch_genome_fasta, + ch_genome_hisat2index, + ch_mt_fai, + ch_mt_fasta, + ch_mt_lastdb, + ch_reads, + ch_subdepth, + val_heavy_strand_origin_start, + val_heavy_strand_origin_end, + val_light_strand_origin_start, + val_light_strand_origin_end, + val_mito_length, + val_mito_name, + val_mitosalt_breakspan, + val_mitosalt_breakthreshold, + val_mitosalt_cluster_threshold, + val_mitosalt_deletion_threshold_max, + val_mitosalt_deletion_threshold_min, + val_mitosalt_evalue_threshold, + val_mitosalt_exclude, + val_mitosalt_flank, + val_mitosalt_heteroplasmy_limit, + val_mitosalt_paired_distance, + val_mitosalt_score_threshold, + val_mitosalt_sizelimit, + val_mitosalt_split_distance_threshold, + val_mitosalt_split_length) + .mitosalt_vcf + .collect{ _meta, vcf -> vcf } + .set { mitosalt_vcf } + } + //merge if (skip_germlinecnvcaller) { if (val_analysis_type.equals("wgs")) { tiddit_vcf .combine(manta_vcf) .combine(cnvnator_vcf) - .toList() - .set { vcf_list } + .set { vcf_paths } } else if (!val_analysis_type.equals("mito")) { manta_vcf - .toList() - .set { vcf_list } + .set { vcf_paths } } } else if (val_analysis_type.equals("wgs")) { tiddit_vcf .combine(manta_vcf) .combine(gcnvcaller_vcf) .combine(cnvnator_vcf) - .toList() - .set { vcf_list } + .set { vcf_paths } } else if (!val_analysis_type.equals("mito")) { manta_vcf .combine(gcnvcaller_vcf) - .toList() - .set { vcf_list } + .set { vcf_paths } } if (!val_analysis_type.equals("mito")) { + if (!mitosalt_vcf.equals('none') && val_analysis_type.equals("wgs")) { + ch_svcaller_priority.combine(["mitosalt"]) + .collect() + .set { ch_svcaller_priority } + vcf_paths.combine(mitosalt_vcf) + .set { vcf_paths } + } + + vcf_list = vcf_paths.toList() ch_case_info .combine(vcf_list) - .set { merge_input_vcfs } + .set { merge_vcfs_in } - SVDB_MERGE (merge_input_vcfs, ch_svcaller_priority, true) + SVDB_MERGE (merge_vcfs_in, ch_svcaller_priority, true) TABIX_TABIX (SVDB_MERGE.out.vcf) ch_merged_svs = SVDB_MERGE.out.vcf ch_merged_tbi = TABIX_TABIX.out.index + } else if (!mitosalt_vcf.equals('none')) { + TABIX_TABIX (mitosalt_vcf) + ch_merged_svs = mitosalt_vcf + ch_merged_tbi = TABIX_TABIX.out.index } ch_publish = ch_merged_svs .mix(ch_merged_tbi) .map { meta, value -> ['call_sv/genome/', [meta, value]] } + ch_publish_mt = CALL_SV_MT.out.publish emit: - vcf = ch_merged_svs // channel: [ val(meta), path(vcf)] - tbi = ch_merged_tbi // channel: [ val(meta), path(tbi)] - publish = ch_publish // channel: [ val(destination), val(value) ] + vcf = ch_merged_svs // channel: [ val(meta), path(vcf)] + tbi = ch_merged_tbi // channel: [ val(meta), path(tbi)] + publish = ch_publish // channel: [ val(destination), val(value) ] + publish_mt = ch_publish_mt // channel: [ val(destination), val(value) ] } diff --git a/subworkflows/local/call_structural_variants/tests/main.nf.test b/subworkflows/local/call_structural_variants/tests/main.nf.test index 94e520bcd..f3dde334c 100644 --- a/subworkflows/local/call_structural_variants/tests/main.nf.test +++ b/subworkflows/local/call_structural_variants/tests/main.nf.test @@ -38,6 +38,51 @@ nextflow_workflow { input[12] = channel.of([[id:'gcnvcaller'], []]) input[13] = 'wgs' input[14] = true + input[15] = channel.of([ + [id:'earlycasualcaiman', sample:'earlycasualcaiman', single_end:false, num_lanes:1, read_group: "'@RG\\\\tID:earlycasualcaiman\\\\tPL:illumina\\\\tSM:earlycasualcaiman'", lane:1, sex:1, phenotype:1, paternal:0, maternal:0, case_id:'justhusky'], + file(params.pipelines_testdata_base_path + 'testdata/earlycasualcaiman_sorted_md.bam', checkIfExists: true), + file(params.pipelines_testdata_base_path + 'testdata/earlycasualcaiman_sorted_md.bam.bai', checkIfExists: true) + ]) + input[16] = channel.of([[id:'genome'], file(params.pipelines_testdata_base_path + 'reference/reference_chr.sizes', checkIfExists: true)]).collect() + input[17] = Channel.from("\$PWD").map { dir -> [[id:'genome'], file(dir)] } + input[18] = channel.of([[id:'genome'], file(params.pipelines_testdata_base_path + 'reference/reference_mt.fa.fai', checkIfExists: true)]).collect() + input[19] = channel.of([[id:'genome'], file(params.pipelines_testdata_base_path + 'reference/reference_mt.fa', checkIfExists: true)]).collect() + input[20] = Channel.from("${projectDir}/subworkflows/local/variant_evaluation/tests").map { dir -> [[id:'mt'], file(dir)] } + input[21] = channel.of([ + [id:'earlycasualcaiman', sample:'earlycasualcaiman', single_end:false, num_lanes:1, read_group: "'@RG\\\\tID:earlycasualcaiman\\\\tPL:illumina\\\\tSM:earlycasualcaiman'", lane:1, sex:1, phenotype:1, paternal:0, maternal:0, case_id:'justhusky'], + [ + file(params.pipelines_testdata_base_path + 'testdata/earlycasualcaiman_mt_1.fastq.gz', checkIfExists: true), + file(params.pipelines_testdata_base_path + 'testdata/earlycasualcaiman_mt_2.fastq.gz', checkIfExists: true) + ] + ]) + input[22] = channel.value(10000000) + input[23] = 16081 + input[24] = 407 + input[25] = 5730 + input[26] = 5763 + input[27] = 16569 + input[28] = 'MT' + input[29] = 15 + input[30] = 2 + input[31] = 5 + input[32] = 30000 + input[33] = 30 + input[34] = 0.00001 + input[35] = 5 + input[36] = 15 + input[37] = 0.01 + input[38] = 1000 + input[39] = 80 + input[40] = 10000 + input[41] = 5 + input[42] = 15 + input[43] = 0.5 + input[44] = 600 + input[45] = 10 + input[46] = 5 + input[47] = 0.3 + input[48] = true + """ } } diff --git a/subworkflows/local/call_sv_MT/main.nf b/subworkflows/local/call_sv_MT/main.nf index 5b74b7ba2..fe708d149 100644 --- a/subworkflows/local/call_sv_MT/main.nf +++ b/subworkflows/local/call_sv_MT/main.nf @@ -2,38 +2,46 @@ // Call SV MT // -include { MT_DELETION } from '../../../modules/local/mt_deletion_script' -include { PREP_MITOSALT } from '../../../modules/local/prep_mitosalt/main' -include { MITOSALT } from '../../../modules/local/mitosalt/main' -include { SEQTK_SAMPLE } from '../../../modules/nf-core/seqtk/sample/main' +include { MT_DELETION } from '../../../modules/local/mt_deletion_script' +include { PREP_MITOSALT } from '../../../modules/local/prep_mitosalt/main' +include { MITOSALT } from '../../../modules/local/mitosalt/main' +include { SEQTK_SAMPLE } from '../../../modules/nf-core/seqtk/sample/main' +include { SALTSHAKER_CALL } from '../../../modules/nf-core/saltshaker/call/main' +include { SALTSHAKER_CLASSIFY } from '../../../modules/nf-core/saltshaker/classify/main' +include { SALTSHAKER_PLOT } from '../../../modules/nf-core/saltshaker/plot/main' workflow CALL_SV_MT { take: - ch_bam_bai // channel: [mandatory] [ val(meta), path(bam) ] - ch_genome_chrsizes // channel: [mandatory] [ path(chrsizes) ] - ch_genome_fai // channel: [mandatory] [ val(meta), path(genomefai) ] - ch_genome_fasta // channel: [mandatory] [ val(meta), path(fasta) ] - ch_genome_hisat2index // channel: [mandatory] [ val(meta), path(hisat2index) ] - ch_mt_fai // channel: [mandatory] [ val(meta), path(mtfai) ] - ch_mt_fasta // channel: [mandatory] [ val(meta), path(mtfasta) ] - ch_mt_lastdb // channel: [mandatory] [ val(meta), path(lastindex) ] - ch_reads // channel: [mandatory] [ val(meta), [path(reads)] ] - ch_subdepth // channel: [mandatory] [ val(mitosalt_depth) ] - val_breakspan // string: [mandatory] mitosalt_break_span - val_breakthreshold // string: [mandatory] mitosalt_break_threshold - val_cluster_threshold // string: [mandatory] mitosalt_cluster_threshold - val_deletion_threshold_max // string: [mandatory] mitosalt_del_threshold_max - val_deletion_threshold_min // string: [mandatory] mitosalt_del_threshold_min - val_evalue_threshold // string: [mandatory] mitosalt_evalue_threshold - val_exclude // string: [mandatory] mitosalt_exclude - val_flank // string: [mandatory] mitosalt_flank - val_hplimit // string: [mandatory] mitosalt_hp_limit - val_mito_name // string: [mandatory] mito_name - val_paired_distance // string: [mandatory] mitosalt_paired_distance - val_score_threshold // string: [mandatory] mitosalt_score_threshold - val_sizelimit // string: [mandatory] mitosalt_size_limit - val_split_distance_threshold // string: [mandatory] mitosalt_split_dist_threshold - val_split_length // string: [mandatory] mitosalt_split_length + ch_bam_bai // channel: [mandatory] [ val(meta), path(bam) ] + ch_genome_chrsizes // channel: [mandatory] [ path(chrsizes) ] + ch_genome_fai // channel: [mandatory] [ val(meta), path(genomefai) ] + ch_genome_fasta // channel: [mandatory] [ val(meta), path(fasta) ] + ch_genome_hisat2index // channel: [mandatory] [ val(meta), path(hisat2index) ] + ch_mt_fai // channel: [mandatory] [ val(meta), path(mtfai) ] + ch_mt_fasta // channel: [mandatory] [ val(meta), path(mtfasta) ] + ch_mt_lastdb // channel: [mandatory] [ val(meta), path(lastindex) ] + ch_reads // channel: [mandatory] [ val(meta), [path(reads)] ] + ch_subdepth // channel: [mandatory] [ val(mitosalt_depth) ] + val_heavy_strand_origin_start // string: [mandatory] mitochondira_heavy_strand_origin_start + val_heavy_strand_origin_end // string: [mandatory] mitochondira_heavy_strand_origin_end + val_light_strand_origin_start // string: [mandatory] mitochondira_light_strand_origin_start + val_light_strand_origin_end // string: [mandatory] mitochondira_light_strand_origin_end + val_mito_length // string: [mandatory] mito_length + val_mito_name // string: [mandatory] mito_name + val_mitosalt_breakspan // string: [mandatory] mitosalt_breakspan + val_mitosalt_breakthreshold // string: [mandatory] mitosalt_breakthreshold + val_mitosalt_cluster_threshold // string: [mandatory] mitosalt_cluster_threshold + val_mitosalt_deletion_threshold_max // string: [mandatory] mitosalt_deletion_threshold_max + val_mitosalt_deletion_threshold_min // string: [mandatory] mitosalt_deletion_threshold_min + val_mitosalt_evalue_threshold // string: [mandatory] mitosalt_evalue_threshold + val_mitosalt_exclude // string: [mandatory] mitosalt_exclude + val_mitosalt_flank // string: [mandatory] mitosalt_flank + val_mitosalt_heteroplasmy_limit // string: [mandatory] mitosalt_heteroplasmy_limit + val_mitosalt_paired_distance // string: [mandatory] mitosalt_paired_distance + val_mitosalt_score_threshold // string: [mandatory] mitosalt_score_threshold + val_mitosalt_sizelimit // string: [mandatory] mitosalt_sizelimit + val_mitosalt_split_distance_threshold // string: [mandatory] mitosalt_split_distance_threshold + val_mitosalt_split_length // string: [mandatory] mitosalt_split_length main: ch_mitosalt_publish = channel.empty() @@ -50,21 +58,21 @@ workflow CALL_SV_MT { ch_mt_fai, ch_mt_fasta, ch_mt_lastdb, - val_breakspan, - val_breakthreshold, - val_cluster_threshold, - val_deletion_threshold_max, - val_deletion_threshold_min, - val_evalue_threshold, - val_exclude, - val_flank, - val_hplimit, + val_mitosalt_breakspan, + val_mitosalt_breakthreshold, + val_mitosalt_cluster_threshold, + val_mitosalt_deletion_threshold_max, + val_mitosalt_deletion_threshold_min, + val_mitosalt_evalue_threshold, + val_mitosalt_exclude, + val_mitosalt_flank, + val_mitosalt_heteroplasmy_limit, val_mito_name, - val_paired_distance, - val_score_threshold, - val_sizelimit, - val_split_distance_threshold, - val_split_length + val_mitosalt_paired_distance, + val_mitosalt_score_threshold, + val_mitosalt_sizelimit, + val_mitosalt_split_distance_threshold, + val_mitosalt_split_length ) MITOSALT( @@ -77,9 +85,48 @@ workflow CALL_SV_MT { ch_mt_fasta, ch_mt_lastdb ) - ch_mitosalt_publish = MITOSALT.out.breakpoint - .mix(MITOSALT.out.cluster) + MITOSALT.out.cluster + .filter{ _meta, out -> out.countLines() > 0 } + .set{ch_cluster} + ch_saltshaker_txt = channel.empty() + ch_saltshaker_vcf = channel.empty() + ch_saltshaker_plot = channel.empty() + ch_mitosalt_publish = channel.empty() + + if (ch_cluster) { + MITOSALT.out.breakpoint + .join(ch_cluster) + .set{ch_saltshaker_in} + + SALTSHAKER_CALL( + ch_saltshaker_in, + ch_mt_fasta, + val_mitosalt_flank, + val_mitosalt_heteroplasmy_limit, + val_mito_length, + val_heavy_strand_origin_start, + val_heavy_strand_origin_end, + val_light_strand_origin_start, + val_light_strand_origin_end + ) + + SALTSHAKER_CLASSIFY( + SALTSHAKER_CALL.out.call, + val_mito_name + ) + ch_saltshaker_txt = SALTSHAKER_CLASSIFY.out.txt + ch_saltshaker_vcf = SALTSHAKER_CLASSIFY.out.vcf + + SALTSHAKER_PLOT( + SALTSHAKER_CLASSIFY.out.classify + ) + ch_saltshaker_plot = SALTSHAKER_PLOT.out.plot + + ch_mitosalt_publish = ch_saltshaker_txt + .mix(ch_saltshaker_vcf) + .mix(ch_saltshaker_plot) + } } MT_DELETION(ch_bam_bai, ch_genome_fasta) @@ -88,8 +135,9 @@ workflow CALL_SV_MT { .map { meta, value -> ['call_sv/mitochondria/', [meta, value]] } emit: - mitosalt_breakpoint = MITOSALT.out.breakpoint // channel: [ val(meta), path(breakpoint) ] - mitosalt_cluster = MITOSALT.out.cluster // channel: [ val(meta), path(cluster) ] - mt_del_result = MT_DELETION.out.mt_del_result // channel: [ val(meta), path(txt) ] - publish = ch_publish // channel: [ val(destination), val(value) ] + mitosalt_classify = ch_saltshaker_txt // channel: [ val(meta), path(txt) ] + mitosalt_vcf = ch_saltshaker_vcf.ifEmpty(['meta','none']) // channel: [ val(meta), path(vcf) ] + mitosalt_plot = ch_saltshaker_plot // channel: [ val(meta), path(png) ] + mt_del_result = MT_DELETION.out.mt_del_result // channel: [ val(meta), path(txt) ] + publish = ch_publish // channel: [ val(destination), val(value) ] } diff --git a/subworkflows/local/call_sv_MT/tests/main.nf.test b/subworkflows/local/call_sv_MT/tests/main.nf.test index 002a4ccdc..bf9930146 100644 --- a/subworkflows/local/call_sv_MT/tests/main.nf.test +++ b/subworkflows/local/call_sv_MT/tests/main.nf.test @@ -37,21 +37,26 @@ nextflow_workflow { ] ]) input[9] = channel.value(10000000) - input[10] = 15 - input[11] = 2 - input[12] = 2 - input[13] = 30000 - input[14] = 30 - input[15] = 0.00001 - input[16] = 5 - input[17] = 15 - input[18] = 0.01 - input[19] = 'chrM' - input[20] = 1000 - input[21] = 80 - input[22] = 10000 - input[23] = 5 - input[24] = 15 + input[10] = 16081 + input[11] = 407 + input[12] = 5730 + input[13] = 5763 + input[14] = 16569 + input[15] = 'chrM' + input[16] = 15 + input[17] = 2 + input[18] = 2 + input[19] = 30000 + input[20] = 30 + input[21] = 0.00001 + input[22] = 5 + input[23] = 15 + input[24] = 0.01 + input[25] = 1000 + input[26] = 80 + input[27] = 10000 + input[28] = 5 + input[29] = 15 """ } } diff --git a/tests/default.nf.test.snap b/tests/default.nf.test.snap index dcae17c82..11cdd475c 100644 --- a/tests/default.nf.test.snap +++ b/tests/default.nf.test.snap @@ -324,6 +324,15 @@ "RHOCALL_VIZ": { "rhocall": "0.5.1" }, + "SALTSHAKER_CALL": { + "saltshaker": "1.0.0" + }, + "SALTSHAKER_CLASSIFY": { + "saltshaker": "1.0.0" + }, + "SALTSHAKER_PLOT": { + "saltshaker": "1.0.0" + }, "SAMTOOLS_COLLATE": { "samtools": "1.22.1" }, @@ -677,12 +686,24 @@ "call_sv/mitochondria", "call_sv/mitochondria/earlycasualcaiman_LNUMBER1.breakpoint", "call_sv/mitochondria/earlycasualcaiman_LNUMBER1.cluster", + "call_sv/mitochondria/earlycasualcaiman_LNUMBER1.saltshaker.png", + "call_sv/mitochondria/earlycasualcaiman_LNUMBER1.saltshaker.vcf", + "call_sv/mitochondria/earlycasualcaiman_LNUMBER1.saltshaker_classify.txt", + "call_sv/mitochondria/earlycasualcaiman_LNUMBER1.saltshaker_classify_metadata.tsv", "call_sv/mitochondria/earlycasualcaiman_mitochondria_deletions.txt", "call_sv/mitochondria/hugelymodelbat_LNUMBER3.breakpoint", "call_sv/mitochondria/hugelymodelbat_LNUMBER3.cluster", + "call_sv/mitochondria/hugelymodelbat_LNUMBER3.saltshaker.png", + "call_sv/mitochondria/hugelymodelbat_LNUMBER3.saltshaker.vcf", + "call_sv/mitochondria/hugelymodelbat_LNUMBER3.saltshaker_classify.txt", + "call_sv/mitochondria/hugelymodelbat_LNUMBER3.saltshaker_classify_metadata.tsv", "call_sv/mitochondria/hugelymodelbat_mitochondria_deletions.txt", "call_sv/mitochondria/slowlycivilbuck_LNUMBER2.breakpoint", "call_sv/mitochondria/slowlycivilbuck_LNUMBER2.cluster", + "call_sv/mitochondria/slowlycivilbuck_LNUMBER2.saltshaker.png", + "call_sv/mitochondria/slowlycivilbuck_LNUMBER2.saltshaker.vcf", + "call_sv/mitochondria/slowlycivilbuck_LNUMBER2.saltshaker_classify.txt", + "call_sv/mitochondria/slowlycivilbuck_LNUMBER2.saltshaker_classify_metadata.tsv", "call_sv/mitochondria/slowlycivilbuck_mitochondria_deletions.txt", "fastqc", "fastqc/earlycasualcaiman_LNUMBER1", @@ -1093,8 +1114,9 @@ ], "timestamp": "2026-03-17T16:52:17.64647915", "meta": { - "nf-test": "0.9.4", - "nextflow": "25.10.4" - } + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-03-03T15:58:44.610805525" } } \ No newline at end of file diff --git a/tests/test_bam.nf.test.snap b/tests/test_bam.nf.test.snap index 9d91e0519..312c4c3e4 100644 --- a/tests/test_bam.nf.test.snap +++ b/tests/test_bam.nf.test.snap @@ -1022,8 +1022,9 @@ ], "timestamp": "2026-03-17T16:58:22.361988613", "meta": { - "nf-test": "0.9.4", - "nextflow": "25.10.4" - } + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-02-27T09:07:42.308738867" } } \ No newline at end of file diff --git a/tests/test_singleton.nf.test.snap b/tests/test_singleton.nf.test.snap index c86d7ea91..58d706c23 100644 --- a/tests/test_singleton.nf.test.snap +++ b/tests/test_singleton.nf.test.snap @@ -316,6 +316,15 @@ "RHOCALL_VIZ": { "rhocall": "0.5.1" }, + "SALTSHAKER_CALL": { + "saltshaker": "1.0.0" + }, + "SALTSHAKER_CLASSIFY": { + "saltshaker": "1.0.0" + }, + "SALTSHAKER_PLOT": { + "saltshaker": "1.0.0" + }, "SAMTOOLS_COLLATE": { "samtools": "1.22.1" }, @@ -542,6 +551,10 @@ "call_sv/mitochondria", "call_sv/mitochondria/hugelymodelbat_LNUMBER1.breakpoint", "call_sv/mitochondria/hugelymodelbat_LNUMBER1.cluster", + "call_sv/mitochondria/hugelymodelbat_LNUMBER1.saltshaker.png", + "call_sv/mitochondria/hugelymodelbat_LNUMBER1.saltshaker.vcf", + "call_sv/mitochondria/hugelymodelbat_LNUMBER1.saltshaker_classify.txt", + "call_sv/mitochondria/hugelymodelbat_LNUMBER1.saltshaker_classify_metadata.tsv", "call_sv/mitochondria/hugelymodelbat_mitochondria_deletions.txt", "fastqc", "fastqc/hugelymodelbat_LNUMBER1", @@ -712,8 +725,9 @@ ], "timestamp": "2026-03-17T17:02:07.664289531", "meta": { - "nf-test": "0.9.4", - "nextflow": "25.10.4" - } + "nf-test": "0.9.3", + "nextflow": "25.10.2" + }, + "timestamp": "2026-03-04T08:25:27.608017744" } -} \ No newline at end of file +} diff --git a/workflows/raredisease.nf b/workflows/raredisease.nf index 93220ee89..0d05274c0 100644 --- a/workflows/raredisease.nf +++ b/workflows/raredisease.nf @@ -52,7 +52,6 @@ include { CALL_MOBILE_ELEMENTS } from '.. include { CALL_REPEAT_EXPANSIONS } from '../subworkflows/local/call_repeat_expansions' include { CALL_SNV } from '../subworkflows/local/call_snv' include { CALL_STRUCTURAL_VARIANTS } from '../subworkflows/local/call_structural_variants' -include { CALL_SV_MT } from '../subworkflows/local/call_sv_MT' include { GENERATE_CYTOSURE_FILES } from '../subworkflows/local/generate_cytosure_files' include { GENS } from '../subworkflows/local/gens' include { PREPARE_REFERENCES } from '../subworkflows/local/prepare_references' @@ -568,7 +567,7 @@ workflow RAREDISEASE { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - CALL AND ANNOTATE NUCLEAR SVs + CALL AND ANNOTATE NUCLEAR AND MITOCHONDRIAL SVs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -589,8 +588,38 @@ workflow RAREDISEASE { ch_gcnvcaller_model, val_analysis_type, skip_germlinecnvcaller, + ch_mapped.mt_bam_bai, + ch_genome_chrsizes, + ch_genome_hisat2index, + ch_mt_fai, + ch_mt_fasta, + ch_mt_lastdb, + ch_input_fastqs, + ch_subdepth, + params.heavy_strand_origin_start, + params.heavy_strand_origin_end, + params.light_strand_origin_start, + params.light_strand_origin_end, + params.mito_length, + params.mito_name, + params.mitosalt_breakspan, + params.mitosalt_breakthreshold, + params.mitosalt_cluster_threshold, + params.mitosalt_deletion_threshold_max, + params.mitosalt_deletion_threshold_min, + params.mitosalt_evalue_threshold, + params.mitosalt_exclude, + params.mitosalt_flank, + params.mitosalt_heteroplasmy_limit, + params.mitosalt_paired_distance, + params.mitosalt_score_threshold, + params.mitosalt_sizelimit, + params.mitosalt_split_distance_threshold, + params.mitosalt_split_length, + val_run_mt_for_wes ) ch_call_sv_publish = CALL_STRUCTURAL_VARIANTS.out.publish + ch_call_sv_mt_publish = CALL_STRUCTURAL_VARIANTS.out.publish_mt // // ANNOTATE STRUCTURAL VARIANTS @@ -659,42 +688,7 @@ workflow RAREDISEASE { } } /* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - CALL MITOCHONDRIAL SVs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -*/ - if (val_analysis_type.matches("wgs|mito") || val_run_mt_for_wes) { - CALL_SV_MT( - ch_mapped.genome_marked_bam_bai, - ch_genome_chrsizes, - ch_genome_fai, - ch_genome_fasta, - ch_genome_hisat2index, - ch_mt_fai, - ch_mt_fasta, - ch_mt_lastdb, - ch_input_fastqs, - ch_subdepth, - params.breakspan, - params.breakthreshold, - params.cluster_threshold, - params.deletion_threshold_max, - params.deletion_threshold_min, - params.evalue_threshold, - params.exclude, - params.flank, - params.hplimit, - params.mito_name, - params.paired_distance, - params.score_threshold, - params.sizelimit, - params.split_distance_threshold, - params.split_length - ) - ch_call_sv_mt_publish = CALL_SV_MT.out.publish - } -/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CALL AND ANNOTATE MOBILE ELEMENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~