Skip to content

Fix search settings, Sage run matching and rescoring tolerance units; add independent LFQ groups - #734

Open
Shen-YuFei wants to merge 17 commits into
bigbio:devfrom
Shen-YuFei:dev
Open

Shen-YuFei wants to merge 17 commits into
bigbio:devfrom
Shen-YuFei:dev

Conversation

@Shen-YuFei

@Shen-YuFei Shen-YuFei commented Sep 16, 2026 •

Copy link
Copy Markdown

Summary

Fixes found while re-analysing public spike-in datasets with quantms.

Search modifications from SDRF

  • Allow an empty fixed-modification set from SDRF without adding a default modification.
  • Trim the selected variable-modification value before validation.
  • Reject MS-GF+ searches when both modification sets are empty, because OpenMS MSGFPlusAdapter would otherwise enable fixed Carbamidomethyl (C).

Comet fragment binning

  • New optional Comet settings: --comet_fragment_bin_tol (native full width in Da), --comet_fragment_bin_offset and --comet_instrument. Per-run overrides are available through ext.comet_fragment_bin_tol, ext.comet_fragment_bin_offset and ext.comet_instrument. The width is divided by two for OpenMS CometAdapter.
  • Without these settings, the automatic behaviour is kept. Da tolerances use the same adapter tolerance, offset and instrument mode as before. ppm tolerances use the previous guess (below 50 ppm: 0.03 Da bin, offset 0, high_res; otherwise 1.0005 Da, offset 0.4, low_res) and log a warning.
  • The effective settings are written to .nextflow.log at DEBUG level.

MS2 rescoring tolerance

Sage

  • Match each split identification file to its run by exact name. Before, the glob fr1*_sage.idparquet also matched fr10 (seen in PXD011691).
  • Declare Sage outputs for Bruker .d runs without the .d suffix, as IDRipper names them.

Independent LFQ groups (--lfq_group_by)

Other

  • Make .hooks/block_pipeline_outdir.sh executable, as the pre-commit hook calls it directly.

quantms-rescoring: 0.0.25 and the pending release

When PSM_CLEAN merges several search engines, 0.0.24 could give identifications of different spectra the same index. 0.0.25 indexes each engine's identifications separately, which removes those collisions, but a spectrum found by two engines then becomes two identifications:

  • test_lfq: on identical Comet and Sage results, 0.0.24 gives psm 51, feature 41, pg 25, and 0.0.25 gives psm 61, feature 46, pg 23. The 9 PSMs found only with 0.0.24 all had the same PEP (0.047619); of 16 checked spectra found only with 0.0.25, 11 match BSA.
  • test_tmt and test_tmt_corr fail in CI. IsobaricWorkflow quantifies each identification, so a spectrum with two identifications is quantified twice, and the QPX export rejects the repeated feature key (ConsensusMapArrowExport: refusing invalid QPX feature batch ... repeats the QPX feature primary key). In test_tmt, 1851 of 5599 spectra were split, and 46 still had two identifications after Percolator.

bigbio/quantms-rescoring#102 keeps one identification per spectrum. Until a release includes it, test_tmt and test_tmt_corr fail on this branch; after the release, the four rescoring modules move to that version. With a container built from quantms-rescoring main plus #102:

  • test_tmt: one identification per spectrum after Percolator. Against 0.0.24, target PSMs go from 3166 to 3282 and proteins in the MSstats input from 463 to 477.
  • test_lfq: psm 62, feature 46, pg 24.

Verification

