From 5c2a34e4717629d6667d5303ecbaa6794a0342ba Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Wed, 6 May 2026 14:42:37 +0200 Subject: [PATCH 1/5] New modlue: finaletoolkit/delfi This uses FinaleToolkit to calculate the DELFI score (Cristiano et al., 2019). cf. #11365 --- .../finaletoolkit/delfi/environment.yml | 7 ++ modules/nf-core/finaletoolkit/delfi/main.nf | 45 +++++++++ modules/nf-core/finaletoolkit/delfi/meta.yml | 95 +++++++++++++++++++ .../finaletoolkit/delfi/tests/main.nf.test | 93 ++++++++++++++++++ 4 files changed, 240 insertions(+) create mode 100644 modules/nf-core/finaletoolkit/delfi/environment.yml create mode 100644 modules/nf-core/finaletoolkit/delfi/main.nf create mode 100644 modules/nf-core/finaletoolkit/delfi/meta.yml create mode 100644 modules/nf-core/finaletoolkit/delfi/tests/main.nf.test diff --git a/modules/nf-core/finaletoolkit/delfi/environment.yml b/modules/nf-core/finaletoolkit/delfi/environment.yml new file mode 100644 index 000000000000..64701682a25a --- /dev/null +++ b/modules/nf-core/finaletoolkit/delfi/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::finaletoolkit=0.11.1" diff --git a/modules/nf-core/finaletoolkit/delfi/main.nf b/modules/nf-core/finaletoolkit/delfi/main.nf new file mode 100644 index 000000000000..324cec3b38c2 --- /dev/null +++ b/modules/nf-core/finaletoolkit/delfi/main.nf @@ -0,0 +1,45 @@ +process FINALETOOLKIT_DELFI { + tag "${meta.id}" + label 'process_medium' + + 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/34/34f01d128ed135aedc33ddb62fced3911bef6d1a909694291b7184bf83719402/data' + : 'community.wave.seqera.io/library/finaletoolkit:0.11.1--8fe5ba6ec9e2ec95'}" + + input: + tuple val(meta), path(bam), path(bai) + path bins + path chromosome_sizes + path genome_2bit + + output: + tuple val(meta), path("*.bed"), emit: bed + tuple val("${task.process}"), val('finaletoolkit'), eval("finaletoolkit --version | sed 's/FinaleToolkit //g'"), topic: versions, emit: versions_finaletoolkit + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + finaletoolkit \\ + delfi \\ + -w ${task.cpus} + ${bam} \\ + ${chromosome_sizes} \\ + ${genome_2bit} \\ + ${args} \\ + -o "${prefix}_delfi.bed" + """ + + stub: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + """ + echo ${args} + + touch ${prefix}_delfi.bed + """ +} diff --git a/modules/nf-core/finaletoolkit/delfi/meta.yml b/modules/nf-core/finaletoolkit/delfi/meta.yml new file mode 100644 index 000000000000..131f1418cc6c --- /dev/null +++ b/modules/nf-core/finaletoolkit/delfi/meta.yml @@ -0,0 +1,95 @@ +name: "finaletoolkit_delfi" +description: Generate a binned fragment profile and a fragment distribution histogram +keywords: + - sort + - genomics + - fragmentomics +tools: + - "finaletoolkit": + description: "Extract cfDNA fragmentation features from sequencing data." + homepage: "https://epifluidlab.github.io/FinaleToolkit/" + documentation: "https://epifluidlab.github.io/FinaleToolkit/documentation/index.html" + tool_dev_url: "https://github.com/epifluidlab/FinaleToolkit" + doi: "10.1093/bioadv/vbaf236" + licence: + - "MIT" + identifier: biotools:finaletoolkit +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - bam: + type: file + description: Sorted BAM file + pattern: "*.bam" + ontologies: + - edam: "http://edamontology.org/format_2572" + - bai: + type: file + description: BAM file index + pattern: "*.bai" + ontologies: + - edam: "http://edamontology.org/format_3327" # BAI + - bins: + type: file + description: | + BED containing binned genomic coordinates + pattern: "*.bed" + ontologies: + - edam: "http://edamontology.org/format_3003" # BED + - chromosome_sizes: + type: file + pattern: "*.sizes" + description: | + Two-column file containing the size for each chromosome + ontologies: + - edam: "http://edamontology.org/format_3475" # TSV + - genome_2bit: + type: file + description: | + 2bit compressed genome file (must be the same as the one used for the + BAM file) + pattern: "*.2bit" + ontologies: + - edam: "http://edamontology.org/format_3009" # 2bit +output: + bed: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - "*.bed": + type: file + pattern: "*.bed" + description: | + BED file containing DELFI scores + ontologies: + - edam: "http://edamontology.org/format_3003" # BED + versions_finaletoolkit: + - - ${task.process}: + type: string + description: The name of the process + - finaletoolkit: + type: string + description: The name of the tool + - "finaletoolkit --version | sed 's/FinaleToolkit //g'": + type: eval + description: The expression to obtain the version of the tool +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - finaletoolkit: + type: string + description: The name of the tool + - "finaletoolkit --version | sed 's/FinaleToolkit //g'": + type: eval + description: The expression to obtain the version of the tool +authors: + - "@lbeltrame" +maintainers: + - "@lbeltrame" diff --git a/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test new file mode 100644 index 000000000000..03ef5b079543 --- /dev/null +++ b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test @@ -0,0 +1,93 @@ +nextflow_process { + + name "Test Process FINALETOOLKIT_DELFI" + script "../main.nf" + process "FINALETOOLKIT_DELFI" + + tag "modules" + tag "modules_nfcore" + tag "finaletoolkit" + tag "finaletoolkit/delfi" + + test("homo_sapiens - bam") { + + + when { + params { + // Chr22 is too small for meaningful data, so remove all filters + // We need 2 workers because otherwise there is a bug (fixed upstream, but without a new version) + // that breaks the program with only one worker + task.cpus = 2 + ext.args = "--no-merge-bins --no-gc-correct --quality-threshold 0" + } + process { + """ + input[0] = [ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/NA12878.chr22.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/NA12878.chr22.bam.bai', checkIfExists: true), + ] + input[1] = [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.bins_10kb.bed', checkIfExists: true) + ] + input[2] = [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.sizes', checkIfExists: true) + ] + input[3] = [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.2bit', checkIfExists: true) + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + process.out, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() } + ) + } + + } + + test("homo_sapiens - stub") { + + options "-stub" + + when { + + process { + """ + input[0] = [ + [ id:'test' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/NA12878.chr22.bam', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/NA12878.chr22.bam.bai', checkIfExists: true), + ] + input[1] = [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.bins_10kb.bed', checkIfExists: true) + ] + input[2] = [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.sizes', checkIfExists: true) + ] + input[3] = [ + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.2bit', checkIfExists: true) + ] + """ + } + } + + then { + assert process.success + assertAll( + { assert snapshot( + process.out, + process.out.findAll { key, val -> key.startsWith("versions") } + ).match() } + ) + } + + } + +} From 27240ffec56d0c8d5e5070975b45b6abc6bfad22 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Thu, 7 May 2026 08:55:19 +0200 Subject: [PATCH 2/5] New module: finaletoolkit/delfi --- modules/nf-core/finaletoolkit/delfi/main.nf | 3 +- .../finaletoolkit/delfi/tests/delfi.config | 6 ++ .../finaletoolkit/delfi/tests/main.nf.test | 6 +- .../delfi/tests/main.nf.test.snap | 102 ++++++++++++++++++ 4 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 modules/nf-core/finaletoolkit/delfi/tests/delfi.config create mode 100644 modules/nf-core/finaletoolkit/delfi/tests/main.nf.test.snap diff --git a/modules/nf-core/finaletoolkit/delfi/main.nf b/modules/nf-core/finaletoolkit/delfi/main.nf index 324cec3b38c2..a0832237d948 100644 --- a/modules/nf-core/finaletoolkit/delfi/main.nf +++ b/modules/nf-core/finaletoolkit/delfi/main.nf @@ -26,10 +26,11 @@ process FINALETOOLKIT_DELFI { """ finaletoolkit \\ delfi \\ - -w ${task.cpus} + -w ${task.cpus} \\ ${bam} \\ ${chromosome_sizes} \\ ${genome_2bit} \\ + ${bins} \\ ${args} \\ -o "${prefix}_delfi.bed" """ diff --git a/modules/nf-core/finaletoolkit/delfi/tests/delfi.config b/modules/nf-core/finaletoolkit/delfi/tests/delfi.config new file mode 100644 index 000000000000..3a402b3e64bf --- /dev/null +++ b/modules/nf-core/finaletoolkit/delfi/tests/delfi.config @@ -0,0 +1,6 @@ +process { + withName: 'FINALETOOLKIT_DELFI' { + cpus = 2 + ext.args = "--no-merge-bins --no-gc-correct --quality-threshold 0" + } +} diff --git a/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test index 03ef5b079543..eebad78ccb46 100644 --- a/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test +++ b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test @@ -17,10 +17,12 @@ nextflow_process { // Chr22 is too small for meaningful data, so remove all filters // We need 2 workers because otherwise there is a bug (fixed upstream, but without a new version) // that breaks the program with only one worker - task.cpus = 2 - ext.args = "--no-merge-bins --no-gc-correct --quality-threshold 0" + module_ } process { + + config "./delfi.config" + """ input[0] = [ [ id:'test' ], diff --git a/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test.snap b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test.snap new file mode 100644 index 000000000000..803c95e35bb2 --- /dev/null +++ b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test.snap @@ -0,0 +1,102 @@ +{ + "homo_sapiens - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test_delfi.bed:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "FINALETOOLKIT_DELFI", + "finaletoolkit", + "0.11.1" + ] + ], + "bed": [ + [ + { + "id": "test" + }, + "test_delfi.bed:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_finaletoolkit": [ + [ + "FINALETOOLKIT_DELFI", + "finaletoolkit", + "0.11.1" + ] + ] + }, + { + "versions_finaletoolkit": [ + [ + "FINALETOOLKIT_DELFI", + "finaletoolkit", + "0.11.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-05-06T14:45:08.530570524" + }, + "homo_sapiens - bam": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test_delfi.bed:md5,125dc88e57b66bf668a8961c81ede4a8" + ] + ], + "1": [ + [ + "FINALETOOLKIT_DELFI", + "finaletoolkit", + "0.11.1" + ] + ], + "bed": [ + [ + { + "id": "test" + }, + "test_delfi.bed:md5,125dc88e57b66bf668a8961c81ede4a8" + ] + ], + "versions_finaletoolkit": [ + [ + "FINALETOOLKIT_DELFI", + "finaletoolkit", + "0.11.1" + ] + ] + }, + { + "versions_finaletoolkit": [ + [ + "FINALETOOLKIT_DELFI", + "finaletoolkit", + "0.11.1" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.4" + }, + "timestamp": "2026-05-07T08:54:00.659731417" + } +} \ No newline at end of file From 277c6b277555f7fdf22af8f95c5d6ab63f09e3a7 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Thu, 7 May 2026 13:53:39 +0200 Subject: [PATCH 3/5] Remove unwanted leftovers --- modules/nf-core/finaletoolkit/delfi/tests/delfi.config | 3 +++ modules/nf-core/finaletoolkit/delfi/tests/main.nf.test | 7 +------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/modules/nf-core/finaletoolkit/delfi/tests/delfi.config b/modules/nf-core/finaletoolkit/delfi/tests/delfi.config index 3a402b3e64bf..ce7d8dbb8565 100644 --- a/modules/nf-core/finaletoolkit/delfi/tests/delfi.config +++ b/modules/nf-core/finaletoolkit/delfi/tests/delfi.config @@ -1,5 +1,8 @@ process { withName: 'FINALETOOLKIT_DELFI' { + // Chr22 is too small for meaningful data, so remove all filters + // We need 2 workers because otherwise there is a bug (fixed upstream, but without a new version) + // that breaks the program with only one worker cpus = 2 ext.args = "--no-merge-bins --no-gc-correct --quality-threshold 0" } diff --git a/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test index eebad78ccb46..1ecdb151830f 100644 --- a/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test +++ b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test @@ -13,12 +13,7 @@ nextflow_process { when { - params { - // Chr22 is too small for meaningful data, so remove all filters - // We need 2 workers because otherwise there is a bug (fixed upstream, but without a new version) - // that breaks the program with only one worker - module_ - } + process { config "./delfi.config" From 329b108287b6d2ed19f94196435e3554db377696 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Fri, 8 May 2026 11:00:17 +0200 Subject: [PATCH 4/5] Address review feedback --- modules/nf-core/finaletoolkit/delfi/main.nf | 6 +- modules/nf-core/finaletoolkit/delfi/meta.yml | 63 ++++++++++++------- .../finaletoolkit/delfi/tests/delfi.config | 9 --- .../finaletoolkit/delfi/tests/main.nf.test | 26 +++++--- 4 files changed, 60 insertions(+), 44 deletions(-) delete mode 100644 modules/nf-core/finaletoolkit/delfi/tests/delfi.config diff --git a/modules/nf-core/finaletoolkit/delfi/main.nf b/modules/nf-core/finaletoolkit/delfi/main.nf index a0832237d948..32d2a056b613 100644 --- a/modules/nf-core/finaletoolkit/delfi/main.nf +++ b/modules/nf-core/finaletoolkit/delfi/main.nf @@ -9,9 +9,9 @@ process FINALETOOLKIT_DELFI { input: tuple val(meta), path(bam), path(bai) - path bins - path chromosome_sizes - path genome_2bit + tuple val(meta2), path(genome_2bit) + tuple val(meta3), path(chromosome_sizes) + tuple val(meta4), path(bins) output: tuple val(meta), path("*.bed"), emit: bed diff --git a/modules/nf-core/finaletoolkit/delfi/meta.yml b/modules/nf-core/finaletoolkit/delfi/meta.yml index 131f1418cc6c..03eedfc182a9 100644 --- a/modules/nf-core/finaletoolkit/delfi/meta.yml +++ b/modules/nf-core/finaletoolkit/delfi/meta.yml @@ -31,29 +31,44 @@ input: description: BAM file index pattern: "*.bai" ontologies: - - edam: "http://edamontology.org/format_3327" # BAI - - bins: - type: file - description: | - BED containing binned genomic coordinates - pattern: "*.bed" - ontologies: - - edam: "http://edamontology.org/format_3003" # BED - - chromosome_sizes: - type: file - pattern: "*.sizes" - description: | - Two-column file containing the size for each chromosome - ontologies: - - edam: "http://edamontology.org/format_3475" # TSV - - genome_2bit: - type: file - description: | - 2bit compressed genome file (must be the same as the one used for the - BAM file) - pattern: "*.2bit" - ontologies: - - edam: "http://edamontology.org/format_3009" # 2bit + - edam: "http://edamontology.org/format_3327" + - - meta2: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - genome_2bit: + type: file + description: | + 2bit compressed genome file (must be the same as the one used for the + BAM file) + pattern: "*.2bit" + ontologies: + - edam: "http://edamontology.org/format_3009" + - - meta3: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - chromosome_sizes: + type: file + pattern: "*.sizes" + description: | + Two-column file containing the size for each chromosome + ontologies: + - edam: "http://edamontology.org/format_3475" + - - meta4: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'sample1' ]` + - bins: + type: file + description: | + BED containing binned genomic coordinates + pattern: "*.bed" + ontologies: + - edam: "http://edamontology.org/format_3003" output: bed: - - meta: @@ -67,7 +82,7 @@ output: description: | BED file containing DELFI scores ontologies: - - edam: "http://edamontology.org/format_3003" # BED + - edam: "http://edamontology.org/format_3003" versions_finaletoolkit: - - ${task.process}: type: string diff --git a/modules/nf-core/finaletoolkit/delfi/tests/delfi.config b/modules/nf-core/finaletoolkit/delfi/tests/delfi.config deleted file mode 100644 index ce7d8dbb8565..000000000000 --- a/modules/nf-core/finaletoolkit/delfi/tests/delfi.config +++ /dev/null @@ -1,9 +0,0 @@ -process { - withName: 'FINALETOOLKIT_DELFI' { - // Chr22 is too small for meaningful data, so remove all filters - // We need 2 workers because otherwise there is a bug (fixed upstream, but without a new version) - // that breaks the program with only one worker - cpus = 2 - ext.args = "--no-merge-bins --no-gc-correct --quality-threshold 0" - } -} diff --git a/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test index 1ecdb151830f..f856c6e7b3b7 100644 --- a/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test +++ b/modules/nf-core/finaletoolkit/delfi/tests/main.nf.test @@ -11,12 +11,11 @@ nextflow_process { test("homo_sapiens - bam") { - when { process { - config "./delfi.config" + config "./nextflow.config" """ input[0] = [ @@ -25,13 +24,17 @@ nextflow_process { file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/NA12878.chr22.bam.bai', checkIfExists: true), ] input[1] = [ - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.bins_10kb.bed', checkIfExists: true) + [id: 'genome' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.2bit', checkIfExists: true) + , ] input[2] = [ - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.sizes', checkIfExists: true) + [id: 'sizes' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.sizes', checkIfExists: true), ] input[3] = [ - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.2bit', checkIfExists: true) + [id: 'bins' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.bins_10kb.bed', checkIfExists: true), ] """ } @@ -56,6 +59,9 @@ nextflow_process { when { process { + + config "./nextflow.config" + """ input[0] = [ [ id:'test' ], @@ -63,13 +69,17 @@ nextflow_process { file(params.modules_testdata_base_path + 'genomics/homo_sapiens/illumina/bam/NA12878.chr22.bam.bai', checkIfExists: true), ] input[1] = [ - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.bins_10kb.bed', checkIfExists: true) + [id: 'genome' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.2bit', checkIfExists: true) + , ] input[2] = [ - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.sizes', checkIfExists: true) + [id: 'sizes' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.sizes', checkIfExists: true), ] input[3] = [ - file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.2bit', checkIfExists: true) + [id: 'bins' ], + file(params.modules_testdata_base_path + 'genomics/homo_sapiens/genome/genome.bins_10kb.bed', checkIfExists: true), ] """ } From e0ece2374ff1f4fe1dbf1a54c86ebf342e87f365 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Fri, 8 May 2026 11:02:10 +0200 Subject: [PATCH 5/5] Add missing file --- .../nf-core/finaletoolkit/delfi/tests/nextflow.config | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 modules/nf-core/finaletoolkit/delfi/tests/nextflow.config diff --git a/modules/nf-core/finaletoolkit/delfi/tests/nextflow.config b/modules/nf-core/finaletoolkit/delfi/tests/nextflow.config new file mode 100644 index 000000000000..ce7d8dbb8565 --- /dev/null +++ b/modules/nf-core/finaletoolkit/delfi/tests/nextflow.config @@ -0,0 +1,9 @@ +process { + withName: 'FINALETOOLKIT_DELFI' { + // Chr22 is too small for meaningful data, so remove all filters + // We need 2 workers because otherwise there is a bug (fixed upstream, but without a new version) + // that breaks the program with only one worker + cpus = 2 + ext.args = "--no-merge-bins --no-gc-correct --quality-threshold 0" + } +}