From e57aed58ccda65a89be742ec6d62e5de319dda77 Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Thu, 23 Apr 2026 19:02:55 +0000 Subject: [PATCH 01/11] Release 3.2.0: bump version and address PR #447 review feedback. --- .nf-core.yml | 2 +- CHANGELOG.md | 11 ++++- assets/multiqc_config.yml | 2 +- assets/search_presets.tsv | 20 ++++----- conf/modules.config | 4 +- conf/test_single_quant.config | 13 +++--- .../environment.yml | 2 +- .../featurefinderidentification/main.nf | 4 +- .../openms/fileconverter/environment.yml | 2 +- modules/local/openms/fileconverter/main.nf | 6 +-- .../openms/idconflictresolver/environment.yml | 2 +- .../local/openms/idconflictresolver/main.nf | 4 +- modules/local/openms/idmassaccuracy/main.nf | 2 +- modules/local/openms/idmassaccuracy/meta.yml | 44 ++++++++++++------- nextflow.config | 2 +- ro-crate-metadata.json | 38 ++++++++-------- .../utils_nfcore_mhcquant_pipeline/main.nf | 18 +------- tests/sdrf.nf.test | 1 + tests/test_single_quant.nf.test | 2 +- 19 files changed, 94 insertions(+), 85 deletions(-) diff --git a/.nf-core.yml b/.nf-core.yml index 1a7fed6b..49880877 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -15,4 +15,4 @@ template: skip_features: - fastqc - igenomes - version: 3.2.0dev + version: 3.2.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index bb435261..c6f733b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,13 @@ 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). -## 3.2.0dev - [release name] - [date] +## 3.2.0 - [release name] - 23/04/26 ### `Added` - Added support for single run quantification [#438](https://github.com/nf-core/mhcquant/pull/438) - Added per-sample search parameter support via samplesheet with SearchPreset column and individual parameter overrides [#439](https://github.com/nf-core/mhcquant/pull/439) +- Added PRIDE ID and SDRF sheet support [#445](https://github.com/nf-core/mhcquant/pull/445) - Added ion mobility export and MultiQC distribution plot for timsTOF data [#441](https://github.com/nf-core/mhcquant/pull/441) ### `Fixed` @@ -16,10 +17,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed `EPICORE` running only once instead of per sample when `--fasta` is used, by broadcasting `ch_fasta` to `EPICORE` via `.first()` [#446](https://github.com/nf-core/mhcquant/pull/446) - Fixed `SUMMARIZE_RESULTS` crash with `--quantify` caused by OpenMS 3.5.0 TextExporter phantom column bug ([OpenMS/OpenMS#9120](https://github.com/OpenMS/OpenMS/issues/9120)) [#444](https://github.com/nf-core/mhcquant/pull/444) - Fixed an issue where stripping the sequence in `SUMMARIZE_RESULTS` did not work for complex modifications [#436](https://github.com/nf-core/mhcquant/pull/436) +- Fixed `tdf2mzml` container entrypoint issue by pinning to `0.4_noentry` and invoking the CLI explicitly [#447](https://github.com/nf-core/mhcquant/pull/447) +- Fixed `OPENMS_FILECONVERTER` version extraction emitting a SOH byte due to a single-backslash sed backreference [#447](https://github.com/nf-core/mhcquant/pull/447) +- Fixed `OPENMS_IDMASSACCURACY` process argument to use `meta.precursor_error_units` so per-sample preset overrides apply [#447](https://github.com/nf-core/mhcquant/pull/447) ### `Changed` - Migrate to topic channels [#431](https://github.com/nf-core/mhcquant/pull/431) +- Bumped `openms/fileconverter`, `openms/featurefinderidentification` and `openms/idconflictresolver` local modules to OpenMS 3.5.0 [#447](https://github.com/nf-core/mhcquant/pull/447) +- Rewrote `openms/idmassaccuracy` meta.yml to reflect the actual process I/O [#447](https://github.com/nf-core/mhcquant/pull/447) +- Tightened preset normalization so empty/whitespace `FixedMods` and `VariableMods` cells are treated as empty and the `-fixed_modifications` CLI flag is omitted when empty [#447](https://github.com/nf-core/mhcquant/pull/447) +- Migrated `conf/test_single_quant.config` from deprecated `max_cpus`/`max_memory`/`max_time` to `process.resourceLimits` [#447](https://github.com/nf-core/mhcquant/pull/447) ### `Dependencies` @@ -29,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | `MultiQC` | 1.31.0 | 1.33.0 | | `Nf-core` | 3.4.1 | 3.5.1 | | `openms` | 3.4.1 | 3.5.0 | +| `tdf2mzml` | 0.3.0 | 0.4.0 | ## 3.1.0 - BlüBa - 07/01/26 diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 43d2b7f2..fd22b7a7 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -3,7 +3,7 @@ custom_logo_url: https://github.com/nf-core/mhcquant custom_logo_title: "nf-core/mhcquant" report_comment: > - This report has been generated by the nf-core/mhcquant analysis pipeline. For information about how to interpret these results, please see the documentation. + This report has been generated by the nf-core/mhcquant analysis pipeline. For information about how to interpret these results, please see the documentation. report_section_order: "nf-core-mhcquant-methods-description": order: -1000 diff --git a/assets/search_presets.tsv b/assets/search_presets.tsv index 1f49d97e..0ac7b979 100644 --- a/assets/search_presets.tsv +++ b/assets/search_presets.tsv @@ -1,11 +1,11 @@ PresetName PeptideMinLength PeptideMaxLength PrecursorMassRange PrecursorCharge PrecursorMassTolerance PrecursorErrorUnit FragmentMassTolerance FragmentBinOffset MS2PIPModel ActivationMethod Instrument NumberMods FixedMods VariableMods -lumos_class1 8 14 800:2500 2:3 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 3 Oxidation (M) -lumos_class2 8 30 800:5000 2:5 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 5 Oxidation (M) -qe_class1 8 14 800:2500 2:3 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 3 Oxidation (M) -qe_class2 8 30 800:5000 2:5 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 5 Oxidation (M) -timstof_class1 8 14 800:2500 1:4 20 ppm 0.01 0.0 timsTOF CID high_res 3 Oxidation (M) -timstof_class2 8 30 800:5000 1:5 20 ppm 0.01 0.0 timsTOF CID high_res 5 Oxidation (M) -astral_class1 8 14 800:2500 2:3 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 3 Oxidation (M) -astral_class2 8 30 800:5000 2:5 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 5 Oxidation (M) -xl_class1 8 14 800:2500 2:3 5 ppm 0.50025 0.4 CIDch2 CID low_res 3 Oxidation (M) -xl_class2 8 30 800:5000 2:5 5 ppm 0.50025 0.4 CIDch2 CID low_res 5 Oxidation (M) +lumos_class1 8 14 800:2500 2:3 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 3 Oxidation (M) +lumos_class2 8 30 800:5000 2:5 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 5 Oxidation (M) +qe_class1 8 14 800:2500 2:3 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 3 Oxidation (M) +qe_class2 8 30 800:5000 2:5 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 5 Oxidation (M) +timstof_class1 8 14 800:2500 1:4 20 ppm 0.01 0.0 timsTOF CID high_res 3 Oxidation (M) +timstof_class2 8 30 800:5000 1:5 20 ppm 0.01 0.0 timsTOF CID high_res 5 Oxidation (M) +astral_class1 8 14 800:2500 2:3 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 3 Oxidation (M) +astral_class2 8 30 800:5000 2:5 5 ppm 0.01 0.0 Immuno-HCD HCD high_res 5 Oxidation (M) +xl_class1 8 14 800:2500 2:3 5 ppm 0.50025 0.4 CIDch2 CID low_res 3 Oxidation (M) +xl_class2 8 30 800:5000 2:5 5 ppm 0.50025 0.4 CIDch2 CID low_res 5 Oxidation (M) diff --git a/conf/modules.config b/conf/modules.config index c1121e11..5603bad4 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -106,7 +106,7 @@ process { "-max_variable_mods_in_peptide ${meta.number_mods}", "-missed_cleavages 0", "-precursor_charge ${meta.prec_charge}", - "-fixed_modifications ${meta.fixed_mods.trim() ? meta.fixed_mods.tokenize(',').collect { "'${it.trim()}'" }.join(' ') : ''}", + meta.fixed_mods.trim() ? "-fixed_modifications ${meta.fixed_mods.tokenize(',').collect { "'${it.trim()}'" }.join(' ')}" : "", meta.variable_mods.trim() ? "-variable_modifications ${meta.variable_mods.tokenize(',').collect { "'${it.trim()}'" }.join(' ')}" : "", "-enzyme '${params.enzyme}'", "-use_X_ions ${params.use_x_ions}", @@ -370,7 +370,7 @@ process { withName: 'OPENMS_IDMASSACCURACY' { ext.prefix = {"${meta.spectra}"} ext.args = { [ - (params.precursor_error_units == 'ppm') ? "-precursor_error_ppm": "", + (meta.precursor_error_units == 'ppm') ? "-precursor_error_ppm": "", "-fragment_mass_tolerance ${meta.fragment_mass_tolerance}" ].join(' ').trim() } publishDir = [ diff --git a/conf/test_single_quant.config b/conf/test_single_quant.config index f9033cfd..8aa31871 100644 --- a/conf/test_single_quant.config +++ b/conf/test_single_quant.config @@ -7,15 +7,18 @@ * nextflow run main.nf -profile test_single_quant, * */ +process { + resourceLimits = [ + cpus: 2, + memory: '6.GB', + time: '6.h' + ] +} + params { config_profile_name = 'Test single replicate profile' config_profile_description = 'Test dataset to check pipeline function for single replicate' - // Limit resources so that this can run on GitHub Actions - max_cpus = 2 - max_memory = '6.GB' - max_time = '6.h' - // Input data input = params.pipelines_testdata_base_path + 'mhcquant/testdata/sample_sheet_single_quant.tsv' diff --git a/modules/local/openms/featurefinderidentification/environment.yml b/modules/local/openms/featurefinderidentification/environment.yml index d85a93ef..c8bad1b9 100644 --- a/modules/local/openms/featurefinderidentification/environment.yml +++ b/modules/local/openms/featurefinderidentification/environment.yml @@ -4,4 +4,4 @@ channels: - conda-forge - bioconda dependencies: - - bioconda::openms=3.4.1 + - bioconda::openms=3.5.0 diff --git a/modules/local/openms/featurefinderidentification/main.nf b/modules/local/openms/featurefinderidentification/main.nf index 9630331d..a4febac8 100644 --- a/modules/local/openms/featurefinderidentification/main.nf +++ b/modules/local/openms/featurefinderidentification/main.nf @@ -4,8 +4,8 @@ process OPENMS_FEATUREFINDERIDENTIFICATION { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/openms:3.4.1--h81ffffe_1' : - 'biocontainers/openms:3.4.1--h81ffffe_1' }" + 'https://depot.galaxyproject.org/singularity/openms:3.5.0--h78fb946_0' : + 'biocontainers/openms:3.5.0--h78fb946_0' }" input: diff --git a/modules/local/openms/fileconverter/environment.yml b/modules/local/openms/fileconverter/environment.yml index e9ab2df5..6e3cb81e 100644 --- a/modules/local/openms/fileconverter/environment.yml +++ b/modules/local/openms/fileconverter/environment.yml @@ -4,4 +4,4 @@ channels: - bioconda - defaults dependencies: - - bioconda::openms=3.4.1 + - bioconda::openms=3.5.0 diff --git a/modules/local/openms/fileconverter/main.nf b/modules/local/openms/fileconverter/main.nf index f4a5a885..a689ef56 100644 --- a/modules/local/openms/fileconverter/main.nf +++ b/modules/local/openms/fileconverter/main.nf @@ -4,15 +4,15 @@ process OPENMS_FILECONVERTER { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/openms:3.4.1--h81ffffe_1' : - 'biocontainers/openms:3.4.1--h81ffffe_1' }" + 'https://depot.galaxyproject.org/singularity/openms:3.5.0--h78fb946_0' : + 'biocontainers/openms:3.5.0--h78fb946_0' }" input: tuple val(meta), path(file), val(suffix) output: tuple val(meta), path("*.${suffix}"), emit: consensusxml - tuple val("${task.process}"), val('openms'), eval("FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\1/p'"), topic: versions + tuple val("${task.process}"), val('openms'), eval("FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'"), topic: versions when: task.ext.when == null || task.ext.when diff --git a/modules/local/openms/idconflictresolver/environment.yml b/modules/local/openms/idconflictresolver/environment.yml index d85a93ef..c8bad1b9 100644 --- a/modules/local/openms/idconflictresolver/environment.yml +++ b/modules/local/openms/idconflictresolver/environment.yml @@ -4,4 +4,4 @@ channels: - conda-forge - bioconda dependencies: - - bioconda::openms=3.4.1 + - bioconda::openms=3.5.0 diff --git a/modules/local/openms/idconflictresolver/main.nf b/modules/local/openms/idconflictresolver/main.nf index 4a77cfbd..37d6206d 100644 --- a/modules/local/openms/idconflictresolver/main.nf +++ b/modules/local/openms/idconflictresolver/main.nf @@ -4,8 +4,8 @@ process OPENMS_IDCONFLICTRESOLVER { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/openms:3.4.1--h81ffffe_1' : - 'biocontainers/openms:3.4.1--h81ffffe_1' }" + 'https://depot.galaxyproject.org/singularity/openms:3.5.0--h78fb946_0' : + 'biocontainers/openms:3.5.0--h78fb946_0' }" input: tuple val(meta), path(consensus) diff --git a/modules/local/openms/idmassaccuracy/main.nf b/modules/local/openms/idmassaccuracy/main.nf index d601ead7..8b9ac99d 100644 --- a/modules/local/openms/idmassaccuracy/main.nf +++ b/modules/local/openms/idmassaccuracy/main.nf @@ -13,7 +13,7 @@ process OPENMS_IDMASSACCURACY { output: tuple val(meta), path("*frag_mass_err.tsv") , emit: frag_err tuple val(meta), path("*prec_mass_err.tsv") , emit: prec_err, optional: true - tuple val("${task.process}"), val('openms'), eval("FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'"), topic: versions + tuple val("${task.process}"), val('openms'), eval("FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'"), topic: versions when: task.ext.when == null || task.ext.when diff --git a/modules/local/openms/idmassaccuracy/meta.yml b/modules/local/openms/idmassaccuracy/meta.yml index ce2eeb82..0cab4aea 100644 --- a/modules/local/openms/idmassaccuracy/meta.yml +++ b/modules/local/openms/idmassaccuracy/meta.yml @@ -1,9 +1,10 @@ -name: "openms_idfilter" -description: Filters peptide/protein identification results by different - criteria. +name: "openms_idmassaccuracy" +description: Computes the fragment and/or precursor mass accuracy of identified + peptides against the original spectra. keywords: - - filter + - mass accuracy - idXML + - mzML - openms - proteomics tools: @@ -22,28 +23,39 @@ input: type: map description: | Groovy Map containing sample information - e.g. `[ id:'test', single_end:false ]` - - id_file: + e.g. `[ id:'test' ]` + - mzmls: type: file - description: Peptide-spectrum matches. - pattern: "*.{idXML,consensusXML}" + description: Spectra files used for identification. + pattern: "*.{mzML}" ontologies: [] - - filter_file: + - idxmls: type: file - description: Optional idXML file to filter on/out peptides or proteins - patter: "*.{idXML,fasta}" + description: Peptide identification results matching the spectra. + pattern: "*.{idXML}" ontologies: [] output: - filtered: + frag_err: - - meta: type: map description: | Groovy Map containing sample information - e.g. `[ id:'test', single_end:false ]` - - "*.{idXML,consensusXML}": + e.g. `[ id:'test' ]` + - "*frag_mass_err.tsv": type: file - description: Filtered peptide-spectrum matches. - pattern: "*.{idXML,consensusXML}" + description: TSV with fragment mass error measurements per PSM. + pattern: "*frag_mass_err.tsv" + ontologies: [] + prec_err: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test' ]` + - "*prec_mass_err.tsv": + type: file + description: Optional TSV with precursor mass error measurements per PSM. + pattern: "*prec_mass_err.tsv" ontologies: [] topics: versions: diff --git a/nextflow.config b/nextflow.config index 46484ee2..2a64e98f 100644 --- a/nextflow.config +++ b/nextflow.config @@ -359,7 +359,7 @@ manifest { mainScript = 'main.nf' defaultBranch = 'master' nextflowVersion = '!>=25.04.0' - version = '3.2.0dev' + version = '3.2.0' doi = '10.1186/s13059-025-03763-8' } diff --git a/ro-crate-metadata.json b/ro-crate-metadata.json index ff701c73..6d8cccf0 100644 --- a/ro-crate-metadata.json +++ b/ro-crate-metadata.json @@ -21,8 +21,8 @@ { "@id": "./", "@type": "Dataset", - "creativeWorkStatus": "InProgress", - "datePublished": "2026-01-08T12:19:18+00:00", + "creativeWorkStatus": "Stable", + "datePublished": "2026-04-23T17:13:25+00:00", "description": "