Nextflow 26.04.6, Docker.

  • pre-commit run --files <changed files>: passed.
  • nf-core pipelines lint: 0 failures.
  • nf-test (--profile docker): 37 tests passed (Comet binning 17, LFQ grouping 3, qpx/openmsconsensus 1, create_input_channel 16).
  • pytest tests/test_sage_outputs.py: 1 passed.
  • -profile test_lfq,docker: passed with released containers only. QPX rows: psm 61, feature 46, pg 23.
  • -profile test_lfq,docker --lfq_group_by 'source name': passed with released containers (quantms-utils 0.0.31 biocontainer). Three groups, three ProteomicsLFQ tasks, and each QPX dataset contains only the two fractions of its sample. The report step receives lfq_groups.tsv: with pmultiqc 0.0.48 the report data are the same as without it, and pmultiqc from feat(qpx): Report cross-run QC per quantms LFQ group pmultiqc#746, run on the same report inputs, shows one summary row per group.
  • -profile test_dda_id_ms2pip,docker with the SDRF's 0.2 Da and with a 20 ppm copy: passed with quantms-rescoring 0.0.25 (MS2PIP 4.2.0). MS2PIP received the tolerance in Da and in ppm.
  • Comet with ppm: PXD000001 searched with Comet only at 20 ppm (automatic binning, high_res) gave 2766 PSMs at q < 0.01, compared with 2778 at the SDRF's 0.2 Da.
  • All 7 CI profiles (test_lfq, test_tmt, test_tmt_corr, test_localize, test_dda_id_ms2pip, test_dda_id_alphapeptdeep, test_dda_id_fine_tuning) with -profile <test>,docker,dev on Nextflow 25.10.4 and 26.10.2, using the quantms-rescoring container built with [DOC] how to debug #102: passed.
  • -profile test_lfq_sage,docker fails at SAMPLESHEET_CHECK (AttributeError: 'float' object has no attribute 'strip' in sdrf-pipelines) on both this branch and current dev. test_lfq runs Sage as well (search_engines = "comet,sage").

@coderabbitai

coderabbitai Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: dd52668a-4d33-4633-a9b8-5cdd1bcea3a9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Shen-YuFei
Shen-YuFei marked this pull request as ready for review September 16, 2026 07:56
Copilot AI lite review requested due to automatic review settings September 16, 2026 07:56
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Allow empty SDRF fixed modifications with safe MS-GF+ validation

🐞 Bug fix 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Preserve empty SDRF fixed-modification sets without injecting Carbamidomethyl.
• Reject MS-GF+ only when fixed and fallback-resolved variable modifications are both empty.
• Add regression coverage, user guidance, changelog notes, and restore hook executability.
Diagram

