Skip to content

Implement iter_chromatograms so TIC/BPC/SRM chromatograms reach mzML output #17

Description

@Nathan-D-R

Companion to Sigilweaver/OpenMassSpecCore#1 (the shared mzML writer already emits <chromatogramList> whenever SpectrumSource::iter_chromatograms yields anything, landed 2026-07-15) and precedent-setter Sigilweaver/OpenWRaw#9 (closed 2026-07-15, commit 574fe09), which did the equivalent wiring for Waters. OpenTFRaw currently has no iter_chromatograms override, so OpenTfRawSource (crates/opentfraw/src/mzml.rs:498-556) falls through to the trait's empty default and no TIC/BPC/SRM chromatograms reach mzML output.

Finding: this is a "wire up existing decoded data" issue, not new decode work. OpenTFRaw already fully decodes the per-scan chromatogram fields, and even has convenience methods sitting unused:

  • ScanIndexEntry (crates/opentfraw/src/scan_index.rs:7-20, parsed at :40-80) already carries start_time (RT, minutes), total_current (TIC), base_intensity (base peak intensity), and base_mz (base peak m/z) for every scan, decoded straight off disk.
  • RawFileReader already exposes retention_times() (reader.rs:1082-1084), tic_chromatogram() -> Vec<(f64, f64)> (reader.rs:1087-1092), and bpc_chromatogram() -> Vec<(f64, f64, f64)> (reader.rs:1095-1099), all built directly from the ScanIndexEntry fields above. None of these three methods are called anywhere outside reader.rs itself (no callers in mzml.rs, examples/, or opentfraw-py) - fully decoded, currently dead-ended.
  • For SRM files (flat_peaks mode, v63/v66), RawFileReader also already builds srm_q1_by_event: HashMap<u16, f64>, srm_q3_windows: HashMap<u16, Vec<(f32, f32)>>, and srm_ce_by_event: HashMap<u16, f64> (reader.rs:301-312, populated at :664-778), keyed by scan_event. These are already used to build scan-filter strings (reader.rs:1019-1076) and give everything needed to group scans into per-transition SRM chromatograms without any new binary parsing - just grouping/summing over already-decoded scans.

Proposed scope:

  1. Implement OpenTfRawSource::iter_chromatograms (crates/opentfraw/src/mzml.rs) returning at minimum a TIC (chromatogram_type = "total ion current chromatogram") and a BPC (chromatogram_type = "basepeak chromatogram") built from tic_chromatogram()/bpc_chromatogram()/retention_times(), mapped into openmassspec_core::ChromatogramRecord (src/types.rs ~line 163). Note start_time is in minutes; ChromatogramRecord.time_sec needs * 60.0.
  2. Optionally (can be split into a follow-up if scope creeps): for flat_peaks SRM files, group scans by scan_event using the existing srm_q1_by_event/srm_q3_windows maps and emit one chromatogram_type = "selected reaction monitoring chromatogram" record per transition, with precursor_mz/product_mz populated from the Q1/Q3 maps.
  3. This alone won't produce output until callers pick it up through OpenMassSpecCore's mzML writer - it's already wired to call iter_chromatograms per OpenMassSpecCore#1, so no downstream change should be needed there.

Clean-room note: per CONTRIBUTING.md, correctness must be argued from open references only (PSI-MS mzML schema, roundtrip/self-consistency checks, independent open-source format checkers) - do not validate the new TIC/BPC/SRM output against vendor software (e.g. Xcalibur, msconvert) or any tool that reads .raw through the vendor SDK.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions