/// <summary> /// Looks through the result and finds ion mobility values. /// Note that this method only returns new values that were found in results. /// The returned dictionary should be merged with the existing values in /// order to preserve those existing values. /// </summary> public Dictionary <LibKey, IonMobilityAndCCS> FindIonMobilityPeaks() { // Overwrite any existing measurements with newly derived ones var measured = new Dictionary <LibKey, IonMobilityAndCCS>(); if (_document.Settings.MeasuredResults == null) { return(measured); } var filepaths = _document.Settings.MeasuredResults.MSDataFilePaths.ToArray(); _totalSteps = filepaths.Length * _document.MoleculeTransitionGroupCount; if (_totalSteps == 0) { return(measured); } using (_msDataFileScanHelper = new MsDataFileScanHelper(SetScans, HandleLoadScanException)) { // // Avoid opening and re-opening raw files - make these the outer loop // _ms1IonMobilities = new Dictionary <LibKey, List <IonMobilityIntensityPair> >(); _ms2IonMobilities = new Dictionary <LibKey, List <IonMobilityIntensityPair> >(); var twopercent = (int)Math.Ceiling(_totalSteps * 0.02); _totalSteps += twopercent; _currentStep = twopercent; if (_progressMonitor != null) { _progressStatus = new ProgressStatus(filepaths.First().GetFileName()); _progressStatus = _progressStatus.UpdatePercentCompleteProgress(_progressMonitor, _currentStep, _totalSteps); // Make that inital lag seem less dismal to the user } foreach (var fp in filepaths) { if (!ProcessFile(fp)) { return(null); // User cancelled } } // Find ion mobilities based on MS1 data foreach (var dt in _ms1IonMobilities) { // Choose the ion mobility which gave the largest signal // CONSIDER: average IM and CCS values that fall "near" the IM of largest signal? var ms1IonMobility = dt.Value.OrderByDescending(p => p.Intensity).First().IonMobility; // Check for MS2 data to use for high energy offset List <IonMobilityIntensityPair> listDt; var ms2IonMobility = _ms2IonMobilities.TryGetValue(dt.Key, out listDt) ? listDt.OrderByDescending(p => p.Intensity).First().IonMobility : ms1IonMobility; var value = IonMobilityAndCCS.GetIonMobilityAndCCS(ms1IonMobility.IonMobility, ms1IonMobility.CollisionalCrossSectionSqA, ms2IonMobility.IonMobility.Mobility.Value - ms1IonMobility.IonMobility.Mobility.Value); if (!measured.ContainsKey(dt.Key)) { measured.Add(dt.Key, value); } else { measured[dt.Key] = value; } } // Check for data for which we have only MS2 to go on foreach (var im in _ms2IonMobilities) { if (!_ms1IonMobilities.ContainsKey(im.Key)) { // Only MS2 ion mobility values found, use that as a reasonable inference of MS1 ion mobility var driftTimeIntensityPair = im.Value.OrderByDescending(p => p.Intensity).First(); var value = driftTimeIntensityPair.IonMobility; // Note collisional cross section if (_msDataFileScanHelper.ProvidesCollisionalCrossSectionConverter) { var mz = im.Key.PrecursorMz ?? GetMzFromDocument(im.Key); var ccs = _msDataFileScanHelper.CCSFromIonMobility(value.IonMobility, mz, im.Key.Charge); if (ccs.HasValue) { value = IonMobilityAndCCS.GetIonMobilityAndCCS(value.IonMobility, ccs, value.HighEnergyIonMobilityValueOffset); } } if (!measured.ContainsKey(im.Key)) { measured.Add(im.Key, value); } else { measured[im.Key] = value; } } } } return(measured); }
private void EvaluateBestIonMobilityValue(int msLevel, LibKey libKey, float tolerance, List <TransitionFullScanInfo> transitions) { IonMobilityValue ionMobilityValue = IonMobilityValue.EMPTY; double maxIntensity = 0; // Avoid picking MS2 ion mobility values wildly different from MS1 valuess IonMobilityValue ms1IonMobilityBest; if ((msLevel == 2) && _ms1IonMobilities.ContainsKey(libKey)) { ms1IonMobilityBest = _ms1IonMobilities[libKey].OrderByDescending(p => p.Intensity) .FirstOrDefault() .IonMobility.IonMobility; } else { ms1IonMobilityBest = IonMobilityValue.EMPTY; } double maxHighEnergyDriftOffsetMsec = UseHighEnergyOffset ? 2 : 0; // CONSIDER(bspratt): user definable? or dynamically set by looking at scan to scan drift delta? Or resolving power? foreach (var scan in _msDataFileScanHelper.MsDataSpectra.Where(scan => scan != null)) { if (!scan.IonMobility.HasValue || !scan.Mzs.Any()) { continue; } if (ms1IonMobilityBest.HasValue && (scan.IonMobility.Mobility < ms1IonMobilityBest.Mobility - maxHighEnergyDriftOffsetMsec || scan.IonMobility.Mobility > ms1IonMobilityBest.Mobility + maxHighEnergyDriftOffsetMsec)) { continue; } // Get the total intensity for all transitions of current msLevel double totalIntensity = 0; foreach (var t in transitions) { Assume.IsTrue(t.ProductMz.IsNegative == scan.NegativeCharge); // It would be strange if associated scan did not have same polarity var mzPeak = t.ProductMz; var halfwin = (t.ExtractionWidth ?? tolerance) / 2; var mzLow = mzPeak - halfwin; var mzHigh = mzPeak + halfwin; var first = Array.BinarySearch(scan.Mzs, mzLow); if (first < 0) { first = ~first; } for (var i = first; i < scan.Mzs.Length; i++) { if (scan.Mzs[i] > mzHigh) { break; } totalIntensity += scan.Intensities[i]; } } if (maxIntensity < totalIntensity) { ionMobilityValue = scan.IonMobility; maxIntensity = totalIntensity; } } if (ionMobilityValue.HasValue) { var dict = (msLevel == 1) ? _ms1IonMobilities : _ms2IonMobilities; var ccs = msLevel == 1 && _msDataFileScanHelper.ProvidesCollisionalCrossSectionConverter ? _msDataFileScanHelper.CCSFromIonMobility(ionMobilityValue, transitions.First().PrecursorMz, libKey.Charge) : null; var result = new IonMobilityIntensityPair { IonMobility = IonMobilityAndCCS.GetIonMobilityAndCCS(ionMobilityValue, ccs, 0), Intensity = maxIntensity }; List <IonMobilityIntensityPair> listPairs; if (!dict.TryGetValue(libKey, out listPairs)) { listPairs = new List <IonMobilityIntensityPair>(); dict.Add(libKey, listPairs); } listPairs.Add(result); } }