Skip to content

LookupIndexConverter: derive m/z calibration from the SDK, not a 2-point fit#396

Merged
theGreatHerrLebert merged 1 commit into
mainfrom
fix/lookup-mz-calibration
May 19, 2026
Merged

LookupIndexConverter: derive m/z calibration from the SDK, not a 2-point fit#396
theGreatHerrLebert merged 1 commit into
mainfrom
fix/lookup-mz-calibration

Conversation

@theGreatHerrLebert
Copy link
Copy Markdown
Owner

Problem

with_calibration (the fast parallel-extraction path) builds its
LookupIndexConverter with a 2-point boundary m/z model: sqrt(mz) is
anchored only at the m/z acquisition-range extremes (mz_min/mz_max over
tof_max_index). On datasets where the true TOF↔m/z relationship deviates from
that 2-point line, the error is large — measured at a ~4.6 Da median (up to
6.2 Da) on one timsTOF dataset, versus 0.0001 Da for the regression-fit
Calibrated converter on the same file.

A multi-Da m/z error silently corrupts m/z-sensitive extraction. In our case
isotope-envelope intensities came back all-zero (isotope_cosim = 0) while
total_intensity stayed healthy — the extraction was integrating unrelated
peaks several Da away from the real isotopes.

Fix

When a Bruker SDK path is available, LookupIndexConverter now calibrates m/z
with the same regression fit (derive_mz_calibration) that the Calibrated
converter already uses, via a new LookupIndexConverter::with_mz_fit. The
2-point boundary model stays as the fallback when no SDK is available (macOS,
or bruker_lib_path = "NO_SDK"). The ion-mobility calibration (the
precomputed scan→1/K0 lookup) is unchanged.

with_calibration / new_with_calibration gain an optional
bruker_lib_path argument (default "NO_SDK"), so existing callers are
unaffected; passing a real SDK path opts into the accurate m/z.

Compatibility

  • Python API: 3-argument with_calibration(...) calls keep working (boundary
    fallback); the 4th bruker_lib_path argument is optional.
  • No change to the IM lookup path, or to the Calibrated / BrukerLib /
    Simple converters.

Validation

cargo check passes for rustdf + imspy-connector. Empirical end-to-end
confirmation — re-extracting the affected dataset and verifying isotope_cosim
recovers — is in progress; please hold the merge until that result is posted
here.

…int fit

The with_calibration path built its LookupIndexConverter with a 2-point
boundary m/z model (sqrt(mz) anchored only at the m/z acquisition-range
extremes). On some datasets that is off by several Da, silently corrupting
m/z-sensitive extraction such as isotope-envelope intensities.

When a Bruker SDK path is supplied, m/z is now calibrated with the same
regression fit (derive_mz_calibration) the Calibrated converter already
uses; the 2-point boundary model stays as the fallback when no SDK is
available. with_calibration / new_with_calibration gain an optional
bruker_lib_path argument (default "NO_SDK"), so existing callers are
unaffected.
@theGreatHerrLebert
Copy link
Copy Markdown
Owner Author

Empirical confirmation — fix verified ✅

Rebuilt imspy-connector from this branch, deployed it, and re-ran extraction
on the dataset that exposed the bug (a timsTOF rat dataset, PXD050342, LPAc
acquisition batch).

Took 12 precursors whose stored isotope envelope was all-zero
(isotope_cosim ≈ 0) and extracted their MS1 signal both ways on the same
.d file:

with_calibration variant precursors with a non-zero isotope envelope
3-arg — boundary m/z (old behaviour) 2 / 12
4-arg with bruker_lib_path — SDK-derived m/z (this PR) 12 / 12

With the fix every previously-broken precursor recovers a clean, decaying
isotope envelope (M+0…M+3), e.g.:

  • [37605, 41717, 21451, 8039]
  • [162081, 106447, 50976, 17829]
  • [806773, 863248, 552274, 261180]

cargo check is clean for rustdf + imspy-connector, the release wheel
builds and installs, and the new 4-arg signature is live. Good to merge.

@theGreatHerrLebert theGreatHerrLebert merged commit 055f402 into main May 19, 2026
2 checks passed
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.

1 participant