From 83c686fcff444257b6a69130be52011e99693be8 Mon Sep 17 00:00:00 2001 From: alejandrogzi Date: Wed, 14 Oct 2026 22:47:33 +0200 Subject: [PATCH 1/6] feat: add SpliceAI2 subworkflow and workflow --- modules/nextflow/spliceai2/chunk/main.nf | 60 +++++++++ modules/nextflow/spliceai2/predict/main.nf | 64 +++++++++ modules/nextflow/spliceai2/publish/main.nf | 40 ++++++ .../nextflow/spliceai2/transcripts/main.nf | 64 +++++++++ subworkflows/spliceai2/main.nf | 122 ++++++++++++++++++ workflows/spliceai2/main.nf | 75 +++++++++++ 6 files changed, 425 insertions(+) create mode 100644 modules/nextflow/spliceai2/chunk/main.nf create mode 100644 modules/nextflow/spliceai2/predict/main.nf create mode 100644 modules/nextflow/spliceai2/publish/main.nf create mode 100644 modules/nextflow/spliceai2/transcripts/main.nf create mode 100644 subworkflows/spliceai2/main.nf create mode 100644 workflows/spliceai2/main.nf diff --git a/modules/nextflow/spliceai2/chunk/main.nf b/modules/nextflow/spliceai2/chunk/main.nf new file mode 100644 index 0000000..7dc7b26 --- /dev/null +++ b/modules/nextflow/spliceai2/chunk/main.nf @@ -0,0 +1,60 @@ +/* +Copyright (c) 2026 The Hiller Lab at the Senckenberg Gessellschaft für Naturforschung +Distributed under the terms of the Apache License, Version 2.0. +*/ + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + SPLICEAI2_CHUNK — Chunk genomic sequences for parallel SpliceAI2 prediction. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +process SPLICEAI2_CHUNK { + tag "$meta.id" + label 'process_low' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + '' : + 'ghcr.io/hillerlab/spliceai2:latest' }" + + input: + tuple val(meta), path(genome) + val compression + + output: + tuple val(meta), path("chunks/*.fa"), optional: true, emit: fasta + tuple val(meta), path("chunks/*.fa.gz"), optional: true, emit: fasta_gz + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def gzip = compression ? '--gzip' : '' + """ + spliceai2 chunk \\ + $args \\ + $gzip \\ + --threads ${task.cpus} \\ + --sequence "${genome}" \\ + --outdir chunks + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + spliceai2 chunk: \$( spliceai2 chunk --version | sed 's/spliceai2 chunk //g' ) + END_VERSIONS + """ + + stub: + def extension = compression ? 'fa.gz' : 'fa' + """ + mkdir -p chunks + touch chunks/stub.${extension} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + spliceai2 chunk: \$( spliceai2 chunk --version | sed 's/spliceai2 chunk //g' ) + END_VERSIONS + """ +} diff --git a/modules/nextflow/spliceai2/predict/main.nf b/modules/nextflow/spliceai2/predict/main.nf new file mode 100644 index 0000000..294c30f --- /dev/null +++ b/modules/nextflow/spliceai2/predict/main.nf @@ -0,0 +1,64 @@ +/* +Copyright (c) 2026 The Hiller Lab at the Senckenberg Gessellschaft für Naturforschung +Distributed under the terms of the Apache License, Version 2.0. +*/ + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + SPLICEAI2_PREDICT — Predict splice sites for one sequence chunk. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +process SPLICEAI2_PREDICT { + tag "$meta.id" + label 'process_low' + label 'process_gpu' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + '' : + 'ghcr.io/hillerlab/spliceai2:latest' }" + + input: + tuple val(meta), path(fasta) + path models + val species + + output: + tuple val(meta), path("spliceai/*.donor_plus.wig"), emit: donor_plus + tuple val(meta), path("spliceai/*.donor_minus.wig"), emit: donor_minus + tuple val(meta), path("spliceai/*.acceptor_plus.wig"), emit: acceptor_plus + tuple val(meta), path("spliceai/*.acceptor_minus.wig"), emit: acceptor_minus + tuple val(meta), path("spliceai/*.wig"), emit: all + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + """ + spliceai2 predict \\ + $args \\ + --sequence "${fasta}" \\ + --models "${models}" \\ + --species "${species}" \\ + --outdir spliceai + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + spliceai2: \$( spliceai2 --version | sed 's/^spliceai2 //g' ) + END_VERSIONS + """ + + stub: + """ + mkdir -p spliceai + touch spliceai/stub.donor_plus.wig spliceai/stub.donor_minus.wig \\ + spliceai/stub.acceptor_plus.wig spliceai/stub.acceptor_minus.wig + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + spliceai2: \$( spliceai2 --version | sed 's/^spliceai2 //g' ) + END_VERSIONS + """ +} diff --git a/modules/nextflow/spliceai2/publish/main.nf b/modules/nextflow/spliceai2/publish/main.nf new file mode 100644 index 0000000..affe72f --- /dev/null +++ b/modules/nextflow/spliceai2/publish/main.nf @@ -0,0 +1,40 @@ +/* +Copyright (c) 2026 The Hiller Lab at the Senckenberg Gessellschaft für Naturforschung +Distributed under the terms of the Apache License, Version 2.0. +*/ + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + SPLICEAI2_PUBLISH — Collect the four SpliceAI2 strand and site BigWigs. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +process SPLICEAI2_PUBLISH { + tag "publish" + label 'process_single' + + input: + tuple val(meta), path(donor_plus) + tuple val(meta1), path(donor_minus) + tuple val(meta2), path(acceptor_plus) + tuple val(meta3), path(acceptor_minus) + + output: + path("spliceai2"), emit: spliceai2 + + script: + """ + mkdir -p spliceai2 + cp "${donor_plus}" spliceai2/ + cp "${donor_minus}" spliceai2/ + cp "${acceptor_plus}" spliceai2/ + cp "${acceptor_minus}" spliceai2/ + """ + + stub: + """ + mkdir -p spliceai2 + touch spliceai2/donor_plus.bw spliceai2/donor_minus.bw \\ + spliceai2/acceptor_plus.bw spliceai2/acceptor_minus.bw + """ +} diff --git a/modules/nextflow/spliceai2/transcripts/main.nf b/modules/nextflow/spliceai2/transcripts/main.nf new file mode 100644 index 0000000..5fbb9fb --- /dev/null +++ b/modules/nextflow/spliceai2/transcripts/main.nf @@ -0,0 +1,64 @@ +/* +Copyright (c) 2026 The Hiller Lab at the Senckenberg Gessellschaft für Naturforschung +Distributed under the terms of the Apache License, Version 2.0. +*/ + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + SPLICEAI2_TRANSCRIPTS — Decode transcript structures for a set of loci. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +process SPLICEAI2_TRANSCRIPTS { + tag "$meta.id" + label 'process_low' + label 'process_gpu' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + '' : + 'ghcr.io/hillerlab/spliceai2:latest' }" + + input: + tuple val(meta), path(genome), path(genome_fai), path(loci) + path models + val species + + output: + tuple val(meta), path("transcripts/*.transcripts.bed"), emit: transcripts + tuple val(meta), path("transcripts/*.skipped.bed"), emit: skipped + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + """ + test -s "${genome_fai}" + + spliceai2 transcripts \\ + $args \\ + --genome "${genome}" \\ + --loci "${loci}" \\ + --models "${models}" \\ + --species "${species}" \\ + --outdir transcripts + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + spliceai2: \$( spliceai2 --version | sed 's/^spliceai2 //g' ) + END_VERSIONS + """ + + stub: + def prefix = meta.id + """ + mkdir -p transcripts + touch "transcripts/${prefix}.transcripts.bed" "transcripts/${prefix}.skipped.bed" + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + spliceai2: \$( spliceai2 --version | sed 's/^spliceai2 //g' ) + END_VERSIONS + """ +} diff --git a/subworkflows/spliceai2/main.nf b/subworkflows/spliceai2/main.nf new file mode 100644 index 0000000..f29f93f --- /dev/null +++ b/subworkflows/spliceai2/main.nf @@ -0,0 +1,122 @@ +/* +Copyright (c) 2026 The Hiller Lab at the Senckenberg Gessellschaft für Naturforschung +Distributed under the terms of the Apache License, Version 2.0. +*/ + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + IMPORT LOCAL MODULES/SUBWORKFLOWS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +include { SPLICEAI2_CHUNK } from '../../modules/nextflow/spliceai2/chunk/main.nf' +include { SPLICEAI2_PREDICT } from '../../modules/nextflow/spliceai2/predict/main.nf' +include { SPLICEAI2_TRANSCRIPTS } from '../../modules/nextflow/spliceai2/transcripts/main.nf' +include { SPLICEAI2_PUBLISH } from '../../modules/nextflow/spliceai2/publish/main.nf' + +include { UCSC_WIGTOBIGWIG as WIGTOBIGWIG_DONOR_PLUS } from '../../modules/nextflow/ucsc/wigtobigwig/main.nf' +include { UCSC_WIGTOBIGWIG as WIGTOBIGWIG_DONOR_MINUS } from '../../modules/nextflow/ucsc/wigtobigwig/main.nf' +include { UCSC_WIGTOBIGWIG as WIGTOBIGWIG_ACCEPTOR_PLUS } from '../../modules/nextflow/ucsc/wigtobigwig/main.nf' +include { UCSC_WIGTOBIGWIG as WIGTOBIGWIG_ACCEPTOR_MINUS } from '../../modules/nextflow/ucsc/wigtobigwig/main.nf' + +include { BIGWIGMERGE as BIGWIGMERGE_DONOR_PLUS } from '../../modules/nextflow/bigtools/bigwigmerge/main.nf' +include { BIGWIGMERGE as BIGWIGMERGE_DONOR_MINUS } from '../../modules/nextflow/bigtools/bigwigmerge/main.nf' +include { BIGWIGMERGE as BIGWIGMERGE_ACCEPTOR_PLUS } from '../../modules/nextflow/bigtools/bigwigmerge/main.nf' +include { BIGWIGMERGE as BIGWIGMERGE_ACCEPTOR_MINUS } from '../../modules/nextflow/bigtools/bigwigmerge/main.nf' + +workflow SPLICEAI2 { + take: + genome // channel: [ val(meta), path(genome) ] + chromsizes // channel: [ path(chromsizes) ] + compression // bool + models // path(model directory) + species // SpliceAI2 species name + genome_fai // path(genome.fai), required with loci + loci // path(BED6), optional + ch_versions // channel: [ path(version) ] + + main: + ch_chunks = Channel.empty() + ch_transcripts = Channel.empty() + ch_skipped = Channel.empty() + SPLICEAI2_CHUNK(genome, compression) + + if (compression) { + SPLICEAI2_CHUNK.out.fasta_gz + .flatMap { + meta, fa -> + def fas = fa instanceof List ? fa : [fa] + fas.collect { it -> + def identifier = it.baseName.replaceFirst(/^tmp\./, '').replaceFirst(/\.fa$/, '') + [ [ id: meta.id + '.' + identifier ], it ] + } + } + .set { ch_chunks } + } else { + SPLICEAI2_CHUNK.out.fasta + .flatMap { + meta, fa -> + def fas = fa instanceof List ? fa : [fa] + fas.collect { it -> + def identifier = it.baseName.replaceFirst(/^tmp\./, '').replaceFirst(/\.fa$/, '') + [ [ id: meta.id + '.' + identifier ], it ] + } + } + .set { ch_chunks } + } + + SPLICEAI2_PREDICT(ch_chunks, models, species) + + WIGTOBIGWIG_DONOR_PLUS(SPLICEAI2_PREDICT.out.donor_plus, chromsizes) + WIGTOBIGWIG_DONOR_MINUS(SPLICEAI2_PREDICT.out.donor_minus, chromsizes) + WIGTOBIGWIG_ACCEPTOR_PLUS(SPLICEAI2_PREDICT.out.acceptor_plus, chromsizes) + WIGTOBIGWIG_ACCEPTOR_MINUS(SPLICEAI2_PREDICT.out.acceptor_minus, chromsizes) + + BIGWIGMERGE_DONOR_PLUS( + WIGTOBIGWIG_DONOR_PLUS.out.bigwig + .map { meta, bigwig -> bigwig } + .collect() + .map { bws -> [ [ id : 'donor_plus' ], bws ] }, + ) + BIGWIGMERGE_DONOR_MINUS( + WIGTOBIGWIG_DONOR_MINUS.out.bigwig + .map { meta, bigwig -> bigwig } + .collect() + .map { bws -> [ [ id : 'donor_minus' ], bws ] }, + ) + BIGWIGMERGE_ACCEPTOR_PLUS( + WIGTOBIGWIG_ACCEPTOR_PLUS.out.bigwig + .map { meta, bigwig -> bigwig } + .collect() + .map { bws -> [ [ id : 'acceptor_plus' ], bws ] }, + ) + BIGWIGMERGE_ACCEPTOR_MINUS( + WIGTOBIGWIG_ACCEPTOR_MINUS.out.bigwig + .map { meta, bigwig -> bigwig } + .collect() + .map { bws -> [ [ id : 'acceptor_minus' ], bws ] }, + ) + + SPLICEAI2_PUBLISH( + BIGWIGMERGE_DONOR_PLUS.out.bigwig, + BIGWIGMERGE_DONOR_MINUS.out.bigwig, + BIGWIGMERGE_ACCEPTOR_PLUS.out.bigwig, + BIGWIGMERGE_ACCEPTOR_MINUS.out.bigwig, + ) + + if (loci != null) { + SPLICEAI2_TRANSCRIPTS( + genome.map { meta, fasta -> [ meta, fasta, genome_fai, loci ] }, + models, + species + ) + ch_transcripts = SPLICEAI2_TRANSCRIPTS.out.transcripts + ch_skipped = SPLICEAI2_TRANSCRIPTS.out.skipped + } + + emit: + spliceai2 = SPLICEAI2_PUBLISH.out.spliceai2 + transcripts = ch_transcripts + skipped = ch_skipped + versions = ch_versions +} diff --git a/workflows/spliceai2/main.nf b/workflows/spliceai2/main.nf new file mode 100644 index 0000000..1e44dac --- /dev/null +++ b/workflows/spliceai2/main.nf @@ -0,0 +1,75 @@ +/* +Copyright (c) 2026 The Hiller Lab at the Senckenberg Gessellschaft für Naturforschung +Distributed under the terms of the Apache License, Version 2.0. +*/ + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + IMPORT LOCAL SUBWORKFLOWS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +include { SPLICEAI2 as RUN } from '../../subworkflows/spliceai2/main.nf' +include { GENOME } from '../../subworkflows/genome/main.nf' + +params.genome = null +params.compression = false +params.spliceai2_models = null +params.spliceai2_species = 'GRCh38' +params.spliceai2_loci = null + +workflow SPLICEAI2 { + main: + if (!params.genome) { + error "Missing required --genome" + } + if (!params.spliceai2_models) { + error "Missing required --spliceai2_models" + } + + def genome_fasta = file(params.genome, checkIfExists: true) + def models = file(params.spliceai2_models, checkIfExists: true) + if (!models.toFile().isDirectory()) { + error "--spliceai2_models must be a directory" + } + + def compression = params.compression?.toString()?.toBoolean() ?: false + def species = (params.spliceai2_species ?: 'GRCh38').toString() + def genome_fai = null + def loci = null + if (params.spliceai2_loci) { + if (!(genome_fasta.name.toLowerCase().endsWith('.fa') || genome_fasta.name.toLowerCase().endsWith('.fasta'))) { + error "--spliceai2_loci requires an uncompressed FASTA with a sibling .fai index" + } + genome_fai = file("${genome_fasta}.fai", checkIfExists: true) + if (!genome_fai.toFile().isFile()) { + error "Missing FASTA index '${genome_fasta}.fai' required by --spliceai2_loci" + } + loci = file(params.spliceai2_loci, checkIfExists: true) + if (!loci.toFile().isFile()) { + error "--spliceai2_loci must be a BED file" + } + } + + GENOME(genome_fasta) + + RUN( + GENOME.out.genome.map { g -> [ [ id: g.baseName ], g ] }, + GENOME.out.chrom_sizes, + compression, + models, + species, + genome_fai, + loci, + GENOME.out.versions + ) + + emit: + spliceai2 = RUN.out.spliceai2 + transcripts = RUN.out.transcripts + skipped = RUN.out.skipped +} + +workflow { + SPLICEAI2() +} From 68d348bedc1464ad097d267977cdd60fb450f651 Mon Sep 17 00:00:00 2001 From: alejandrogzi Date: Wed, 14 Oct 2026 23:17:12 +0200 Subject: [PATCH 2/6] fix: collect SpliceAI2 chunks from CLI output --- modules/nextflow/spliceai2/chunk/main.nf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/nextflow/spliceai2/chunk/main.nf b/modules/nextflow/spliceai2/chunk/main.nf index 7dc7b26..b7e307c 100644 --- a/modules/nextflow/spliceai2/chunk/main.nf +++ b/modules/nextflow/spliceai2/chunk/main.nf @@ -22,8 +22,8 @@ process SPLICEAI2_CHUNK { val compression output: - tuple val(meta), path("chunks/*.fa"), optional: true, emit: fasta - tuple val(meta), path("chunks/*.fa.gz"), optional: true, emit: fasta_gz + tuple val(meta), path("chunks/chunks/*.fa"), optional: true, emit: fasta + tuple val(meta), path("chunks/chunks/*.fa.gz"), optional: true, emit: fasta_gz path "versions.yml", emit: versions when: @@ -49,8 +49,8 @@ process SPLICEAI2_CHUNK { stub: def extension = compression ? 'fa.gz' : 'fa' """ - mkdir -p chunks - touch chunks/stub.${extension} + mkdir -p chunks/chunks + touch chunks/chunks/stub.${extension} cat <<-END_VERSIONS > versions.yml "${task.process}": From d18899e87c6e5a02c90bd566178f6108ea0041ad Mon Sep 17 00:00:00 2001 From: alejandrogzi Date: Wed, 14 Oct 2026 23:52:54 +0200 Subject: [PATCH 3/6] fix: address SpliceAI2 Nextflow review --- assets/catalog/catalog.md | 6 ++++++ modules/nextflow/spliceai2/chunk/main.nf | 15 +++++++-------- modules/nextflow/spliceai2/predict/main.nf | 2 +- modules/nextflow/spliceai2/transcripts/main.nf | 4 ++-- subworkflows/spliceai2/main.nf | 8 ++++---- workflows/spliceai2/main.nf | 10 ++++++---- 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/assets/catalog/catalog.md b/assets/catalog/catalog.md index 452203e..d6e96d0 100644 --- a/assets/catalog/catalog.md +++ b/assets/catalog/catalog.md @@ -100,6 +100,12 @@ - [spliceai/derive](../../modules/nextflow/spliceai/derive/main.nf) — Derive splice event scores from SpliceAI predictions. - [spliceai/predict](../../modules/nextflow/spliceai/predict/main.nf) — Predict splice junctions from genomic sequences using SpliceAI. - [spliceai/publish](../../modules/nextflow/spliceai/publish/main.nf) — Collect and organize SpliceAI output files. +- [spliceai2/chunk](../../modules/nextflow/spliceai2/chunk/main.nf) — Chunk genomic sequences for parallel SpliceAI2 prediction. +- [spliceai2/predict](../../modules/nextflow/spliceai2/predict/main.nf) — Predict splice sites from genomic sequences using SpliceAI2. +- [spliceai2/publish](../../modules/nextflow/spliceai2/publish/main.nf) — Collect and organize SpliceAI2 output files. +- [spliceai2/transcripts](../../modules/nextflow/spliceai2/transcripts/main.nf) — Decode transcript structures for a set of loci using SpliceAI2. +- [subworkflows/spliceai2](../../subworkflows/spliceai2/main.nf) — Chunk, predict, convert, merge, and optionally decode SpliceAI2 outputs. +- [workflows/spliceai2](../../workflows/spliceai2/main.nf) — Standalone SpliceAI2 pipeline. - [aparent/chunk](../../modules/nextflow/aparent/chunk/main.nf) — Chunk genomic regions for parallel APARENT prediction. - [aparent/predict](../../modules/nextflow/aparent/predict/main.nf) — Predict polyadenylation sites using the APARENT deep learning model. - [minisplice/download](../../modules/nextflow/minisplice/download/main.nf) — Download MiniSplice model and calibration files. diff --git a/modules/nextflow/spliceai2/chunk/main.nf b/modules/nextflow/spliceai2/chunk/main.nf index b7e307c..6ad7a0b 100644 --- a/modules/nextflow/spliceai2/chunk/main.nf +++ b/modules/nextflow/spliceai2/chunk/main.nf @@ -22,8 +22,8 @@ process SPLICEAI2_CHUNK { val compression output: - tuple val(meta), path("chunks/chunks/*.fa"), optional: true, emit: fasta - tuple val(meta), path("chunks/chunks/*.fa.gz"), optional: true, emit: fasta_gz + tuple val(meta), path("chunks/*.fa"), optional: true, emit: fasta + tuple val(meta), path("chunks/*.fa.gz"), optional: true, emit: fasta_gz path "versions.yml", emit: versions when: @@ -37,24 +37,23 @@ process SPLICEAI2_CHUNK { $args \\ $gzip \\ --threads ${task.cpus} \\ - --sequence "${genome}" \\ - --outdir chunks + --sequence "${genome}" cat <<-END_VERSIONS > versions.yml "${task.process}": - spliceai2 chunk: \$( spliceai2 chunk --version | sed 's/spliceai2 chunk //g' ) + spliceai2 chunk: \$( spliceai2 chunk --version | sed 's/^spliceai-chunk //g' ) END_VERSIONS """ stub: def extension = compression ? 'fa.gz' : 'fa' """ - mkdir -p chunks/chunks - touch chunks/chunks/stub.${extension} + mkdir -p chunks + touch chunks/stub.${extension} cat <<-END_VERSIONS > versions.yml "${task.process}": - spliceai2 chunk: \$( spliceai2 chunk --version | sed 's/spliceai2 chunk //g' ) + spliceai2 chunk: \$( spliceai2 chunk --version | sed 's/^spliceai-chunk //g' ) END_VERSIONS """ } diff --git a/modules/nextflow/spliceai2/predict/main.nf b/modules/nextflow/spliceai2/predict/main.nf index 294c30f..e983b56 100644 --- a/modules/nextflow/spliceai2/predict/main.nf +++ b/modules/nextflow/spliceai2/predict/main.nf @@ -37,7 +37,7 @@ process SPLICEAI2_PREDICT { script: def args = task.ext.args ?: '' """ - spliceai2 predict \\ + spliceai2 predict --no-compile \\ $args \\ --sequence "${fasta}" \\ --models "${models}" \\ diff --git a/modules/nextflow/spliceai2/transcripts/main.nf b/modules/nextflow/spliceai2/transcripts/main.nf index 5fbb9fb..33b7bed 100644 --- a/modules/nextflow/spliceai2/transcripts/main.nf +++ b/modules/nextflow/spliceai2/transcripts/main.nf @@ -11,7 +11,7 @@ Distributed under the terms of the Apache License, Version 2.0. process SPLICEAI2_TRANSCRIPTS { tag "$meta.id" - label 'process_low' + label 'process_high' label 'process_gpu' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? @@ -36,7 +36,7 @@ process SPLICEAI2_TRANSCRIPTS { """ test -s "${genome_fai}" - spliceai2 transcripts \\ + spliceai2 transcripts --no-compile \\ $args \\ --genome "${genome}" \\ --loci "${loci}" \\ diff --git a/subworkflows/spliceai2/main.nf b/subworkflows/spliceai2/main.nf index f29f93f..cb946e1 100644 --- a/subworkflows/spliceai2/main.nf +++ b/subworkflows/spliceai2/main.nf @@ -67,10 +67,10 @@ workflow SPLICEAI2 { SPLICEAI2_PREDICT(ch_chunks, models, species) - WIGTOBIGWIG_DONOR_PLUS(SPLICEAI2_PREDICT.out.donor_plus, chromsizes) - WIGTOBIGWIG_DONOR_MINUS(SPLICEAI2_PREDICT.out.donor_minus, chromsizes) - WIGTOBIGWIG_ACCEPTOR_PLUS(SPLICEAI2_PREDICT.out.acceptor_plus, chromsizes) - WIGTOBIGWIG_ACCEPTOR_MINUS(SPLICEAI2_PREDICT.out.acceptor_minus, chromsizes) + WIGTOBIGWIG_DONOR_PLUS(SPLICEAI2_PREDICT.out.donor_plus.map { meta, wig -> [ [ id: 'donor_plus' ], wig ] }, chromsizes) + WIGTOBIGWIG_DONOR_MINUS(SPLICEAI2_PREDICT.out.donor_minus.map { meta, wig -> [ [ id: 'donor_minus' ], wig ] }, chromsizes) + WIGTOBIGWIG_ACCEPTOR_PLUS(SPLICEAI2_PREDICT.out.acceptor_plus.map { meta, wig -> [ [ id: 'acceptor_plus' ], wig ] }, chromsizes) + WIGTOBIGWIG_ACCEPTOR_MINUS(SPLICEAI2_PREDICT.out.acceptor_minus.map { meta, wig -> [ [ id: 'acceptor_minus' ], wig ] }, chromsizes) BIGWIGMERGE_DONOR_PLUS( WIGTOBIGWIG_DONOR_PLUS.out.bigwig diff --git a/workflows/spliceai2/main.nf b/workflows/spliceai2/main.nf index 1e44dac..7970b22 100644 --- a/workflows/spliceai2/main.nf +++ b/workflows/spliceai2/main.nf @@ -38,12 +38,14 @@ workflow SPLICEAI2 { def genome_fai = null def loci = null if (params.spliceai2_loci) { - if (!(genome_fasta.name.toLowerCase().endsWith('.fa') || genome_fasta.name.toLowerCase().endsWith('.fasta'))) { - error "--spliceai2_loci requires an uncompressed FASTA with a sibling .fai index" + def uncompressed_genome = genome_fasta.toString().replaceFirst(/\.gz$/, '') + def fasta_name = uncompressed_genome.toLowerCase() + if (!(fasta_name.endsWith('.fa') || fasta_name.endsWith('.fasta'))) { + error "--spliceai2_loci requires a .fa or .fasta genome, optionally compressed as .gz" } - genome_fai = file("${genome_fasta}.fai", checkIfExists: true) + genome_fai = file("${uncompressed_genome}.fai", checkIfExists: false) if (!genome_fai.toFile().isFile()) { - error "Missing FASTA index '${genome_fasta}.fai' required by --spliceai2_loci" + error "Missing FASTA index '${genome_fai}' required by --spliceai2_loci; provide an index made with samtools faidx because this repository has no samtools faidx module" } loci = file(params.spliceai2_loci, checkIfExists: true) if (!loci.toFile().isFile()) { From ab9ad676f280acca6d4119f8236b536601cada3c Mon Sep 17 00:00:00 2001 From: alejandrogzi Date: Thu, 15 Oct 2026 00:07:36 +0200 Subject: [PATCH 4/6] fix: keep SpliceAI2 intermediates distinct from merges --- subworkflows/spliceai2/main.nf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/subworkflows/spliceai2/main.nf b/subworkflows/spliceai2/main.nf index cb946e1..bf181c5 100644 --- a/subworkflows/spliceai2/main.nf +++ b/subworkflows/spliceai2/main.nf @@ -67,10 +67,10 @@ workflow SPLICEAI2 { SPLICEAI2_PREDICT(ch_chunks, models, species) - WIGTOBIGWIG_DONOR_PLUS(SPLICEAI2_PREDICT.out.donor_plus.map { meta, wig -> [ [ id: 'donor_plus' ], wig ] }, chromsizes) - WIGTOBIGWIG_DONOR_MINUS(SPLICEAI2_PREDICT.out.donor_minus.map { meta, wig -> [ [ id: 'donor_minus' ], wig ] }, chromsizes) - WIGTOBIGWIG_ACCEPTOR_PLUS(SPLICEAI2_PREDICT.out.acceptor_plus.map { meta, wig -> [ [ id: 'acceptor_plus' ], wig ] }, chromsizes) - WIGTOBIGWIG_ACCEPTOR_MINUS(SPLICEAI2_PREDICT.out.acceptor_minus.map { meta, wig -> [ [ id: 'acceptor_minus' ], wig ] }, chromsizes) + WIGTOBIGWIG_DONOR_PLUS(SPLICEAI2_PREDICT.out.donor_plus.map { meta, wig -> [ [ id: meta.id + '.donor_plus' ], wig ] }, chromsizes) + WIGTOBIGWIG_DONOR_MINUS(SPLICEAI2_PREDICT.out.donor_minus.map { meta, wig -> [ [ id: meta.id + '.donor_minus' ], wig ] }, chromsizes) + WIGTOBIGWIG_ACCEPTOR_PLUS(SPLICEAI2_PREDICT.out.acceptor_plus.map { meta, wig -> [ [ id: meta.id + '.acceptor_plus' ], wig ] }, chromsizes) + WIGTOBIGWIG_ACCEPTOR_MINUS(SPLICEAI2_PREDICT.out.acceptor_minus.map { meta, wig -> [ [ id: meta.id + '.acceptor_minus' ], wig ] }, chromsizes) BIGWIGMERGE_DONOR_PLUS( WIGTOBIGWIG_DONOR_PLUS.out.bigwig From 2e4c0cd1ea7d9fce21ba7949b54284d9ddcb7fba Mon Sep 17 00:00:00 2001 From: alejandrogzi Date: Thu, 15 Oct 2026 00:51:12 +0200 Subject: [PATCH 5/6] fix: say the loci FASTA index is built on the uncompressed genome --- workflows/spliceai2/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/spliceai2/main.nf b/workflows/spliceai2/main.nf index 7970b22..ddf70f7 100644 --- a/workflows/spliceai2/main.nf +++ b/workflows/spliceai2/main.nf @@ -45,7 +45,7 @@ workflow SPLICEAI2 { } genome_fai = file("${uncompressed_genome}.fai", checkIfExists: false) if (!genome_fai.toFile().isFile()) { - error "Missing FASTA index '${genome_fai}' required by --spliceai2_loci; provide an index made with samtools faidx because this repository has no samtools faidx module" + error "Missing FASTA index '${genome_fai}' required by --spliceai2_loci; index the uncompressed FASTA (e.g. gunzip -k genome.fa.gz && samtools faidx genome.fa) and keep the .fai next to --genome" } loci = file(params.spliceai2_loci, checkIfExists: true) if (!loci.toFile().isFile()) { From b3009ca6bca945b581adb439f5875963fd8417f4 Mon Sep 17 00:00:00 2001 From: Alejandro Gonzales-Irribarren Date: Thu, 15 Oct 2026 02:02:29 +0200 Subject: [PATCH 6/6] Escape dollar sign in version command --- modules/nextflow/splicetransformer/derive/main.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/nextflow/splicetransformer/derive/main.nf b/modules/nextflow/splicetransformer/derive/main.nf index 0db9b8d..dbaace5 100644 --- a/modules/nextflow/splicetransformer/derive/main.nf +++ b/modules/nextflow/splicetransformer/derive/main.nf @@ -46,7 +46,7 @@ process SPLICETRANSFORMER_DERIVE { cat <<-END_VERSIONS > versions.yml "${task.process}": - splicetransformer derive: $( splicetransformer derive --version | sed 's/splicetransformer derive //g' ) + splicetransformer derive: \$( splicetransformer derive --version | sed 's/splicetransformer derive //g' ) END_VERSIONS """ @@ -56,7 +56,7 @@ process SPLICETRANSFORMER_DERIVE { cat <<-END_VERSIONS > versions.yml "${task.process}": - splicetransformer derive: $( splicetransformer derive --version | sed 's/splicetransformer derive //g' ) + splicetransformer derive: \$( splicetransformer derive --version | sed 's/splicetransformer derive //g' ) END_VERSIONS """ }