From c535e85a82dc29608e66dacb3a9fae4b07ef00c7 Mon Sep 17 00:00:00 2001 From: alejandrogzi Date: Fri, 4 Sep 2026 16:42:57 +0200 Subject: [PATCH] feat(tiberius): add prediction, merge, and scatter workflow Integrates Tiberius as Nextflow modules, a reusable subworkflow, and a standalone workflow. Scatter modes none/chromosome/weighted; merge restores FASTA order and rewrites gene IDs by (seqid, start, strand). --- assets/catalog/catalog.md | 4 + modules/nextflow/tiberius/merge/main.nf | 74 +++++ modules/nextflow/tiberius/predict/main.nf | 95 ++++++ subworkflows/tiberius/main.nf | 382 ++++++++++++++++++++++ workflows/tiberius/main.nf | 86 +++++ 5 files changed, 641 insertions(+) create mode 100644 modules/nextflow/tiberius/merge/main.nf create mode 100644 modules/nextflow/tiberius/predict/main.nf create mode 100644 subworkflows/tiberius/main.nf create mode 100644 workflows/tiberius/main.nf diff --git a/assets/catalog/catalog.md b/assets/catalog/catalog.md index 212851e..09dfd85 100644 --- a/assets/catalog/catalog.md +++ b/assets/catalog/catalog.md @@ -116,6 +116,10 @@ - [annevo/decoding](../../modules/nextflow/annevo/decoding/main.nf) — Decode ANNEVO H5 predictions into GFF3 on CPU. - [subworkflows/annevo](../../subworkflows/annevo/main.nf) — Scatter (none / chromosome / weighted), predict, decode, and gather GFF3. - [workflows/annevo](../../workflows/annevo/main.nf) — Standalone ANNEVO pipeline. Bundled ANNEVO is non-commercial. +- [tiberius/predict](../../modules/nextflow/tiberius/predict/main.nf) — Tiberius ab initio inference (`process_gpu`; CPU if no GPU profile). Scatter jobs emit GTF only. +- [tiberius/merge](../../modules/nextflow/tiberius/merge/main.nf) — Merge chunk GTFs with merge_annotations.py, restore FASTA order, emit GTF/GFF3 and optional protein/CDS FASTAs. +- [subworkflows/tiberius](../../subworkflows/tiberius/main.nf) — Scatter (none / chromosome / weighted), predict, and merge. +- [workflows/tiberius](../../workflows/tiberius/main.nf) — Standalone Tiberius pipeline. --- diff --git a/modules/nextflow/tiberius/merge/main.nf b/modules/nextflow/tiberius/merge/main.nf new file mode 100644 index 0000000..0cd19a6 --- /dev/null +++ b/modules/nextflow/tiberius/merge/main.nf @@ -0,0 +1,74 @@ +/* +Copyright (c) 2026 The Hiller Lab at the Senckenberg Gessellschaft für Naturforschung +Distributed under the terms of the Apache License, Version 2.0. +*/ + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + TIBERIUS_MERGE — Merge scattered Tiberius chunk annotations. + + Runs merge_annotations.py (full mode), restores FASTA record order from + the sequence-order manifest, writes GTF, and optionally extracts + protein/CDS FASTAs from the merged annotation plus the original genome. + Gene IDs are assigned by (seqid, start, strand), not chunk order. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +process TIBERIUS_MERGE { + tag "$meta.id" + label 'process_medium' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + '' : + 'ghcr.io/hillerlab/tiberius:latest' }" + + input: + tuple val(meta), path(annotations), path(manifest), path(genome) + val emit_protseq + val emit_codingseq + + output: + tuple val(meta), path("*.gtf"), emit: gtf + tuple val(meta), path("*.gff3"), emit: gff + tuple val(meta), path("*.prot"), emit: proteins, optional: true + tuple val(meta), path("*.cds"), emit: codingseq, optional: true + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def prefix = task.ext.prefix ?: "${meta.id}" + def prot_flag = emit_protseq ? '--protseq' : '' + def cds_flag = emit_codingseq ? '--codingseq' : '' + """ + tiberius-merge \\ + --manifest ${manifest} \\ + --out-prefix ${prefix} \\ + --genome ${genome} \\ + ${prot_flag} \\ + ${cds_flag} \\ + ${annotations} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + tiberius: \$( python3 -c "import tiberius; print(tiberius.__version__)" ) + END_VERSIONS + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + def emit_prot = emit_protseq ? true : false + def emit_cds = emit_codingseq ? true : false + """ + touch ${prefix}.gtf + touch ${prefix}.gff3 + ${emit_prot ? "touch ${prefix}.prot" : ''} + ${emit_cds ? "touch ${prefix}.cds" : ''} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + tiberius: \$( python3 -c "import tiberius; print(tiberius.__version__)" 2>/dev/null || echo "2.0.7" ) + END_VERSIONS + """ +} diff --git a/modules/nextflow/tiberius/predict/main.nf b/modules/nextflow/tiberius/predict/main.nf new file mode 100644 index 0000000..922acde --- /dev/null +++ b/modules/nextflow/tiberius/predict/main.nf @@ -0,0 +1,95 @@ +/* +Copyright (c) 2026 The Hiller Lab at the Senckenberg Gessellschaft für Naturforschung +Distributed under the terms of the Apache License, Version 2.0. +*/ + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + TIBERIUS_PREDICT — Ab initio Tiberius inference. + + Thin wrapper around `tiberius.py`. GPU is used when `-profile gpu` + exposes a device; otherwise TensorFlow falls back to CPU. Scatter jobs + should emit GTF only; GFF3 and protein/CDS FASTAs of a scattered run + come from TIBERIUS_MERGE. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +process TIBERIUS_PREDICT { + tag "$meta.id" + label 'process_high' + label 'process_gpu' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + '' : + 'ghcr.io/hillerlab/tiberius:latest' }" + + input: + tuple val(meta), path(genome) + val model_cfg + + output: + tuple val(meta), path("*.gtf"), emit: gtf + tuple val(meta), path("*.gff3"), emit: gff, optional: true + tuple val(meta), path("*.prot"), emit: proteins, optional: true + tuple val(meta), path("*.cds"), emit: codingseq, optional: true + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = [task.ext.args ?: '', meta.tiberius_args ?: ''].findAll { it }.join(' ') + def prefix = task.ext.prefix ?: "${meta.id}" + def custom = task.ext.model ?: (meta.model ?: '') + def emit_gff3 = meta.tiberius_emit_gff3 == true + def emit_prot = meta.tiberius_protseq == true + def emit_cds = meta.tiberius_codingseq == true + def out_files = emit_gff3 ? "${prefix}.gtf ${prefix}.gff3" : "${prefix}.gtf" + def prot_flag = emit_prot ? "--protseq ${prefix}.prot" : '' + def cds_flag = emit_cds ? "--codingseq ${prefix}.cds" : '' + """ + set -euo pipefail + model_args="" + if [ -n "${custom}" ]; then + model_args="--model ${custom}" + else + yaml=\$(awk -F '\\t' -v a="${model_cfg}" '\$1==a {print \$2; exit}' /opt/tiberius/models.tsv) + if [ -z "\$yaml" ]; then + echo "Unknown --model_cfg alias '${model_cfg}'. Use a hiller_alias from /opt/tiberius/models.tsv." >&2 + awk -F '\\t' '\$1 !~ /^#/ && \$1 != "hiller_alias" {print \$1}' /opt/tiberius/models.tsv >&2 + exit 1 + fi + model_args="--model_cfg /opt/tiberius/model_cfg/\$yaml" + fi + + tiberius.py \\ + --genome ${genome} \\ + \${model_args} \\ + --out ${out_files} \\ + ${prot_flag} \\ + ${cds_flag} \\ + ${args} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + tiberius: \$( python3 -c "import tiberius; print(tiberius.__version__)" ) + END_VERSIONS + """ + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + def emit_gff3 = meta.tiberius_emit_gff3 == true + def emit_prot = meta.tiberius_protseq == true + def emit_cds = meta.tiberius_codingseq == true + """ + touch ${prefix}.gtf + ${emit_gff3 ? "touch ${prefix}.gff3" : ''} + ${emit_prot ? "touch ${prefix}.prot" : ''} + ${emit_cds ? "touch ${prefix}.cds" : ''} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + tiberius: \$( python3 -c "import tiberius; print(tiberius.__version__)" 2>/dev/null || echo "2.0.7" ) + END_VERSIONS + """ +} diff --git a/subworkflows/tiberius/main.nf b/subworkflows/tiberius/main.nf new file mode 100644 index 0000000..22e4586 --- /dev/null +++ b/subworkflows/tiberius/main.nf @@ -0,0 +1,382 @@ +/* +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 { FXSPLIT } from '../../modules/nextflow/fxsplit/main.nf' +include { TIBERIUS_PREDICT } from '../../modules/nextflow/tiberius/predict/main.nf' +include { TIBERIUS_MERGE } from '../../modules/nextflow/tiberius/merge/main.nf' + +def tiberiusScatterModes() { + return ['none', 'chromosome', 'weighted'] +} + +def tiberiusBool(value) { + if (value == null) { + return false + } + if (value instanceof Boolean) { + return value + } + def text = value.toString().trim().toLowerCase() + if (text in ['true', '1', 'yes', 'y', 'on']) { + return true + } + if (text in ['false', '0', 'no', 'n', 'off', '']) { + return false + } + error "Cannot parse boolean value: ${value}" +} + +def tiberiusPackBins(records, nBins) { + def n = Math.max(1, nBins as int) + n = Math.min(n, records.size()) + def bins = (0.. + def byLen = (b.length as long) <=> (a.length as long) + byLen != 0 ? byLen : ((a.order as int) <=> (b.order as int)) + }.each { rec -> + def target = bins.min { x, y -> x.total <=> y.total ?: x.order <=> y.order } + target.records << rec + target.total += rec.length as long + } + bins.each { bin -> bin.records = bin.records.toSorted { it.order } } + return bins.findAll { !it.records.isEmpty() } +} + +def tiberiusResolveRecord(seqid, files) { + def exact = files.find { it.baseName == seqid } + if (exact) { + return exact + } + def prefixed = files.find { it.baseName.startsWith(seqid + '.') || it.baseName.startsWith(seqid + '_') } + if (prefixed) { + return prefixed + } + def safe = seqid.replaceAll(/[^A-Za-z0-9._-]/, '_') + def sanitized = files.find { it.baseName == safe || it.baseName.startsWith(safe + '.') } + if (sanitized) { + return sanitized + } + error "No FXSPLIT output for FASTA record '${seqid}' among ${files*.name}" +} + +def tiberiusParseManifest(path) { + def records = [] + path.withReader { reader -> + def header = reader.readLine() + reader.eachLine { line -> + if (!line) { + return + } + def parts = line.split('\t', 3) + records << [ + order : parts[0] as int, + seqid : parts[1], + length: parts[2] as long, + ] + } + } + return records +} + +process TIBERIUS_MANIFEST { + tag "$meta.id" + label 'process_single' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + '' : + 'ghcr.io/hillerlab/tiberius:latest' }" + + input: + tuple val(meta), path(fasta) + + output: + tuple val(meta), path("records.tsv"), emit: manifest + path "versions.yml", emit: versions + + script: + """ + awk 'BEGIN { OFS="\\t"; print "order", "seqid", "length" } + /^>/ { + if (seqid != "") print order, seqid, len + if (seqid != "") order++ + seqid = substr(\$1, 2) + len = 0 + next + } + { + gsub(/\\r/, "") + len += length(\$0) + } + END { + if (seqid != "") print order, seqid, len + }' ${fasta} > records.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + awk: \$( { awk --version || awk -W version || echo awk; } 2>/dev/null | head -n 1 ) + END_VERSIONS + """ + + stub: + """ + echo -e "order\\tseqid\\tlength\\n0\\tchr1\\t1" > records.tsv + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + awk: \$( { awk --version || awk -W version || echo awk; } 2>/dev/null | head -n 1 ) + END_VERSIONS + """ +} + +process TIBERIUS_CAT { + tag "$meta.id" + label 'process_single' + + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + '' : + 'ghcr.io/hillerlab/tiberius:latest' }" + + input: + tuple val(meta), path(fastas) + + output: + tuple val(meta), path("*.fa"), emit: fasta + path "versions.yml", emit: versions + + script: + prefix = task.ext.prefix ?: "${meta.id}" + """ + cat ${fastas} > ${prefix}.fa + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: \$( cat --version | head -n 1 | sed 's/.* //' ) + END_VERSIONS + """ + + stub: + prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.fa + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: na + END_VERSIONS + """ +} + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + TIBERIUS subworkflow +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +workflow TIBERIUS { + take: + genome // channel: [ val(meta), path(fasta) ] + model_cfg // value: hiller alias from models.tsv + scatter_mode // value: none|chromosome|weighted + scatter_bins // value: integer, used when scatter_mode == weighted + emit_protseq // value: boolean + emit_codingseq // value: boolean + + main: + ch_versions = Channel.empty() + + def mode = scatter_mode.toString() + def scatter_modes = tiberiusScatterModes() + if (!scatter_modes.contains(mode)) { + error "Unsupported Tiberius scatter mode '${mode}'. Choose one of: ${scatter_modes.join(', ')}" + } + + def want_prot = tiberiusBool(emit_protseq) + def want_cds = tiberiusBool(emit_codingseq) + + def n_bins = 8 + if (scatter_bins != null && scatter_bins.toString().trim()) { + n_bins = scatter_bins as int + } + if (mode == 'weighted' && n_bins < 1) { + error "Tiberius weighted scatter requires --tiberius_bins >= 1" + } + + genome + .map { meta, fasta -> + def sample = meta.id ?: fasta.baseName + def flags = [] + if (meta.tiberius_args) { + flags << meta.tiberius_args + } + [ + meta + [ + sample : sample, + tiberius_args : flags.join(' '), + tiberius_protseq: want_prot, + tiberius_codingseq: want_cds, + ], + fasta, + ] + } + .set { ch_prepared } + + if (mode == 'none') { + ch_prepared + .map { meta, fasta -> + [ + meta + [ + id : meta.sample, + chunk : 'all', + order : 0, + tiberius_emit_gff3 : true, + ], + fasta, + ] + } + .set { ch_pieces } + + TIBERIUS_PREDICT(ch_pieces, model_cfg) + ch_versions = ch_versions.mix(TIBERIUS_PREDICT.out.versions) + + TIBERIUS_PREDICT.out.gtf + .map { meta, gtf -> [[id: meta.sample, sample: meta.sample], gtf] } + .set { ch_gtf } + TIBERIUS_PREDICT.out.gff + .map { meta, gff -> [[id: meta.sample, sample: meta.sample], gff] } + .set { ch_gff } + TIBERIUS_PREDICT.out.proteins + .map { meta, fa -> [[id: meta.sample, sample: meta.sample], fa] } + .set { ch_prot } + TIBERIUS_PREDICT.out.codingseq + .map { meta, fa -> [[id: meta.sample, sample: meta.sample], fa] } + .set { ch_cds } + } else { + TIBERIUS_MANIFEST(ch_prepared) + ch_versions = ch_versions.mix(TIBERIUS_MANIFEST.out.versions) + + TIBERIUS_MANIFEST.out.manifest + .map { meta, man -> + def records = tiberiusParseManifest(man) + if (records.isEmpty()) { + error "No FASTA records found for ${meta.sample}" + } + if (mode == 'chromosome' && records.size() > 1000) { + error "Tiberius chromosome scatter would launch ${records.size()} jobs for ${meta.sample}. Use --tiberius_scatter weighted --tiberius_bins N" + } + tuple(meta.sample, true) + } + .set { ch_scatter_ok } + + ch_prepared + .map { meta, fasta -> tuple(meta.sample, meta, fasta) } + .join(ch_scatter_ok) + .map { sample, meta, fasta, ok -> [meta + [headers: true], fasta] } + .set { ch_to_split } + + FXSPLIT(ch_to_split) + ch_versions = ch_versions.mix(FXSPLIT.out.versions) + + FXSPLIT.out.fastx + .mix(FXSPLIT.out.fastx_gz) + .map { meta, files -> tuple(meta.sample, meta, files instanceof List ? files : [files]) } + .join( + TIBERIUS_MANIFEST.out.manifest.map { meta, man -> tuple(meta.sample, man) } + ) + .flatMap { sample, meta, files, man -> + def records = tiberiusParseManifest(man) + if (records.isEmpty()) { + error "No FASTA records found for ${sample}" + } + if (mode == 'weighted') { + return tiberiusPackBins(records, n_bins).collect { bin -> + def chunk = "bin${(bin.order + 1).toString().padLeft(3, '0')}" + [ + meta + [ + id : "${meta.sample}.${chunk}", + chunk : chunk, + order : bin.order, + records : bin.records.collect { it.seqid }, + tiberius_emit_gff3 : false, + tiberius_protseq : false, + tiberius_codingseq : false, + ], + bin.records.collect { rec -> tiberiusResolveRecord(rec.seqid, files) }, + ] + } + } + records.collect { rec -> + [ + meta + [ + id : "${meta.sample}.${rec.seqid}", + chunk : rec.seqid, + order : rec.order, + records : [rec.seqid], + tiberius_emit_gff3 : false, + tiberius_protseq : false, + tiberius_codingseq : false, + ], + tiberiusResolveRecord(rec.seqid, files), + ] + } + } + .set { ch_split } + + if (mode == 'weighted') { + TIBERIUS_CAT(ch_split) + ch_versions = ch_versions.mix(TIBERIUS_CAT.out.versions) + TIBERIUS_CAT.out.fasta.set { ch_pieces } + } else { + ch_split.set { ch_pieces } + } + + TIBERIUS_PREDICT(ch_pieces, model_cfg) + ch_versions = ch_versions.mix(TIBERIUS_PREDICT.out.versions) + + TIBERIUS_PREDICT.out.gtf + .map { meta, gtf -> tuple(meta.sample, meta, gtf) } + .groupTuple() + .map { sample, metas, gtfs -> + def paired = [metas, gtfs].transpose().sort { it[0].order } + def first = paired[0][0] + [ + [ + id : sample, + sample : sample, + ], + paired.collect { it[1] }, + ] + } + .map { meta, gtfs -> tuple(meta.id, meta, gtfs) } + .join( + TIBERIUS_MANIFEST.out.manifest.map { meta, man -> tuple(meta.sample, man) } + ) + .join( + ch_prepared.map { meta, fasta -> tuple(meta.sample, fasta) } + ) + .map { sample, meta, gtfs, man, fasta -> [meta, gtfs, man, fasta] } + .set { ch_merge_input } + + TIBERIUS_MERGE(ch_merge_input, want_prot, want_cds) + ch_versions = ch_versions.mix(TIBERIUS_MERGE.out.versions) + + TIBERIUS_MERGE.out.gtf.set { ch_gtf } + TIBERIUS_MERGE.out.gff.set { ch_gff } + TIBERIUS_MERGE.out.proteins.set { ch_prot } + TIBERIUS_MERGE.out.codingseq.set { ch_cds } + } + + emit: + gtf = ch_gtf + gff = ch_gff + proteins = ch_prot + codingseq = ch_cds + versions = ch_versions +} diff --git a/workflows/tiberius/main.nf b/workflows/tiberius/main.nf new file mode 100644 index 0000000..fb6c524 --- /dev/null +++ b/workflows/tiberius/main.nf @@ -0,0 +1,86 @@ +/* +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 { TIBERIUS as RUN } from '../../subworkflows/tiberius/main.nf' +include { GENOME } from '../../subworkflows/genome/main.nf' + +params.genome = null +params.model_cfg = null +params.tiberius_scatter = 'chromosome' +params.tiberius_bins = 8 +params.tiberius_batch_size = null +params.tiberius_seq_len = null +params.tiberius_protseq = false +params.tiberius_codingseq = false +params.tiberius_extra_args = '' + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + WORKFLOWS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +workflow TIBERIUS { + main: + if (!params.genome) { + error "Missing required --genome" + } + if (!params.model_cfg) { + error "Missing required --model_cfg (Hiller alias from models.tsv, e.g. mammalia_nosoftmasking_v2)" + } + + def scatter = (params.tiberius_scatter ?: 'chromosome').toString() + def bins = params.tiberius_bins ?: 8 + + def flags = [] + if (params.tiberius_batch_size) { + flags << "--batch_size ${params.tiberius_batch_size}" + } + if (params.tiberius_seq_len) { + flags << "--seq_len ${params.tiberius_seq_len}" + } + if (params.tiberius_extra_args) { + flags << params.tiberius_extra_args + } + + GENOME(params.genome) + + GENOME.out.genome + .map { fasta -> + [ + [ + id : fasta.baseName, + tiberius_args : flags.join(' '), + ], + fasta, + ] + } + .set { ch_input } + + RUN( + ch_input, + params.model_cfg, + scatter, + bins, + params.tiberius_protseq, + params.tiberius_codingseq + ) + + emit: + gtf = RUN.out.gtf + gff = RUN.out.gff + proteins = RUN.out.proteins + codingseq = RUN.out.codingseq +} + +workflow { + TIBERIUS() +}