/// <summary> /// Store custom SIC values defined in CustomMZSearchValues /// </summary> /// <param name="scanList"></param> /// <param name="defaultSICTolerance"></param> /// <param name="sicToleranceIsPPM"></param> /// <param name="defaultScanOrAcqTimeTolerance"></param> public void AddCustomSICValues( clsScanList scanList, double defaultSICTolerance, bool sicToleranceIsPPM, float defaultScanOrAcqTimeTolerance) { var scanOrAcqTimeSumCount = 0; float scanOrAcqTimeSumForAveraging = 0; try { if (CustomMZSearchValues.Count == 0) { return; } var scanNumScanConverter = new clsScanNumScanTimeConversion(); RegisterEvents(scanNumScanConverter); foreach (var customMzSearchValue in CustomMZSearchValues) { // Add a new parent ion entry to .ParentIons() for this custom MZ value var currentParentIon = new clsParentIonInfo(customMzSearchValue.MZ); if (customMzSearchValue.ScanOrAcqTimeCenter < float.Epsilon) { // Set the SurveyScanIndex to the center of the analysis currentParentIon.SurveyScanIndex = scanNumScanConverter.FindNearestSurveyScanIndex( scanList, 0.5F, eCustomSICScanTypeConstants.Relative); } else { currentParentIon.SurveyScanIndex = scanNumScanConverter.FindNearestSurveyScanIndex( scanList, customMzSearchValue.ScanOrAcqTimeCenter, ScanToleranceType); } // Find the next MS2 scan that occurs after the survey scan (parent scan) var surveyScanNumberAbsolute = 0; if (currentParentIon.SurveyScanIndex < scanList.SurveyScans.Count) { surveyScanNumberAbsolute = scanList.SurveyScans[currentParentIon.SurveyScanIndex].ScanNumber + 1; } if (scanList.MasterScanOrderCount == 0) { currentParentIon.FragScanIndices.Add(0); } else { var fragScanIndexMatch = clsBinarySearch.BinarySearchFindNearest( scanList.MasterScanNumList, surveyScanNumberAbsolute, clsBinarySearch.eMissingDataModeConstants.ReturnClosestPoint); while (fragScanIndexMatch < scanList.MasterScanOrderCount && scanList.MasterScanOrder[fragScanIndexMatch].ScanType == clsScanList.eScanTypeConstants.SurveyScan) { fragScanIndexMatch += 1; } if (fragScanIndexMatch == scanList.MasterScanOrderCount) { // Did not find the next frag scan; find the previous frag scan fragScanIndexMatch -= 1; while (fragScanIndexMatch > 0 && scanList.MasterScanOrder[fragScanIndexMatch].ScanType == clsScanList.eScanTypeConstants.SurveyScan) { fragScanIndexMatch -= 1; } if (fragScanIndexMatch < 0) { fragScanIndexMatch = 0; } } // This is a custom SIC-based parent ion // Prior to August 2014, we set .FragScanIndices(0) = 0, which made it appear that the fragmentation scan was the first MS2 spectrum in the dataset for all custom SICs // This caused undesirable display results in MASIC browser, so we now set it to the next MS2 scan that occurs after the survey scan (parent scan) if (scanList.MasterScanOrder[fragScanIndexMatch].ScanType == clsScanList.eScanTypeConstants.FragScan) { currentParentIon.FragScanIndices.Add(scanList.MasterScanOrder[fragScanIndexMatch].ScanIndexPointer); } else { currentParentIon.FragScanIndices.Add(0); } } currentParentIon.CustomSICPeak = true; currentParentIon.CustomSICPeakComment = customMzSearchValue.Comment; currentParentIon.CustomSICPeakMZToleranceDa = customMzSearchValue.MZToleranceDa; currentParentIon.CustomSICPeakScanOrAcqTimeTolerance = customMzSearchValue.ScanOrAcqTimeTolerance; if (currentParentIon.CustomSICPeakMZToleranceDa < double.Epsilon) { if (sicToleranceIsPPM) { currentParentIon.CustomSICPeakMZToleranceDa = clsUtilities.PPMToMass(defaultSICTolerance, currentParentIon.MZ); } else { currentParentIon.CustomSICPeakMZToleranceDa = defaultSICTolerance; } } if (currentParentIon.CustomSICPeakScanOrAcqTimeTolerance < float.Epsilon) { currentParentIon.CustomSICPeakScanOrAcqTimeTolerance = defaultScanOrAcqTimeTolerance; } else { scanOrAcqTimeSumForAveraging += currentParentIon.CustomSICPeakScanOrAcqTimeTolerance; scanOrAcqTimeSumCount += 1; } if (currentParentIon.SurveyScanIndex < scanList.SurveyScans.Count) { currentParentIon.OptimalPeakApexScanNumber = scanList.SurveyScans[currentParentIon.SurveyScanIndex].ScanNumber; } else { currentParentIon.OptimalPeakApexScanNumber = 1; } currentParentIon.PeakApexOverrideParentIonIndex = -1; scanList.ParentIons.Add(currentParentIon); } if (scanOrAcqTimeSumCount == CustomMZSearchValues.Count && scanOrAcqTimeSumForAveraging > 0) { // All of the entries had a custom scan or acq time tolerance defined // Update mScanOrAcqTimeTolerance to the average of the values ScanOrAcqTimeTolerance = (float)(Math.Round(scanOrAcqTimeSumForAveraging / scanOrAcqTimeSumCount, 4)); } } catch (Exception ex) { OnErrorEvent("Error in AddCustomSICValues", ex); } }
/// <summary> /// Add the parent ion, or associate the fragmentation scan with an existing parent ion /// /// Checks to see if the parent ion specified by surveyScanIndex and parentIonMZ exists in .ParentIons() /// If mrmDaughterMZ is > 0, also considers that value when determining uniqueness /// /// If the parent ion entry already exists, adds an entry to .FragScanIndices() /// If it does not exist, adds a new entry to .ParentIons() /// </summary> /// <param name="scanList"></param> /// <param name="surveyScanIndex"> /// If this is less than 0 the first MS2 scan(s) in the file occurred before we encountered a survey scan /// In this case, we cannot properly associate the fragmentation scan with a survey scan /// </param> /// <param name="parentIonMZ"></param> /// <param name="mrmDaughterMZ"></param> /// <param name="mrmToleranceHalfWidth"></param> /// <param name="fragScanIndex">This is typically equal to scanList.FragScans.Count - 1</param> /// <param name="spectraCache"></param> /// <param name="sicOptions"></param> private void AddUpdateParentIons( clsScanList scanList, int surveyScanIndex, double parentIonMZ, double mrmDaughterMZ, double mrmToleranceHalfWidth, int fragScanIndex, clsSpectraCache spectraCache, clsSICOptions sicOptions) { const double MINIMUM_TOLERANCE_PPM = 0.01; const double MINIMUM_TOLERANCE_DA = 0.0001; var parentIonIndex = 0; double parentIonTolerance; if (sicOptions.SICToleranceIsPPM) { parentIonTolerance = sicOptions.SICTolerance / sicOptions.CompressToleranceDivisorForPPM; if (parentIonTolerance < MINIMUM_TOLERANCE_PPM) { parentIonTolerance = MINIMUM_TOLERANCE_PPM; } } else { parentIonTolerance = sicOptions.SICTolerance / sicOptions.CompressToleranceDivisorForDa; if (parentIonTolerance < MINIMUM_TOLERANCE_DA) { parentIonTolerance = MINIMUM_TOLERANCE_DA; } } // See if an entry exists yet in .ParentIons for the parent ion for this fragmentation scan var matchFound = false; if (mrmDaughterMZ > 0) { if (sicOptions.SICToleranceIsPPM) { // Force the tolerances to 0.01 m/z units parentIonTolerance = MINIMUM_TOLERANCE_PPM; } else { // Force the tolerances to 0.01 m/z units parentIonTolerance = MINIMUM_TOLERANCE_DA; } } if (parentIonMZ > 0) { var parentIonToleranceDa = GetParentIonToleranceDa(sicOptions, parentIonMZ, parentIonTolerance); for (parentIonIndex = scanList.ParentIons.Count - 1; parentIonIndex >= 0; parentIonIndex += -1) { if (scanList.ParentIons[parentIonIndex].SurveyScanIndex >= surveyScanIndex) { if (Math.Abs(scanList.ParentIons[parentIonIndex].MZ - parentIonMZ) <= parentIonToleranceDa) { if (mrmDaughterMZ < double.Epsilon || Math.Abs(scanList.ParentIons[parentIonIndex].MRMDaughterMZ - mrmDaughterMZ) <= parentIonToleranceDa) { matchFound = true; break; } } } else { break; } } } if (!matchFound) { // Add a new parent ion entry to .ParentIons(), but only if surveyScanIndex is non-negative if (surveyScanIndex < 0) { return; } var newParentIon = new clsParentIonInfo(parentIonMZ) { SurveyScanIndex = surveyScanIndex, CustomSICPeak = false, MRMDaughterMZ = mrmDaughterMZ, MRMToleranceHalfWidth = mrmToleranceHalfWidth }; newParentIon.FragScanIndices.Add(fragScanIndex); newParentIon.OptimalPeakApexScanNumber = scanList.SurveyScans[surveyScanIndex].ScanNumber; // Was: .FragScans(fragScanIndex).ScanNumber newParentIon.PeakApexOverrideParentIonIndex = -1; scanList.FragScans[fragScanIndex].FragScanInfo.ParentIonInfoIndex = scanList.ParentIons.Count; // Look for .MZ in the survey scan, using a tolerance of parentIonTolerance // If found, then update the mass to the matched ion // This is done to determine the parent ion mass more precisely if (sicOptions.RefineReportedParentIonMZ) { if (FindClosestMZ(spectraCache, scanList.SurveyScans, surveyScanIndex, parentIonMZ, parentIonTolerance, out var parentIonMZMatch)) { newParentIon.UpdateMz(parentIonMZMatch); } } scanList.ParentIons.Add(newParentIon); return; } // Add a new entry to .FragScanIndices() for the matching parent ion // However, do not add a new entry if this is an MRM scan if (mrmDaughterMZ < double.Epsilon) { scanList.ParentIons[parentIonIndex].FragScanIndices.Add(fragScanIndex); scanList.FragScans[fragScanIndex].FragScanInfo.ParentIonInfoIndex = parentIonIndex; } }