\n \n \n \"nf-core/mhcquant\"\n \n

\n\n[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/mhcquant)\n[![GitHub Actions CI Status](https://github.com/nf-core/mhcquant/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/mhcquant/actions/workflows/nf-test.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/mhcquant/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/mhcquant/actions/workflows/linting.yml)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/mhcquant/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.8427707-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.8427707)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.04.0-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-3.5.1-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/3.5.1)\n[![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/mhcquant)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23mhcquant-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/mhcquant)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nfcore/mhcquant** is a best-practice bioinformatics pipeline to process data-dependent acquisition (DDA) immunopeptidomics data. This involves mass spectrometry-based identification and quantification of immunopeptides presented on major histocompatibility complex (MHC) molecules which mediate T cell immunosurveillance. Immunopeptidomics has central implications for clinical research, in the context of [T cell-centric immunotherapies](https://www.sciencedirect.com/science/article/pii/S1044532323000180).\n\nThe pipeline is based on the OpenMS C++ framework for computational mass spectrometry. Spectrum files (mzML/Thermo raw/Bruker tdf) serve as inputs and a database search (Comet) is performed based on a given input protein database. Peptide properties are predicted by MS\u00b2Rescore. FDR rescoring is applied using Percolator or Mokapot based on a competitive target-decoy approach. The pipeline supports both local FDR control (per sample-condition group) and global FDR control (across all samples). For label-free quantification, all input files undergo identification-based retention time alignment and targeted feature extraction matching ids between runs. The pipeline can also generate spectrum libraries suitable for DIA-based searches as well as computing consensus epitopes using epicore.\n\n![overview](assets/mhcquant_subway.png)\n\nThe pipeline is built using [Nextflow](https://www.nextflow.io), a workflow tool to run tasks across multiple compute infrastructures in a very portable manner. It uses Docker/Singularity containers making installation trivial and results highly reproducible. The [Nextflow DSL2](https://www.nextflow.io/docs/latest/dsl2.html) implementation of this pipeline uses one container per process which makes it much easier to maintain and update software dependencies. Where possible, these processes have been submitted to and installed from [nf-core/modules](https://github.com/nf-core/modules) in order to make them available to all nf-core pipelines, and to everyone within the Nextflow community!\n\nOn release, automated continuous integration tests run the pipeline on a full-sized dataset on the AWS cloud infrastructure. This ensures that the pipeline runs on AWS, has sensible resource allocation defaults set to run on real-world datasets, and permits the persistent storage of results to benchmark between pipeline releases and other analysis sources. The results obtained from the full-sized test can be viewed on the [nf-core website](https://nf-co.re/mhcquant/results).\n\n## Usage\n\n> [!NOTE]\n> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how\n> to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline)\n> with `-profile test` before running the workflow on actual data.\n\nFirst, prepare a samplesheet with your input data that looks as follows:\n\n`samplesheet.tsv`\n\n```tsv title=\"samplesheet.tsv\nID\tSample\tCondition\tReplicateFileName\n1\ttumor\ttreated\t/path/to/msrun1.raw|mzML|d\n2\ttumor\ttreated\t/path/to/msrun2.raw|mzML|d\n3\ttumor\tuntreated\t/path/to/msrun3.raw|mzML|d\n4\ttumor\tuntreated\t/path/to/msrun4.raw|mzML|d\n```\n\nEach row represents a mass spectrometry run in one of the formats: raw, RAW, mzML, mzML.gz, d, d.tar.gz, d.zip\n\nNow, you can run the pipeline using:\n\n```bash\nnextflow run nf-core/mhcquant \\\n -profile \\\n --input 'samplesheet.tsv' \\\n --fasta 'SWISSPROT_2020.fasta' \\\n --outdir ./results\n```\n\nOptional parameters for additional functionality:\n\n```bash\n# Enable quantification, global FDR and spectrum library generation, ion annotations, and consenus epitopes\nnextflow run nf-core/mhcquant \\\n --input 'samplesheet.tsv' \\\n --fasta 'SWISSPROT_2020.fasta' \\\n --annotate_ions \\\n --epicore \\\n --generate_speclib \\\n --global_fdr \\\n --quantify \\\n --outdir ./results \\\n -profile docker\n```\n\n> [!WARNING]\n> Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; see [docs](https://nf-co.re/docs/usage/getting_started/configuration#custom-configuration-files).\n\nFor more details and further functionality, please refer to the [usage documentation](https://nf-co.re/mhcquant/usage) and the [parameter documentation](https://nf-co.re/mhcquant/parameters).\n\n## Pipeline summary\n\n### Default Steps\n\nBy default the pipeline currently performs identification of MHC class I peptides with HCD settings:\n\n- **Spectra Preparation**: Preparing spectra dependent on the input format (`PREPARE_SPECTRA` subworkflow)\n- **Database Preparation**: Creation of reversed decoy database (`DecoyDatabase`)\n- **Peptide Identification**: Identification of peptides in the MS/MS spectra (`CometAdapter`)\n- **Database Indexing**: Refreshes protein references for all peptide hits and adds target/decoy information (`PeptideIndexer`)\n- **Identification Merging**: Merges identification files with the same `Sample` and `Condition` label (`IDMerger`)\n- **Rescoring**: Feature prediction and peptide-spectrum-match rescoring (`RESCORE` subworkflow)\n - Prediction of retention times and MS2 intensities (`MS\u00b2Rescore`)\n - Extract PSM features for rescoring engines (`PSMFeatureExtractor`)\n - Peptide-spectrum-match rescoring using Percolator or Mokapot (`PercolatorAdapter`)\n - Filters peptide identification result according to configurable FDR threshold (`IDFilter`)\n- **Export**: Converts identification result to tab-separated files (`TextExporter`)\n\n### FDR Control Modes\n\nThe pipeline supports two FDR control strategies:\n\n- **Local FDR** (default): FDR control applied per `Sample` and `Condition` group\n- **Global FDR**: FDR control applied across all samples in the dataset (enable with `--global_fdr`)\n\n### Additional Steps\n\nAdditional functionality contained by the pipeline currently includes:\n\n#### Quantification (`QUANT` subworkflow)\n\nWhen enabled with `--quantify`, the pipeline performs label-free quantification:\n\n- **Alignment**: Corrects retention time distortions between runs (`MAP_ALIGNMENT` subworkflow)\n - Corrects retention time distortions between runs (`MapAlignerIdentification`)\n - Applies retention time transformations to runs (`MapRTTransformer`)\n- **Feature Processing**: Detects and processes features (`PROCESS_FEATURE` subworkflow)\n - Detects features in MS1 data based on peptide identifications (`FeatureFinderIdentification`)\n - Group corresponding features across label-free experiments (`FeatureLinkerUnlabeledKD`)\n - Resolves ambiguous annotations of features with peptide identifications (`IDConflictResolver`)\n\n#### Spectrum Library Generation (`SPECLIB` subworkflow)\n\nWhen enabled with `--generate_speclib`, the pipeline generates spectrum libraries suitable for DIA-based searches. Outputs one library per sample or a single library across all samples (if global FDR mode is enabled with `--global_fdr`).\n\n#### Ion Annotation (`IONANNOTATOR` subworkflow)\n\nThe pipeline annotates the final list of peptides with their respective ions and charges:\n\n- Annotates final list of peptides with their respective ions and charges (`IonAnnotator`)\n\n#### Output\n\n## Documentation\n\nTo see the the results of a test run with a full size dataset refer to the [results](https://nf-co.re/mhcquant/results) tab on the nf-core website pipeline page.\nFor more details about the output files and reports, please refer to the\n[output documentation](https://nf-co.re/mhcquant/output).\n\n1. [Nextflow installation](https://nf-co.re/usage/installation)\n2. Pipeline configuration\n - [Pipeline installation](https://nf-co.re/docs/usage/getting_started/offline)\n - [Adding your own system config](https://nf-co.re/usage/adding_own_config)\n3. [Running the pipeline](https://nf-co.re/mhcquant/docs/usage.md)\n - This includes tutorials, FAQs, and troubleshooting instructions\n4. [Output and how to interpret the results](https://nf-co.re/mhcquant/docs/output.md)\n\n## Credits\n\nnf-core/mhcquant was originally written by [Leon Bichmann](https://github.com/Leon-Bichmann) from the [Kohlbacher Lab](https://kohlbacherlab.org/). The pipeline was re-written in Nextflow DSL2 by [Marissa Dubbelaar](https://github.com/marissaDubbelaar) and was significantly improved by [Jonas Scheid](https://github.com/jonasscheid) and [Steffen Lemke](https://github.com/steffenlem) from [Peptide-based Immunotherapy](https://www.medizin.uni-tuebingen.de/en-de/peptid-basierte-immuntherapie) and [Quantitative Biology Center](https://uni-tuebingen.de/forschung/forschungsinfrastruktur/zentrum-fuer-quantitative-biologie-qbic/) in T\u00fcbingen.\n\nHelpful contributors:\n\n- [Lukas Heumos](https://github.com/Zethson)\n- [Alexander Peltzer](https://github.com/apeltzer)\n- [Maxime Garcia](https://github.com/maxulysse)\n- [Gisela Gabernet](https://github.com/ggabernet)\n- [Susanne Jodoin](https://github.com/SusiJo)\n- [Oskar Wacker](https://github.com/WackerO)\n- [Leon Kuchenbecker](https://github.com/lkuchenb)\n- [Phil Ewels](https://github.com/ewels)\n- [Christian Fufezan](https://github.com/fu)\n- [Sven Fillinger](https://github.com/sven1103)\n- [Kevin Menden](https://github.com/KevinMenden)\n- [Julia Graf](https://github.com/JuliaGraf)\n- [Jana Hoffmann](https://github.com/janaHoffmann1)\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#mhcquant` channel](https://nfcore.slack.com/channels/mhcquant) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\nIf you use nf-core/mhcquant for your analysis, please cite the corresponding manuscript: [10.1186/s13059-025-03763-8](https://doi.org/10.1186/s13059-025-03763-8)\n\n> **MHCquant2 refines immunopeptidomics tumor antigen discovery**\n>\n> Jonas Scheid, Steffen Lemke, Naomi Hoenisch-Gravel, Anna Dengler, Timo Sachsenberg, Arthur Declerq, Ralf Gabriels, Jens Bauer, Marcel Wacker, Leon Bichmann, Lennart Martens, Marissa L. Dubbelaar, Sven Nahnsen & Juliane S. Walz\n>\n> _Genome Biology_ 2025 26 (1), 290. doi: [10.1021/acs.jproteome.9b00313](https://pubs.acs.org/doi/10.1021/acs.jproteome.9b00313)\n\n> **MHCquant: Automated and Reproducible Data Analysis for Immunopeptidomics**\n>\n> Leon Bichmann, Annika Nelde, Michael Ghosh, Lukas Heumos, Christopher Mohr, Alexander Peltzer, Leon Kuchenbecker, Timo Sachsenberg, Juliane S. Walz, Stefan Stevanovi\u0107, Hans-Georg Rammensee & Oliver Kohlbacher\n>\n> _Journal of Proteome Research_ 2019 18 (11), 3876-3884. doi: [10.1021/acs.jproteome.9b00313](https://pubs.acs.org/doi/10.1021/acs.jproteome.9b00313)\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n\nIn addition, references of tools and data used in this pipeline are as follows:\n\n> **OpenMS framework**\n>\n> Pfeuffer J. et al, _Nat Methods_ 2024 Mar;21(3):365-367. doi: [0.1038/s41592-024-02197-7](https://www.nature.com/articles/s41592-024-02197-7).\n>\n> **Comet Search Engine**\n>\n> Eng J.K. et al, _J Am Soc Mass Spectrom._ 2015 Nov;26(11):1865-74. doi: [10.1007/s13361-015-1179-x](https://pubs.acs.org/doi/10.1007/s13361-015-1179-x).\n>\n> **Retention time prediction**\n>\n> Bouwmeester R. et al, _Nature Methods_ 2021 Oct;18(11):1363-1369. doi: [10.1038/s41592-021-01301-5](https://www.nature.com/articles/s41592-021-01301-5)\n>\n> **MS\u00b2 Peak intensity prediction**\n>\n> Declercq A. et al, _Nucleic Acids Res._ 2023 Jul 5;51(W1):W338-W342. doi: [10.1093/nar/gkad335](https://academic.oup.com/nar/article/51/W1/W338/7151340?login=false)\n>\n> **CCS prediction**\n>\n> Declercq A. et al _Journal of Proteome Research_ 2025 Feb 6. doi: [10.1021/acs.jproteome.4c00609](https://pubs.acs.org/doi/10.1021/acs.jproteome.4c00609)\n>\n> **MS\u00b2Rescore framework**\n>\n> Buur L. M. et al, \\_J Proteome Res. 2024 Mar 16. doi: [10.1021/acs.jproteome.3c00785](https://pubs.acs.org/doi/10.1021/acs.jproteome.3c00785)\n>\n> **Percolator**\n>\n> K\u00e4ll L. et al, _Nat Methods_ 2007 Nov;4(11):923-5. doi: [10.1038/nmeth1113](https://www.nature.com/articles/nmeth1113).\n>\n> **Identification based RT Alignment**\n>\n> Weisser H. et al, _J Proteome Res._ 2013 Apr 5;12(4):1628-44. doi: [10.1021/pr300992u](https://pubs.acs.org/doi/10.1021/pr300992u)\n>\n> **Targeted peptide quantification**\n>\n> Weisser H. et al, _J Proteome Res._ 2017 Aug 4;16(8):2964-2974. doi: [10.1021/acs.jproteome.7b00248](https://pubs.acs.org/doi/10.1021/acs.jproteome.7b00248)\n", "hasPart": [ { @@ -105,7 +105,7 @@ }, "mentions": [ { - "@id": "#9e4c34cf-5530-46b2-be64-79e63d572382" + "@id": "#e4e0c685-2808-442a-8eb5-658cae88a6be" } ], "name": "nf-core/mhcquant" @@ -134,13 +134,13 @@ ], "creator": [ { - "@id": "https://orcid.org/0000-0002-4930-1467" + "@id": "https://orcid.org/0000-0002-8937-3457" }, { "@id": "https://orcid.org/0000-0001-7135-0073" }, { - "@id": "https://orcid.org/0000-0002-8937-3457" + "@id": "https://orcid.org/0000-0002-4930-1467" }, { "@id": "https://orcid.org/0000-0002-5923-1343" @@ -150,7 +150,7 @@ } ], "dateCreated": "", - "dateModified": "2026-01-08T12:19:18Z", + "dateModified": "2026-04-23T17:13:25Z", "dct:conformsTo": "https://bioschemas.org/profiles/ComputationalWorkflow/1.0-RELEASE/", "keywords": [ "nf-core", @@ -167,10 +167,10 @@ ], "maintainer": [ { - "@id": "https://orcid.org/0000-0002-4930-1467" + "@id": "https://orcid.org/0000-0001-7135-0073" }, { - "@id": "https://orcid.org/0000-0001-7135-0073" + "@id": "https://orcid.org/0000-0002-4930-1467" }, { "@id": "https://orcid.org/0000-0002-5923-1343" @@ -187,10 +187,10 @@ }, "url": [ "https://github.com/nf-core/mhcquant", - "https://nf-co.re/mhcquant/dev/" + "https://nf-co.re/mhcquant/3.2.0/" ], "version": [ - "3.2.0dev" + "3.2.0" ] }, { @@ -206,11 +206,11 @@ "version": "!>=25.04.0" }, { - "@id": "#9e4c34cf-5530-46b2-be64-79e63d572382", + "@id": "#e4e0c685-2808-442a-8eb5-658cae88a6be", "@type": "TestSuite", "instance": [ { - "@id": "#b55a3b6b-29d0-41f1-a5dc-9c168ffc0064" + "@id": "#f6624107-de51-45f5-b07d-4d3124654972" } ], "mainEntity": { @@ -219,7 +219,7 @@ "name": "Test suite for nf-core/mhcquant" }, { - "@id": "#b55a3b6b-29d0-41f1-a5dc-9c168ffc0064", + "@id": "#f6624107-de51-45f5-b07d-4d3124654972", "@type": "TestInstance", "name": "GitHub Actions workflow for testing nf-core/mhcquant", "resource": "repos/nf-core/mhcquant/actions/workflows/nf-test.yml", @@ -358,10 +358,10 @@ "url": "https://nf-co.re/" }, { - "@id": "https://orcid.org/0000-0002-4930-1467", + "@id": "https://orcid.org/0000-0002-8937-3457", "@type": "Person", - "email": "marissa.dubbelaar@gmail.com", - "name": "Marissa Dubbelaar" + "email": "lukas.heumos@gmail.com", + "name": "Lukas Heumos" }, { "@id": "https://orcid.org/0000-0001-7135-0073", @@ -370,10 +370,10 @@ "name": "Leon Bichmann" }, { - "@id": "https://orcid.org/0000-0002-8937-3457", + "@id": "https://orcid.org/0000-0002-4930-1467", "@type": "Person", - "email": "lukas.heumos@gmail.com", - "name": "Lukas Heumos" + "email": "marissa.dubbelaar@gmail.com", + "name": "Marissa Dubbelaar" }, { "@id": "https://orcid.org/0000-0002-5923-1343", diff --git a/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf b/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf index 125e62fa..2c340693 100644 --- a/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf @@ -132,7 +132,7 @@ workflow PIPELINE_INITIALISATION { // samplesheetToList wraps all-meta rows in a list def row = (item instanceof List) ? item[0] : item // nf-schema parses empty TSV cells as [] instead of ''; normalize for string operations - ['fixed_mods', 'variable_mods'].each { key -> if (!row[key]) row[key] = '' } + ['fixed_mods', 'variable_mods'].each { key -> if (!row[key]?.trim()) row[key] = '' } [(row.preset_name): row] } } @@ -309,22 +309,6 @@ def resolveSearchParams(meta, presetsMap) { return result } -// -// Validate channels from input samplesheet -// -// Keeping this as an example for future samplesheet checks if additional fields are added (e.g. alleles) -def validateInputSamplesheet(input) { - def (metas, fastqs) = input[1..2] - - // Check that multiple runs of the same sample are of the same datatype i.e. single-end / paired-end - def endedness_ok = metas.collect{ meta -> meta.single_end }.unique().size == 1 - if (!endedness_ok) { - error("Please check input samplesheet -> Multiple runs of a sample must be of the same datatype i.e. single-end or paired-end: ${metas[0].id}") - } - - return [ metas[0], fastqs ] -} - def getCustomExtension(file) { def name = file.getName() if (name =~ /.*\.(d\.tar\.gz|d\.tar|d\.zip|mzML\.gz|raw|RAW|mzML|d)$/) { diff --git a/tests/sdrf.nf.test b/tests/sdrf.nf.test index 6c2d121f..4511590a 100644 --- a/tests/sdrf.nf.test +++ b/tests/sdrf.nf.test @@ -3,6 +3,7 @@ nextflow_pipeline { name "Test pipeline with SDRF input" script "../main.nf" tag "pipeline" + tag "test_sdrf" profile "test_sdrf" test("-profile test_sdrf") { diff --git a/tests/test_single_quant.nf.test b/tests/test_single_quant.nf.test index 456957f8..b044560a 100644 --- a/tests/test_single_quant.nf.test +++ b/tests/test_single_quant.nf.test @@ -3,7 +3,7 @@ nextflow_pipeline { name "Test pipeline for single replicate" script "../main.nf" tag "pipeline" - tag "single_replicate" + tag "test_single_quant" test("-profile test_single_quant") { From d5862f0f99e2e4dc87383adf0c716a42db56e194 Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Thu, 23 Apr 2026 19:32:15 +0000 Subject: [PATCH 02/11] Revert fixed_modifications flag-omission (behavior-changing), harden preset normalization for ArrayList cells, bump snapshot version strings. --- CHANGELOG.md | 2 +- conf/modules.config | 2 +- subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf | 5 ++++- tests/default.nf.test.snap | 2 +- tests/ionannotator.nf.test.snap | 2 +- tests/mokapot.nf.test.snap | 2 +- tests/sdrf.nf.test.snap | 2 +- tests/search_presets.nf.test.snap | 2 +- tests/speclib.nf.test.snap | 2 +- tests/test_single_quant.nf.test.snap | 2 +- 10 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6f733b1..2cadb7fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Migrate to topic channels [#431](https://github.com/nf-core/mhcquant/pull/431) - Bumped `openms/fileconverter`, `openms/featurefinderidentification` and `openms/idconflictresolver` local modules to OpenMS 3.5.0 [#447](https://github.com/nf-core/mhcquant/pull/447) - Rewrote `openms/idmassaccuracy` meta.yml to reflect the actual process I/O [#447](https://github.com/nf-core/mhcquant/pull/447) -- Tightened preset normalization so empty/whitespace `FixedMods` and `VariableMods` cells are treated as empty and the `-fixed_modifications` CLI flag is omitted when empty [#447](https://github.com/nf-core/mhcquant/pull/447) +- Tightened preset normalization so empty/whitespace `FixedMods` and `VariableMods` cells are treated as empty (preserves the existing `-fixed_modifications` CLI call) [#447](https://github.com/nf-core/mhcquant/pull/447) - Migrated `conf/test_single_quant.config` from deprecated `max_cpus`/`max_memory`/`max_time` to `process.resourceLimits` [#447](https://github.com/nf-core/mhcquant/pull/447) ### `Dependencies` diff --git a/conf/modules.config b/conf/modules.config index 5603bad4..95704b36 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -106,7 +106,7 @@ process { "-max_variable_mods_in_peptide ${meta.number_mods}", "-missed_cleavages 0", "-precursor_charge ${meta.prec_charge}", - meta.fixed_mods.trim() ? "-fixed_modifications ${meta.fixed_mods.tokenize(',').collect { "'${it.trim()}'" }.join(' ')}" : "", + "-fixed_modifications ${meta.fixed_mods.trim() ? meta.fixed_mods.tokenize(',').collect { "'${it.trim()}'" }.join(' ') : ''}", meta.variable_mods.trim() ? "-variable_modifications ${meta.variable_mods.tokenize(',').collect { "'${it.trim()}'" }.join(' ')}" : "", "-enzyme '${params.enzyme}'", "-use_X_ions ${params.use_x_ions}", diff --git a/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf b/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf index 2c340693..ee4003d9 100644 --- a/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf @@ -132,7 +132,10 @@ workflow PIPELINE_INITIALISATION { // samplesheetToList wraps all-meta rows in a list def row = (item instanceof List) ? item[0] : item // nf-schema parses empty TSV cells as [] instead of ''; normalize for string operations - ['fixed_mods', 'variable_mods'].each { key -> if (!row[key]?.trim()) row[key] = '' } + ['fixed_mods', 'variable_mods'].each { key -> + def v = row[key] + if (!v || (v instanceof String && !v.trim())) row[key] = '' + } [(row.preset_name): row] } } diff --git a/tests/default.nf.test.snap b/tests/default.nf.test.snap index a6669ef5..b8003be5 100644 --- a/tests/default.nf.test.snap +++ b/tests/default.nf.test.snap @@ -41,7 +41,7 @@ "pyopenms": "3.4.1" }, "Workflow": { - "nf-core/mhcquant": "v3.2.0dev" + "nf-core/mhcquant": "v3.2.0" } }, [ diff --git a/tests/ionannotator.nf.test.snap b/tests/ionannotator.nf.test.snap index 77dd99cd..3c1d48a4 100644 --- a/tests/ionannotator.nf.test.snap +++ b/tests/ionannotator.nf.test.snap @@ -45,7 +45,7 @@ "pyopenms": "3.4.1" }, "Workflow": { - "nf-core/mhcquant": "v3.2.0dev" + "nf-core/mhcquant": "v3.2.0" } }, [ diff --git a/tests/mokapot.nf.test.snap b/tests/mokapot.nf.test.snap index 9dfb2ea9..c0d58dbf 100644 --- a/tests/mokapot.nf.test.snap +++ b/tests/mokapot.nf.test.snap @@ -38,7 +38,7 @@ "pyopenms": "3.4.1" }, "Workflow": { - "nf-core/mhcquant": "v3.2.0dev" + "nf-core/mhcquant": "v3.2.0" } }, [ diff --git a/tests/sdrf.nf.test.snap b/tests/sdrf.nf.test.snap index 560fff3d..e4388d6f 100644 --- a/tests/sdrf.nf.test.snap +++ b/tests/sdrf.nf.test.snap @@ -50,7 +50,7 @@ "thermorawfileparser": "1.4.5" }, "Workflow": { - "nf-core/mhcquant": "v3.2.0dev" + "nf-core/mhcquant": "v3.2.0" } }, [ diff --git a/tests/search_presets.nf.test.snap b/tests/search_presets.nf.test.snap index 4410fbaf..7cab22d4 100644 --- a/tests/search_presets.nf.test.snap +++ b/tests/search_presets.nf.test.snap @@ -41,7 +41,7 @@ "pyopenms": "3.4.1" }, "Workflow": { - "nf-core/mhcquant": "v3.2.0dev" + "nf-core/mhcquant": "v3.2.0" } }, [ diff --git a/tests/speclib.nf.test.snap b/tests/speclib.nf.test.snap index 2c4e1612..2d73faab 100644 --- a/tests/speclib.nf.test.snap +++ b/tests/speclib.nf.test.snap @@ -51,7 +51,7 @@ "pyopenms": "3.4.1" }, "Workflow": { - "nf-core/mhcquant": "v3.2.0dev" + "nf-core/mhcquant": "v3.2.0" } }, [ diff --git a/tests/test_single_quant.nf.test.snap b/tests/test_single_quant.nf.test.snap index 12b2533a..c9cefb49 100644 --- a/tests/test_single_quant.nf.test.snap +++ b/tests/test_single_quant.nf.test.snap @@ -41,7 +41,7 @@ "pyopenms": "3.4.1" }, "Workflow": { - "nf-core/mhcquant": "v3.2.0dev" + "nf-core/mhcquant": "v3.2.0" } }, [ From c234a316a018bb4dcb11515c0fdc6adf256de9c3 Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Thu, 23 Apr 2026 20:42:23 +0000 Subject: [PATCH 03/11] Migrate openms/textexporter from local to nf-core module; inject ext.prefix to preserve _exported output suffix. --- CHANGELOG.md | 1 + conf/modules.config | 1 + modules.json | 5 ++ modules/local/openms/textexporter/main.nf | 38 ---------- .../openms/textexporter/environment.yml | 2 +- modules/nf-core/openms/textexporter/main.nf | 36 ++++++++++ modules/nf-core/openms/textexporter/meta.yml | 69 +++++++++++++++++++ .../openms/textexporter/tests/main.nf.test | 61 ++++++++++++++++ .../textexporter/tests/main.nf.test.snap | 62 +++++++++++++++++ subworkflows/local/rescore/main.nf | 2 +- workflows/mhcquant.nf | 2 +- 11 files changed, 238 insertions(+), 41 deletions(-) delete mode 100644 modules/local/openms/textexporter/main.nf rename modules/{local => nf-core}/openms/textexporter/environment.yml (85%) create mode 100644 modules/nf-core/openms/textexporter/main.nf create mode 100644 modules/nf-core/openms/textexporter/meta.yml create mode 100644 modules/nf-core/openms/textexporter/tests/main.nf.test create mode 100644 modules/nf-core/openms/textexporter/tests/main.nf.test.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cadb7fb..21a9adfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rewrote `openms/idmassaccuracy` meta.yml to reflect the actual process I/O [#447](https://github.com/nf-core/mhcquant/pull/447) - Tightened preset normalization so empty/whitespace `FixedMods` and `VariableMods` cells are treated as empty (preserves the existing `-fixed_modifications` CLI call) [#447](https://github.com/nf-core/mhcquant/pull/447) - Migrated `conf/test_single_quant.config` from deprecated `max_cpus`/`max_memory`/`max_time` to `process.resourceLimits` [#447](https://github.com/nf-core/mhcquant/pull/447) +- Replaced local `openms/textexporter` module with the `nf-core/modules` equivalent; preserved the `_exported` output suffix via `ext.prefix` to keep SUMMARIZE_RESULTS input/output filenames distinct [#447](https://github.com/nf-core/mhcquant/pull/447) ### `Dependencies` diff --git a/conf/modules.config b/conf/modules.config index 95704b36..962c0ce7 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -470,6 +470,7 @@ process { } withName: 'OPENMS_TEXTEXPORTER' { + ext.prefix = { "${meta.id}_exported" } ext.args = '-id:peptides_only -id:add_hit_metavalues 0 -id:add_metavalues 0' publishDir = [ enabled: false diff --git a/modules.json b/modules.json index 4d2cd03e..73377cae 100644 --- a/modules.json +++ b/modules.json @@ -55,6 +55,11 @@ "git_sha": "ca1cd2456f36c913fb3cfb6bdfbf9d1794fd493b", "installed_by": ["modules"] }, + "openms/textexporter": { + "branch": "master", + "git_sha": "6d46786420b4d7bc88eba026eb389c0c5535d120", + "installed_by": ["modules"] + }, "openmsthirdparty/cometadapter": { "branch": "master", "git_sha": "ca1cd2456f36c913fb3cfb6bdfbf9d1794fd493b", diff --git a/modules/local/openms/textexporter/main.nf b/modules/local/openms/textexporter/main.nf deleted file mode 100644 index 21b9aa04..00000000 --- a/modules/local/openms/textexporter/main.nf +++ /dev/null @@ -1,38 +0,0 @@ -process OPENMS_TEXTEXPORTER { - tag "$meta.id" - label 'process_single' - - conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/openms:3.5.0--h78fb946_0' : - 'biocontainers/openms:3.5.0--h78fb946_0' }" - - input: - tuple val(meta), path(file) - - output: - tuple val(meta), path("*.tsv"), emit: tsv - tuple val("${task.process}"), val('openms'), eval("FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'"), topic: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - - """ - TextExporter \\ - -in $file \\ - -out ${prefix}_exported.tsv \\ - -threads $task.cpus \\ - $args - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - - """ - touch ${prefix}.tsv - """ -} diff --git a/modules/local/openms/textexporter/environment.yml b/modules/nf-core/openms/textexporter/environment.yml similarity index 85% rename from modules/local/openms/textexporter/environment.yml rename to modules/nf-core/openms/textexporter/environment.yml index c8bad1b9..fd1026d7 100644 --- a/modules/local/openms/textexporter/environment.yml +++ b/modules/nf-core/openms/textexporter/environment.yml @@ -4,4 +4,4 @@ channels: - conda-forge - bioconda dependencies: - - bioconda::openms=3.5.0 + - "bioconda::openms=3.5.0" diff --git a/modules/nf-core/openms/textexporter/main.nf b/modules/nf-core/openms/textexporter/main.nf new file mode 100644 index 00000000..e9eb1ae2 --- /dev/null +++ b/modules/nf-core/openms/textexporter/main.nf @@ -0,0 +1,36 @@ +process OPENMS_TEXTEXPORTER { + tag "${meta.id}" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container + ? 'https://depot.galaxyproject.org/singularity/openms:3.5.0--h78fb946_0' + : 'quay.io/biocontainers/openms:3.5.0--h78fb946_0'}" + + input: + tuple val(meta), path(input_file) + + output: + tuple val(meta), path("${prefix}.tsv"), emit: tsv + tuple val("${task.process}"), val('openms'), eval("FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'"), emit: versions_openms, topic: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" + """ + TextExporter \\ + -in ${input_file} \\ + -out ${prefix}.tsv \\ + -threads ${task.cpus} \\ + ${args} + """ + + stub: + prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.tsv + """ +} diff --git a/modules/nf-core/openms/textexporter/meta.yml b/modules/nf-core/openms/textexporter/meta.yml new file mode 100644 index 00000000..44696053 --- /dev/null +++ b/modules/nf-core/openms/textexporter/meta.yml @@ -0,0 +1,69 @@ +name: "openms_textexporter" +description: Exports various OpenMS XML formats (featureXML, consensusXML, idXML, mzML) to a human-readable text format. +keywords: + - export + - openms + - proteomics + - text + - tsv +tools: + - "openms": + description: "OpenMS is an open-source software C++ library for LC-MS data management and analyses" + homepage: "https://openms.de" + documentation: "https://openms.readthedocs.io/en/latest/index.html" + tool_dev_url: "https://github.com/OpenMS/OpenMS" + doi: "10.1038/s41592-024-02197-7" + licence: ["BSD"] + identifier: "" + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test' ]` + - input_file: + type: file + description: OpenMS data file to export to text format. + pattern: "*.{featureXML,consensusXML,idXML,mzML}" + ontologies: [] + +output: + tsv: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test' ]` + - "${prefix}.tsv": + type: file + description: Tab-separated text export of the input data. + pattern: "*.tsv" + ontologies: [] + versions_openms: + - - ${task.process}: + type: string + description: The name of the process + - openms: + type: string + description: The name of the tool + - "FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'": + type: eval + description: The expression to obtain the version of the tool + +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - openms: + type: string + description: The name of the tool + - "FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'": + type: eval + description: The expression to obtain the version of the tool + +authors: + - "@jonasscheid" +maintainers: + - "@jonasscheid" diff --git a/modules/nf-core/openms/textexporter/tests/main.nf.test b/modules/nf-core/openms/textexporter/tests/main.nf.test new file mode 100644 index 00000000..b2ead3fe --- /dev/null +++ b/modules/nf-core/openms/textexporter/tests/main.nf.test @@ -0,0 +1,61 @@ +nextflow_process { + + name "Test Process OPENMS_TEXTEXPORTER" + script "../main.nf" + process "OPENMS_TEXTEXPORTER" + + tag "modules" + tag "modules_nfcore" + tag "openms" + tag "openms/textexporter" + + test("proteomics - export - idxml") { + + when { + process { + """ + input[0] = [ + [ id:'test' ], + file(params.modules_testdata_base_path + 'proteomics/openms/HepG2_rep1_small.idXML', checkIfExists: true) + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + // TSV rows are prefixed with their data type (PROTEIN, PEPTIDE, etc.) + { assert file(process.out.tsv[0][1]).readLines().any { it.contains('PROTEIN') } }, + { assert file(process.out.tsv[0][1]).readLines().any { it.contains('PEPTIDE') } }, + { assert snapshot( + file(process.out.tsv[0][1]).readLines().findAll { it.startsWith('#') }.join('\n').md5(), + process.out.findAll { key, val -> key.startsWith('versions') } + ).match() } + ) + } + } + + test("proteomics - export - idxml - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ + [ id:'test' ], + file(params.modules_testdata_base_path + 'proteomics/openms/HepG2_rep1_small.idXML', checkIfExists: true) + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/openms/textexporter/tests/main.nf.test.snap b/modules/nf-core/openms/textexporter/tests/main.nf.test.snap new file mode 100644 index 00000000..b825d0d3 --- /dev/null +++ b/modules/nf-core/openms/textexporter/tests/main.nf.test.snap @@ -0,0 +1,62 @@ +{ + "proteomics - export - idxml": { + "content": [ + "ed2ef59b2d596b75b2eccd9ebc735bff", + { + "versions_openms": [ + [ + "OPENMS_TEXTEXPORTER", + "openms", + "3.5.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-03-13T11:15:36.013331439" + }, + "proteomics - export - idxml - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "OPENMS_TEXTEXPORTER", + "openms", + "3.5.0" + ] + ], + "tsv": [ + [ + { + "id": "test" + }, + "test.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_openms": [ + [ + "OPENMS_TEXTEXPORTER", + "openms", + "3.5.0" + ] + ] + } + ], + "meta": { + "nf-test": "0.9.3", + "nextflow": "25.10.3" + }, + "timestamp": "2026-03-12T11:45:40.246356282" + } +} \ No newline at end of file diff --git a/subworkflows/local/rescore/main.nf b/subworkflows/local/rescore/main.nf index 15c6aea3..3ea3b884 100644 --- a/subworkflows/local/rescore/main.nf +++ b/subworkflows/local/rescore/main.nf @@ -12,7 +12,7 @@ include { OPENMS_PERCOLATORADAPTER ; OPENMS_PERCOLATORADAPTER as OPENMS_PERCOLATORADAPTER_GLOBAL } from '../../../modules/local/openmsthirdparty/percolatoradapter' -include { OPENMS_TEXTEXPORTER as OPENMS_TEXTEXPORTER_GLOBAL } from '../../../modules/local/openms/textexporter' +include { OPENMS_TEXTEXPORTER as OPENMS_TEXTEXPORTER_GLOBAL } from '../../../modules/nf-core/openms/textexporter/main' // // MODULE: Installed directly from nf-core/modules // diff --git a/workflows/mhcquant.nf b/workflows/mhcquant.nf index ae38634c..4d126106 100644 --- a/workflows/mhcquant.nf +++ b/workflows/mhcquant.nf @@ -11,7 +11,7 @@ include { PYOPENMS_CHROMATOGRAMEXTRACTOR } from '../modules/local/pyopenms/chromatogramextractor' include { PYOPENMS_IONANNOTATOR } from '../modules/local/pyopenms/ionannotator' include { OPENMS_IDMASSACCURACY } from '../modules/local/openms/idmassaccuracy/main' -include { OPENMS_TEXTEXPORTER } from '../modules/local/openms/textexporter' +include { OPENMS_TEXTEXPORTER } from '../modules/nf-core/openms/textexporter/main' include { SUMMARIZE_RESULTS } from '../modules/local/pyopenms/summarize_results' include { EPICORE } from '../modules/local/epicore' From 1747a4a21ca4d3c3370a81b16eae41d05b058222 Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Fri, 24 Apr 2026 06:39:47 +0000 Subject: [PATCH 04/11] Set 3.2.0 release name to Solitude. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21a9adfd..9fb2034c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## 3.2.0 - [release name] - 23/04/26 +## 3.2.0 - Solitude - 23/04/26 ### `Added` From b41f2f417e172c7e2d08c4921002d7c140f410a1 Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Tue, 28 Apr 2026 09:12:11 +0000 Subject: [PATCH 05/11] Bump tdf2mzml container to 0.5_noentry and update changelog. --- CHANGELOG.md | 4 ++-- modules/local/tdf2mzml/main.nf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fb2034c..1df179da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed `EPICORE` running only once instead of per sample when `--fasta` is used, by broadcasting `ch_fasta` to `EPICORE` via `.first()` [#446](https://github.com/nf-core/mhcquant/pull/446) - Fixed `SUMMARIZE_RESULTS` crash with `--quantify` caused by OpenMS 3.5.0 TextExporter phantom column bug ([OpenMS/OpenMS#9120](https://github.com/OpenMS/OpenMS/issues/9120)) [#444](https://github.com/nf-core/mhcquant/pull/444) - Fixed an issue where stripping the sequence in `SUMMARIZE_RESULTS` did not work for complex modifications [#436](https://github.com/nf-core/mhcquant/pull/436) -- Fixed `tdf2mzml` container entrypoint issue by pinning to `0.4_noentry` and invoking the CLI explicitly [#447](https://github.com/nf-core/mhcquant/pull/447) +- Fixed `tdf2mzml` container entrypoint issue by pinning to `0.5_noentry` and invoking the CLI explicitly [#447](https://github.com/nf-core/mhcquant/pull/447) - Fixed `OPENMS_FILECONVERTER` version extraction emitting a SOH byte due to a single-backslash sed backreference [#447](https://github.com/nf-core/mhcquant/pull/447) - Fixed `OPENMS_IDMASSACCURACY` process argument to use `meta.precursor_error_units` so per-sample preset overrides apply [#447](https://github.com/nf-core/mhcquant/pull/447) @@ -38,7 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | `MultiQC` | 1.31.0 | 1.33.0 | | `Nf-core` | 3.4.1 | 3.5.1 | | `openms` | 3.4.1 | 3.5.0 | -| `tdf2mzml` | 0.3.0 | 0.4.0 | +| `tdf2mzml` | 0.3.0 | 0.5.0 | ## 3.1.0 - BlüBa - 07/01/26 diff --git a/modules/local/tdf2mzml/main.nf b/modules/local/tdf2mzml/main.nf index de677d4e..a6c6388d 100644 --- a/modules/local/tdf2mzml/main.nf +++ b/modules/local/tdf2mzml/main.nf @@ -1,7 +1,7 @@ process TDF2MZML { tag "$meta.id" - container "docker.io/mfreitas/tdf2mzml:0.4_noentry" + container "docker.io/mfreitas/tdf2mzml:0.5_noentry" input: tuple val(meta), path(tdf) From 2ea7862bd019cb2ea1c22b4c41e3ddf81456fdc9 Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Wed, 29 Apr 2026 11:47:06 +0000 Subject: [PATCH 06/11] Seal search-preset values: drop workflow.commandLine regex from resolveSearchParams (renamed resolvePresetParams), add regression test, document precedence. --- CHANGELOG.md | 3 +- docs/usage.md | 13 +- .../utils_nfcore_mhcquant_pipeline/main.nf | 21 +- tests/search_presets.nf.test | 39 ++ tests/search_presets.nf.test.snap | 437 +++++++++++++++++- 5 files changed, 492 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1df179da..014f7779 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### `Added` - Added support for single run quantification [#438](https://github.com/nf-core/mhcquant/pull/438) -- Added per-sample search parameter support via samplesheet with SearchPreset column and individual parameter overrides [#439](https://github.com/nf-core/mhcquant/pull/439) +- Added per-sample search parameter support via samplesheet with SearchPreset column [#439](https://github.com/nf-core/mhcquant/pull/439) - Added PRIDE ID and SDRF sheet support [#445](https://github.com/nf-core/mhcquant/pull/445) - Added ion mobility export and MultiQC distribution plot for timsTOF data [#441](https://github.com/nf-core/mhcquant/pull/441) @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Tightened preset normalization so empty/whitespace `FixedMods` and `VariableMods` cells are treated as empty (preserves the existing `-fixed_modifications` CLI call) [#447](https://github.com/nf-core/mhcquant/pull/447) - Migrated `conf/test_single_quant.config` from deprecated `max_cpus`/`max_memory`/`max_time` to `process.resourceLimits` [#447](https://github.com/nf-core/mhcquant/pull/447) - Replaced local `openms/textexporter` module with the `nf-core/modules` equivalent; preserved the `_exported` output suffix via `ext.prefix` to keep SUMMARIZE_RESULTS input/output filenames distinct [#447](https://github.com/nf-core/mhcquant/pull/447) +- Sealed search-preset values: when a samplesheet row sets `SearchPreset`, preset values are no longer overridable via `--`, `-params-file`, or `-c` for that row; rows without a preset resolve from `params` as before. Removed the fragile `workflow.commandLine` regex from `resolveSearchParams` (renamed to `resolvePresetParams`). Documented the new precedence in `docs/usage.md`. [#449](https://github.com/nf-core/mhcquant/pull/449) ### `Dependencies` diff --git a/docs/usage.md b/docs/usage.md index 993f0b5b..f68fae22 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -90,13 +90,16 @@ ID Sample Condition ReplicateFileName SearchPreset ### Parameter precedence -Search parameters are resolved with the following precedence (highest to lowest): +When a samplesheet row sets `SearchPreset`, the preset's values are sealed in for that row's search — they cannot be overridden by `--` on the CLI, by `-params-file`, or by `-c`. The following keys are sealed by the preset: -1. **Command-line parameters** (e.g. `--fragment_mass_tolerance 0.05`) -- CLI overrides take highest priority and apply to all samples -2. **Search preset** (e.g. `SearchPreset: lumos_class1`) -- preset values fill in any parameters not specified via CLI -3. **Config defaults** (`nextflow.config`) -- built-in defaults are used as a final fallback +`instrument`, `activation_method`, `digest_mass_range`, `prec_charge`, `precursor_mass_tolerance`, `precursor_error_units`, `fragment_mass_tolerance`, `fragment_bin_offset`, `number_mods`, `ms2pip_model`, `peptide_min_length`, `peptide_max_length`, `fixed_mods`, `variable_mods`. -This means a CLI flag like `--fragment_mass_tolerance 0.05` will override all presets for that parameter. +Rows that do **not** set `SearchPreset` resolve every key from the global Nextflow parameters (CLI > `-params-file` > `nextflow.config` defaults), exactly as in earlier releases. + +If you want to deviate from a built-in preset for a one-off run, either: + +1. Leave `SearchPreset` empty on the relevant rows and pass the values via `--` / `-params-file` / a custom config; or +2. Add a custom row to your own search-presets TSV (see `--search_presets`) and reference it by name from `SearchPreset`. > [!NOTE] > When using `--global_fdr`, samples sharing the same `SearchPreset` value are grouped together for global FDR estimation. Samples without a preset are grouped under a common `global` group. diff --git a/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf b/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf index ee4003d9..52586a22 100644 --- a/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf @@ -164,7 +164,7 @@ workflow PIPELINE_INITIALISATION { ch_samplesheet = ch_samplesheet_raw .combine(ch_presets_map) .map { meta, file, fasta, presetsMap -> - [resolveSearchParams(meta, presetsMap), file] + [resolvePresetParams(meta, presetsMap), file] } // @@ -280,9 +280,13 @@ def detectInputType(input) { } // -// Resolve a search parameter with priority: CLI params > samplesheet preset > nextflow.config default +// Resolve search parameters for a row. When the row sets a preset, the preset's values +// win for every key the preset defines (the preset is sealed and cannot be overridden +// via --, -params-file, or -c). Keys not defined by the preset (or rows without +// a preset) fall through to params[key], which Nextflow resolves through its native +// precedence (CLI > -params-file > config defaults). // -def resolveSearchParams(meta, presetsMap) { +def resolvePresetParams(meta, presetsMap) { def searchParamKeys = ['instrument', 'activation_method', 'digest_mass_range', 'prec_charge', 'precursor_mass_tolerance', 'precursor_error_units', 'fragment_mass_tolerance', 'fragment_bin_offset', 'number_mods', 'ms2pip_model', @@ -297,18 +301,9 @@ def resolveSearchParams(meta, presetsMap) { if (!presetConfig) { presetConfig = [:] } def result = new LinkedHashMap(meta) - searchParamKeys.each { key -> - def cliOverride = (workflow.commandLine =~ /--${key}[\s=]/).find() - if (cliOverride) { - result.put(key, params[key]) - } else if (presetConfig.containsKey(key)) { - result.put(key, presetConfig[key]) - } else { - result.put(key, params[key]) - } + result.put(key, presetConfig.containsKey(key) ? presetConfig[key] : params[key]) } - return result } diff --git a/tests/search_presets.nf.test b/tests/search_presets.nf.test index 3d441f0b..b45add28 100644 --- a/tests/search_presets.nf.test +++ b/tests/search_presets.nf.test @@ -53,4 +53,43 @@ nextflow_pipeline { ) } } + + test("-profile test_search_presets with --precursor_mass_tolerance override is ignored") { + + // Sealed-preset contract: when a samplesheet row sets SearchPreset, the preset + // values win for every key the preset defines. Overriding via params (here a + // wildly wrong precursor_mass_tolerance) must NOT change identifications, + // because the preset's value (5 ppm for qe/lumos_class1) is sealed in. + when { + params { + outdir = "$outputDir" + precursor_mass_tolerance = 999 + } + } + + then { + def peptidoform_data = [] + new File(params.outdir).eachFileRecurse { file -> + if (file.name.endsWith('.tsv') && file.parentFile.path == params.outdir) { + def lines = file.readLines() + if (lines.size() > 0) { + def header = lines[0].split('\t') + def peptidoformIndex = header.findIndexOf { it == 'peptidoform' } + if (peptidoformIndex >= 0) { + def peptidoforms = lines.drop(1).collect { line -> + def fields = line.split('\t') + fields.size() > peptidoformIndex ? fields[peptidoformIndex] : '' + }.findAll { it != '' }.sort() + peptidoform_data.add([file.name, peptidoforms]) + } + } + } + } + peptidoform_data.sort { it[0] } + assertAll( + { assert workflow.success }, + { assert snapshot(peptidoform_data).match() } + ) + } + } } diff --git a/tests/search_presets.nf.test.snap b/tests/search_presets.nf.test.snap index 7cab22d4..1951c050 100644 --- a/tests/search_presets.nf.test.snap +++ b/tests/search_presets.nf.test.snap @@ -548,10 +548,443 @@ ] ] ], + "timestamp": "2026-02-13T13:35:56.062370587", "meta": { "nf-test": "0.9.3", "nextflow": "25.10.3" - }, - "timestamp": "2026-02-13T13:35:56.062370587" + } + }, + "-profile test_search_presets with --precursor_mass_tolerance override is ignored": { + "content": [ + [ + [ + "HepG2_lumos.tsv", + [ + "AAAAAAHRL", + "AAATVTKKV", + "AAHPNVQKV", + "AAM(Oxidation)SVAKRV", + "AANAQPHKL", + "AANDKLKKM", + "AAPPPPAHA", + "AAPRPPPKPM(Oxidation)", + "AAQAHTREL", + "AARAEAREI", + "AASPVGKRL", + "AASSLKKQY", + "AATDHSNQL", + "AATPAKKTV", + "AATVTKKTY", + "AKDVKFGAD", + "ALKDKIEKA", + "ALKDSVQRA", + "ALKKALAAA", + "ALSDLHAHK", + "AM(Oxidation)AARPHSI", + "AM(Oxidation)AASPHAV", + "AM(Oxidation)ASHLTST", + "AM(Oxidation)HGHVEVV", + "AM(Oxidation)NARPHKV", + "AMNARPHKV", + "APPKAHEV", + "APRADAKAY", + "APRPPPKPM(Oxidation)", + "AVM(Oxidation)GNPKVKA", + "DAAVHQRI", + "DAKKQIKQV", + "DAPAGRKV", + "DGKKKISTV", + "DHVITNM(Oxidation)N", + "DNKKTRII", + "DPM(Oxidation)KARVV", + "DPPKTHVT", + "DPPRDSKGL", + "EYSKQM(Oxidation)QRF", + "FADPHSKRV", + "GKVGAHAGEY", + "GKVGAHAGEYG", + "GLAKSIHHA", + "GMAKKVTKA", + "GNVAEGETKPD", + "GPNKKPRF", + "GPRVVERH", + "GRVVEPKR", + "GVM(Oxidation)VGM(Oxidation)GQKD", + "HAAENPGKY", + "HAANPNGRYY", + "HAASRIKTI", + "HAATYTKKV", + "HAGPTAHEL", + "HAKEKLTAL", + "HAM(Oxidation)EEVKKF", + "HAPELTKKV", + "HLKEDQTEY", + "HPAENGKSNF", + "HPESERISM(Oxidation)", + "HPGRPQPPA", + "HPNKVKVY", + "HPYQPDPKM(Oxidation)", + "HSRNEGVATY", + "HSSPASSNY", + "HVKDANGNSF", + "HVSTHQHNY", + "IAPKTTRV", + "IASQTVKKV", + "IKTVETRD", + "IPQKFHRSV", + "ISSEAHREV", + "IVGRPRHQ", + "KAAKPKVV", + "KAAQPKSL", + "KAASHIKSI", + "KAISGVHTV", + "KAKPGTYKV", + "KALVKPKEV", + "KASEQAKVV", + "KASGTLREY", + "KAYGKAKSI", + "KGTGASGSF", + "KLAESVEKA", + "KLAPPPKKA", + "KLQPGSVKKV", + "KM(Oxidation)DESKHEI", + "KM(Oxidation)LDHEYTT", + "KMDESKHEI", + "KMNESTRSV", + "KMQEHSDQV", + "KRSSPETL", + "KSKEFVQKV", + "KTKEGVREV", + "KTLGTDVVKS", + "KVHLGARAS", + "KVVNPTQK", + "KYKERGTVL", + "KYLGKTHNL", + "KYNRQSM(Oxidation)TL", + "KYQPKPKQI", + "KYTPPPHHI", + "LAKTGVHHY", + "LLKSEKSSY", + "LPSENHKEM", + "LPSENHKEM(Oxidation)", + "M(Oxidation)PPEHVRKI", + "M(Oxidation)PQGGGQHY", + "M(Oxidation)PQKERESI", + "MPNKVRKI", + "MPQKERESI", + "NAHGEPRNY", + "NALAHKYH", + "NAM(Oxidation)KIRDV", + "NASARDNTI", + "NATAVVRHM(Oxidation)", + "NGTHVVRHY", + "NIQKITKS", + "NLKVKGPVR", + "NLRPKKKVK", + "NLSSLSKK", + "NLTEEEEKSKS", + "NNLSSLSKK", + "NPAAYENDK", + "NPFGGASHAKG", + "NPKESSSSL", + "NPKREIQKI", + "NQNPGSPRPGSTG", + "NSHSTTSSF", + "NTKGGDAPAAGEDA", + "NTKIGPRR", + "NVDYSKLKK", + "NVINGGSHA", + "NVKIVKVKKE", + "PPNKKPKV", + "QAGPINREM(Oxidation)", + "QALKHLKQY", + "QGVM(Oxidation)VGM(Oxidation)GQK", + "QPAKADKESF", + "QTHQPPAPNS", + "QYKKDGADF", + "RLAEAHAKV", + "RLHEKIQEA", + "RLNNKSAKV", + "RMARTPQTV", + "RPVPKGATY", + "RSNEHIREL", + "RSTENVNKL", + "RSTQYM(Oxidation)KEY", + "RVAGIHKKV", + "RYDDM(Oxidation)AAAM(Oxidation)", + "SAKLERSHY", + "SAM(Oxidation)SNPRAM", + "SAQTTSHEL", + "SARAGETRF", + "SASEAAKKY", + "SATGHPRKV", + "SATSNKHLL", + "SAYANAKKY", + "SEVAHRFK", + "SIVGRPRH", + "SLDKTSHSV", + "SLDTQPKKV", + "SM(Oxidation)LGSPHKV", + "SM(Oxidation)NTHLKAV", + "SM(Oxidation)PEQAHKV", + "SQRFPKAE", + "SSRSYTSGPG", + "SVKNDHSAY", + "SVQVFGRKK", + "SYVGDEAQSKR", + "SYVGDEAQSKRG", + "TAPQTQHV", + "TARPGPRAV", + "TDKSFVEK", + "TDRETGKL", + "TEDSPGLK", + "TEKLVTSK", + "TEKLVTSKG", + "TGSETESPRNPSSA", + "THYDPPRK", + "TKELPSGKKY", + "TKGGDAPAAGEDA", + "TRLSRTPGNR", + "TSGPGSRISSSS", + "TSTPNAKTV", + "TTPISEQKG", + "TVKDSRTVY", + "VAPPKAHEV", + "VARAGQKGY", + "VHLTPEEK", + "VITDKEKAE", + "VPKEGKVV", + "VQKTIAEN", + "VSKGTLVQTKGT", + "VVRHQLLKT", + "VYAQKHQQL", + "VYGKTSHL", + "YADEKTKDV", + "YGSTVSKRV", + "YLNKHIQKV", + "YTKKLNTQ", + "YTM(Oxidation)KKVHAL", + "YVGDEAQSKR", + "YVGDEAQSKRG" + ] + ], + [ + "HepG2_qe.tsv", + [ + "AAAAAAHRL", + "AAATVTKKV", + "AAHPNVQKV", + "AAM(Oxidation)SVAKRV", + "AANAQPHKL", + "AANDKLKKM", + "AAPPPPAHA", + "AAPRPPPKPM(Oxidation)", + "AAQAHTREL", + "AARAEAREI", + "AASPVGKRL", + "AASSLKKQY", + "AATDHSNQL", + "AATPAKKTV", + "AATVTKKTY", + "AKDVKFGAD", + "ALKDKIEKA", + "ALKDSVQRA", + "ALKKALAAA", + "ALSDLHAHK", + "AM(Oxidation)AARPHSI", + "AM(Oxidation)AASPHAV", + "AM(Oxidation)ASHLTST", + "AM(Oxidation)HGHVEVV", + "AM(Oxidation)NARPHKV", + "AMNARPHKV", + "APPKAHEV", + "APRADAKAY", + "APRPPPKPM(Oxidation)", + "AVM(Oxidation)GNPKVKA", + "DAAVHQRI", + "DAKKQIKQV", + "DAPAGRKV", + "DGKKKISTV", + "DHVITNM(Oxidation)N", + "DNKKTRII", + "DPM(Oxidation)KARVV", + "DPPKTHVT", + "DPPRDSKGL", + "EYSKQM(Oxidation)QRF", + "FADPHSKRV", + "GKVGAHAGEY", + "GKVGAHAGEYG", + "GLAKSIHHA", + "GMAKKVTKA", + "GNVAEGETKPD", + "GPNKKPRF", + "GPRVVERH", + "GRVVEPKR", + "GVM(Oxidation)VGM(Oxidation)GQKD", + "HAAENPGKY", + "HAANPNGRYY", + "HAASRIKTI", + "HAATYTKKV", + "HAGPTAHEL", + "HAKEKLTAL", + "HAM(Oxidation)EEVKKF", + "HAPELTKKV", + "HLKEDQTEY", + "HPAENGKSNF", + "HPESERISM(Oxidation)", + "HPGRPQPPA", + "HPNKVKVY", + "HPYQPDPKM(Oxidation)", + "HSRNEGVATY", + "HSSPASSNY", + "HVKDANGNSF", + "HVSTHQHNY", + "IAPKTTRV", + "IASQTVKKV", + "IKTVETRD", + "IPQKFHRSV", + "ISSEAHREV", + "IVGRPRHQ", + "KAAKPKVV", + "KAAQPKSL", + "KAASHIKSI", + "KAISGVHTV", + "KAKPGTYKV", + "KALVKPKEV", + "KASEQAKVV", + "KASGTLREY", + "KAYGKAKSI", + "KGTGASGSF", + "KLAESVEKA", + "KLAPPPKKA", + "KLQPGSVKKV", + "KM(Oxidation)DESKHEI", + "KM(Oxidation)LDHEYTT", + "KMDESKHEI", + "KMNESTRSV", + "KMQEHSDQV", + "KRSSPETL", + "KSKEFVQKV", + "KTKEGVREV", + "KTLGTDVVKS", + "KVHLGARAS", + "KVVNPTQK", + "KYKERGTVL", + "KYLGKTHNL", + "KYNRQSM(Oxidation)TL", + "KYQPKPKQI", + "KYTPPPHHI", + "LAKTGVHHY", + "LLKSEKSSY", + "LPSENHKEM", + "LPSENHKEM(Oxidation)", + "M(Oxidation)PPEHVRKI", + "M(Oxidation)PQGGGQHY", + "M(Oxidation)PQKERESI", + "MPNKVRKI", + "MPQKERESI", + "NAHGEPRNY", + "NALAHKYH", + "NAM(Oxidation)KIRDV", + "NASARDNTI", + "NATAVVRHM(Oxidation)", + "NGTHVVRHY", + "NIQKITKS", + "NLKVKGPVR", + "NLRPKKKVK", + "NLSSLSKK", + "NLTEEEEKSKS", + "NNLSSLSKK", + "NPAAYENDK", + "NPFGGASHAKG", + "NPKESSSSL", + "NPKREIQKI", + "NQNPGSPRPGSTG", + "NSHSTTSSF", + "NTKGGDAPAAGEDA", + "NTKIGPRR", + "NVDYSKLKK", + "NVINGGSHA", + "NVKIVKVKKE", + "PPNKKPKV", + "QAGPINREM(Oxidation)", + "QALKHLKQY", + "QGVM(Oxidation)VGM(Oxidation)GQK", + "QPAKADKESF", + "QTHQPPAPNS", + "QYKKDGADF", + "RLAEAHAKV", + "RLHEKIQEA", + "RLNNKSAKV", + "RMARTPQTV", + "RPVPKGATY", + "RSNEHIREL", + "RSTENVNKL", + "RSTQYM(Oxidation)KEY", + "RVAGIHKKV", + "RYDDM(Oxidation)AAAM(Oxidation)", + "SAKLERSHY", + "SAM(Oxidation)SNPRAM", + "SAQTTSHEL", + "SARAGETRF", + "SASEAAKKY", + "SATGHPRKV", + "SATSNKHLL", + "SAYANAKKY", + "SEVAHRFK", + "SIVGRPRH", + "SLDKTSHSV", + "SLDTQPKKV", + "SM(Oxidation)LGSPHKV", + "SM(Oxidation)NTHLKAV", + "SM(Oxidation)PEQAHKV", + "SQRFPKAE", + "SSRSYTSGPG", + "SVKNDHSAY", + "SVQVFGRKK", + "SYVGDEAQSKR", + "SYVGDEAQSKRG", + "TAPQTQHV", + "TARPGPRAV", + "TDKSFVEK", + "TDRETGKL", + "TEDSPGLK", + "TEKLVTSK", + "TEKLVTSKG", + "TGSETESPRNPSSA", + "THYDPPRK", + "TKELPSGKKY", + "TKGGDAPAAGEDA", + "TRLSRTPGNR", + "TSGPGSRISSSS", + "TSTPNAKTV", + "TTPISEQKG", + "TVKDSRTVY", + "VAPPKAHEV", + "VARAGQKGY", + "VHLTPEEK", + "VITDKEKAE", + "VPKEGKVV", + "VQKTIAEN", + "VSKGTLVQTKGT", + "VVRHQLLKT", + "VYAQKHQQL", + "VYGKTSHL", + "YADEKTKDV", + "YGSTVSKRV", + "YLNKHIQKV", + "YTKKLNTQ", + "YTM(Oxidation)KKVHAL", + "YVGDEAQSKR", + "YVGDEAQSKRG" + ] + ] + ] + ], + "timestamp": "2026-04-29T10:42:09.931526712", + "meta": { + "nf-test": "0.9.5", + "nextflow": "25.10.4" + } } } \ No newline at end of file From 11d3078bf7292c2b4e90fb444399b0e8f2f5be99 Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Wed, 29 Apr 2026 11:47:11 +0000 Subject: [PATCH 07/11] Lower PYOPENMS_CHROMATOGRAMEXTRACTOR resource class from process_single to process_low. --- modules/local/pyopenms/chromatogramextractor/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/pyopenms/chromatogramextractor/main.nf b/modules/local/pyopenms/chromatogramextractor/main.nf index 8f7456fd..b73f1024 100644 --- a/modules/local/pyopenms/chromatogramextractor/main.nf +++ b/modules/local/pyopenms/chromatogramextractor/main.nf @@ -1,6 +1,6 @@ process PYOPENMS_CHROMATOGRAMEXTRACTOR { tag "$meta.id" - label 'process_single' + label 'process_low' conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? From 34cc9a3ee9e0938cd73734aaec68d1d035c2db46 Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Wed, 29 Apr 2026 13:19:46 +0000 Subject: [PATCH 08/11] Remove dangling test_percolator/test_epicore profile includes (configs were deleted; NF 25.04 errors on missing include sources). --- nextflow.config | 2 -- 1 file changed, 2 deletions(-) diff --git a/nextflow.config b/nextflow.config index 2a64e98f..4ef1f300 100644 --- a/nextflow.config +++ b/nextflow.config @@ -232,9 +232,7 @@ profiles { test { includeConfig 'conf/test.config' } test_sdrf { includeConfig 'conf/test_sdrf.config' } test_mokapot { includeConfig 'conf/test_mokapot.config' } - test_percolator { includeConfig 'conf/test_percolator.config' } test_ionannotator { includeConfig 'conf/test_ionannotator.config' } - test_epicore { includeConfig 'conf/test_epicore.config' } test_speclib { includeConfig 'conf/test_speclib.config' } test_timstof { includeConfig 'conf/test_timstof.config' } test_single_quant { includeConfig 'conf/test_single_quant.config' } From a3b50b97d205d7414eee7254c9f71b91c25e5bcf Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Wed, 29 Apr 2026 13:30:02 +0000 Subject: [PATCH 09/11] Fix TDF2MZML resource selector: quote the name and drop leftover check_max args (NF 25.04 rejects unquoted multi-colon selectors). --- conf/base.config | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/base.config b/conf/base.config index 3c351c0f..4789a117 100644 --- a/conf/base.config +++ b/conf/base.config @@ -58,10 +58,10 @@ process { errorStrategy = 'retry' maxRetries = 2 } - withName:NFCORE_MHCQUANT:MHCQUANT:PREPARE_SPECTRA:TDF2MZML { - cpus = { 1 * task.attempt, 'cpus' } - memory = { 10.GB * task.attempt, 'memory' } - time = { 16.h * task.attempt, 'time' } + withName: 'TDF2MZML' { + cpus = { 1 * task.attempt } + memory = { 10.GB * task.attempt } + time = { 16.h * task.attempt } } withName: 'PRIDEPY_FETCH_SDRF' { errorStrategy = 'retry' From d3fe65ba9736659da1f4521f4ade7a6ee7224569 Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Wed, 29 Apr 2026 13:45:49 +0000 Subject: [PATCH 10/11] Lift conditional withName selectors out of if-blocks (NF strict syntax forbids withName: inside if {}). --- conf/modules.config | 77 ++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 962c0ce7..3d676577 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -273,37 +273,31 @@ process { } process { - if (params.quantify) { - withName: 'NFCORE_MHCQUANT:MHCQUANT:QUANT:OPENMS_IDSCORESWITCHER' { - ext.args = [ - "-new_score COMET:xcorr", - "-new_score_orientation higher_better", - "-old_score q-value" - ].join(' ').trim() - publishDir = [ - mode: params.publish_dir_mode, - pattern: '*.idXML', - enabled: false - ] - } + withName: 'NFCORE_MHCQUANT:MHCQUANT:QUANT:OPENMS_IDSCORESWITCHER' { + ext.args = [ + "-new_score COMET:xcorr", + "-new_score_orientation higher_better", + "-old_score q-value" + ].join(' ').trim() + publishDir = [ + mode: params.publish_dir_mode, + pattern: '*.idXML', + enabled: false + ] } -} -process { - if (params.rescoring_engine == 'mokapot') { - withName: 'NFCORE_MHCQUANT:MHCQUANT:RESCORE:OPENMS_IDSCORESWITCHER' { - ext.prefix = {"${meta.id}"} - ext.args = [ - "-new_score q-value", - "-new_score_orientation lower_better", - "-old_score expect" - ].join(' ').trim() - publishDir = [ - mode: params.publish_dir_mode, - pattern: '*.idXML', - enabled: false - ] - } + withName: 'NFCORE_MHCQUANT:MHCQUANT:RESCORE:OPENMS_IDSCORESWITCHER' { + ext.prefix = {"${meta.id}"} + ext.args = [ + "-new_score q-value", + "-new_score_orientation lower_better", + "-old_score expect" + ].join(' ').trim() + publishDir = [ + mode: params.publish_dir_mode, + pattern: '*.idXML', + enabled: false + ] } } @@ -547,19 +541,16 @@ process { } process { - - if (params.annotate_ions) { - withName: 'PYOPENMS_IONANNOTATOR' { - ext.args = { [ - "--precursor_charge ${meta.prec_charge}", - "--fragment_mass_tolerance ${meta.fragment_mass_tolerance}", - "--remove_precursor_peak ${params.remove_precursor_peak}" - ].join(' ').trim() } - publishDir = [ - path: {"${params.outdir}/intermediate_results/ion_annotations"}, - mode: params.publish_dir_mode, - pattern: '*.tsv' - ] - } + withName: 'PYOPENMS_IONANNOTATOR' { + ext.args = { [ + "--precursor_charge ${meta.prec_charge}", + "--fragment_mass_tolerance ${meta.fragment_mass_tolerance}", + "--remove_precursor_peak ${params.remove_precursor_peak}" + ].join(' ').trim() } + publishDir = [ + path: {"${params.outdir}/intermediate_results/ion_annotations"}, + mode: params.publish_dir_mode, + pattern: '*.tsv' + ] } } From d70bd252f9b40812e51579181efd29a78a5f9b3e Mon Sep 17 00:00:00 2001 From: jonasscheid Date: Wed, 29 Apr 2026 14:19:43 +0000 Subject: [PATCH 11/11] Migrate openms/fileconverter from local to nf-core module; rename .consensusxml output to .converted in PROCESS_FEATURE. --- CHANGELOG.md | 1 + modules.json | 5 ++ .../openms/fileconverter/environment.yml | 7 -- modules/local/openms/fileconverter/main.nf | 36 -------- .../openms/fileconverter/environment.yml | 7 ++ modules/nf-core/openms/fileconverter/main.nf | 36 ++++++++ modules/nf-core/openms/fileconverter/meta.yml | 77 +++++++++++++++++ .../openms/fileconverter/tests/main.nf.test | 57 +++++++++++++ .../fileconverter/tests/main.nf.test.snap | 84 +++++++++++++++++++ subworkflows/local/process_feature/main.nf | 49 ++++++----- 10 files changed, 291 insertions(+), 68 deletions(-) delete mode 100644 modules/local/openms/fileconverter/environment.yml delete mode 100644 modules/local/openms/fileconverter/main.nf create mode 100644 modules/nf-core/openms/fileconverter/environment.yml create mode 100644 modules/nf-core/openms/fileconverter/main.nf create mode 100644 modules/nf-core/openms/fileconverter/meta.yml create mode 100644 modules/nf-core/openms/fileconverter/tests/main.nf.test create mode 100644 modules/nf-core/openms/fileconverter/tests/main.nf.test.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index 014f7779..5b23c7c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Tightened preset normalization so empty/whitespace `FixedMods` and `VariableMods` cells are treated as empty (preserves the existing `-fixed_modifications` CLI call) [#447](https://github.com/nf-core/mhcquant/pull/447) - Migrated `conf/test_single_quant.config` from deprecated `max_cpus`/`max_memory`/`max_time` to `process.resourceLimits` [#447](https://github.com/nf-core/mhcquant/pull/447) - Replaced local `openms/textexporter` module with the `nf-core/modules` equivalent; preserved the `_exported` output suffix via `ext.prefix` to keep SUMMARIZE_RESULTS input/output filenames distinct [#447](https://github.com/nf-core/mhcquant/pull/447) +- Replaced local `openms/fileconverter` module with the `nf-core/modules` equivalent; updated `PROCESS_FEATURE` to consume the renamed `converted` output channel [#449](https://github.com/nf-core/mhcquant/pull/449) - Sealed search-preset values: when a samplesheet row sets `SearchPreset`, preset values are no longer overridable via `--`, `-params-file`, or `-c` for that row; rows without a preset resolve from `params` as before. Removed the fragile `workflow.commandLine` regex from `resolveSearchParams` (renamed to `resolvePresetParams`). Documented the new precedence in `docs/usage.md`. [#449](https://github.com/nf-core/mhcquant/pull/449) ### `Dependencies` diff --git a/modules.json b/modules.json index 73377cae..78e69c4a 100644 --- a/modules.json +++ b/modules.json @@ -20,6 +20,11 @@ "git_sha": "ca1cd2456f36c913fb3cfb6bdfbf9d1794fd493b", "installed_by": ["modules"] }, + "openms/fileconverter": { + "branch": "master", + "git_sha": "924841ecdeb9b2aea108eda13cacf45a2452d35f", + "installed_by": ["modules"] + }, "openms/filefilter": { "branch": "master", "git_sha": "ca1cd2456f36c913fb3cfb6bdfbf9d1794fd493b", diff --git a/modules/local/openms/fileconverter/environment.yml b/modules/local/openms/fileconverter/environment.yml deleted file mode 100644 index 6e3cb81e..00000000 --- a/modules/local/openms/fileconverter/environment.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: openms_fileconverter -channels: - - conda-forge - - bioconda - - defaults -dependencies: - - bioconda::openms=3.5.0 diff --git a/modules/local/openms/fileconverter/main.nf b/modules/local/openms/fileconverter/main.nf deleted file mode 100644 index a689ef56..00000000 --- a/modules/local/openms/fileconverter/main.nf +++ /dev/null @@ -1,36 +0,0 @@ -process OPENMS_FILECONVERTER { - tag "$meta.id" - label 'process_single' - - conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/openms:3.5.0--h78fb946_0' : - 'biocontainers/openms:3.5.0--h78fb946_0' }" - - input: - tuple val(meta), path(file), val(suffix) - - output: - tuple val(meta), path("*.${suffix}"), emit: consensusxml - tuple val("${task.process}"), val('openms'), eval("FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'"), topic: versions - - when: - task.ext.when == null || task.ext.when - - script: - def prefix = task.ext.prefix ?: "${meta.id}" - - """ - FileConverter \\ - -in $file \\ - -out ${prefix}.${suffix} \\ - -threads $task.cpus - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - - """ - touch ${prefix}.${suffix} - """ -} diff --git a/modules/nf-core/openms/fileconverter/environment.yml b/modules/nf-core/openms/fileconverter/environment.yml new file mode 100644 index 00000000..c8bad1b9 --- /dev/null +++ b/modules/nf-core/openms/fileconverter/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::openms=3.5.0 diff --git a/modules/nf-core/openms/fileconverter/main.nf b/modules/nf-core/openms/fileconverter/main.nf new file mode 100644 index 00000000..1d3157da --- /dev/null +++ b/modules/nf-core/openms/fileconverter/main.nf @@ -0,0 +1,36 @@ +process OPENMS_FILECONVERTER { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine in ['singularity', 'apptainer'] && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/openms:3.5.0--h78fb946_0' : + 'quay.io/biocontainers/openms:3.5.0--h78fb946_0' }" + + input: + tuple val(meta), path(input_file), val(out_type) + + output: + tuple val(meta), path("${prefix}.${out_type}"), emit: converted + tuple val("${task.process}"), val('openms'), eval("FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'"), emit: versions_openms, topic: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + prefix = task.ext.prefix ?: "${meta.id}" + """ + FileConverter \\ + -in $input_file \\ + -out ${prefix}.${out_type} \\ + -threads $task.cpus \\ + $args + """ + + stub: + prefix = task.ext.prefix ?: "${meta.id}" + """ + touch ${prefix}.${out_type} + """ +} diff --git a/modules/nf-core/openms/fileconverter/meta.yml b/modules/nf-core/openms/fileconverter/meta.yml new file mode 100644 index 00000000..cc144cb6 --- /dev/null +++ b/modules/nf-core/openms/fileconverter/meta.yml @@ -0,0 +1,77 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json +name: "openms_fileconverter" +description: Converts between different mass spectrometry file formats (e.g. mzML, mzXML, mgf, mzData, dta, dta2d, featureXML, consensusXML, idXML). +keywords: + - file conversion + - mass spectrometry + - mzml + - mzxml + - openms + - proteomics +tools: + - "openms": + description: "OpenMS is an open-source software C++ library for LC-MS data management + and analyses" + homepage: "https://openms.de" + documentation: "https://openms.readthedocs.io/en/latest/index.html" + tool_dev_url: "https://github.com/OpenMS/OpenMS" + doi: "10.1038/s41592-024-02197-7" + licence: ["BSD"] + identifier: "biotools:openms" + +input: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - input_file: + type: file + description: Mass spectrometry data file in any OpenMS-supported input format + pattern: "*.{mzML,mzXML,mgf,mzData,dta,dta2d,edta,featureXML,consensusXML,idXML,traML,fid,raw}" + ontologies: + - edam: http://edamontology.org/data_2536 # Mass spectrometry data + - out_type: + type: string + description: Target output file extension/format (e.g. `mzML`, `mgf`, `featureXML`) + +output: + converted: + - - meta: + type: map + description: | + Groovy Map containing sample information + e.g. `[ id:'test', single_end:false ]` + - "${prefix}.${out_type}": + type: file + description: Converted mass spectrometry file in the format specified via `out_type` + pattern: "*.{mzML,mzXML,mgf,mzData,dta,dta2d,edta,featureXML,consensusXML,idXML,traML}" + ontologies: + - edam: http://edamontology.org/data_2536 # Mass spectrometry data + versions_openms: + - - ${task.process}: + type: string + description: The name of the process + - openms: + type: string + description: The name of the tool + - "FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'": + type: eval + description: The expression to obtain the version of the tool + +topics: + versions: + - - ${task.process}: + type: string + description: The name of the process + - openms: + type: string + description: The name of the tool + - "FileInfo --help 2>&1 | sed -nE 's/^Version: ([0-9.]+).*/\\1/p'": + type: eval + description: The expression to obtain the version of the tool + +authors: + - "@jonasscheid" +maintainers: + - "@jonasscheid" diff --git a/modules/nf-core/openms/fileconverter/tests/main.nf.test b/modules/nf-core/openms/fileconverter/tests/main.nf.test new file mode 100644 index 00000000..7048707a --- /dev/null +++ b/modules/nf-core/openms/fileconverter/tests/main.nf.test @@ -0,0 +1,57 @@ +nextflow_process { + + name "Test Process OPENMS_FILECONVERTER" + script "../main.nf" + process "OPENMS_FILECONVERTER" + + tag "modules" + tag "modules_nfcore" + tag "openms" + tag "openms/fileconverter" + + test("proteomics - mzML - to mgf") { + + when { + process { + """ + input[0] = [ + [ id:'test' ], + file(params.modules_testdata_base_path + 'proteomics/msspectra/peakpicker_tutorial_1.mzML', checkIfExists: true), + 'mgf' + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("proteomics - mzML - to mgf - stub") { + + options "-stub" + + when { + process { + """ + input[0] = [ + [ id:'test_stub' ], + file(params.modules_testdata_base_path + 'proteomics/msspectra/peakpicker_tutorial_1.mzML', checkIfExists: true), + 'mgf' + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/openms/fileconverter/tests/main.nf.test.snap b/modules/nf-core/openms/fileconverter/tests/main.nf.test.snap new file mode 100644 index 00000000..62287c54 --- /dev/null +++ b/modules/nf-core/openms/fileconverter/tests/main.nf.test.snap @@ -0,0 +1,84 @@ +{ + "proteomics - mzML - to mgf - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test_stub" + }, + "test_stub.mgf:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "1": [ + [ + "OPENMS_FILECONVERTER", + "openms", + "3.5.0" + ] + ], + "converted": [ + [ + { + "id": "test_stub" + }, + "test_stub.mgf:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ], + "versions_openms": [ + [ + "OPENMS_FILECONVERTER", + "openms", + "3.5.0" + ] + ] + } + ], + "timestamp": "2026-04-29T08:42:49.654780453", + "meta": { + "nf-test": "0.9.5", + "nextflow": "25.10.4" + } + }, + "proteomics - mzML - to mgf": { + "content": [ + { + "0": [ + [ + { + "id": "test" + }, + "test.mgf:md5,2ab0ae45c51f98d1eca2ba862ad74be8" + ] + ], + "1": [ + [ + "OPENMS_FILECONVERTER", + "openms", + "3.5.0" + ] + ], + "converted": [ + [ + { + "id": "test" + }, + "test.mgf:md5,2ab0ae45c51f98d1eca2ba862ad74be8" + ] + ], + "versions_openms": [ + [ + "OPENMS_FILECONVERTER", + "openms", + "3.5.0" + ] + ] + } + ], + "timestamp": "2026-04-29T08:42:37.179934897", + "meta": { + "nf-test": "0.9.5", + "nextflow": "25.10.4" + } + } +} \ No newline at end of file diff --git a/subworkflows/local/process_feature/main.nf b/subworkflows/local/process_feature/main.nf index e414883c..f27e5605 100644 --- a/subworkflows/local/process_feature/main.nf +++ b/subworkflows/local/process_feature/main.nf @@ -2,41 +2,40 @@ * Perform the quantification by extracting the feature intensities and group runs corresponding to the same sample and condition. */ -include { OPENMS_FEATUREFINDERIDENTIFICATION } from '../../../modules/local/openms/featurefinderidentification' -include { OPENMS_FEATURELINKERUNLABELEDKD } from '../../../modules/local/openmsthirdparty/featurelinkerunlabeledkd' -include { OPENMS_IDCONFLICTRESOLVER } from '../../../modules/local/openms/idconflictresolver/main' -include { OPENMS_FILECONVERTER } from '../../../modules/local/openms/fileconverter/main' +include { OPENMS_FEATUREFINDERIDENTIFICATION } from '../../../modules/local/openms/featurefinderidentification' +include { OPENMS_FEATURELINKERUNLABELEDKD } from '../../../modules/local/openmsthirdparty/featurelinkerunlabeledkd' +include { OPENMS_IDCONFLICTRESOLVER } from '../../../modules/local/openms/idconflictresolver/main' +include { OPENMS_FILECONVERTER } from '../../../modules/nf-core/openms/fileconverter/main' workflow PROCESS_FEATURE { take: - ch_runs_to_be_quantified + ch_runs_to_be_quantified main: - // Quantify identifications using targeted feature extraction - OPENMS_FEATUREFINDERIDENTIFICATION(ch_runs_to_be_quantified).featurexml - .map { meta, featurexml -> [ groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count), featurexml] } - .groupTuple() - .set { ch_featuresxmls } + // Quantify identifications using targeted feature extraction + OPENMS_FEATUREFINDERIDENTIFICATION(ch_runs_to_be_quantified).featurexml + .map { meta, featurexml -> [groupKey([id: "${meta.sample}_${meta.condition}"], meta.group_count), featurexml] } + .groupTuple() + .set { ch_featuresxmls } - ch_featuresxmls - .branch { - single: it[1].size() == 1 - multiple: it[1].size() > 1 - } - .set { ch_features } + ch_featuresxmls + .branch { _meta, features -> + single: features.size() == 1 + multiple: features.size() > 1 + } + .set { ch_features } - // Link extracted features - OPENMS_FEATURELINKERUNLABELEDKD(ch_features.multiple) + // Link extracted features + OPENMS_FEATURELINKERUNLABELEDKD(ch_features.multiple) - // Single replicate: promote featureXML to consensusXML - OPENMS_FILECONVERTER(ch_features.single.map { meta, features -> [ meta, features[0], "consensusXML" ] }) + // Single replicate: promote featureXML to consensusXML + OPENMS_FILECONVERTER(ch_features.single.map { meta, features -> [meta, features[0], "consensusXML"] }) - // Resolve conflicting ids matching to the same feature - ch_consensus_input = OPENMS_FEATURELINKERUNLABELEDKD.out.consensusxml - .mix(OPENMS_FILECONVERTER.out.consensusxml) + // Resolve conflicting ids matching to the same feature + ch_consensus_input = OPENMS_FEATURELINKERUNLABELEDKD.out.consensusxml.mix(OPENMS_FILECONVERTER.out.converted) - OPENMS_IDCONFLICTRESOLVER(ch_consensus_input) + OPENMS_IDCONFLICTRESOLVER(ch_consensus_input) emit: - consensusxml = OPENMS_IDCONFLICTRESOLVER.out.consensusxml + consensusxml = OPENMS_IDCONFLICTRESOLVER.out.consensusxml }