Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified .hooks/block_pipeline_outdir.sh
100644 → 100755
Empty file.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +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).

## [Unreleased]

### `Fixed`

- Allow an empty fixed-modification set from SDRF without adding a default modification. Reject searches using MS-GF+ when both modification sets remain empty after the variable-modification fallback, to prevent OpenMS MSGFPlusAdapter from silently enabling fixed Carbamidomethyl (C).
- Trim the selected variable-modification value before validation so a whitespace-only fallback cannot bypass the MS-GF+ empty-modification check.

## [1.10.0] bigbio/quantms - [15/08/2026] - [London]

### `Changed`
Expand Down
8 changes: 8 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ nextflow run bigbio/quantms --input '/url/path/to/your/experiment_design.sdrf.ts

The input file must be in [Sample-to-data-relationship format (SDRF)](https://pubs.acs.org/doi/abs/10.1021/acs.jproteome.0c00376) and can have `.sdrf`, `.tsv`, or `.csv` file extensions.

### Search modifications from SDRF

Fixed modifications are read exclusively from the SDRF modification annotations. If the parsed `FixedModifications` value is empty, quantms preserves an empty fixed-modification set instead of rejecting the input or adding a default modification such as Carbamidomethyl (C). Annotate modifications according to the experimental protocol; a modification that is variable must not be declared fixed just to make the input pass validation.

Variable modifications are read from the SDRF when present. If the parsed variable-modification set is empty or contains only whitespace, quantms uses `--variable_mods`, whose default is `Oxidation (M)`. Leading and trailing whitespace is removed from the selected SDRF or fallback value before validation; a whitespace-only fallback is treated as an empty set. An empty fixed-modification set with a nonempty variable-modification set is supported with Comet, MS-GF+, and Sage.

For a search without either fixed or variable modifications, select Comet and/or Sage and set `variable_mods` to an empty string. When `--search_engines` includes `msgf`, quantms rejects an empty final set of both modification types: the current OpenMS MSGFPlusAdapter would otherwise enable fixed Carbamidomethyl (C). This check runs after the variable-modification fallback has been applied.

### Supported file formats

The pipeline supports the following mass spectrometry data file formats:
Expand Down
2 changes: 1 addition & 1 deletion nextflow_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"mimetype": "text/csv",
"pattern": "^\\S+\\.(?:csv|tsv|sdrf)$",
"description": "URI/path to an SDRF file in SDRF format with .sdrf, .tsv, or .csv extension. For more info see help text or docs.",
"help_text": "Input is specified by using a path or URI to a PRIDE Sample to Data Relation Format file (SDRF), e.g. as part of a submitted and annotated PRIDE experiment (see [here](https://github.com/bigbio/proteomics-metadata-standard/tree/master/annotated-projects) for examples). Input files will be downloaded and cached from the URIs specified in the SDRF file.\n\nThe SDRF file can have .sdrf, .tsv, or .csv extensions. An OpenMS-style experimental design will be generated based on the factor columns of the SDRF.\n\nThe following parameters are read **exclusively** from the SDRF file (required columns):\n\n * `acquisition_method` (from 'Proteomics Data Acquisition Method' column),\n * `labelling_type` (from 'Label' column),\n * `enzyme` (from 'Enzyme' column),\n * `fixed_mods` (from 'FixedModifications' column)\n\nThe following parameters are read from SDRF but can be **overridden via command line**:\n\n * `precursor_mass_tolerance` (from 'PrecursorMassTolerance' column),\n * `precursor_mass_tolerance_unit` (from 'PrecursorMassToleranceUnit' column),\n * `fragment_mass_tolerance` (from 'FragmentMassTolerance' column),\n * `fragment_mass_tolerance_unit` (from 'FragmentMassToleranceUnit' column),\n * `variable_mods` (from 'VariableModifications' column)",
"help_text": "Input is specified by using a path or URI to a PRIDE Sample to Data Relation Format file (SDRF), e.g. as part of a submitted and annotated PRIDE experiment (see [here](https://github.com/bigbio/proteomics-metadata-standard/tree/master/annotated-projects) for examples). Input files will be downloaded and cached from the URIs specified in the SDRF file.\n\nThe SDRF file can have .sdrf, .tsv, or .csv extensions. An OpenMS-style experimental design will be generated based on the factor columns of the SDRF.\n\nThe following parameters are read **exclusively** from the SDRF file (required columns):\n\n * `acquisition_method` (from 'Proteomics Data Acquisition Method' column),\n * `labelling_type` (from 'Label' column),\n * `enzyme` (from 'Enzyme' column)\n\nFixed modifications are also read exclusively from the SDRF ('FixedModifications' column). An empty set is supported and does not add a default fixed modification. When using MS-GF+, at least one fixed or variable modification must remain after applying the variable-modification fallback, because OpenMS MSGFPlusAdapter otherwise enables fixed Carbamidomethyl (C). Use Comet or Sage for a search without modifications.\n\nThe following parameters are read from SDRF but can be **overridden via command line**:\n\n * `precursor_mass_tolerance` (from 'PrecursorMassTolerance' column),\n * `precursor_mass_tolerance_unit` (from 'PrecursorMassToleranceUnit' column),\n * `fragment_mass_tolerance` (from 'FragmentMassTolerance' column),\n * `fragment_mass_tolerance_unit` (from 'FragmentMassToleranceUnit' column),\n * `variable_mods` (from 'VariableModifications' column)",
"fa_icon": "fas fa-file-csv"
},
"outdir": {
Expand Down
14 changes: 10 additions & 4 deletions subworkflows/local/create_input_channel/main.nf
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ def create_meta_channel(LinkedHashMap row, enzymes, files, wrapper) {
// Validate required SDRF columns - these parameters are exclusively read from SDRF (no command-line override)
def requiredColumns = [
'Label': row.Label,
'Enzyme': row.Enzyme,
'FixedModifications': row.FixedModifications
'Enzyme': row.Enzyme
]

def missingColumns = []
Expand All @@ -122,9 +121,9 @@ def create_meta_channel(LinkedHashMap row, enzymes, files, wrapper) {
exit(1)
}

// Set values from SDRF (required columns)
// An empty fixed-modification set must remain an empty string for the search adapters.
meta.labelling_type = row.Label
meta.fixedmodifications = row.FixedModifications
meta.fixedmodifications = row.FixedModifications?.toString()?.trim() ?: ''
meta.enzyme = row.Enzyme

// Set tolerance values: use SDRF if available, otherwise fall back to params
Expand Down Expand Up @@ -182,6 +181,13 @@ def create_meta_channel(LinkedHashMap row, enzymes, files, wrapper) {
} else {
meta.variablemodifications = params.variable_mods
}
meta.variablemodifications = meta.variablemodifications?.toString()?.trim() ?: ''

if (params.search_engines.contains('msgf') && !meta.fixedmodifications && !meta.variablemodifications) {
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
exit(1, "ERROR: Both modification lists are empty for '${filestr}', " +
'but OpenMS MSGFPlusAdapter would enable fixed Carbamidomethyl (C) in this case. ' +
'Use Comet or Sage for a search without modifications.')
}

enzymes += row.Enzyme
if (enzymes.size() > 1) {
Expand Down
97 changes: 97 additions & 0 deletions subworkflows/local/create_input_channel/tests/main.nf.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
nextflow_function {

name "SDRF search modification metadata"
script "../main.nf"
function "create_meta_channel"
tag "create_input_channel"

[
[name: 'empty fixed set', fixed: '', variable: 'Carbamidomethyl (C),Oxidation (M),Propionamide (C)'],
[name: 'missing fixed set', fixed: null, variable: 'Oxidation (M)'],
[
name: 'whitespace fixed set', fixed: ' ',
variable: 'Carbamidomethyl (C),Oxidation (M),Acetyl (Protein N-term)'
],
[name: 'nonempty fixed set', fixed: 'Carbamidomethyl (C)', variable: 'Oxidation (M)'],
[name: 'variable fallback', fixed: '', variable: null],
[
name: 'trim SDRF variable set', fixed: '', variable: ' Oxidation (M) ',
fallback: 'Acetyl (Protein N-term)', expectedVariable: 'Oxidation (M)'
],
[
name: 'trim variable fallback', fixed: '', variable: null,
fallback: ' Oxidation (M) ', expectedVariable: 'Oxidation (M)'
],
[
name: 'whitespace SDRF uses fallback', fixed: '', variable: ' ',
expectedVariable: 'Oxidation (M)'
],
[name: 'no modifications with Comet', fixed: '', variable: '', fallback: '', engines: 'comet'],
[name: 'no modifications with Sage', fixed: '', variable: '', fallback: '', engines: 'sage'],
[
name: 'whitespace fallback with Comet', fixed: '', variable: '', fallback: ' ',
engines: 'comet', expectedVariable: ''
],
[
name: 'null fallback with Sage', fixed: '', variable: null, fallback: null,
engines: 'sage', expectedVariable: ''
],
[
name: 'reject MSGF implicit CAM', fixed: '', variable: '', fallback: '',
error: 'Both modification lists are empty'
],
[
name: 'reject MSGF whitespace fallback', fixed: '', variable: '', fallback: ' ',
error: 'Both modification lists are empty'
],
[
name: 'require label', fixed: '', variable: 'Oxidation (M)', label: '',
error: 'Missing or empty required SDRF columns'
],
[
name: 'require enzyme', fixed: '', variable: 'Oxidation (M)', enzyme: '',
error: 'Missing or empty required SDRF columns'
],
].each { example ->
test(example.name) {
when {
params {
root_folder = null
search_engines = example.engines ?: 'comet,msgf,sage'
variable_mods = example.containsKey('fallback') ? example.fallback : 'Oxidation (M)'
}
function {
"""
input[0] = [
URI: "${projectDir}/main.nf",
'Proteomics Data Acquisition Method': 'data-dependent acquisition',
DissociationMethod: 'BEAM-TYPE COLLISION-INDUCED DISSOCIATION',
Label: ${(example.containsKey('label') ? example.label : 'label free sample').inspect()},
Enzyme: ${(example.containsKey('enzyme') ? example.enzyme : 'Trypsin').inspect()},
FixedModifications: ${example.fixed.inspect()},
VariableModifications: ${example.variable.inspect()}
]
input[1] = [] as Set
input[2] = [] as Set
input[3] = [labelling_type: '', experiment_id: 'test.sdrf.tsv']
"""
}
}
then {
if (example.error) {
assert function.failed
assert function.stdout.any { it.contains(example.error) }
} else {
assert function.success
assert function.result[0].fixedmodifications == (example.fixed?.trim() ?: '')
def fallback = example.containsKey('fallback') ? example.fallback : 'Oxidation (M)'
def expectedVariable = example.containsKey('expectedVariable')
? example.expectedVariable : (example.variable ?: fallback)
assert function.result[0].variablemodifications == expectedVariable
assert function.result[0].enzyme == 'Trypsin'
assert function.result[0].labelling_type == 'label free sample'
}
}
}
}
}
Loading