graph TD
    A["SDRF row"] --> B["Validate fields"] --> C["Normalize fixed"] --> D["Variable fallback"] --> E{"MS-GF+ empty?"}
    E -- "Yes" --> F["Reject search"]
    E -- "No" --> G["Search metadata"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Patch or upgrade MSGFPlusAdapter
  • ➕ Would permit truly modification-free MS-GF+ searches
  • ➕ Could remove the pipeline-specific compatibility check
  • ➖ Depends on an upstream OpenMS behavior change or maintained patch
  • ➖ Introduces dependency and version-compatibility risk
2. Validate inside search-engine modules
  • ➕ Would colocate the restriction with the affected MS-GF+ adapter
  • ➕ Could isolate engine-specific rules from input parsing
  • ➖ Would detect invalid input later in pipeline execution
  • ➖ May duplicate validation across downstream module paths

Recommendation: Keep the PR's centralized, post-fallback validation because it fails early and preserves valid empty-fixed inputs for every engine. An upstream MSGFPlusAdapter fix would be preferable long-term, but relying on it now would not protect current OpenMS versions.

Files changed (6) +81 / -5

Bug fix (1) +7 / -4
main.nfPreserve empty fixed modifications and guard MS-GF+ +7/-4

Preserve empty fixed modifications and guard MS-GF+

• Removes FixedModifications from nonempty required-field validation and normalizes absent or whitespace values to an empty string. After variable fallback resolution, rejects MS-GF+ searches only when both modification sets remain empty.

subworkflows/local/create_input_channel/main.nf

Tests (1) +59 / -0
main.nf.testCover SDRF modification normalization and engine safeguards +59/-0

Cover SDRF modification normalization and engine safeguards

• Adds parameterized nf-test cases for empty, missing, whitespace, and populated fixed modifications; variable fallback behavior; modification-free Comet and Sage searches; MS-GF+ rejection; and retained label and enzyme validation.

subworkflows/local/create_input_channel/tests/main.nf.test

Documentation (3) +15 / -1
CHANGELOG.mdRecord empty fixed-modification handling +6/-0

Record empty fixed-modification handling

• Adds an Unreleased fix entry describing supported empty fixed modifications and the MS-GF+ safeguard against implicit Carbamidomethyl.

CHANGELOG.md

usage.mdDocument SDRF modification resolution rules +8/-0

Document SDRF modification resolution rules

• Explains fixed and variable modification sourcing, fallback behavior, and modification-free search support. Documents why fully empty sets are rejected for MS-GF+ but accepted for Comet and Sage.

docs/usage.md

nextflow_schema.jsonClarify SDRF modification requirements in input help +1/-1

Clarify SDRF modification requirements in input help

• Updates schema help text so fixed modifications are no longer presented as nonempty required values. Describes empty-set support and the MS-GF+ compatibility restriction.

nextflow_schema.json

Other (1) +0 / -0
block_pipeline_outdir.shRestore pipeline-output guard executability +0/-0

Restore pipeline-output guard executability

• Restores the executable file mode for the existing commit guard. The script logic is unchanged.

.hooks/block_pipeline_outdir.sh

@qodo-code-review

qodo-code-review Bot commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Three test cases exceed line limit ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The reject MSGF implicit CAM, require label, and require enzyme test maps each place all
fields on one line beyond the 120-character target. Adding another input or assertion attribute to
these regression cases will further obscure which values distinguish each scenario.
Code

subworkflows/local/create_input_channel/tests/main.nf.test[R16-18]

+        [name: 'reject MSGF implicit CAM', 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'],
Evidence
Compliance rule 10 applies the 120-character target to changed Nextflow and Groovy code. The three
added test definitions each combine enough map fields on one line to exceed that target.

AGENTS.md: Nextflow and Groovy Code Must Follow Project Style
subworkflows/local/create_input_channel/tests/main.nf.test[16-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Three newly added test-case definitions avoidably exceed the project's 120-character line-length target.

## Fix Focus Areas
- subworkflows/local/create_input_channel/tests/main.nf.test[16-18]

## Recommended Fix
Expand each long test map across multiple indented lines while preserving every field and expected value.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Validation text exceeds line limit ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new exit call places the entire interpolated validation message on a single line that exceeds
the project's 120-character target. Editing this message requires scanning a dense line, and future
additions can make the validation branch still harder to review.
Code

subworkflows/local/create_input_channel/main.nf[186]

+        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.")
Evidence
Compliance rule 10 requires changed Nextflow and Groovy code to generally remain within 120
characters and identifies avoidable excess as a failure. The added validation message is written
entirely on one substantially longer line.

AGENTS.md: Nextflow and Groovy Code Must Follow Project Style
subworkflows/local/create_input_channel/main.nf[186-186]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new MS-GF+ validation message avoidably exceeds the project's 120-character line-length target.

## Fix Focus Areas
- subworkflows/local/create_input_channel/main.nf[186-186]

## Recommended Fix
Build the message from wrapped interpolated and single-quoted string segments, preserving its exact rendered text, and pass the resulting value to `exit`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Whitespace-only searches reach MS-GF+ ✓ Resolved 🐞 Bug ≡ Correctness
Description
The new guard tests meta.variablemodifications by Groovy truthiness even though the fallback
assigns params.variable_mods without trimming or normalizing it. When fixed modifications are
empty and --variable_mods contains only whitespace, the guard accepts the row and the MS-GF+
command receives that whitespace as a modification token.
Code

subworkflows/local/create_input_channel/main.nf[185]

+    if (params.search_engines.contains('msgf') && !meta.fixedmodifications && !meta.variablemodifications) {
Evidence
The fallback is stored without normalization, while the added guard only checks truthiness; the
schema permits arbitrary strings, and the downstream adapter tokenizes the stored value directly
into command arguments.

subworkflows/local/create_input_channel/main.nf[178-186]
nextflow_schema.json[302-307]
modules/local/openms/msgf/main.nf[90-91]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The MS-GF+ empty-modification guard treats a whitespace-only `params.variable_mods` value as nonempty, allowing a semantically empty search configuration to reach the adapter.

## Fix Focus Areas
- subworkflows/local/create_input_channel/main.nf[178-186]
- subworkflows/local/create_input_channel/tests/main.nf.test[8-18]

## Recommended Fix
Normalize the selected SDRF or fallback variable-modification value with null-safe string conversion and `trim()`, producing an empty string when appropriate, before applying the MS-GF+ guard. Add a regression case using an empty fixed set and a whitespace-only fallback with MS-GF+ and assert that validation rejects it.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced: This changes runtime SDRF validation and MS-GF+ behavior, including fallback ordering and adapter safety, so it carries meaningful correctness risk despite the localized scope.

Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread subworkflows/local/create_input_channel/main.nf Outdated
Comment thread subworkflows/local/create_input_channel/tests/main.nf.test Outdated
Comment thread subworkflows/local/create_input_channel/main.nf

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Trim or normalize whitespace-only variable modifications and add regression coverage.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Fixes SDRF handling so empty fixed modifications remain valid while preventing unsafe empty MS-GF+ modification configurations.

Changes:

  • Updates modification validation and fallback behavior.
  • Adds regression tests and documentation/changelog updates.
  • Restores the pipeline-output hook executable bit.

Review note: whitespace-only variable modifications can bypass the MS-GF+ validation and require normalization.

File summaries
File Summary
subworkflows/local/create_input_channel/tests/main.nf.test Adds regression coverage for SDRF modifications.
subworkflows/local/create_input_channel/main.nf Adjusts modification validation and metadata handling.
nextflow_schema.json Documents updated input behavior.
docs/usage.md Explains modification handling and MS-GF+ limitations.
CHANGELOG.md Records the fix.
.hooks/block_pipeline_outdir.sh Restores the executable bit without logic changes.
Review details
  • Files reviewed: 5/6 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

meta.variablemodifications = params.variable_mods
}

if (params.search_engines.contains('msgf') && !meta.fixedmodifications && !meta.variablemodifications) {
@Shen-YuFei Shen-YuFei closed this Sep 17, 2026
@nikita-a-v

Copy link
Copy Markdown

I encountered the same issue, and this commit appears to fix it for me (at least while using comet search engine). I noticed the PR was closed without being merged, but I couldn’t find an explanation in the thread. Was the fix superseded or intentionally not merged?

@Shen-YuFei

Copy link
Copy Markdown
Author

@nikita-a-v Hi Nikita, I ran into this issue (and a few others) while running quantms on a batch of PXD datasets, so I'm planning to fix all the problems I hit before reopening this PR. It should be done soon.

@Shen-YuFei Shen-YuFei reopened this Oct 9, 2026
@Shen-YuFei Shen-YuFei changed the title Fix input to allow empty fixed modifications from SDRF Fix search settings, Sage run matching and rescoring tolerance units; add independent LFQ groups Oct 9, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

nf-core pipelines lint overall result: Passed ✅ ⚠️

Posted for pipeline commit ff88f92

+| ✅ 117 tests passed       |+
#| ❔  18 tests were ignored |#
#| ❔   1 tests had warnings |#
!| ❗   7 tests had warnings |!
Details

❗ Test warnings:

  • pipeline_todos - TODO string in nextflow.config: Optionally, you can add a pipeline-specific nf-core config at https://github.com/nf-core/configs
  • pipeline_todos - TODO string in nextflow.config: Specify any additional parameters here
  • pipeline_todos - TODO string in base.config: Check the defaults for all processes
  • pipeline_todos - TODO string in base.config: Customise requirements for specific processes.
  • pipeline_todos - TODO string in CONTRIBUTING.md: Add any pipeline specific contribution guidelines here, such as coding styles, procedures, checklists etc.
  • pipeline_if_empty_null - ifEmpty(null) found in /home/runner/work/quantms/quantms/subworkflows/local/dda_id/main.nf: _ ch_software_versions = ch_software_versions.mix(PHOSPHO_SCORING.out.versions.ifEmpty(null))
    _
  • pipeline_if_empty_null - ifEmpty(null) found in /home/runner/work/quantms/quantms/subworkflows/local/id/main.nf: _ ch_software_versions = ch_software_versions.mix(PHOSPHO_SCORING.out.versions.ifEmpty(null))
    _

❔ Tests ignored:

❔ Tests fixed:

✅ Tests passed:

Run details

  • nf-core/tools version 4.1.0
  • Run at 2026-10-16 13:56:50

@ypriverol
ypriverol requested a review from jpfeuffer October 9, 2026 05:41
@ypriverol
ypriverol requested a review from daichengxin October 9, 2026 05:41
Comment thread bin/split_lfq_groups.py Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using the quantms-utils container, it is best to move this Python script into the quantms-utils package.

@jpfeuffer

Copy link
Copy Markdown
Collaborator

Can you elaborate on why the auto settings for comet were a problem? Most people do not know what a bin width is for that or how to set it.
.
Independent LFQ groups: Cool feature. How is this reported in QC report, qpx etc?

@Shen-YuFei

Copy link
Copy Markdown
Author

Can you elaborate on why the auto settings for comet were a problem? Most people do not know what a bin width is for that or how to set it. . Independent LFQ groups: Cool feature. How is this reported in QC report, qpx etc?

Thanks @jpfeuffer.

Comet: You are right, this is too strict. The old automatic settings were not a real problem: in all the ppm datasets I re-analysed, I used the same values. I will restore the automatic default and keep the new Comet options as optional overrides.

LFQ groups: each group has its own QPX output (qpx/<group>.sdrf/) and MSstats table, and sdrf/lfq_groups/lfq_groups.tsv shows which run is in which group. The pmultiqc report still shows all groups together.

@jpfeuffer

Copy link
Copy Markdown
Collaborator

Yeah that is better, thank you!

@ypriverol

Copy link
Copy Markdown
Contributor

@Shen-YuFei What is the split LFQS? Can you explain the problem here.?

@Shen-YuFei

Copy link
Copy Markdown
Author

@Shen-YuFei What is the split LFQS? Can you explain the problem here.?

@ypriverol I had this problem with the following two datasets. Each dataset has parts that cannot be processed together in one ProteomicsLFQ run:

  • PXD003472: 24 LTQ Orbitrap Velos runs (fragment tolerance 0.5 Da, 30 and 240 min gradients) and 12 Q Exactive HF runs (20 ppm, 60 and 120 min).
  • PXD003841: three sample preparation methods. One uses fixed carbamidomethylation, and the other two use variable modifications.

ProteomicsLFQ combines the identifications from all runs. When the search settings are different, it stops with an error.

--lfq_group_by takes one or more SDRF columns, for example comment[instrument],comment[gradient duration]. Runs with the same values form one group. The search and rescoring still run once for all files. After that, each group has its own ProteomicsLFQ run, design, MSstats table and QPX output. Before this, the only way was to split the SDRF by hand and run quantms once for each group.

@jpfeuffer

Copy link
Copy Markdown
Collaborator

I think it is an interesting feature that I was also thinking about for longer. I wasnt sure how much complexity this adds though.
I think pmultiqc or something would also need to support this to be fully integrated.
Obviously the easier choice would be to split this SDRF into as many SDRFs as needed BEFORE the workflow.

@Shen-YuFei

Copy link
Copy Markdown
Author

I think it is an interesting feature that I was also thinking about for longer. I wasnt sure how much complexity this adds though. I think pmultiqc or something would also need to support this to be fully integrated. Obviously the easier choice would be to split this SDRF into as many SDRFs as needed BEFORE the workflow.

Thanks @jpfeuffer. The added complexity is small: only ProteomicsLFQ and the QPX conversion run once per group. Search, rescoring and the report still run once. Splitting the SDRF beforehand also works, but then each part needs its own pipeline run and report.

You are right about pmultiqc. It still builds the report. I tried it locally: group support adds about 180 lines to pmultiqc, mostly in the QPX module, plus tests, and quantms only needs to pass the group table it already writes to the report step. The summary counts and the cross-run comparisons (PCA, heatmap, intensity SD) are then computed per group; the per-run sections do not change.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

6 participants