From 15919fae742d5985e709e00130985e7452d398d9 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 18 Nov 2025 14:42:48 +0100 Subject: [PATCH 01/27] Mport the ichorCNA modules --- modules.json | 10 + .../ichorcna/createpon/environment.yml | 7 + modules/nf-core/ichorcna/createpon/main.nf | 85 +++++++++ modules/nf-core/ichorcna/createpon/meta.yml | 82 ++++++++ .../ichorcna/createpon/tests/main.nf.test | 96 ++++++++++ .../createpon/tests/main.nf.test.snap | 44 +++++ modules/nf-core/ichorcna/run/environment.yml | 7 + modules/nf-core/ichorcna/run/main.nf | 108 +++++++++++ modules/nf-core/ichorcna/run/meta.yml | 176 ++++++++++++++++++ .../nf-core/ichorcna/run/tests/main.nf.test | 78 ++++++++ .../ichorcna/run/tests/main.nf.test.snap | 167 +++++++++++++++++ .../ichorcna/run/tests/nextflow.config | 8 + 12 files changed, 868 insertions(+) create mode 100644 modules/nf-core/ichorcna/createpon/environment.yml create mode 100644 modules/nf-core/ichorcna/createpon/main.nf create mode 100644 modules/nf-core/ichorcna/createpon/meta.yml create mode 100644 modules/nf-core/ichorcna/createpon/tests/main.nf.test create mode 100644 modules/nf-core/ichorcna/createpon/tests/main.nf.test.snap create mode 100644 modules/nf-core/ichorcna/run/environment.yml create mode 100644 modules/nf-core/ichorcna/run/main.nf create mode 100644 modules/nf-core/ichorcna/run/meta.yml create mode 100644 modules/nf-core/ichorcna/run/tests/main.nf.test create mode 100644 modules/nf-core/ichorcna/run/tests/main.nf.test.snap create mode 100644 modules/nf-core/ichorcna/run/tests/nextflow.config diff --git a/modules.json b/modules.json index bb07e224..8ae03f00 100644 --- a/modules.json +++ b/modules.json @@ -60,6 +60,16 @@ "git_sha": "3db80bad01d58d9977654212253a30af42c9fd48", "installed_by": ["modules"] }, + "ichorcna/createpon": { + "branch": "master", + "git_sha": "41dfa3f7c0ffabb96a6a813fe321c6d1cc5b6e46", + "installed_by": ["modules"] + }, + "ichorcna/run": { + "branch": "master", + "git_sha": "4412c4b83c62cb2036b9a6939551a7fd988177d0", + "installed_by": ["modules"] + }, "multiqc": { "branch": "master", "git_sha": "af27af1be706e6a2bb8fe454175b0cdf77f47b49", diff --git a/modules/nf-core/ichorcna/createpon/environment.yml b/modules/nf-core/ichorcna/createpon/environment.yml new file mode 100644 index 00000000..54df91b4 --- /dev/null +++ b/modules/nf-core/ichorcna/createpon/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: + - bioconda::r-ichorcna=0.5.1 diff --git a/modules/nf-core/ichorcna/createpon/main.nf b/modules/nf-core/ichorcna/createpon/main.nf new file mode 100644 index 00000000..ab19a033 --- /dev/null +++ b/modules/nf-core/ichorcna/createpon/main.nf @@ -0,0 +1,85 @@ +process ICHORCNA_CREATEPON { + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/r-ichorcna:0.5.1--r43hdfd78af_0' : + 'biocontainers/r-ichorcna:0.5.1--r43hdfd78af_0' }" + + input: + path wigs + path gc_wig + path map_wig + path centromere + path rep_time_wig + path exons + + output: + path "${prefix}*.rds", emit: rds + path "${prefix}*.txt", emit: txt + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "PoN" + def map = map_wig ? "mapWig='${map_wig}'," : 'mapWig=NULL,' + def centro = centromere ? "centromere='${centromere}'," : '' + def rep = rep_time_wig ? "repTimeWig='${rep_time_wig}'," : 'repTimeWig=NULL,' + def exons = exons ? "exons.bed='${exons}'," : '' + + """ + #!/usr/bin/env Rscript + library("ichorCNA") + library("yaml") + + write.table(strsplit("${wigs}"," ")[[1]],"filelist.txt", row.names = FALSE, col.names = FALSE) + + createPanelOfNormals( + gcWig='${gc_wig}', + ${map} + ${rep} + filelist = "filelist.txt", + outfile = "${prefix}", + ${exons} + ${centro} + $args + ) + + ### Make Versions YAML for NF-Core ### + versions = list() + versions["r"] <- paste(R.Version()\$major, R.Version()\$minor, sep=".") + versions["ichorCNA"] <- paste(packageVersion("ichorCNA"), sep=".") + + yaml_str <- as.yaml( + list( + "${task.process}" = versions + ) + ) + writeLines(yaml_str, file("versions.yml")) + """ + + stub: + prefix = task.ext.prefix ?: "PoN" + """ + #!/usr/bin/env Rscript + library("yaml") + + file.create("${prefix}.rds") + file.create("${prefix}.txt") + + ### Make Versions YAML for NF-Core ### + versions = list() + versions["r"] <- paste(R.Version()\$major, R.Version()\$minor, sep=".") + versions["ichorCNA"] <- paste(packageVersion("ichorCNA"), sep=".") + + yaml_str <- as.yaml( + list( + "${task.process}" = versions + ) + ) + writeLines(yaml_str, file("versions.yml")) + """ +} diff --git a/modules/nf-core/ichorcna/createpon/meta.yml b/modules/nf-core/ichorcna/createpon/meta.yml new file mode 100644 index 00000000..9380eb5d --- /dev/null +++ b/modules/nf-core/ichorcna/createpon/meta.yml @@ -0,0 +1,82 @@ +name: ichorcna_createpon +description: ichorCNA is an R package for calculating copy number alteration from + (low-pass) whole genome sequencing, particularly for use in cell-free DNA. This + module generates a panel of normals +keywords: + - ichorcna + - cnv + - cna + - cfDNA + - wgs + - panel_of_normals +tools: + - ichorcna: + description: Estimating tumor fraction in cell-free DNA from ultra-low-pass whole + genome sequencing. + homepage: https://github.com/broadinstitute/ichorCNA + documentation: https://github.com/broadinstitute/ichorCNA/wiki + tool_dev_url: https://github.com/broadinstitute/ichorCNA + doi: "10.1038/s41467-017-00965-y" + licence: ["GPL v3"] + identifier: "" +input: + - wigs: + type: file + description: Any number of hmmcopy/readCounter processed .wig files giving the + number of reads in the sample, in each genomic window. These will be averaged + over to generate the panel of normals. + pattern: "*.{wig}" + ontologies: [] + - gc_wig: + type: file + description: hmmcopy/gcCounter processed .wig file giving the gc content in the + reference fasta, in each genomic window + pattern: "*.{wig}" + ontologies: [] + - map_wig: + type: file + description: hmmcopy/mapCounter processed .wig file giving the mapability in the + reference fasta, in each genomic window + pattern: "*.{wig}" + ontologies: [] + - centromere: + type: file + description: Text file giving centromere locations of each genome, to exclude + these windows + pattern: "*.{txt}" + ontologies: [] + - rep_time_wig: + type: file + description: Replication/timing .wig file. + pattern: "*.{wig}" + ontologies: [] + - exons: + type: file + description: BED file for exon regions to annotate CNA regions. + pattern: "*.{bed}" + ontologies: [] +output: + rds: + - ${prefix}*.rds: + type: file + description: R data file (.rds) containing panel of normals data, medians of + each bin. + pattern: "*.rds" + ontologies: [] + txt: + - ${prefix}*.txt: + type: file + description: Text file containing panel of normals data, medians of each bin. + pattern: "*.txt" + ontologies: [] + versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + ontologies: + - edam: http://edamontology.org/format_3750 # YAML +authors: + - "@sppearce" +maintainers: + - "@sppearce" diff --git a/modules/nf-core/ichorcna/createpon/tests/main.nf.test b/modules/nf-core/ichorcna/createpon/tests/main.nf.test new file mode 100644 index 00000000..41ac2da8 --- /dev/null +++ b/modules/nf-core/ichorcna/createpon/tests/main.nf.test @@ -0,0 +1,96 @@ +nextflow_process { + + name "Test Process ICHORCNA_CREATEPON" + script "../main.nf" + process "ICHORCNA_CREATEPON" + + tag "modules" + tag "modules_nfcore" + tag "ichorcna" + tag "ichorcna/createpon" + + test("hg19 - one file") { + + when { + process { + """ + input[0] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/MBC_315.ctDNA.reads.wig", checkIfExists: true) + input[1] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/gc_hg19_1000kb.wig", checkIfExists: true) + input[2] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/map_hg19_1000kb.wig", checkIfExists: true) + input[3] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/GRCh37.p13_centromere_UCSC-gapTable.txt", checkIfExists: true) + input[4] = [] + input[5] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + file(process.out.rds[0]).name, + file(process.out.txt[0]).name, + process.out.versions + ).match() } + ) + } + + } + + + test("hg19 - two files") { + + when { + process { + """ + input[0] = [ file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/MBC_315.ctDNA.reads.wig", checkIfExists: true), + file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/MBC_315_T2.ctDNA.reads.wig", checkIfExists: true) + ] + input[1] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/gc_hg19_1000kb.wig", checkIfExists: true) + input[2] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/map_hg19_1000kb.wig", checkIfExists: true) + input[3] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/GRCh37.p13_centromere_UCSC-gapTable.txt", checkIfExists: true) + input[4] = [] + input[5] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + file(process.out.rds[0]).name, + file(process.out.txt[0]).name, + process.out.versions + ).match() } + ) + } + } + + test("stub") { + options "-stub" + when { + process { + """ + input[0] = [] + input[1] = [] + input[2] = [] + input[3] = [] + input[4] = [] + input[5] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + file(process.out.rds[0]).name, + file(process.out.txt[0]).name, + process.out.versions + ).match() } + ) + } + } +} diff --git a/modules/nf-core/ichorcna/createpon/tests/main.nf.test.snap b/modules/nf-core/ichorcna/createpon/tests/main.nf.test.snap new file mode 100644 index 00000000..fe73d2ef --- /dev/null +++ b/modules/nf-core/ichorcna/createpon/tests/main.nf.test.snap @@ -0,0 +1,44 @@ +{ + "hg19 - one file": { + "content": [ + "PoN_median.rds", + "PoN_median.txt", + [ + "versions.yml:md5,59dbc83520b9e00a736f49ed2513657a" + ] + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-08-06T14:23:09.884294332" + }, + "stub": { + "content": [ + "PoN.rds", + "PoN.txt", + [ + "versions.yml:md5,59dbc83520b9e00a736f49ed2513657a" + ] + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-08-06T14:25:38.594727235" + }, + "hg19 - two files": { + "content": [ + "PoN_median.rds", + "PoN_median.txt", + [ + "versions.yml:md5,59dbc83520b9e00a736f49ed2513657a" + ] + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-08-06T14:23:29.496424481" + } +} \ No newline at end of file diff --git a/modules/nf-core/ichorcna/run/environment.yml b/modules/nf-core/ichorcna/run/environment.yml new file mode 100644 index 00000000..54df91b4 --- /dev/null +++ b/modules/nf-core/ichorcna/run/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: + - bioconda::r-ichorcna=0.5.1 diff --git a/modules/nf-core/ichorcna/run/main.nf b/modules/nf-core/ichorcna/run/main.nf new file mode 100644 index 00000000..68cf302f --- /dev/null +++ b/modules/nf-core/ichorcna/run/main.nf @@ -0,0 +1,108 @@ +process ICHORCNA_RUN { + tag "$meta.id" + label 'process_low' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/r-ichorcna:0.5.1--r43hdfd78af_0' : + 'biocontainers/r-ichorcna:0.5.1--r43hdfd78af_0' }" + + input: + tuple val(meta), path(wig) + path gc_wig + path map_wig + path normal_wig + path normal_background + path centromere + path rep_time_wig + path exons + + output: + tuple val(meta), path("${prefix}.RData") , emit: rdata + tuple val(meta), path("${prefix}.seg") , emit: seg + tuple val(meta), path("${prefix}.cna.seg") , emit: cna_seg + tuple val(meta), path("${prefix}.seg.txt") , emit: seg_txt + tuple val(meta), path("${prefix}.correctedDepth.txt"), emit: corrected_depth + tuple val(meta), path("${prefix}.params.txt") , emit: ichorcna_params + tuple val(meta), path("${prefix}/*.pdf") , emit: plots + tuple val(meta), path("**/${prefix}_genomeWide.pdf") , emit: genome_plot + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" + def norm = normal_wig ? "normal_wig='${normal_wig}'," : 'normal_wig=NULL,' + def pon = normal_background ? "normal_panel='${normal_background}'," : 'normal_panel=NULL,' + def map = map_wig ? "mapWig='${map_wig}'," : 'mapWig=NULL,' + def centro = centromere ? "centromere='${centromere}'," : '' + def rep = rep_time_wig ? "repTimeWig='${rep_time_wig}'," : 'repTimeWig=NULL,' + def exon = exons ? "exons.bed='${exons}'," : '' + """ + #!/usr/bin/env Rscript + library("ichorCNA") + library("yaml") + + run_ichorCNA( + tumor_wig='${wig}', + id='${prefix}', + cores=${task.cpus}, + gcWig='${gc_wig}', + $norm + $pon + $map + $centro + $rep + $exon + $args + outDir="." + ) + + + ### Make Versions YAML for NF-Core ### + versions = list() + versions["r"] <- paste(R.Version()\$major, R.Version()\$minor, sep=".") + versions["ichorCNA"] <- paste(packageVersion("ichorCNA"), sep=".") + + yaml_str <- as.yaml( + list( + "${task.process}" = versions + ) + ) + writeLines(yaml_str, file("versions.yml")) + """ + + stub: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" + + """ + #!/usr/bin/env Rscript + library("ichorCNA") + library("yaml") + + file.create('${prefix}.RData') + file.create('${prefix}.seg') + file.create('${prefix}.cna.seg') + file.create('${prefix}.seg.txt') + file.create('${prefix}.correctedDepth.txt') + file.create('${prefix}.params.txt') + dir.create('${prefix}') + file.create('${prefix}/${prefix}_genomeWide.pdf') + + ### Make Versions YAML for NF-Core ### + versions = list() + versions["r"] <- paste(R.Version()\$major, R.Version()\$minor, sep=".") + versions["ichorCNA"] <- paste(packageVersion("ichorCNA"), sep=".") + + yaml_str <- as.yaml( + list( + "${task.process}" = versions + ) + ) + writeLines(yaml_str, file("versions.yml")) + """ + +} diff --git a/modules/nf-core/ichorcna/run/meta.yml b/modules/nf-core/ichorcna/run/meta.yml new file mode 100644 index 00000000..bddbaabd --- /dev/null +++ b/modules/nf-core/ichorcna/run/meta.yml @@ -0,0 +1,176 @@ +name: ichorcna_run +description: ichorCNA is an R package for calculating copy number alteration from + (low-pass) whole genome sequencing, particularly for use in cell-free DNA +keywords: + - ichorcna + - cnv + - cna + - cfDNA + - wgs +tools: + - ichorcna: + description: Estimating tumor fraction in cell-free DNA from ultra-low-pass whole + genome sequencing. + homepage: https://github.com/broadinstitute/ichorCNA + documentation: https://github.com/broadinstitute/ichorCNA/wiki + tool_dev_url: https://github.com/broadinstitute/ichorCNA + doi: "10.1038/s41467-017-00965-y" + licence: ["GPL v3"] + identifier: "" +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - wig: + type: file + description: hmmcopy/readCounter processed .wig file giving the number of reads + in the sample, in each genomic window + pattern: "*.{wig}" + ontologies: [] + - gc_wig: + type: file + description: hmmcopy/gcCounter processed .wig file giving the gc content in the + reference fasta, in each genomic window + pattern: "*.{wig}" + ontologies: [] + - map_wig: + type: file + description: hmmcopy/mapCounter processed .wig file giving the mapability in the + reference fasta, in each genomic window + pattern: "*.{wig}" + ontologies: [] + - normal_wig: + type: file + description: hmmcopy/readCounter processed .wig file giving the number of reads + in the normal sample, in each genomic window + pattern: "*.{wig}" + ontologies: [] + - normal_background: + type: file + description: Panel of normals data, generated by calling ichorCNA on a set of + normal samples with the same window size etc. + pattern: "*.{rds}" + ontologies: [] + - centromere: + type: file + description: Text file giving centromere locations of each genome, to exclude + these windows + pattern: "*.{txt}" + ontologies: [] + - rep_time_wig: + type: file + description: Replication/timing .wig file. + pattern: "*.{wig}" + ontologies: [] + - exons: + type: file + description: BED file for exon regions to annotate CNA regions. + pattern: "*.{bed}" + ontologies: [] +output: + rdata: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - ${prefix}.RData: + type: file + description: RData file containing all the intermediate R objects + pattern: "*.{cng.seg}" + ontologies: [] + seg: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - ${prefix}.seg: + type: file + description: Predicted copy number variation per segment + pattern: "*.{seg}" + ontologies: [] + cna_seg: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - ${prefix}.cna.seg: + type: file + description: Predicted copy number variation per segment + pattern: "*.{cng.seg}" + ontologies: [] + seg_txt: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - ${prefix}.seg.txt: + type: file + description: Predicted copy number variation per segment + pattern: "*.{seg.txt}" + ontologies: [] + corrected_depth: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - ${prefix}.correctedDepth.txt: + type: file + description: A text file with corrected depth per bin + pattern: "*.{params.txt}" + ontologies: [] + ichorcna_params: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - ${prefix}.params.txt: + type: file + description: A text file showing the values that ichorCNA has estimated for + tumour fraction, ploidy etc + pattern: "*.{params.txt}" + ontologies: [] + plots: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - ${prefix}/*.pdf: + type: file + description: Plots with e.g. individual chromosomes and different considered + ploidy + pattern: "*.{pdf}" + ontologies: [] + genome_plot: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test'] + - "**/${prefix}_genomeWide.pdf": + type: file + description: A plot with the best-fit genome-wide CNV data + pattern: "*.{genomeWide.pdf}" + ontologies: [] + versions: + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" + ontologies: + - edam: http://edamontology.org/format_3750 # YAML +authors: + - "@sppearce" + - "@adamrtalbot" + +maintainers: + - "@sppearce" + - "@adamrtalbot" diff --git a/modules/nf-core/ichorcna/run/tests/main.nf.test b/modules/nf-core/ichorcna/run/tests/main.nf.test new file mode 100644 index 00000000..0c67ef1b --- /dev/null +++ b/modules/nf-core/ichorcna/run/tests/main.nf.test @@ -0,0 +1,78 @@ +nextflow_process { + + name "Test Process ICHORCNA_RUN" + script "../main.nf" + process "ICHORCNA_RUN" + + tag "modules" + tag "modules_nfcore" + tag "ichorcna" + tag "ichorcna/run" + + test("no_panel") { + config "./nextflow.config" + when { + process { + """ + input[0] = [ [ id:'test'], // meta map + file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/MBC_315.ctDNA.reads.wig", checkIfExists: true) + ] + input[1] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/gc_hg19_1000kb.wig", checkIfExists: true) + input[2] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/map_hg19_1000kb.wig", checkIfExists: true) + input[3] = [] + input[4] = [] + input[5] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/GRCh37.p13_centromere_UCSC-gapTable.txt", checkIfExists: true) + input[6] = [] + input[7] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.versions, + file(process.out.genome_plot.get(0).get(1)).name, + file(process.out.rdata.get(0).get(1)).name, + process.out.plots.get(0).get(1).size(), + path(process.out.cna_seg.get(0).get(1)).readLines()[0], + path(process.out.seg.get(0).get(1)).readLines()[0], + path(process.out.seg_txt.get(0).get(1)).readLines()[0], + path(process.out.corrected_depth.get(0).get(1)).readLines()[0], + path(process.out.ichorcna_params.get(0).get(1)).readLines()[0] + ).match() } + ) + } + + } + + test("no_panel - stub") { + options "-stub" + when { + process { + """ + input[0] = [ [ id:'test'], // meta map + file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/MBC_315.ctDNA.reads.wig", checkIfExists: true) + ] + input[1] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/gc_hg19_1000kb.wig", checkIfExists: true) + input[2] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/map_hg19_1000kb.wig", checkIfExists: true) + input[3] = [] + input[4] = [] + input[5] = file("https://raw.githubusercontent.com/gavinhalab/ichorCNA/master/inst/extdata/GRCh37.p13_centromere_UCSC-gapTable.txt", checkIfExists: true) + input[6] = [] + input[7] = [] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + + } + +} diff --git a/modules/nf-core/ichorcna/run/tests/main.nf.test.snap b/modules/nf-core/ichorcna/run/tests/main.nf.test.snap new file mode 100644 index 00000000..7a70f7bd --- /dev/null +++ b/modules/nf-core/ichorcna/run/tests/main.nf.test.snap @@ -0,0 +1,167 @@ +{ + "no_panel - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.RData:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + { + "id": "test" + }, + "test.seg:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "2": [ + [ + { + "id": "test" + }, + "test.cna.seg:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "3": [ + [ + { + "id": "test" + }, + "test.seg.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "4": [ + [ + { + "id": "test" + }, + "test.correctedDepth.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "5": [ + [ + { + "id": "test" + }, + "test.params.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "6": [ + [ + { + "id": "test" + }, + "test_genomeWide.pdf:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "7": [ + [ + { + "id": "test" + }, + "test_genomeWide.pdf:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "8": [ + "versions.yml:md5,d2d30cedd49c7bffdcb74313e8a074c4" + ], + "cna_seg": [ + [ + { + "id": "test" + }, + "test.cna.seg:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "corrected_depth": [ + [ + { + "id": "test" + }, + "test.correctedDepth.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "genome_plot": [ + [ + { + "id": "test" + }, + "test_genomeWide.pdf:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "ichorcna_params": [ + [ + { + "id": "test" + }, + "test.params.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "plots": [ + [ + { + "id": "test" + }, + "test_genomeWide.pdf:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "rdata": [ + [ + { + "id": "test" + }, + "test.RData:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "seg": [ + [ + { + "id": "test" + }, + "test.seg:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "seg_txt": [ + [ + { + "id": "test" + }, + "test.seg.txt:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions": [ + "versions.yml:md5,d2d30cedd49c7bffdcb74313e8a074c4" + ] + } + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-08-02T06:38:13.140234725" + }, + "no_panel": { + "content": [ + [ + "versions.yml:md5,d2d30cedd49c7bffdcb74313e8a074c4" + ], + "test_genomeWide.pdf", + "test.RData", + 52, + "chr\tstart\tend\ttest.copy.number\ttest.event\ttest.logR\ttest.subclone.status\ttest.Corrected_Copy_Number\ttest.Corrected_Call\ttest.logR_Copy_Number", + "sample\tchr\tstart\tend\tevent\tcopy.number\tbins\tmedian", + "ID\tchrom\tstart\tend\tnum.mark\tseg.median.logR\tcopy.number\tcall\tsubclone.status\tlogR_Copy_Number\tCorrected_Copy_Number\tCorrected_Call", + "chr\tstart\tend\tlog2_TNratio_corrected", + "Sample\tTumor Fraction\tPloidy" + ], + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-08-03T06:43:21.625713563" + } +} \ No newline at end of file diff --git a/modules/nf-core/ichorcna/run/tests/nextflow.config b/modules/nf-core/ichorcna/run/tests/nextflow.config new file mode 100644 index 00000000..1bd94c5b --- /dev/null +++ b/modules/nf-core/ichorcna/run/tests/nextflow.config @@ -0,0 +1,8 @@ +process { + // We need this parameter otherwise it bugs out + withName: 'ICHORCNA_RUN' { + ext.args = """ + scStates='c(1,3)', + """ + } +} \ No newline at end of file From dbb44da393e11605c1aeac79e0f2949f3c3fba9d Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 18 Nov 2025 14:43:16 +0100 Subject: [PATCH 02/27] Port the option handling to the newer ichorCNA modules --- conf/modules.config | 48 +++++++++++++++++++++++++++++ subworkflows/local/ichorcna/main.nf | 37 ++++++++++++---------- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 2499630e..b922fd5f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -262,6 +262,23 @@ process { } } + withName: ICHORCNA_CREATEPON { + publishDir = [ + path: { "${params.outdir}/ichorcna/PoN/" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + ] + ext.args = [ + "genomeStyle='${params.ichorcna_genome_style}'", + "chrs=\"${params.ichorcna_chrs_to_use}\"", + "minMapScore=${params.ichorcna_min_map_score}", + "fracReadsInChrYForMale=${params.ichorcna_fraction_reads_male}", + "maleChrXLogRThres=${params.ichorcna_male_chrX_logR}" + + ].join(",\n") + + } + withName: ICHORCNA_GENERATE_PON { publishDir = [ path: { "${params.outdir}/ichorcna/PoN/" }, @@ -277,6 +294,37 @@ process { ext.prefix = { params.pon_name ? "${params.pon_name}" : "PoN" } } + withName: ICHORCNA_RUN { + publishDir = [ + path: { "${params.outdir}/ichorcna" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, + ] + ext.args = [ + "maxCN=${params.ichorcna_max_cn}", + "chrTrain=\"${params.ichorcna_chrs_to_train}\"", + "chrs=\"${params.ichorcna_chrs_to_use}\"", + "chrNormalize=\"${params.ichorcna_chrs_to_normalize}\"", + "txnE=${params.ichorcna_txne}", + "txnStrength=${params.ichorcna_trx_strength}", + "minMapScore=${params.ichorcna_min_map_score}", + "fracReadsInChrYForMale=${params.ichorcna_fraction_reads_male}", + "minSegmentBins=${params.ichorcna_min_segment_bins}", + "maxFracCNASubclone=${params.ichorcna_max_frac_genome_subclone}", + params.ichorcna_include_homd ? "includeHOMD=TRUE" : "includeHOMD=FALSE", + "altFracThreshold=${params.ichorcna_alt_frac_threshold}", + "genomeStyle='${params.ichorcna_genome_style}'", + "plotFileType='${params.ichorcna_plotfiletype}'", + "plotYLim='${params.ichorcna_plotylim}'", + "normal='c(${params.ichorcna_normal_states})'", + "genomeBuild='${params.genome}'", + params.ichorcna_estimate_ploidy ? "ploidy='c(2, 3, 4, 5)'" : "ploidy='2'", + params.ichorcna_estimate_ploidy ? "estimatePloidy=TRUE" : "estimatePloidy=FALSE", + params.ichorcna_estimate_sc ? "estimateScPrevalence=TRUE" : "estimateScPrevalence=FALSE", + params.ichorcna_estimate_sc ? "scStates='c(1,3)'" : "scStates='c()'" + ].join(",\n") + } + withName: RUN_ICHORCNA { publishDir = [ path: { "${params.outdir}/ichorcna" }, diff --git a/subworkflows/local/ichorcna/main.nf b/subworkflows/local/ichorcna/main.nf index d5052b90..a9e1f853 100644 --- a/subworkflows/local/ichorcna/main.nf +++ b/subworkflows/local/ichorcna/main.nf @@ -1,3 +1,4 @@ +include { ICHORCNA_RUN } from '../../../modules/nf-core/ichorcna/run/main' include { RUN_ICHORCNA } from '../../../modules/local/ichorcna/run/main' include { AGGREGATE_ICHORCNA_TABLE } from '../../../modules/local/aggregate_ichorcna_table/main' include { HMMCOPY_READCOUNTER as HMMCOPY_READCOUNTER_ICHORCNA } from '../../../modules/nf-core/hmmcopy/readcounter/main' @@ -7,13 +8,13 @@ include { PLOT_ICHORCNA } from '../../../m workflow ICHORCNA { take: - ch_bam_bai // [meta, bam, bai] + ch_bam_bai // [meta, bam, bai] ch_normal_panel // Channel: path (optional) - ch_gc_wig // Channel: path - ch_map_wig // Channel: path - ch_centromere // Channel: path - ch_reptime_wig // Channel: path (optional) - ch_fasta // Channel: [meta, fasta] + ch_gc_wig // Channel: path + ch_map_wig // Channel: path + ch_centromere // Channel: path + ch_reptime_wig // Channel: path (optional) + ch_fasta // Channel: [meta, fasta] main: @@ -24,29 +25,33 @@ workflow ICHORCNA { HMMCOPY_READCOUNTER_ICHORCNA(ch_bam_bai, ch_fasta) ch_versions = ch_versions.mix(HMMCOPY_READCOUNTER_ICHORCNA.out.versions) // Step 2: run ichorCNA - RUN_ICHORCNA( + + ICHORCNA_RUN( HMMCOPY_READCOUNTER_ICHORCNA.out.wig, ch_gc_wig, ch_map_wig, + [], ch_normal_panel, ch_centromere, ch_reptime_wig, + [], ) - ch_versions = ch_versions.mix(RUN_ICHORCNA.out.versions) - called_segments = RUN_ICHORCNA.out.cna_seg - bins = RUN_ICHORCNA.out.bins - genome_plot = RUN_ICHORCNA.out.genome_plot + ch_versions = ch_versions.mix(ICHORCNA_RUN.out.versions) + + called_segments = ICHORCNA_RUN.out.seg_txt + bins = ICHORCNA_RUN.out.cna_seg + genome_plot = ICHORCNA_RUN.out.genome_plot // Step 3: produce an aggregate table of the results AGGREGATE_ICHORCNA_TABLE( - RUN_ICHORCNA.out.ichorcna_params.collect() + ICHORCNA_RUN.out.ichorcna_params.collect { _meta, params -> params } ) ch_versions = ch_versions.mix(AGGREGATE_ICHORCNA_TABLE.out.versions) ch_reports = ch_reports.mix(AGGREGATE_ICHORCNA_TABLE.out.ichorcna_summary) - RUN_ICHORCNA.out.cna_seg + ICHORCNA_RUN.out.cna_seg .map { _meta, data -> data } .collectFile( storeDir: "${params.outdir}/ichorcna/", @@ -56,17 +61,15 @@ workflow ICHORCNA { ) .set { gistic_file } - ch_versions = ch_versions.mix(RUN_ICHORCNA.out.versions) - CORRECT_LOGR_ICHORCNA(gistic_file, AGGREGATE_ICHORCNA_TABLE.out.ichorcna_summary) ch_versions = ch_versions.mix(CORRECT_LOGR_ICHORCNA.out.versions) corrected_gistic_file = CORRECT_LOGR_ICHORCNA.out.gistic_file - PLOT_ICHORCNA(RUN_ICHORCNA.out.cna_seg, RUN_ICHORCNA.out.bins, RUN_ICHORCNA.out.ichorcna_params) + PLOT_ICHORCNA(RUN_ICHORCNA.out.cna_seg, RUN_ICHORCNA.out.bins, RUN_ICHORCNA.out.ichorcna_params.map { _meta, param -> param }) ch_versions = ch_versions.mix(PLOT_ICHORCNA.out.versions) // Step 4: Aggregate bin-level plots into a single file - CONCATENATE_BIN_PLOTS(RUN_ICHORCNA.out.genome_plot.collect()) + CONCATENATE_BIN_PLOTS(RUN_ICHORCNA.out.genome_plot.collect { _meta, plot -> plot }) ch_versions = ch_versions.mix(CONCATENATE_BIN_PLOTS.out.versions) emit: From 088a8a2a151fef2a5f162fe3187a9230501c211f Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 18 Nov 2025 15:03:06 +0100 Subject: [PATCH 03/27] Finish porting to the newer ichorCNA modules --- subworkflows/local/build_pon/main.nf | 26 +++++++++++++++--------- subworkflows/local/ichorcna/main.nf | 9 +++++--- subworkflows/local/liquid_biopsy/main.nf | 14 +++++++++++-- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/subworkflows/local/build_pon/main.nf b/subworkflows/local/build_pon/main.nf index 83d9fb70..45616f10 100644 --- a/subworkflows/local/build_pon/main.nf +++ b/subworkflows/local/build_pon/main.nf @@ -1,5 +1,5 @@ include { HMMCOPY_READCOUNTER as HMMCOPY_READCOUNTER_PON } from "../../../modules/nf-core//hmmcopy/readcounter/main" -include { ICHORCNA_GENERATE_PON } from "../../../modules/local/ichorcna/create_pon/main" +include { ICHORCNA_CREATEPON } from '../../../modules/nf-core/ichorcna/createpon/main' include { SAMBAMBA_FILTER } from "../../../modules/local//sambamba/filterfragment/main" include { WISECONDORX_CONVERT as NORMAL_CONVERT } from '../../../modules/nf-core/wisecondorx/convert/main' include { WISECONDORX_NEWREF } from '../../../modules/nf-core/wisecondorx/newref/main' @@ -14,6 +14,7 @@ workflow BUILD_PON { map_wig reptime centromere + filter_bam_pon main: @@ -21,7 +22,9 @@ workflow BUILD_PON { ch_bam_files = channel.fromFilePairs( "${normal_dir}/*.bam{,.bai}", checkIfExists: true - ).ifEmpty { error("No BAM or BAI files found at ${normal_dir}") }.map { meta, file -> + ) + .ifEmpty { error("No BAM or BAI files found at ${normal_dir}") } + .map { meta, file -> def fmeta = [:] fmeta.id = meta tuple(fmeta, file[0], file[1]) @@ -29,7 +32,7 @@ workflow BUILD_PON { if (caller == "ichorcna") { // FIXME: We shouldn't depend on parameters here - if (params.filter_bam_pon) { + if (filter_bam_pon) { SAMBAMBA_FILTER(ch_bam_files) ch_bam_for_pon = SAMBAMBA_FILTER.out.filtered_bam ch_versions = ch_versions.mix(SAMBAMBA_FILTER.out.versions) @@ -48,24 +51,27 @@ workflow BUILD_PON { ch_versions = ch_versions.mix(HMMCOPY_READCOUNTER_PON.out.versions) - ICHORCNA_GENERATE_PON( + ICHORCNA_CREATEPON( wigfiles, gc_wig, map_wig, centromere, reptime, + [], /* exons */ ) - normal_panel = ICHORCNA_GENERATE_PON.out.pon_file - ch_versions = ch_versions.mix(ICHORCNA_GENERATE_PON.out.versions) + normal_panel = ICHORCNA_CREATEPON.out.rds + ch_versions = ch_versions.mix(ICHORCNA_CREATEPON.out.versions) } else if (caller == "wisecondorx") { NORMAL_CONVERT(ch_bam_files, fasta, fai) ch_versions = ch_versions.mix(NORMAL_CONVERT.out.versions) - WISECONDORX_NEWREF(NORMAL_CONVERT.out.npz.map {meta, npz -> - def new_meta = meta + [id: "joined"] - [new_meta, npz] - }.groupTuple()) + WISECONDORX_NEWREF( + NORMAL_CONVERT.out.npz.map { meta, npz -> + def new_meta = meta + [id: "joined"] + [new_meta, npz] + }.groupTuple() + ) normal_panel = WISECONDORX_NEWREF.out.npz ch_versions = ch_versions.mix(WISECONDORX_NEWREF.out.versions) } diff --git a/subworkflows/local/ichorcna/main.nf b/subworkflows/local/ichorcna/main.nf index a9e1f853..1d9d1a76 100644 --- a/subworkflows/local/ichorcna/main.nf +++ b/subworkflows/local/ichorcna/main.nf @@ -1,5 +1,4 @@ include { ICHORCNA_RUN } from '../../../modules/nf-core/ichorcna/run/main' -include { RUN_ICHORCNA } from '../../../modules/local/ichorcna/run/main' include { AGGREGATE_ICHORCNA_TABLE } from '../../../modules/local/aggregate_ichorcna_table/main' include { HMMCOPY_READCOUNTER as HMMCOPY_READCOUNTER_ICHORCNA } from '../../../modules/nf-core/hmmcopy/readcounter/main' include { CORRECT_LOGR_ICHORCNA } from '../../../modules/local/correct_logR_ichorcna/main' @@ -65,11 +64,15 @@ workflow ICHORCNA { ch_versions = ch_versions.mix(CORRECT_LOGR_ICHORCNA.out.versions) corrected_gistic_file = CORRECT_LOGR_ICHORCNA.out.gistic_file - PLOT_ICHORCNA(RUN_ICHORCNA.out.cna_seg, RUN_ICHORCNA.out.bins, RUN_ICHORCNA.out.ichorcna_params.map { _meta, param -> param }) + PLOT_ICHORCNA( + ICHORCNA_RUN.out.seg_txt, + ICHORCNA_RUN.out.cna_seg, + ICHORCNA_RUN.out.ichorcna_params.map { _meta, param -> param }, + ) ch_versions = ch_versions.mix(PLOT_ICHORCNA.out.versions) // Step 4: Aggregate bin-level plots into a single file - CONCATENATE_BIN_PLOTS(RUN_ICHORCNA.out.genome_plot.collect { _meta, plot -> plot }) + CONCATENATE_BIN_PLOTS(ICHORCNA_RUN.out.genome_plot.collect { _meta, plot -> plot }) ch_versions = ch_versions.mix(CONCATENATE_BIN_PLOTS.out.versions) emit: diff --git a/subworkflows/local/liquid_biopsy/main.nf b/subworkflows/local/liquid_biopsy/main.nf index 225dfa54..846ff9f1 100644 --- a/subworkflows/local/liquid_biopsy/main.nf +++ b/subworkflows/local/liquid_biopsy/main.nf @@ -30,7 +30,17 @@ workflow LIQUID_BIOPSY { // If we want to build the normal panel if (build_pon) { - BUILD_PON(pon_path, caller, ch_fasta, ch_fai, ch_gc_wig, ch_map_wig, ch_reptiming, ch_centromere) + BUILD_PON( + pon_path, + caller, + ch_fasta, + ch_fai, + ch_gc_wig, + ch_map_wig, + ch_reptiming, + ch_centromere, + params.filter_bam_pon, + ) ch_versions = ch_versions.mix(BUILD_PON.out.versions) pon_file = BUILD_PON.out.normal_panel.collect() } @@ -60,7 +70,7 @@ workflow LIQUID_BIOPSY { corrected_gistic_file = ICHORCNA.out.gistic_file } else if (caller == "wisecondorx") { - + BAM_CNV_WISECONDORX(ch_bam_bai, ch_fasta, ch_fai, pon_file, ch_blacklist) ch_versions = ch_versions.mix(BAM_CNV_WISECONDORX.out.versions) From f3e0c4350d08fa2505c649f3f4b56db978690da2 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 18 Nov 2025 15:09:27 +0100 Subject: [PATCH 04/27] Remove local ichorCNA modules --- modules/local/ichorcna/create_pon/main.nf | 54 -------------------- modules/local/ichorcna/run/main.nf | 62 ----------------------- 2 files changed, 116 deletions(-) delete mode 100644 modules/local/ichorcna/create_pon/main.nf delete mode 100644 modules/local/ichorcna/run/main.nf diff --git a/modules/local/ichorcna/create_pon/main.nf b/modules/local/ichorcna/create_pon/main.nf deleted file mode 100644 index 21575235..00000000 --- a/modules/local/ichorcna/create_pon/main.nf +++ /dev/null @@ -1,54 +0,0 @@ -process ICHORCNA_GENERATE_PON { - -label "process_low" -tag "Generate panel of normals" - -container "quay.io/dincalcilab/ichorcna:0.4.0-2ab0be2" - -input: - path wigfile_list - path gc_wig - path map_wig - path centromere - path reptime_file - -output: - path "*.rds" , emit: pon_file - path "*.txt" , emit: txt_file - path "versions.yml", emit: versions - -when: - task.ext.when == null || task.ext.when - -script: - -def args = task.ext.args ?: '' -def prefix = task.ext.prefix ?: "PoN" -def centro = params.ichorcna_centromere_file ? "--centromere ${params.ichorcna_centromere_file}" : '' -def reptime = params.ichorcna_reptime_wig ? "--repTimeWig ${params.ichorcna_reptime_wig}": '' -def ichorcna_script = "createPanelOfNormals.R" - -def VERSION = '0.3.2' // Version information not provided by tool on CLI - -""" - -echo "${wigfile_list.join("\n")}" > PoN_wigfiles.txt - -${ichorcna_script}\\ - --filelist PoN_wigfiles.txt \\ - --gcWig ${gc_wig} \\ - --mapWig ${map_wig} \\ - ${centro} \\ - ${args} \\ - ${reptime} \\ - --outfile ${prefix} - -rm PoN_wigfiles.txt - -cat <<-END_VERSIONS > versions.yml -"${task.process}": - ichorcna: $VERSION -END_VERSIONS -""" - -} diff --git a/modules/local/ichorcna/run/main.nf b/modules/local/ichorcna/run/main.nf deleted file mode 100644 index d46609c0..00000000 --- a/modules/local/ichorcna/run/main.nf +++ /dev/null @@ -1,62 +0,0 @@ -process RUN_ICHORCNA { - - tag "$meta.id" - label 'process_low' - - // WARN: Version information not provided by tool on CLI. Please update version string below when bumping container versions. - container "quay.io/dincalcilab/ichorcna:0.4.0-2ab0be2" - - input: - tuple val(meta), path(wigfile) - path gc_wig - path map_wig - path pon_file - path centromere - path reptime_file - - output: - tuple val(meta), path("*.cna.seg") , emit: bins - tuple val(meta), path("*.seg.txt") , emit: cna_seg - tuple val(meta), path("*.seg") - tuple val(meta), path("*.correctedDepth.txt"), emit: corrected_depth - path("*.params.txt") , emit: ichorcna_params - path "**/*genomeWide.pdf" , emit: genome_plot - path "**/*_genomeWide_all_sols.pdf" - path "*.RData" - path "versions.yml" , emit: versions - - script: - - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - - def pon = pon_file ? "--normalPanel ${pon_file}" : '' - def centro = params.ichorcna_centromere_file ? "--centromere ${params.ichorcna_centromere_file}" : '' - def reptime = params.ichorcna_reptime_wig ? "--repTimeWig ${params.ichorcna_reptime_wig}": '' - def ichorcna_script = "runIchorCNA.R" - def gender = meta.gender ? "--sex ${meta.gender}": '' - - def VERSION = '0.3.2' // WARN: Version information not provided by tool on CLI. - - """ - ${ichorcna_script} \\ - --id ${prefix} \\ - --WIG $wigfile \\ - --gcWig ${gc_wig} \\ - --mapWig ${map_wig} \\ - $args \\ - ${pon} \\ - ${centro} \\ - ${reptime} \\ - ${gender} \\ - --estimateNormal TRUE \\ - --cores "${task.cpus}" \\ - --outDir ./ - - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - ichorcna: $VERSION - END_VERSIONS - """ -} From 92f928f541f8a7a36094203ad270c4752eaa1c00 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 19 Nov 2025 11:06:47 +0100 Subject: [PATCH 05/27] Plumbing to support PoN samples in the samplesheet directly --- assets/schema_input.json | 16 +++++++++++++++- conf/test.config | 2 +- conf/test_ichorcna.config | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/assets/schema_input.json b/assets/schema_input.json index 3865bea8..b5547210 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -63,8 +63,22 @@ } ], "meta": ["gender"] + }, + "status": { + "errorMessage": "Status must be either tumor or normal", + "anyOf": [ + { + "type": "string", + "enum": ["tumor", "normal"] + }, + { + "type": "string", + "maxLength": 0 + } + ], + "meta": ["status"] } }, - "anyOf": [{ "required": ["sample", "fastq_1"] }, { "required": ["sample", "bam"] }] + "anyOf": [{ "required": ["sample", "fastq_1", "status"] }, { "required": ["sample", "bam", "status"] }] } } diff --git a/conf/test.config b/conf/test.config index 6e4d8d6c..d7717e92 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,7 +24,7 @@ params { config_profile_description = 'Minimal test dataset to check pipeline function with ASCAT.sc caller and solid_biopsy subworkflow.' // Input data - input = 'https://github.com/DIncalciLab/samurai-test-data/raw/refs/heads/master/samplesheet.csv' + input = 'https://github.com/DIncalciLab/samurai-test-data/raw/refs/heads/new-samplesheet-format/samplesheet.csv' // Genome references genome = 'hg38' diff --git a/conf/test_ichorcna.config b/conf/test_ichorcna.config index ad8c8477..a078d58e 100644 --- a/conf/test_ichorcna.config +++ b/conf/test_ichorcna.config @@ -24,7 +24,7 @@ params { config_profile_description = 'Minimal test dataset to check pipeline function with ichorCNA caller and liquid_biopsy subworkflow.' // Input data - input = 'https://github.com/DIncalciLab/samurai-test-data/raw/refs/heads/master/samplesheet.csv' + input = 'https://github.com/DIncalciLab/samurai-test-data/raw/refs/heads/new-samplesheet-format/samplesheet.csv' // Genome references genome = 'hg38' From d2b7d9f23bfa11452d5632866e86f7c227ea5180 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 19 Nov 2025 11:18:59 +0100 Subject: [PATCH 06/27] Remove configuration for modules that no longer exist --- conf/modules.config | 46 --------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index b922fd5f..e39a841f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -279,21 +279,6 @@ process { } - withName: ICHORCNA_GENERATE_PON { - publishDir = [ - path: { "${params.outdir}/ichorcna/PoN/" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - ] - ext.args = [ - "--genomeStyle ${params.ichorcna_genome_style}", - "--genomeBuild ${params.genome}", - "--chrs \"${params.ichorcna_chrs_to_use}\"", - "--maleChrXLogRThres \"${params.ichorcna_male_chrX_logR}\"", - ].join(' ') - ext.prefix = { params.pon_name ? "${params.pon_name}" : "PoN" } - } - withName: ICHORCNA_RUN { publishDir = [ path: { "${params.outdir}/ichorcna" }, @@ -325,37 +310,6 @@ process { ].join(",\n") } - withName: RUN_ICHORCNA { - publishDir = [ - path: { "${params.outdir}/ichorcna" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, - ] - ext.args = [ - "--maxCN ${params.ichorcna_max_cn}", - "--chrTrain \"${params.ichorcna_chrs_to_train}\"", - "--chrs \"${params.ichorcna_chrs_to_use}\"", - "--txnE ${params.ichorcna_txne}", - "--txnStrength ${params.ichorcna_trx_strength}", - "--minMapScore ${params.ichorcna_min_map_score}", - "--fracReadsInChrYForMale ${params.ichorcna_fraction_reads_male}", - "--minSegmentBins ${params.ichorcna_min_segment_bins}", - "--maxFracGenomeSubclone ${params.ichorcna_max_frac_genome_subclone}", - "--maxFracCNASubclone ${params.ichorcna_max_frac_cna_subclone}", - "--includeHOMD ${params.ichorcna_include_homd}", - "--altFracThreshold ${params.ichorcna_alt_frac_threshold}", - "--genomeStyle ${params.ichorcna_genome_style}", - "--plotFileType ${params.ichorcna_plotfiletype}", - "--plotYLim '${params.ichorcna_plotylim}'", - "--normal 'c(${params.ichorcna_normal_states})'", - "--genomeBuild ${params.genome}", - params.ichorcna_estimate_ploidy ? "--ploidy 'c(2, 3, 4, 5)'" : "", - params.ichorcna_estimate_ploidy ? "--estimatePloidy TRUE" : "--estimatePloidy FALSE", - params.ichorcna_estimate_sc ? "--estimateScPrevalence TRUE" : "--estimateScPrevalence FALSE", - params.ichorcna_estimate_sc ? "--scStates 'c(1,3)'" : "--scStates 'c()'", - ].join(' ') - } - withName: AGGREGATE_ICHORCNA_TABLE { publishDir = [ path: { "${params.outdir}/ichorcna" }, From 9c2730dd763c5540bafee098e62c1d150844d276 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Thu, 20 Nov 2025 11:54:26 +0100 Subject: [PATCH 07/27] Act defensively when supplying ichorCNA parameters **BREAKING CHANGE**: this changes the format parameters for ichorCNA --- conf/modules.config | 27 ++++++++++++--------------- nextflow.config | 6 +++--- nextflow_schema.json | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index e39a841f..b0cb7617 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -268,15 +268,13 @@ process { mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, ] - ext.args = [ + ext.args = [ "genomeStyle='${params.ichorcna_genome_style}'", "chrs=\"${params.ichorcna_chrs_to_use}\"", "minMapScore=${params.ichorcna_min_map_score}", "fracReadsInChrYForMale=${params.ichorcna_fraction_reads_male}", - "maleChrXLogRThres=${params.ichorcna_male_chrX_logR}" - + "maleChrXLogRThres=${params.ichorcna_male_chrX_logR}", ].join(",\n") - } withName: ICHORCNA_RUN { @@ -285,11 +283,11 @@ process { mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, ] - ext.args = [ + ext.args = [ "maxCN=${params.ichorcna_max_cn}", - "chrTrain=\"${params.ichorcna_chrs_to_train}\"", - "chrs=\"${params.ichorcna_chrs_to_use}\"", - "chrNormalize=\"${params.ichorcna_chrs_to_normalize}\"", + params.ichorcna_genome_style == "UCSC" ? "chrTrain=\"paste0('chr', c(${params.ichorcna_chrs_to_train}))\"" : "chrTrain='c(${params.ichorcna_chrs_to_train})'", + params.ichorcna_genome_style == "UCSC" ? "chrs=\"paste0('chr', c(${params.ichorcna_chrs_to_use}))\"" : "chrs='c(${params.ichorcna_chrs_to_use})'", + params.ichorcna_genome_style == "UCSC" ? "chrNormalize=\"paste0('chr', c(${params.ichorcna_chrs_to_normalize}))\"" : "chrNormalize='c(${params.ichorcna_chrs_to_normalize})'", "txnE=${params.ichorcna_txne}", "txnStrength=${params.ichorcna_trx_strength}", "minMapScore=${params.ichorcna_min_map_score}", @@ -300,13 +298,13 @@ process { "altFracThreshold=${params.ichorcna_alt_frac_threshold}", "genomeStyle='${params.ichorcna_genome_style}'", "plotFileType='${params.ichorcna_plotfiletype}'", - "plotYLim='${params.ichorcna_plotylim}'", + "plotYLim='c(${params.ichorcna_plotylim})'", "normal='c(${params.ichorcna_normal_states})'", "genomeBuild='${params.genome}'", params.ichorcna_estimate_ploidy ? "ploidy='c(2, 3, 4, 5)'" : "ploidy='2'", params.ichorcna_estimate_ploidy ? "estimatePloidy=TRUE" : "estimatePloidy=FALSE", params.ichorcna_estimate_sc ? "estimateScPrevalence=TRUE" : "estimateScPrevalence=FALSE", - params.ichorcna_estimate_sc ? "scStates='c(1,3)'" : "scStates='c()'" + params.ichorcna_estimate_sc ? "scStates='c(1,3)'" : "scStates='c()'", ].join(",\n") } @@ -336,7 +334,7 @@ process { } withName: CONCATENATE_QDNASEQ_PLOTS { - publishDir = [ + publishDir = [ path: { "${params.outdir}/cn_plots/qdnaseq/" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, @@ -434,7 +432,7 @@ process { } withName: CONCATENATE_BIN_PLOTS { - publishDir = [ + publishDir = [ path: { "${params.outdir}/cn_plots/ichorcna" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, @@ -442,7 +440,7 @@ process { } withName: CONCATENATE_ASCATSC_PLOTS { - publishDir = [ + publishDir = [ path: { "${params.outdir}/cn_plots/ascat_sc" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, @@ -450,7 +448,7 @@ process { } withName: CONCATENATE_ASCATSC_REFITTED_PLOTS { - publishDir = [ + publishDir = [ path: { "${params.outdir}/cn_plots/ascat_sc_refitted" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename }, @@ -536,5 +534,4 @@ process { ].join(' ') ext.when = params.run_gistic } - } diff --git a/nextflow.config b/nextflow.config index 6867f753..7b9f2679 100644 --- a/nextflow.config +++ b/nextflow.config @@ -72,9 +72,9 @@ params { ichorcna_genome_style = 'UCSC' ichorcna_readcounter_chrs = "chr1,chr2,chr3,chr4,chr5,chr6,chr7,chr8,chr9,chr10,chr11,chr12,chr13,chr14,chr15,chr16,chr17,chr18,chr19,chr20,chr21,chr22" ichorcna_readcounter_quality = 20 - ichorcna_chrs_to_use = "paste0('chr', c(1:22))" - ichorcna_chrs_to_train = "paste0('chr', c(1:22))" - ichorcna_chrs_to_normalize = "paste0('chr', c(1:22))" + ichorcna_chrs_to_use = "1:22" + ichorcna_chrs_to_train = "1:22" + ichorcna_chrs_to_normalize = "1:22" ichorcna_estimate_normal = "TRUE" ichorcna_fraction_reads_male = 0.001 ichorcna_male_chrX_logR = 0.3 diff --git a/nextflow_schema.json b/nextflow_schema.json index eae7ef5e..a672ca8c 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -414,18 +414,24 @@ }, "ichorcna_chrs_to_use": { "type": "string", - "default": "paste0(\\'chr\\', c(1:22))", - "description": "Chromosomes to use for ichorCNA" + "default": "1:22", + "description": "Chromosomes to use for ichorCNA", + "pattern": "^\\s*(?:\\d+(?:\\s*:\\s*\\d+)?|\\\"[A-Za-z][A-Za-z0-9_]*\\\"|'[A-Za-z][A-Za-z0-9_]*')(?:\\s*,\\s*(?:\\d+(?:\\s*:\\s*\\d+)?|\\\"[A-Za-z][A-Za-z0-9_]*\\\"|'[A-Za-z][A-Za-z0-9_]*'))*\\s*$", + "examples": ["1:22", "1,2,3,4", "1:19, \\\"X\\\"", "3,5:9,'Y'"] }, "ichorcna_chrs_to_train": { "type": "string", - "default": "paste0(\\'chr\\', c(1:22))", - "description": "Chromosomes to use for training during an ichorCNA run" + "default": "1:22", + "description": "Chromosomes to use for training during an ichorCNA run", + "pattern": "^\\s*(?:\\d+(?:\\s*:\\s*\\d+)?|\\\"[A-Za-z][A-Za-z0-9_]*\\\"|'[A-Za-z][A-Za-z0-9_]*')(?:\\s*,\\s*(?:\\d+(?:\\s*:\\s*\\d+)?|\\\"[A-Za-z][A-Za-z0-9_]*\\\"|'[A-Za-z][A-Za-z0-9_]*'))*\\s*$", + "examples": ["1:22", "1,2,3,4", "1:19, \\\"X\\\"", "3,5:9,'Y'"] }, "ichorcna_chrs_to_normalize": { "type": "string", - "default": "paste0(\\'chr\\', c(1:22))", - "description": "Chromosomes to use for normalization during an ichorCNA run" + "default": "1:22", + "description": "Chromosomes to use for normalization during an ichorCNA run", + "pattern": "^\\s*(?:\\d+(?:\\s*:\\s*\\d+)?|\\\"[A-Za-z][A-Za-z0-9_]*\\\"|'[A-Za-z][A-Za-z0-9_]*')(?:\\s*,\\s*(?:\\d+(?:\\s*:\\s*\\d+)?|\\\"[A-Za-z][A-Za-z0-9_]*\\\"|'[A-Za-z][A-Za-z0-9_]*'))*\\s*$", + "examples": ["1:22", "1,2,3,4", "1:19, \\\"X\\\"", "3,5:9,'Y'"] }, "ichorcna_estimate_normal": { "type": "string", From 33c92879aee93aab1e19b9bf5d1c270c9d48d2e2 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Thu, 20 Nov 2025 14:17:09 +0100 Subject: [PATCH 08/27] Convert "string booleans" to proper bools --- nextflow_schema.json | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index a672ca8c..9730fb67 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -71,7 +71,6 @@ "exists": true, "mimetype": "text/plain", "description": "Path to FASTA index file.", - "help_text": "", "fa_icon": "far fa-file-code" }, "dict": { @@ -81,7 +80,6 @@ "exists": true, "mimetype": "text/plain", "description": "Path to FASTA sequence dictionary file.", - "help_text": "", "fa_icon": "far fa-file-code" }, "igenomes_base": { @@ -434,8 +432,8 @@ "examples": ["1:22", "1,2,3,4", "1:19, \\\"X\\\"", "3,5:9,'Y'"] }, "ichorcna_estimate_normal": { - "type": "string", - "default": "TRUE", + "type": "boolean", + "default": true, "description": "Whether ichorCNA should estimate normal contamination or not" }, "ichorcna_fraction_reads_male": { @@ -474,8 +472,7 @@ "description": "Maximum copy number to be considered by ichorCNA" }, "ichorcna_include_homd": { - "type": "string", - "default": "FALSE", + "type": "boolean", "description": "Call also homozygous deletions in ichorCNA" }, "ichorcna_txne": { From a8080e4ebdf9d45e24e37cf2c791e469ab98beff Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Thu, 20 Nov 2025 14:39:45 +0100 Subject: [PATCH 09/27] Port the remnants of the configuration --- conf/modules.config | 1 + nextflow.config | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index b0cb7617..8a91346a 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -301,6 +301,7 @@ process { "plotYLim='c(${params.ichorcna_plotylim})'", "normal='c(${params.ichorcna_normal_states})'", "genomeBuild='${params.genome}'", + params.ichorcna_estimate_normal ? "estimateNormal=TRUE" : "estimateNormal=FALSE", params.ichorcna_estimate_ploidy ? "ploidy='c(2, 3, 4, 5)'" : "ploidy='2'", params.ichorcna_estimate_ploidy ? "estimatePloidy=TRUE" : "estimatePloidy=FALSE", params.ichorcna_estimate_sc ? "estimateScPrevalence=TRUE" : "estimateScPrevalence=FALSE", diff --git a/nextflow.config b/nextflow.config index 7b9f2679..f74de2a8 100644 --- a/nextflow.config +++ b/nextflow.config @@ -75,7 +75,7 @@ params { ichorcna_chrs_to_use = "1:22" ichorcna_chrs_to_train = "1:22" ichorcna_chrs_to_normalize = "1:22" - ichorcna_estimate_normal = "TRUE" + ichorcna_estimate_normal = true ichorcna_fraction_reads_male = 0.001 ichorcna_male_chrX_logR = 0.3 ichorcna_min_map_score = 0.75 @@ -88,7 +88,7 @@ params { ichorcna_alt_frac_threshold = 0.05 ichorcna_trx_strength = 10000 ichorcna_plotfiletype = "pdf" - ichorcna_plotylim = "c(-2,4)" + ichorcna_plotylim = "-2,4" ichorcna_estimate_sc = false ichorcna_estimate_ploidy = true ichorcna_filter_bam_pon = false From e1cf0a2bee54f88c46a48fff808d3ff3bdd7470e Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Thu, 20 Nov 2025 14:50:50 +0100 Subject: [PATCH 10/27] Update default in schema --- nextflow_schema.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 9730fb67..120bec78 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -497,8 +497,8 @@ }, "ichorcna_plotylim": { "type": "string", - "default": "c(-2,4)", - "description": "Y axis limits of the ichorCNA plot" + "default": "-2,4", + "description": "Y axis limits of the ichorCNA plot: comma separated (lower, upper)" }, "ichorcna_estimate_sc": { "type": "boolean", From a4f6770d8eab72d5c661bb31317737095e9607af Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Thu, 20 Nov 2025 15:17:57 +0100 Subject: [PATCH 11/27] Convert the workflow to the use of PoN in the samplesheet --- subworkflows/local/build_pon/main.nf | 18 ++++-------------- subworkflows/local/liquid_biopsy/main.nf | 4 ++-- subworkflows/local/solid_biopsy/main.nf | 4 ++-- workflows/samurai.nf | 17 ++++++++++++----- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/subworkflows/local/build_pon/main.nf b/subworkflows/local/build_pon/main.nf index 45616f10..ba2d6846 100644 --- a/subworkflows/local/build_pon/main.nf +++ b/subworkflows/local/build_pon/main.nf @@ -6,7 +6,7 @@ include { WISECONDORX_NEWREF } from '../../../module workflow BUILD_PON { take: - normal_dir + ch_normal_bam_bai caller fasta fai @@ -19,27 +19,17 @@ workflow BUILD_PON { main: ch_versions = channel.empty() - ch_bam_files = channel.fromFilePairs( - "${normal_dir}/*.bam{,.bai}", - checkIfExists: true - ) - .ifEmpty { error("No BAM or BAI files found at ${normal_dir}") } - .map { meta, file -> - def fmeta = [:] - fmeta.id = meta - tuple(fmeta, file[0], file[1]) - } if (caller == "ichorcna") { // FIXME: We shouldn't depend on parameters here if (filter_bam_pon) { - SAMBAMBA_FILTER(ch_bam_files) + SAMBAMBA_FILTER(ch_normal_bam_bai) ch_bam_for_pon = SAMBAMBA_FILTER.out.filtered_bam ch_versions = ch_versions.mix(SAMBAMBA_FILTER.out.versions) } else { // Remove the BAM index for compatibility with the ReadCounter workflow - ch_bam_for_pon = ch_bam_files + ch_bam_for_pon = ch_normal_bam_bai } HMMCOPY_READCOUNTER_PON(ch_bam_for_pon, fasta) @@ -64,7 +54,7 @@ workflow BUILD_PON { ch_versions = ch_versions.mix(ICHORCNA_CREATEPON.out.versions) } else if (caller == "wisecondorx") { - NORMAL_CONVERT(ch_bam_files, fasta, fai) + NORMAL_CONVERT(ch_normal_bam_bai, fasta, fai) ch_versions = ch_versions.mix(NORMAL_CONVERT.out.versions) WISECONDORX_NEWREF( NORMAL_CONVERT.out.npz.map { meta, npz -> diff --git a/subworkflows/local/liquid_biopsy/main.nf b/subworkflows/local/liquid_biopsy/main.nf index 846ff9f1..ec1ddc8b 100644 --- a/subworkflows/local/liquid_biopsy/main.nf +++ b/subworkflows/local/liquid_biopsy/main.nf @@ -19,7 +19,7 @@ workflow LIQUID_BIOPSY { ch_centromere // channel: path to centromere file ch_reptiming // channel: path to reptiming file build_pon // bool - pon_path // value: path + ch_pon_files // value: path ch_blacklist // channel: [meta, blacklist] main: @@ -31,7 +31,7 @@ workflow LIQUID_BIOPSY { if (build_pon) { BUILD_PON( - pon_path, + ch_pon_files, caller, ch_fasta, ch_fai, diff --git a/subworkflows/local/solid_biopsy/main.nf b/subworkflows/local/solid_biopsy/main.nf index d55041bf..e92b8bca 100644 --- a/subworkflows/local/solid_biopsy/main.nf +++ b/subworkflows/local/solid_biopsy/main.nf @@ -18,7 +18,7 @@ workflow SOLID_BIOPSY { genome // value, genome to use ascat_predict_refit // boolean build_pon // boolean - pon_path + ponfiles ch_normal_panel // channel ch_gc_wig // channel: path to GC wig ch_map_wig // channel: path to mappability wig @@ -127,7 +127,7 @@ workflow SOLID_BIOPSY { // If we want to build the normal panel if (build_pon) { - BUILD_PON(pon_path, caller) + BUILD_PON(ponfiles, caller) ch_versions = ch_versions.mix(BUILD_PON.out.versions) pon_file = BUILD_PON.out.normal_panel } diff --git a/workflows/samurai.nf b/workflows/samurai.nf index d62909a6..34c398fd 100644 --- a/workflows/samurai.nf +++ b/workflows/samurai.nf @@ -213,15 +213,22 @@ workflow SAMURAI { // CN Calling + ch_bam_type = ch_bam_bai.branch{ meta, _bam, _bai -> + normal: meta.status == "normal" + tumor: true // fallback if not specified + } + + ch_ponfiles = build_pon ? ch_bam_type.normal.ifEmpty([[], []]) : [[], []] + if (analysis_type == "solid_biopsy") { SOLID_BIOPSY( - ch_bam_bai, + ch_bam_type.tumor, caller, binsize, genome, ascat_predict_refit, build_pon, - ch_pon_path, + ch_ponfiles, ch_normal_panel, ch_gc_wig, ch_map_wig, @@ -235,7 +242,7 @@ workflow SAMURAI { } else if (analysis_type == "liquid_biopsy") { if (size_selection) { - SIZE_SELECTION(ch_bam_bai, ch_fasta) + SIZE_SELECTION(ch_bam_type.tumor, ch_fasta) ch_versions = ch_versions.mix(SIZE_SELECTION.out.versions.first()) ch_multiqc_files = ch_multiqc_files.mix( @@ -256,7 +263,7 @@ workflow SAMURAI { } } else { - ch_analysis = ch_bam_bai + ch_analysis = ch_bam_type.tumor } LIQUID_BIOPSY( @@ -270,7 +277,7 @@ workflow SAMURAI { ch_centromere, ch_reptime, build_pon, - ch_pon_path, + ch_ponfiles, ch_blacklist) gistic_file = LIQUID_BIOPSY.out.corrected_gistic_file ch_versions = ch_versions.mix(LIQUID_BIOPSY.out.versions) From 1471bc36b718b8b61aade9dc346af0b3c76f23d1 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Mon, 24 Nov 2025 09:26:02 +0100 Subject: [PATCH 12/27] Fix option type --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index f74de2a8..d0720169 100644 --- a/nextflow.config +++ b/nextflow.config @@ -83,7 +83,7 @@ params { ichorcna_max_frac_cna_subclone = 0.7 ichorcna_min_segment_bins = 50 ichorcna_max_cn = 5 - ichorcna_include_homd = "FALSE" + ichorcna_include_homd = false ichorcna_txne = 0.9999 ichorcna_alt_frac_threshold = 0.05 ichorcna_trx_strength = 10000 From 3af75b65996f1ef6c0ae727fcd28979d6fa5ca56 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Mon, 24 Nov 2025 11:49:58 +0100 Subject: [PATCH 13/27] Don't consume the aligner index after first use --- main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.nf b/main.nf index 9472decb..9c928d0c 100644 --- a/main.nf +++ b/main.nf @@ -170,7 +170,7 @@ workflow { if (params.aligner_index) { ch_index = channel.fromPath(params.aligner_index, checkIfExists: true) - .map { idx -> [[id: 'aligner'], idx] } + .map { idx -> [[id: 'aligner'], idx] }.collect() } else if (!params.aligner_index && !params.igenomes_ignore && real_aligner) { ch_index = [ From 24f39fe38c77c212eab654a91bec1bc12c2a3f63 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 25 Nov 2025 09:40:06 +0100 Subject: [PATCH 14/27] Use actually the correct file --- subworkflows/local/ichorcna/main.nf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subworkflows/local/ichorcna/main.nf b/subworkflows/local/ichorcna/main.nf index 1d9d1a76..a2cd9f1f 100644 --- a/subworkflows/local/ichorcna/main.nf +++ b/subworkflows/local/ichorcna/main.nf @@ -50,7 +50,8 @@ workflow ICHORCNA { ch_versions = ch_versions.mix(AGGREGATE_ICHORCNA_TABLE.out.versions) ch_reports = ch_reports.mix(AGGREGATE_ICHORCNA_TABLE.out.ichorcna_summary) - ICHORCNA_RUN.out.cna_seg + // seg.txt are the *segments*, cna.seg are instead bin-level calls + ICHORCNA_RUN.out.seg_txt .map { _meta, data -> data } .collectFile( storeDir: "${params.outdir}/ichorcna/", From d4625308dde062c96aeaa9ebf7cc17d0d28cc09a Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 25 Nov 2025 10:00:04 +0100 Subject: [PATCH 15/27] Switch container image to the polars-based CLI tool --- modules/local/correct_logR_ichorcna/main.nf | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/modules/local/correct_logR_ichorcna/main.nf b/modules/local/correct_logR_ichorcna/main.nf index ee9fd87e..c307cd12 100644 --- a/modules/local/correct_logR_ichorcna/main.nf +++ b/modules/local/correct_logR_ichorcna/main.nf @@ -2,23 +2,23 @@ process CORRECT_LOGR_ICHORCNA { tag "Correcting Log2 for GISTIC Analysis" label "process_low" -// TO DO: Create a container in the repository to be pulled - container "quay.io/dincalcilab/tidyverse:1.0.0-673997e" + container "${workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container + ? 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/83/83908efbfba116e554b59ef27409fc78f1f2cc94158c5e01dde680527c3de567/data' + : 'community.wave.seqera.io/library/polars_procps-ng_typer:d1a53d7945a021e3'}" input: - path(seg_file) - path(ploidy_summary) - + path seg_file + path ploidy_summary output: - path("*_logR_corrected_gistic.seg"), emit: gistic_file - path("versions.yml"), emit: versions + path ("*_logR_corrected_gistic.seg"), emit: gistic_file + path ("versions.yml"), emit: versions script: def VERSION = "0.1" """ - correct_logR_ichorcna.R \\ + correct_logr_ichorcna.py \\ --seg ${seg_file} \\ --ploidy ${ploidy_summary} @@ -27,5 +27,4 @@ process CORRECT_LOGR_ICHORCNA { correct_logR_ichorcna: ${VERSION} END_VERSIONS """ - } From e664f7532f26e69c3a1c034a2dc2f79df135bf5b Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 25 Nov 2025 13:34:10 +0100 Subject: [PATCH 16/27] Use the right column name --- bin/correct_logr_ichorcna.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/correct_logr_ichorcna.py b/bin/correct_logr_ichorcna.py index f9b004c7..e49e9caf 100755 --- a/bin/correct_logr_ichorcna.py +++ b/bin/correct_logr_ichorcna.py @@ -36,7 +36,9 @@ def main( result = ( segments.join( - ploidy_df.select("sample", "Ploidy"), on="sample", how="left" + ploidy_df.select(pl.col("samplename").alias("sample"), "Ploidy"), + on="sample", + how="left", ) .with_columns( pl.col("logR_Copy_Number") From ba848bc5c08d940ef3abfdc3a7e910b413f9dd66 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 25 Nov 2025 15:22:57 +0100 Subject: [PATCH 17/27] The quest for the right column ID continues... --- bin/correct_logr_ichorcna.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/correct_logr_ichorcna.py b/bin/correct_logr_ichorcna.py index e49e9caf..2c8eee44 100755 --- a/bin/correct_logr_ichorcna.py +++ b/bin/correct_logr_ichorcna.py @@ -31,7 +31,7 @@ def main( ), ): - segments = pl.scan_csv(seg, separator="\t") + segments = pl.scan_csv(seg, separator="\t").rename({"ID": "sample"}) ploidy_df = pl.scan_csv(ploidy, separator="\t") result = ( From baa1f35e6a41e4ff91c1fe7c4cdeb5584268c3d7 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 26 Nov 2025 07:21:57 +0100 Subject: [PATCH 18/27] Fix another column namee --- bin/correct_logr_ichorcna.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/correct_logr_ichorcna.py b/bin/correct_logr_ichorcna.py index 2c8eee44..97308331 100755 --- a/bin/correct_logr_ichorcna.py +++ b/bin/correct_logr_ichorcna.py @@ -31,7 +31,9 @@ def main( ), ): - segments = pl.scan_csv(seg, separator="\t").rename({"ID": "sample"}) + segments = pl.scan_csv(seg, separator="\t").rename( + {"ID": "sample", "chrom": "chromosome"} + ) ploidy_df = pl.scan_csv(ploidy, separator="\t") result = ( From 456b6b79ab86c896977e6814a280ed0468fb1855 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 26 Nov 2025 13:30:25 +0100 Subject: [PATCH 19/27] Fix dashes vs underscores --- modules/local/assemble_gistic_output/main.nf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/local/assemble_gistic_output/main.nf b/modules/local/assemble_gistic_output/main.nf index 2b489ddd..a26aa3c4 100644 --- a/modules/local/assemble_gistic_output/main.nf +++ b/modules/local/assemble_gistic_output/main.nf @@ -23,7 +23,7 @@ process ASSEMBLE_GISTIC_OUTPUT { def args = task.ext.args ?: '' """ - gistic lesion_table -t all ${args} ${gistic_folder} gistic_summary_mqc + gistic lesion-table -t all ${args} ${gistic_folder} gistic_summary_mqc mv gistic_summary_mqc.focal_lesions.txt gistic_summary_mqc.txt # Only do the rename if the file was produced, i.e. when broad analysis runs @@ -31,9 +31,9 @@ process ASSEMBLE_GISTIC_OUTPUT { mv gistic_summary_mqc.broad_lesions.txt gistic_broad_lesions_mqc.txt fi - gistic gene_table ${args} ${gistic_folder} gistic - gistic copy_number_state_table ${args} ${gistic_folder} gistic_cn_states.txt - gistic log2ratio_table ${args} ${gistic_folder} gistic_log2R.txt + gistic gene-table ${args} ${gistic_folder} gistic + gistic copy-number-state-table ${args} ${gistic_folder} gistic_cn_states.txt + gistic log2ratio-table ${args} ${gistic_folder} gistic_log2R.txt cat <<-END_VERSIONS > versions.yml "${task.process}": From 7653ecaafb5af64520572c8c32b0b66c540de53e Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 2 Dec 2025 09:07:13 +0100 Subject: [PATCH 20/27] remove all references to pon_path --- main.nf | 4 ---- nextflow.config | 1 - nextflow_schema.json | 4 ---- workflows/samurai.nf | 1 - 4 files changed, 10 deletions(-) diff --git a/main.nf b/main.nf index 9c928d0c..981b05ba 100644 --- a/main.nf +++ b/main.nf @@ -51,7 +51,6 @@ workflow DINCALCILAB_SAMURAI { genome_index caller binsize - pon_path run_fastp build_pon normal_panel @@ -78,7 +77,6 @@ workflow DINCALCILAB_SAMURAI { genome_index, caller, binsize, - pon_path, run_fastp, build_pon, normal_panel, @@ -149,7 +147,6 @@ workflow { genome = params.genome caller = params.analysis_type == "align_only" ? "none" : params.caller - pon_path = params.pon_path && params.build_pon ? params.pon_path : "" analysis_type = params.analysis_type binsize = params.binsize normal_panel = params.normal_panel ? channel.fromPath(params.normal_panel, checkIfExists: true).map{it -> it}.collect() : channel.empty() @@ -207,7 +204,6 @@ workflow { ch_index, caller, binsize, - pon_path, params.run_fastp, params.build_pon, normal_panel, diff --git a/nextflow.config b/nextflow.config index d0720169..7a1beb3c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -54,7 +54,6 @@ params { // Liquid Biopsy Options normal_panel = null - pon_path = null pon_name = "PoN" build_pon = false selection_maxsize = 150 diff --git a/nextflow_schema.json b/nextflow_schema.json index 120bec78..b22787d1 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -365,10 +365,6 @@ "type": "string", "description": "Path to the panel of normals to be used" }, - "pon_path": { - "type": "string", - "description": "Path to BAM files to be used to build the panel of normals" - }, "pon_name": { "type": "string", "default": "PoN", diff --git a/workflows/samurai.nf b/workflows/samurai.nf index 34c398fd..7aa11a1f 100644 --- a/workflows/samurai.nf +++ b/workflows/samurai.nf @@ -67,7 +67,6 @@ workflow SAMURAI { ch_index caller binsize - ch_pon_path run_fastp build_pon ch_normal_panel From ed1cb147b9caf9bc147507921da9b8bda1a510a1 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 2 Dec 2025 09:56:05 +0100 Subject: [PATCH 21/27] Better error message --- main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.nf b/main.nf index 981b05ba..f4c2fb40 100644 --- a/main.nf +++ b/main.nf @@ -185,7 +185,7 @@ workflow { ichorcna_reptime_file = params.ichorcna_reptime_wig ? file(params.ichorcna_reptime_wig, checkIfExists: true) : [] if (caller == "ichorcna" && (!ichorcna_gc_wig || !ichorcna_map_wig)) { - error("ichorCNA calling requires a GC WIG and a mappability WIG") + error("Error: ichorCNA calling requires a GC WIG and a mappability WIG") } wisecondor_blacklist = params.wisecondorx_blacklist ? channel.fromPath(params.wisecondorx_blacklist, checkIfExists: true).map { blacklist -> [[id: "blacklist"], blacklist] }.collect() : [[], []] From 3957a021bf0e71ace1cc47246736d6881be7c828 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Tue, 2 Dec 2025 10:26:49 +0100 Subject: [PATCH 22/27] Add a trailing "," to args --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index 8a91346a..22e30a27 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -306,7 +306,7 @@ process { params.ichorcna_estimate_ploidy ? "estimatePloidy=TRUE" : "estimatePloidy=FALSE", params.ichorcna_estimate_sc ? "estimateScPrevalence=TRUE" : "estimateScPrevalence=FALSE", params.ichorcna_estimate_sc ? "scStates='c(1,3)'" : "scStates='c()'", - ].join(",\n") + ].join(",\n") + ", " } withName: AGGREGATE_ICHORCNA_TABLE { From 0cb46e8b4ed2f21d7ae8d6134c6ddc55452ebfef Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 10 Dec 2025 07:45:17 +0100 Subject: [PATCH 23/27] Try to collect all samples for WisecondorX --- subworkflows/local/build_pon/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subworkflows/local/build_pon/main.nf b/subworkflows/local/build_pon/main.nf index ba2d6846..2c2bc68a 100644 --- a/subworkflows/local/build_pon/main.nf +++ b/subworkflows/local/build_pon/main.nf @@ -60,7 +60,7 @@ workflow BUILD_PON { NORMAL_CONVERT.out.npz.map { meta, npz -> def new_meta = meta + [id: "joined"] [new_meta, npz] - }.groupTuple() + }.groupTuple().collect() ) normal_panel = WISECONDORX_NEWREF.out.npz ch_versions = ch_versions.mix(WISECONDORX_NEWREF.out.versions) From 674e90d8f297c420b7ef2447ec5ba11bae79ba5e Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 10 Dec 2025 07:55:46 +0100 Subject: [PATCH 24/27] Update the ichorCNA modules to add the fix for chrX --- modules.json | 4 ++-- .../nf-core/ichorcna/createpon/environment.yml | 2 +- modules/nf-core/ichorcna/createpon/main.nf | 4 ++-- .../ichorcna/createpon/tests/main.nf.test.snap | 18 +++++++++--------- modules/nf-core/ichorcna/run/environment.yml | 2 +- modules/nf-core/ichorcna/run/main.nf | 4 ++-- .../ichorcna/run/tests/main.nf.test.snap | 12 ++++++------ .../nf-core/ichorcna/run/tests/nextflow.config | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/modules.json b/modules.json index 8ae03f00..1da75a8e 100644 --- a/modules.json +++ b/modules.json @@ -62,12 +62,12 @@ }, "ichorcna/createpon": { "branch": "master", - "git_sha": "41dfa3f7c0ffabb96a6a813fe321c6d1cc5b6e46", + "git_sha": "a507bfc61c00fc9881f408c3fb1b375bb8078a81", "installed_by": ["modules"] }, "ichorcna/run": { "branch": "master", - "git_sha": "4412c4b83c62cb2036b9a6939551a7fd988177d0", + "git_sha": "a507bfc61c00fc9881f408c3fb1b375bb8078a81", "installed_by": ["modules"] }, "multiqc": { diff --git a/modules/nf-core/ichorcna/createpon/environment.yml b/modules/nf-core/ichorcna/createpon/environment.yml index 54df91b4..5073fc57 100644 --- a/modules/nf-core/ichorcna/createpon/environment.yml +++ b/modules/nf-core/ichorcna/createpon/environment.yml @@ -4,4 +4,4 @@ channels: - conda-forge - bioconda dependencies: - - bioconda::r-ichorcna=0.5.1 + - bioconda::r-ichorcna=0.5.1=r44hdfd78af_1 diff --git a/modules/nf-core/ichorcna/createpon/main.nf b/modules/nf-core/ichorcna/createpon/main.nf index ab19a033..3be817e0 100644 --- a/modules/nf-core/ichorcna/createpon/main.nf +++ b/modules/nf-core/ichorcna/createpon/main.nf @@ -3,8 +3,8 @@ process ICHORCNA_CREATEPON { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/r-ichorcna:0.5.1--r43hdfd78af_0' : - 'biocontainers/r-ichorcna:0.5.1--r43hdfd78af_0' }" + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/f0/f07cec06705b4443052d3d7eaccebdbd0078366f7d074bfd4a6893980c6e2c4b/data' : + 'community.wave.seqera.io/library/r-ichorcna:0.5.1--eed4be826f05c9d4' }" input: path wigs diff --git a/modules/nf-core/ichorcna/createpon/tests/main.nf.test.snap b/modules/nf-core/ichorcna/createpon/tests/main.nf.test.snap index fe73d2ef..4a2615ab 100644 --- a/modules/nf-core/ichorcna/createpon/tests/main.nf.test.snap +++ b/modules/nf-core/ichorcna/createpon/tests/main.nf.test.snap @@ -4,21 +4,21 @@ "PoN_median.rds", "PoN_median.txt", [ - "versions.yml:md5,59dbc83520b9e00a736f49ed2513657a" + "versions.yml:md5,4a4f8b452427ec9438cf0ffad62c7a7d" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.3", + "nextflow": "25.10.0" }, - "timestamp": "2024-08-06T14:23:09.884294332" + "timestamp": "2025-12-09T11:34:25.04737792" }, "stub": { "content": [ "PoN.rds", "PoN.txt", [ - "versions.yml:md5,59dbc83520b9e00a736f49ed2513657a" + "versions.yml:md5,4a4f8b452427ec9438cf0ffad62c7a7d" ] ], "meta": { @@ -32,13 +32,13 @@ "PoN_median.rds", "PoN_median.txt", [ - "versions.yml:md5,59dbc83520b9e00a736f49ed2513657a" + "versions.yml:md5,4a4f8b452427ec9438cf0ffad62c7a7d" ] ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.3", + "nextflow": "25.10.0" }, - "timestamp": "2024-08-06T14:23:29.496424481" + "timestamp": "2025-12-09T11:34:34.559159568" } } \ No newline at end of file diff --git a/modules/nf-core/ichorcna/run/environment.yml b/modules/nf-core/ichorcna/run/environment.yml index 54df91b4..5073fc57 100644 --- a/modules/nf-core/ichorcna/run/environment.yml +++ b/modules/nf-core/ichorcna/run/environment.yml @@ -4,4 +4,4 @@ channels: - conda-forge - bioconda dependencies: - - bioconda::r-ichorcna=0.5.1 + - bioconda::r-ichorcna=0.5.1=r44hdfd78af_1 diff --git a/modules/nf-core/ichorcna/run/main.nf b/modules/nf-core/ichorcna/run/main.nf index 68cf302f..f8fd2eea 100644 --- a/modules/nf-core/ichorcna/run/main.nf +++ b/modules/nf-core/ichorcna/run/main.nf @@ -4,8 +4,8 @@ process ICHORCNA_RUN { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/r-ichorcna:0.5.1--r43hdfd78af_0' : - 'biocontainers/r-ichorcna:0.5.1--r43hdfd78af_0' }" + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/f0/f07cec06705b4443052d3d7eaccebdbd0078366f7d074bfd4a6893980c6e2c4b/data' : + 'community.wave.seqera.io/library/r-ichorcna:0.5.1--eed4be826f05c9d4' }" input: tuple val(meta), path(wig) diff --git a/modules/nf-core/ichorcna/run/tests/main.nf.test.snap b/modules/nf-core/ichorcna/run/tests/main.nf.test.snap index 7a70f7bd..13e21009 100644 --- a/modules/nf-core/ichorcna/run/tests/main.nf.test.snap +++ b/modules/nf-core/ichorcna/run/tests/main.nf.test.snap @@ -67,7 +67,7 @@ ] ], "8": [ - "versions.yml:md5,d2d30cedd49c7bffdcb74313e8a074c4" + "versions.yml:md5,0395d992ef3e4aa41ed95603f461cc92" ], "cna_seg": [ [ @@ -134,20 +134,20 @@ ] ], "versions": [ - "versions.yml:md5,d2d30cedd49c7bffdcb74313e8a074c4" + "versions.yml:md5,0395d992ef3e4aa41ed95603f461cc92" ] } ], "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" + "nf-test": "0.9.3", + "nextflow": "25.10.0" }, - "timestamp": "2024-08-02T06:38:13.140234725" + "timestamp": "2025-12-09T11:32:23.125027221" }, "no_panel": { "content": [ [ - "versions.yml:md5,d2d30cedd49c7bffdcb74313e8a074c4" + "versions.yml:md5,0395d992ef3e4aa41ed95603f461cc92" ], "test_genomeWide.pdf", "test.RData", diff --git a/modules/nf-core/ichorcna/run/tests/nextflow.config b/modules/nf-core/ichorcna/run/tests/nextflow.config index 1bd94c5b..6189ced1 100644 --- a/modules/nf-core/ichorcna/run/tests/nextflow.config +++ b/modules/nf-core/ichorcna/run/tests/nextflow.config @@ -5,4 +5,4 @@ process { scStates='c(1,3)', """ } -} \ No newline at end of file +} From 7e3bfb0f2a9de4ae4ed470d7f1d2e5633489aa08 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 10 Dec 2025 08:23:52 +0100 Subject: [PATCH 25/27] Actually fix WisecondorX PoN --- subworkflows/local/build_pon/main.nf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/subworkflows/local/build_pon/main.nf b/subworkflows/local/build_pon/main.nf index 2c2bc68a..94f34027 100644 --- a/subworkflows/local/build_pon/main.nf +++ b/subworkflows/local/build_pon/main.nf @@ -57,10 +57,10 @@ workflow BUILD_PON { NORMAL_CONVERT(ch_normal_bam_bai, fasta, fai) ch_versions = ch_versions.mix(NORMAL_CONVERT.out.versions) WISECONDORX_NEWREF( - NORMAL_CONVERT.out.npz.map { meta, npz -> - def new_meta = meta + [id: "joined"] + NORMAL_CONVERT.out.npz.map { _meta, npz -> + def new_meta = [id: "joined"] [new_meta, npz] - }.groupTuple().collect() + }.groupTuple() ) normal_panel = WISECONDORX_NEWREF.out.npz ch_versions = ch_versions.mix(WISECONDORX_NEWREF.out.versions) From d7514a95a50ccc9be74b87f97c0f52fd8e773c9c Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 10 Dec 2025 08:31:19 +0100 Subject: [PATCH 26/27] CIn signatures: don't plot if there's only one sample (#59) --- bin/compute_signatures.R | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bin/compute_signatures.R b/bin/compute_signatures.R index 2f670f41..287f00b9 100755 --- a/bin/compute_signatures.R +++ b/bin/compute_signatures.R @@ -50,8 +50,12 @@ df_activity <- df_activity %>% readr::write_tsv(df_activity, file = paste0(args$projectname, "_activity.txt"), quote = "needed") -png(filename="ascat_sc_plot_by_component.png") -plotSampleByComponent(object = cnobj) +# Only plot if samples are > 1 +total_samples <- segments %>% pull(sample) %>% unique() %>% length() +png(filename = "ascat_sc_plot_by_component.png") +if (total_samples > 1) { + plotSampleByComponent(object = cnobj) +} dev.off() From 9736ba42347751093667e2fecd9e9ace4d191582 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 10 Dec 2025 08:54:44 +0100 Subject: [PATCH 27/27] Update documentation and CHANGELOG --- CHANGELOG.md | 23 +++++++++++++++++++ docs/usage.md | 62 +++++++++++++++++++++++++-------------------------- 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd1fbdc8..0a76358c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,29 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Breaking changes + +- A new **mandatory** column has been added in the samplesheet, `status`, which can be either `normal` or `tumor` (@lbeltrame, issue #13) +- Option `--pon_path` has been **removed**. Instead, add your normal samples to the samplesheet (same rules as case samples) with the `normal` status +- Due to a bug in ichorCNA with security implications, the syntax of all options regarding ichorCNA chromosome handling (but not the `readcounter` parameters) has changed: + - Specify chromosomes with _identifier only_ (without `chr`) + - Valid values are individual chromosomes, separated by comma (e.g. `1` or `1,2`), ranges using `:` (`1:22`) or a combination of the above (`1:22, X`). If you set `ichorcna_genomestyle`, the right prefix will be appended for you when running the analysis + - `ichorcna_plotylim` now takes a min and max value separated by comma (e.g. `-2,4`) + - All the ichorCNA options that require `TRUE` or `FALSE` now take a boolean `true` or `false` + - Raw R in any ichorCNA option (e.g. `c(1,2)`) is no longer supported and will raise an error. + +### New features + +- Support the latest version of ichorCNA (@lbeltrame and @SaraPotente, PR #57) +- Update the ichorCNA modules to the latest upstream nf-core versions (@lbeltrame, PR #57) +- PoN support in the samplesheet (@lbeltrame, #13, PR #57) + +### Bug fixes + +- Don't raise an error if in CIN quantification there is only one sample (#59, @lbeltrame) + ## v1.3.1 - "Mori Ranmaru" (2025-11-17) This release is a hotfix for a regression in size selection that somehow slipped under the radar. diff --git a/docs/usage.md b/docs/usage.md index 4afc8d5e..8b37ef0b 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -36,6 +36,7 @@ SAMPLE2,path/to/SAMPLE2.bam,male | `fastq_1` | Full path to FastQ file for Illumina short reads 1. | | `fastq_2` | Full path to FastQ file for Illumina short reads 2. | | `bam` | Full path to BAM file. Note: `bam` is _mutually exclusive_ with `fastq_1` or `fastq_2`. | +| `status` | Type of sample, either condition (`tumor`) or reference (`normal`) | | Optional | Description | | -------- | ------------------------------------------------------------------------------------------------------------ | @@ -210,7 +211,6 @@ Specify the path to a specific config file (this is a core Nextflow command). Se | Parameter Name | Description | Default Value | Possible Values | | ---------------------------- | ------------------------------------------------------------------------ | ------------- | -------------------- | | `normal_panel` | Path to the panel of normals to be used | | | -| `pon_path` | Path to BAM files to be used to build the panel of normals | | | | `pon_name` | Name of the panel of normals to build | `"PoN"` | | | `build_pon` | Whether to build a panel of normals or not | `false` | `true`, `false` | | `selection_maxsize` | Maximum insert size in bp to be included for size selection | `150` | Any positive integer | @@ -218,36 +218,36 @@ Specify the path to a specific config file (this is a core Nextflow command). Se #### _ichorCNA specific options_ -| Parameter Name | Description | Default Value | Possible Values | -| ----------------------------------- | --------------------------------------------------------------------------- | ------------------------------------- | ------------------------------------------------ | -| `ichorcna_genome_style` | Genome style to be used by ichorCNA (NCBI or UCSC) | `UCSC` | `NCBI`, `UCSC` | -| `ichorcna_readcounter_chrs` | Chromosomes to be used by ichorCNA | `chr1,chr2,...,chr22` | List of chromosomes (e.g., `chr1,chr2,chr3,...`) | -| `ichorcna_readcounter_quality` | Minimum read count quality to keep in ichorCNA | `20` | Any integer | -| `ichorcna_chrs_to_use` | Chromosomes to use for ichorCNA | `paste0('chr', c(1:22))` | String format (e.g., `"chr1,chr2,...,chr22"`) | -| `ichorcna_chrs_to_train` | Chromosomes to use for training during an ichorCNA run | `paste0('chr', c(1:22))` | String format (e.g., `"chr1,chr2,...,chr22"`) | -| `ichorcna_chrs_to_normalize` | Chromosomes to use for normalization during an ichorCNA run | `paste0('chr', c(1:22))` | String format (e.g., `"chr1,chr2,...,chr22"`) | -| `ichorcna_estimate_normal` | Whether ichorCNA should estimate normal contamination or not | `true` | `true`, `false` | -| `ichorcna_fraction_reads_male` | Fraction of data used for copy number calling | `0.001` | Any value between 0 and 1 | -| `ichorcna_male_chrX_logR` | LogR value for male chromosome X | `0.3` | Any numerical value | -| `ichorcna_min_map_score` | Minimum mapping score for reads to be used by ichorCNA | `0.75` | Any value between 0 and 1 | -| `ichorcna_max_frac_genome_subclone` | Maximum fraction of genome allowed for subclone | `0.5` | Any value between 0 and 1 | -| `ichorcna_max_frac_cna_subclone` | Maximum fraction of CNA allowed for subclone | `0.7` | Any value between 0 and 1 | -| `ichorcna_min_segment_bins` | Minimum number of bins required for segmenting | `50` | Any positive integer | -| `ichorcna_max_cn` | Maximum copy number to be considered by ichorCNA | `5` | Any positive integer | -| `ichorcna_include_homd` | Call also homozygous deletions in ichorCNA | `"FALSE"` | `"TRUE"`, `"FALSE"` | -| `ichorcna_txne` | Tumor-normal estimation strength for ichorCNA | `0.9999` | Any value between 0 and 1 | -| `ichorcna_alt_frac_threshold` | Alternative fraction threshold for ichorCNA | `0.05` | Any value between 0 and 1 | -| `ichorcna_trx_strength` | Strength of transcription effect for ichorCNA | `10000` | Any integer | -| `ichorcna_plotfiletype` | Output plot file type for ichorCNA | `pdf` | `pdf`, `png`, `jpeg`, etc. | -| `ichorcna_plotylim` | Y-axis limits for the generated plots | `c(-2,4)` | String format (e.g., `"c(-2, 4)"`) | -| `ichorcna_estimate_sc` | Estimate copy number subclonality in ichorCNA | `false` | `true`, `false` | -| `ichorcna_estimate_ploidy` | Estimate ploidy in ichorCNA | `true` | `true`, `false` | -| `ichorcna_filter_bam_pon` | Apply PON filtering on BAM files | N/A | `true`, `false` | -| `ichorcna_normal_states` | Fraction of normal copy number states used in ichorCNA | `0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99` | List of numerical values (e.g., `0.5, 0.6, 0.7`) | -| `ichorcna_gc_wig` | Path to a Wiggle file with GC content data for the specified genome | N/A | File path (must be a `.wig` file) | -| `ichorcna_map_wig` | Path to a Wiggle file with mappability scores for the specified genome | N/A | File path (must be a `.wig` file) | -| `ichorcna_reptime_wig` | Path to a Wiggle file with replication timing data for the specified genome | N/A | File path (must be a `.wig` file) | -| `ichorcna_centromere_file` | Path to a file with centromere data for the specified genome | N/A | File path (must be a `.txt` or similar format) | +| Parameter Name | Description | Default Value | Possible Values | +| ----------------------------------- | --------------------------------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------- | +| `ichorcna_genome_style` | Genome style to be used by ichorCNA (NCBI or UCSC) | `UCSC` | `NCBI`, `UCSC` | +| `ichorcna_readcounter_chrs` | Chromosomes to be used by ichorCNA | `chr1,chr2,...,chr22` | List of chromosomes (e.g., `chr1,chr2,chr3,...`) | +| `ichorcna_readcounter_quality` | Minimum read count quality to keep in ichorCNA | `20` | Any integer | +| `ichorcna_chrs_to_use` | Chromosomes to use for ichorCNA | `1:22` | A number, a range (`start:end`) or values separated by commas (e.g., `1:22` or `1,4,5`) | +| `ichorcna_chrs_to_train` | Chromosomes to use for training during an ichorCNA run | `1:22` | A number, a range (`start:end`) or values separated by commas (e.g., `1:22` or `1,4,5`) | +| `ichorcna_chrs_to_normalize` | Chromosomes to use for normalization during an ichorCNA run | `1:22` | A number, a range (`start:end`) or values separated by commas (e.g., `1:22` or `1,4,5`) | +| `ichorcna_estimate_normal` | Whether ichorCNA should estimate normal contamination or not | `true` | `true`, `false` | +| `ichorcna_fraction_reads_male` | Fraction of data used for copy number calling | `0.001` | Any value between 0 and 1 | +| `ichorcna_male_chrX_logR` | LogR value for male chromosome X | `0.3` | Any numerical value | +| `ichorcna_min_map_score` | Minimum mapping score for reads to be used by ichorCNA | `0.75` | Any value between 0 and 1 | +| `ichorcna_max_frac_genome_subclone` | Maximum fraction of genome allowed for subclone | `0.5` | Any value between 0 and 1 | +| `ichorcna_max_frac_cna_subclone` | Maximum fraction of CNA allowed for subclone | `0.7` | Any value between 0 and 1 | +| `ichorcna_min_segment_bins` | Minimum number of bins required for segmenting | `50` | Any positive integer | +| `ichorcna_max_cn` | Maximum copy number to be considered by ichorCNA | `5` | Any positive integer | +| `ichorcna_include_homd` | Call also homozygous deletions in ichorCNA | false | `true`, `false` | +| `ichorcna_txne` | Tumor-normal estimation strength for ichorCNA | `0.9999` | Any value between 0 and 1 | +| `ichorcna_alt_frac_threshold` | Alternative fraction threshold for ichorCNA | `0.05` | Any value between 0 and 1 | +| `ichorcna_trx_strength` | Strength of transcription effect for ichorCNA | `10000` | Any integer | +| `ichorcna_plotfiletype` | Output plot file type for ichorCNA | `pdf` | `pdf`, `png`, `jpeg`, etc. | +| `ichorcna_plotylim` | Y-axis limits for the generated plots | `-2,4` | Two values (min and max), comma separated (e.g. `-2,4`) | +| `ichorcna_estimate_sc` | Estimate copy number subclonality in ichorCNA | `false` | `true`, `false` | +| `ichorcna_estimate_ploidy` | Estimate ploidy in ichorCNA | `true` | `true`, `false` | +| `ichorcna_filter_bam_pon` | Apply PON filtering on BAM files | N/A | `true`, `false` | +| `ichorcna_normal_states` | Fraction of normal copy number states used in ichorCNA | `0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 0.99` | List of numerical values (e.g., `0.5, 0.6, 0.7`) | +| `ichorcna_gc_wig` | Path to a Wiggle file with GC content data for the specified genome | N/A | File path (must be a `.wig` file) | +| `ichorcna_map_wig` | Path to a Wiggle file with mappability scores for the specified genome | N/A | File path (must be a `.wig` file) | +| `ichorcna_reptime_wig` | Path to a Wiggle file with replication timing data for the specified genome | N/A | File path (must be a `.wig` file) | +| `ichorcna_centromere_file` | Path to a file with centromere data for the specified genome | N/A | File path (must be a `.txt` or similar format) | #### _WisecondorX specific options_