public SpectrumFilter(SrmDocument document, MsDataFileUri msDataFileUri, IFilterInstrumentInfo instrumentInfo, IRetentionTimePredictor retentionTimePredictor = null, bool firstPass = false) { _fullScan = document.Settings.TransitionSettings.FullScan; _instrument = document.Settings.TransitionSettings.Instrument; _acquisitionMethod = _fullScan.AcquisitionMethod; if (instrumentInfo != null) _isWatersFile = instrumentInfo.IsWatersFile; IsFirstPass = firstPass; var comparer = PrecursorTextId.PrecursorTextIdComparerInstance; var dictPrecursorMzToFilter = new SortedDictionary<PrecursorTextId, SpectrumFilterPair>(comparer); if (EnabledMs || EnabledMsMs) { if (EnabledMs) { _isHighAccMsFilter = !Equals(_fullScan.PrecursorMassAnalyzer, FullScanMassAnalyzerType.qit); if (!firstPass) { var key = new PrecursorTextId(0, null, ChromExtractor.summed); // TIC dictPrecursorMzToFilter.Add(key, new SpectrumFilterPair(key, PeptideDocNode.UNKNOWN_COLOR, dictPrecursorMzToFilter.Count, _instrument.MinTime, _instrument.MaxTime, null, null, 0, _isHighAccMsFilter, _isHighAccProductFilter)); key = new PrecursorTextId(0, null, ChromExtractor.base_peak); // BPC dictPrecursorMzToFilter.Add(key, new SpectrumFilterPair(key, PeptideDocNode.UNKNOWN_COLOR, dictPrecursorMzToFilter.Count, _instrument.MinTime, _instrument.MaxTime, null, null, 0, _isHighAccMsFilter, _isHighAccProductFilter)); } } if (EnabledMsMs) { _isHighAccProductFilter = !Equals(_fullScan.ProductMassAnalyzer, FullScanMassAnalyzerType.qit); if (_fullScan.AcquisitionMethod == FullScanAcquisitionMethod.DIA && _fullScan.IsolationScheme.IsAllIons) { if (instrumentInfo != null) { _isWatersMse = _isWatersFile; _isAgilentMse = instrumentInfo.IsAgilentFile; } _mseLevel = 1; } } Func<double, double> calcWindowsQ1 = _fullScan.GetPrecursorFilterWindow; Func<double, double> calcWindowsQ3 = _fullScan.GetProductFilterWindow; _minTime = _instrument.MinTime; _maxTime = _instrument.MaxTime; bool canSchedule = CanSchedule(document, retentionTimePredictor); // TODO: Figure out a way to turn off time sharing on first SIM scan so that // times can be shared for MS1 without SIM scans _isSharedTime = !canSchedule; // If we're using bare measured drift times from spectral libraries, go get those now var libraryIonMobilityInfo = document.Settings.PeptideSettings.Prediction.UseLibraryDriftTimes ? document.Settings.GetIonMobilities(msDataFileUri) : null; foreach (var nodePep in document.Molecules) { if (firstPass && !retentionTimePredictor.IsFirstPassPeptide(nodePep)) continue; foreach (TransitionGroupDocNode nodeGroup in nodePep.Children) { if (nodeGroup.Children.Count == 0) continue; double? minTime = _minTime, maxTime = _maxTime; double? startDriftTimeMsec = null, endDriftTimeMsec = null; double windowDT; double highEnergyDriftTimeOffsetMsec = 0; DriftTimeInfo centerDriftTime = document.Settings.PeptideSettings.Prediction.GetDriftTime( nodePep, nodeGroup, libraryIonMobilityInfo, out windowDT); if (centerDriftTime.DriftTimeMsec(false).HasValue) { startDriftTimeMsec = centerDriftTime.DriftTimeMsec(false) - windowDT / 2; // Get the low energy drift time endDriftTimeMsec = startDriftTimeMsec + windowDT; highEnergyDriftTimeOffsetMsec = centerDriftTime.HighEnergyDriftTimeOffsetMsec; } if (canSchedule) { if (RetentionTimeFilterType.scheduling_windows == _fullScan.RetentionTimeFilterType) { double? centerTime = null; double windowRT = 0; if (retentionTimePredictor != null) { centerTime = retentionTimePredictor.GetPredictedRetentionTime(nodePep); } else { var prediction = document.Settings.PeptideSettings.Prediction; if (prediction.RetentionTime == null || !prediction.RetentionTime.IsAutoCalculated) { centerTime = document.Settings.PeptideSettings.Prediction.PredictRetentionTimeForChromImport( document, nodePep, nodeGroup, out windowRT); } } // Force the center time to be at least zero if (centerTime.HasValue && centerTime.Value < 0) centerTime = 0; if (_fullScan.RetentionTimeFilterLength != 0) { windowRT = _fullScan.RetentionTimeFilterLength * 2; } if (centerTime != null) { double startTime = centerTime.Value - windowRT / 2; double endTime = startTime + windowRT; minTime = Math.Max(minTime ?? 0, startTime); maxTime = Math.Min(maxTime ?? double.MaxValue, endTime); } } else if (RetentionTimeFilterType.ms2_ids == _fullScan.RetentionTimeFilterType) { var times = document.Settings.GetBestRetentionTimes(nodePep, msDataFileUri); if (times.Length > 0) { minTime = Math.Max(minTime ?? 0, times.Min() - _fullScan.RetentionTimeFilterLength); maxTime = Math.Min(maxTime ?? double.MaxValue, times.Max() + _fullScan.RetentionTimeFilterLength); } } } SpectrumFilterPair filter; string textId = nodePep.RawTextId; // Modified Sequence for peptides, or some other string for custom ions double mz = nodeGroup.PrecursorMz; var key = new PrecursorTextId(mz, textId, ChromExtractor.summed); if (!dictPrecursorMzToFilter.TryGetValue(key, out filter)) { filter = new SpectrumFilterPair(key, nodePep.Color, dictPrecursorMzToFilter.Count, minTime, maxTime, startDriftTimeMsec, endDriftTimeMsec, highEnergyDriftTimeOffsetMsec, _isHighAccMsFilter, _isHighAccProductFilter); dictPrecursorMzToFilter.Add(key, filter); } if (!EnabledMs) { filter.AddQ3FilterValues(from TransitionDocNode nodeTran in nodeGroup.Children select nodeTran.Mz, calcWindowsQ3); } else if (!EnabledMsMs) { filter.AddQ1FilterValues(GetMS1MzValues(nodeGroup), calcWindowsQ1); } else { filter.AddQ1FilterValues(GetMS1MzValues(nodeGroup), calcWindowsQ1); filter.AddQ3FilterValues(from TransitionDocNode nodeTran in nodeGroup.Children where !nodeTran.IsMs1 select nodeTran.Mz, calcWindowsQ3); } } } _filterMzValues = dictPrecursorMzToFilter.Values.ToArray(); var listChromKeyFilterIds = new List<ChromKey>(); foreach (var spectrumFilterPair in _filterMzValues) { spectrumFilterPair.AddChromKeys(listChromKeyFilterIds); } _productChromKeys = listChromKeyFilterIds.ToArray(); // Sort a copy of the filter pairs by maximum retention time so that we can detect when // filters are no longer active. _filterRTValues = new SpectrumFilterPair[_filterMzValues.Length]; Array.Copy(_filterMzValues, _filterRTValues, _filterMzValues.Length); Array.Sort(_filterRTValues, CompareByRT); } InitRTLimits(); }
public void ProcessExtractedSpectrum(float time, SpectraChromDataProvider.Collectors chromatograms, int scanId, ExtractedSpectrum spectrum, Action<int, ChromCollector> addCollector) { double precursorMz = spectrum.PrecursorMz; double? ionMobilityValue = spectrum.IonMobilityValue; double ionMobilityExtractionWidth = spectrum.IonMobilityExtractionWidth; string textId = spectrum.TextId; ChromExtractor extractor = spectrum.Extractor; int ionScanCount = spectrum.ProductFilters.Length; ChromDataCollector collector; var key = new PrecursorTextId(precursorMz, textId, extractor); int index = spectrum.FilterIndex; while (PrecursorCollectorMap.Count <= index) PrecursorCollectorMap.Add(null); if (PrecursorCollectorMap[index] != null) collector = PrecursorCollectorMap[index].Item2; else { collector = new ChromDataCollector(textId, precursorMz, ionMobilityValue, ionMobilityExtractionWidth, index, IsGroupedTime); PrecursorCollectorMap[index] = new Tuple<PrecursorTextId, ChromDataCollector>(key, collector); } int ionCount = collector.ProductIntensityMap.Count; if (ionCount == 0) ionCount = ionScanCount; // Add new time to the shared time list if not SRM, which doesn't share times, or // the times are shared with the entire set, as in MS1 int lenTimes = collector.TimeCount; if (IsGroupedTime) { // Shared scan ids and times do not belong to a group. collector.AddScanId(scanId); collector.AddGroupedTime(time); lenTimes = collector.GroupedTimesCollector.Count; } // Add intensity values to ion scans for (int j = 0; j < ionScanCount; j++) { var productFilter = spectrum.ProductFilters[j]; var chromIndex = chromatograms.ProductFilterIdToId(productFilter.FilterId); ChromCollector chromCollector; if (!collector.ProductIntensityMap.TryGetValue(productFilter, out chromCollector)) { chromCollector = new ChromCollector(chromIndex, IsSingleTime, spectrum.MassErrors != null); // If more than a single ion scan, add any zeros necessary // to make this new chromatogram have an entry for each time. if (ionScanCount > 1 && lenTimes > 1) chromCollector.FillIntensities(chromIndex, lenTimes - 1, _blockWriter); collector.ProductIntensityMap.Add(productFilter, chromCollector); if (addCollector != null) addCollector(productFilter.FilterId, chromCollector); } if (IsSingleTime) chromCollector.AddTime(chromIndex, time, _blockWriter); if (spectrum.MassErrors != null) chromCollector.AddMassError(chromIndex, spectrum.MassErrors[j], _blockWriter); chromCollector.AddIntensity(chromIndex, spectrum.Intensities[j], _blockWriter); } // Add data for chromatogram graph. if (_allChromData != null && spectrum.PrecursorMz != 0) // Exclude TIC and BPC _allChromData.Add(spectrum.TextId, spectrum.PeptideColor, spectrum.FilterIndex, time, spectrum.Intensities); // If this was a multiple ion scan and not all ions had measurements, // make sure missing ions have zero intensities in the chromatogram. if (ionScanCount > 1 && (ionCount != ionScanCount || ionCount != collector.ProductIntensityMap.Count)) { // Times should have gotten one longer foreach (var item in collector.ProductIntensityMap) { var productFilter = item.Key; var chromCollector = item.Value; var chromIndex = chromatograms.ProductFilterIdToId(productFilter.FilterId); if (chromCollector.Count < lenTimes) chromCollector.AddIntensity(chromIndex, 0, _blockWriter); } } }
public IsotopeDistInfo(MassDistribution massDistribution, double monoisotopicMass, bool isMassH, // Is monoisotopicMass M+H, or just M as in small molecule use? int charge, Func<double, double> calcFilterWindow, double massResolution, double minimumAbundance) { _monoisotopicMass = monoisotopicMass; _charge = charge; _isMassH = isMassH; // Get peak center of mass values for the given resolution var q1FilterValues = MassDistribution.NewInstance(massDistribution, massResolution, 0).Keys.ToList(); // Find the monoisotopic m/z and make sure it is exactly the expected number double monoMz = isMassH ? SequenceMassCalc.GetMZ(_monoisotopicMass, _charge) : BioMassCalc.CalculateIonMz(_monoisotopicMass, _charge); double monoMzDist = monoMz; int monoMassIndex = 0; for (int i = 0; i < q1FilterValues.Count; i++) { double peakCenterMz = q1FilterValues[i]; double filterWindow = calcFilterWindow(peakCenterMz); double startMz = peakCenterMz - filterWindow/2; double endMz = startMz + filterWindow; if (startMz < monoMz && monoMz < endMz) { monoMzDist = q1FilterValues[i]; q1FilterValues[i] = monoMz; monoMassIndex = i; break; } } // Insert a M-1 peak, even if it is not expected in the isotope mass distribution if (monoMassIndex == 0 && q1FilterValues.Count > 1) { // Use the delta from the original distribution monoMz to the next peak q1FilterValues.Insert(0, monoMz + monoMzDist - q1FilterValues[1]); monoMassIndex++; } if (!q1FilterValues.Any()) // As is small molecule docs with mz values only, no formulas return; // Use the filtering algorithm that will be used on real data to determine the // expected proportions of the mass distribution that will end up filtered into // peaks // CONSIDER: Mass accuracy information is not calculated here var key = new PrecursorTextId(q1FilterValues[monoMassIndex], null, ChromExtractor.summed); var filter = new SpectrumFilterPair(key, PeptideDocNode.UNKNOWN_COLOR, 0, null, null, null, null, 0, false, false); filter.AddQ1FilterValues(q1FilterValues, calcFilterWindow); var expectedSpectrum = filter.FilterQ1SpectrumList(new[] { new MsDataSpectrum { Mzs = massDistribution.Keys.ToArray(), Intensities = massDistribution.Values.ToArray() } }); int startIndex = expectedSpectrum.Intensities.IndexOf(inten => inten >= minimumAbundance); if (startIndex == -1) { throw new InvalidOperationException( string.Format(Resources.IsotopeDistInfo_IsotopeDistInfo_Minimum_abundance__0__too_high, minimumAbundance)); } // Always include the M-1 peak, even if it is expected to have zero intensity if (startIndex > monoMassIndex - 1) startIndex = monoMassIndex - 1; if (startIndex < 0) startIndex = 0; int endIndex = expectedSpectrum.Intensities.LastIndexOf(inten => inten >= minimumAbundance) + 1; int countPeaks = endIndex - startIndex; var listProportionIndices = new List<KeyValuePair<float, int>>(countPeaks); for (int i = 0; i < countPeaks; i++) { listProportionIndices.Add(new KeyValuePair<float, int>( expectedSpectrum.Intensities[i + startIndex], i)); } // Sort proportions descending. listProportionIndices.Sort((p1, p2) => Comparer.Default.Compare(p2.Key, p1.Key)); // Set proportions and ranks back in the original locations var expectedProportionRanks = new KeyValuePair<float, int>[countPeaks]; for (int i = 0; i < countPeaks; i++) { expectedProportionRanks[listProportionIndices[i].Value] = new KeyValuePair<float, int>(listProportionIndices[i].Key, i + 1); } // TODO: Can this be discarded? // MassDistribution = massDistribution; MonoMassIndex = monoMassIndex - startIndex; // Find the base peak and fill in the masses and proportions var expectedPeaks = new List<MzRankProportion>(); for (int i = 0; i < countPeaks; i++) { float expectedProportion = expectedProportionRanks[i].Key; int rank = expectedProportionRanks[i].Value; expectedPeaks.Add(new MzRankProportion(q1FilterValues[i + startIndex], rank, expectedProportion)); if (expectedProportion > expectedProportionRanks[BaseMassIndex].Key) BaseMassIndex = i; } ExpectedPeaks = expectedPeaks; }