Skip to content

Implement SRM/PRM chromatogram traces for mzML export#30

Open
Nabejo wants to merge 1 commit into
Sigilweaver:mainfrom
Nabejo:fix/issue-25-srm-prm
Open

Implement SRM/PRM chromatogram traces for mzML export#30
Nabejo wants to merge 1 commit into
Sigilweaver:mainfrom
Nabejo:fix/issue-25-srm-prm

Conversation

@Nabejo

@Nabejo Nabejo commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Issue #25 originally described three pieces of iter_chromatograms work (TIC, BPC, SRM/PRM). PR #26 already implemented and merged the TIC and BPC pieces, and the issue was retitled to track only the remaining SRM/PRM chromatogram work (it was also briefly auto-closed by an accidental "Closes #25" keyword match in #26's original commit message, then manually reopened for this remaining scope).

This PR implements only that remaining SRM/PRM piece, built on top of #26 rather than duplicating it (branched from current main post-#26-merge; confirmed via gh pr view 26 and reading mzml.rs that TIC/BPC were already present and left untouched). Adds srm_chromatograms_for: for each PrmTarget, walks msms_type == 10 frames, looks up PrmFrameMsMsInfo rows, decodes peaks via the same decode_peaks path iter_spectra uses, sums intensity within the target's scan range, and accumulates one point per frame per target. Emits a ChromatogramRecord per target with chromatogram_type = MS:1001473 ("selected reaction monitoring chromatogram") and precursor_mz from PrmTarget::monoisotopic_mz. A new combining wrapper concatenates TIC/BPC with the new SRM records and renumbers index contiguously, since mzML requires unique/contiguous indices across the whole <chromatogramList>; both TdfSource/OwnedTdfSource iter_chromatograms impls now point at this wrapper.

product_mz is deliberately left None: the TDF schema has no discrete product-ion m/z field for prm-PASEF (a full fragment-ion scan per precursor, not a triple-quadrupole transition) - populating it would mean inventing an unbacked value, which the clean-room policy forbids. Genuine zero-intensity frames are kept as real points; targets never appearing in PrmFrameMsMsInfo are omitted rather than emitted empty. Correctness is argued from self-consistency and the PSI-MS mzML schema only, per CONTRIBUTING.md's clean-room note - no vendor software was used.

Addresses the remaining SRM/PRM scope of #25.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo test --workspace (26 unit tests including 5 new ones for the SRM helpers, 1 conformance test, 10 roundtrip tests; cache-gated ones skip cleanly since /corpus/ isn't present here, matching existing CI behavior)

Contributed by @Nabejo.

Wire the remaining piece of iter_chromatograms scoped out of Sigilweaver#26: one
SRM-shaped ChromatogramRecord ("selected reaction monitoring
chromatogram", MS:1001473) per scheduled PrmTarget, built on top of
the TIC/BPC traces Sigilweaver#26 already wired up.

For each PrmTarget, walk every msms_type == 10 (prm-PASEF) frame,
decode its peak payload via the same per-frame decode path
iter_spectra already uses (Reader::decode_peaks), sum the decoded
intensity restricted to that target's PrmFrameMsMsInfo scan-number
range, and append one point per frame. A target with a genuine
zero-intensity frame keeps that point (real data, unlike a frame
missing a Frames column); a target absent from every
PrmFrameMsMsInfo row is omitted rather than emitted with an empty
trace, matching the TIC/BPC no-empty-chromatogram contract.

precursor_mz is the target's scheduled PrmTargets.MonoisotopicMz.
product_mz is left unset: prm-PASEF records a full fragment-ion scan
per precursor rather than a discrete triple-quadrupole product-ion
transition, and the TDF schema has no product-ion m/z field to
report - populating one would mean inventing a value with no schema
backing, which the clean-room policy in CONTRIBUTING.md rules out.

New pure helpers (sum_intensity_in_range, build_srm_record) are unit
tested directly; the frame-walking orchestration
(srm_chromatograms_for) follows the same shape as the existing
spectra_for_frame and isn't unit tested in isolation for the same
reason that one isn't - it's thin glue over already-tested pieces and
a live Reader/bundle. The existing cache-gated prm_pasef_pxd028279
test in tests/roundtrip.rs already exercises the metadata this relies
on (PrmFrameMsMsInfo/PrmTargets shape) and continues to pass.

Distinct from Sigilweaver#13 (prm-PASEF frames skipped in the mzML *spectrum*
projection): that issue is about spectra, this is about the
chromatogram trace.

Addresses the remaining SRM/PRM scope of Sigilweaver#25 (TIC/BPC portion done in
Sigilweaver#26).

Claude-Session: https://claude.ai/code/session_018ijUinjq3SKNZXFNWqTTmV
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement SRM/PRM chromatogram traces for mzML export (TIC/BPC done via #26)

1 participant