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..5b23c7c1 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 - Solitude - 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 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) ### `Fixed` @@ -16,10 +17,20 @@ 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.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) ### `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 (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` @@ -29,6 +40,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.5.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/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' diff --git a/conf/modules.config b/conf/modules.config index c1121e11..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 + ] } } @@ -370,7 +364,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 = [ @@ -470,6 +464,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 @@ -546,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' + ] } } 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/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/modules.json b/modules.json index 4d2cd03e..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", @@ -55,6 +60,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/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 deleted file mode 100644 index e9ab2df5..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.4.1 diff --git a/modules/local/openms/fileconverter/main.nf b/modules/local/openms/fileconverter/main.nf deleted file mode 100644 index f4a5a885..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.4.1--h81ffffe_1' : - 'biocontainers/openms:3.4.1--h81ffffe_1' }" - - 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/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/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/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 ? 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) diff --git a/modules/local/openms/textexporter/environment.yml b/modules/nf-core/openms/fileconverter/environment.yml similarity index 100% rename from modules/local/openms/textexporter/environment.yml rename to modules/nf-core/openms/fileconverter/environment.yml 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/modules/nf-core/openms/textexporter/environment.yml b/modules/nf-core/openms/textexporter/environment.yml new file mode 100644 index 00000000..fd1026d7 --- /dev/null +++ b/modules/nf-core/openms/textexporter/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/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/nextflow.config b/nextflow.config index 46484ee2..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' } @@ -359,7 +357,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/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 } 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/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf b/subworkflows/local/utils_nfcore_mhcquant_pipeline/main.nf index 125e62fa..52586a22 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]) 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] } } @@ -161,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] } // @@ -277,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', @@ -294,37 +301,12 @@ 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 } -// -// 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/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 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/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 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 4410fbaf..1951c050 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" } }, [ @@ -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 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 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") { 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" } }, [ 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'