private bool FindClosestMZ( clsSpectraCache spectraCache, IList <clsScanInfo> scanList, int spectrumIndex, double searchMZ, double toleranceMZ, out double bestMatchMZ) { bool success; bestMatchMZ = 0; try { if (scanList[spectrumIndex].IonCount == 0 && scanList[spectrumIndex].IonCountRaw == 0) { // No data in this spectrum success = false; } else if (!spectraCache.GetSpectrum(scanList[spectrumIndex].ScanNumber, out var spectrum, canSkipPool: true)) { SetLocalErrorCode(clsMASIC.eMasicErrorCodes.ErrorUncachingSpectrum); success = false; } else { success = FindClosestMZ(spectrum.IonsMZ, spectrum.IonCount, searchMZ, toleranceMZ, out bestMatchMZ); } }
/// <summary> /// Searches currentScan.IonsMZ for the maximum intensity value between mzStart and mzEnd /// If a match is found, updates bestMatchMZ to the m/z of the match, /// updates matchIntensity to its intensity, and returns True /// </summary> /// <param name="spectraCache"></param> /// <param name="currentScan"></param> /// <param name="mzStart"></param> /// <param name="mzEnd"></param> /// <param name="bestMatchMZ">Output: m/z of the most intense ion in the given range</param> /// <param name="matchIntensity">Output: intensity of the most intense ion in the given range</param> /// <returns>True if a match is found, false if no data exists within the m/z range (or if the spectrum could not be obtained)</returns> /// <remarks> /// Note that this function performs a linear search of .IonsMZ /// It is therefore good for spectra with less than 10 data points and bad for spectra with more than 10 data points /// As an alternative to this function, use AggregateIonsInRange /// </remarks> public bool FindMaxValueInMZRange( clsSpectraCache spectraCache, clsScanInfo currentScan, double mzStart, double mzEnd, out double bestMatchMZ, out double matchIntensity) { bestMatchMZ = 0; matchIntensity = 0; try { if (!spectraCache.GetSpectrum(currentScan.ScanNumber, out var spectrum, true)) { OnErrorEvent("Error uncaching scan " + currentScan.ScanNumber); return(false); } var success = FindMaxValueInMZRange( spectrum.IonsMZ, spectrum.IonsIntensity, spectrum.IonCount, mzStart, mzEnd, out bestMatchMZ, out matchIntensity); return(success); } catch (Exception ex) { OnErrorEvent("Error in FindMaxValueInMZRange (A): " + ex.Message, ex); return(false); } }
/// <summary> /// Add the parent ion, or associate the fragmentation scan with an existing parent ion /// </summary> /// <param name="scanList"></param> /// <param name="surveyScanIndex"></param> /// <param name="parentIonMZ"></param> /// <param name="fragScanIndex"></param> /// <param name="spectraCache"></param> /// <param name="sicOptions"></param> public void AddUpdateParentIons( clsScanList scanList, int surveyScanIndex, double parentIonMZ, int fragScanIndex, clsSpectraCache spectraCache, clsSICOptions sicOptions) { AddUpdateParentIons(scanList, surveyScanIndex, parentIonMZ, 0, 0, fragScanIndex, spectraCache, sicOptions); }
/// <summary> /// Export MRM data to disk /// </summary> /// <param name="scanList"></param> /// <param name="spectraCache"></param> /// <param name="inputFileName"></param> /// <param name="outputDirectoryPath"></param> /// <returns></returns> public bool ExportMRMDataToDisk( clsScanList scanList, clsSpectraCache spectraCache, string inputFileName, string outputDirectoryPath) { if (!DetermineMRMSettings(scanList, out var mrmSettings, out var srmList)) { return(false); } var success = ExportMRMDataToDisk(scanList, spectraCache, mrmSettings, srmList, inputFileName, outputDirectoryPath); return(success); }
/// <summary> /// Add the parent ion, or associate the fragmentation scan with an existing parent ion /// </summary> /// <param name="scanList"></param> /// <param name="surveyScanIndex"></param> /// <param name="parentIonMZ"></param> /// <param name="mrmInfo"></param> /// <param name="spectraCache"></param> /// <param name="sicOptions"></param> public void AddUpdateParentIons( clsScanList scanList, int surveyScanIndex, double parentIonMZ, clsMRMScanInfo mrmInfo, clsSpectraCache spectraCache, clsSICOptions sicOptions) { for (var mrmIndex = 0; mrmIndex < mrmInfo.MRMMassCount; mrmIndex++) { var mrmDaughterMZ = mrmInfo.MRMMassList[mrmIndex].CentralMass; var mrmToleranceHalfWidth = Math.Round((mrmInfo.MRMMassList[mrmIndex].EndMass - mrmInfo.MRMMassList[mrmIndex].StartMass) / 2, 6); if (mrmToleranceHalfWidth < 0.001) { mrmToleranceHalfWidth = 0.001; } AddUpdateParentIons(scanList, surveyScanIndex, parentIonMZ, mrmDaughterMZ, mrmToleranceHalfWidth, scanList.FragScans.Count - 1, spectraCache, sicOptions); } }
protected void UpdateCacheStats(clsSpectraCache spectraCache) { OnUpdateCacheStats(spectraCache.CacheEventCount, spectraCache.UnCacheEventCount, spectraCache.SpectraPoolHitEventCount); }
/// <summary> /// Looks for the reporter ion peaks using FindReporterIonsWork /// </summary> /// <param name="scanList"></param> /// <param name="spectraCache"></param> /// <param name="inputFilePathFull">Full path to the input file</param> /// <param name="outputDirectoryPath"></param> /// <returns></returns> /// <remarks></remarks> public bool FindReporterIons( clsScanList scanList, clsSpectraCache spectraCache, string inputFilePathFull, string outputDirectoryPath) { const char TAB_DELIMITER = '\t'; var outputFilePath = "??"; try { // Use Xraw to read the .Raw files var readerOptions = new ThermoReaderOptions() { LoadMSMethodInfo = false, LoadMSTuneInfo = false }; var rawFileReader = new XRawFileIO(readerOptions); RegisterEvents(rawFileReader); var includeFtmsColumns = false; if (inputFilePathFull.ToUpper().EndsWith(DataInput.clsDataImport.THERMO_RAW_FILE_EXTENSION.ToUpper())) { // Processing a thermo .Raw file // Check whether any of the frag scans has IsFTMS true for (var masterOrderIndex = 0; masterOrderIndex < scanList.MasterScanOrderCount; masterOrderIndex++) { var scanPointer = scanList.MasterScanOrder[masterOrderIndex].ScanIndexPointer; if (scanList.MasterScanOrder[masterOrderIndex].ScanType == clsScanList.eScanTypeConstants.SurveyScan) { // Skip survey scans continue; } if (scanList.FragScans[scanPointer].IsFTMS) { includeFtmsColumns = true; break; } } if (includeFtmsColumns) { rawFileReader.OpenRawFile(inputFilePathFull); } } if (mOptions.ReporterIons.ReporterIonList.Count == 0) { // No reporter ions defined; default to ITraq mOptions.ReporterIons.SetReporterIonMassMode(clsReporterIons.eReporterIonMassModeConstants.ITraqFourMZ); } // Populate array reporterIons, which we will sort by m/z var reporterIons = new clsReporterIonInfo[mOptions.ReporterIons.ReporterIonList.Count]; var reporterIonIndex = 0; foreach (var reporterIon in mOptions.ReporterIons.ReporterIonList) { reporterIons[reporterIonIndex] = reporterIon; reporterIonIndex += 1; } Array.Sort(reporterIons, new clsReportIonInfoComparer()); outputFilePath = clsDataOutput.ConstructOutputFilePath( Path.GetFileName(inputFilePathFull), outputDirectoryPath, clsDataOutput.eOutputFileTypeConstants.ReporterIonsFile); using (var writer = new StreamWriter(outputFilePath)) { // Write the file headers var reporterIonMZsUnique = new SortedSet <string>(); var headerColumns = new List <string>(7 + reporterIons.Length + 1) { "Dataset", "ScanNumber", "Collision Mode", "ParentIonMZ", "BasePeakIntensity", "BasePeakMZ", "ReporterIonIntensityMax" }; var obsMZHeaders = new List <string>(reporterIons.Length); var uncorrectedIntensityHeaders = new List <string>(reporterIons.Length); var ftmsSignalToNoise = new List <string>(reporterIons.Length); var ftmsResolution = new List <string>(reporterIons.Length); //var ftmsLabelDataMz = new List<string>(reporterIons.Length); var saveUncorrectedIntensities = mOptions.ReporterIons.ReporterIonApplyAbundanceCorrection && mOptions.ReporterIons.ReporterIonSaveUncorrectedIntensities; var dataAggregation = new clsDataAggregation(); RegisterEvents(dataAggregation); foreach (var reporterIon in reporterIons) { if (!reporterIon.ContaminantIon || saveUncorrectedIntensities) { // Construct the reporter ion intensity header // We skip contaminant ions, unless saveUncorrectedIntensities is True, then we include them string mzValue; if (mOptions.ReporterIons.ReporterIonMassMode == clsReporterIons.eReporterIonMassModeConstants.TMTTenMZ || mOptions.ReporterIons.ReporterIonMassMode == clsReporterIons.eReporterIonMassModeConstants.TMTElevenMZ || mOptions.ReporterIons.ReporterIonMassMode == clsReporterIons.eReporterIonMassModeConstants.TMTSixteenMZ) { mzValue = reporterIon.MZ.ToString("#0.000"); } else { mzValue = ((int)Math.Round(reporterIon.MZ, 0)).ToString(); } if (reporterIonMZsUnique.Contains(mzValue)) { // Uniquify the m/z value mzValue += "_" + reporterIonIndex; } try { reporterIonMZsUnique.Add(mzValue); } catch (Exception ex) { // Error updating the SortedSet; // this shouldn't happen based on the .ContainsKey test above } // Append the reporter ion intensity title to the headers headerColumns.Add("Ion_" + mzValue); // This string will only be included in the header line if mOptions.ReporterIons.ReporterIonSaveObservedMasses is true obsMZHeaders.Add("Ion_" + mzValue + "_ObsMZ"); // This string will be included in the header line if saveUncorrectedIntensities is true uncorrectedIntensityHeaders.Add("Ion_" + mzValue + "_OriginalIntensity"); // This string will be included in the header line if includeFtmsColumns is true ftmsSignalToNoise.Add("Ion_" + mzValue + "_SignalToNoise"); ftmsResolution.Add("Ion_" + mzValue + "_Resolution"); // Uncomment to include the label data m/z value in the _ReporterIons.txt file // This string will only be included in the header line if mOptions.ReporterIons.ReporterIonSaveObservedMasses is true //ftmsLabelDataMz.Add("Ion_" + mzValue + "_LabelDataMZ"); } } headerColumns.Add("Weighted Avg Pct Intensity Correction"); if (mOptions.ReporterIons.ReporterIonSaveObservedMasses) { headerColumns.AddRange(obsMZHeaders); } if (saveUncorrectedIntensities) { headerColumns.AddRange(uncorrectedIntensityHeaders); } if (includeFtmsColumns) { headerColumns.AddRange(ftmsSignalToNoise); headerColumns.AddRange(ftmsResolution); // Uncomment to include the label data m/z value in the _ReporterIons.txt file // If mOptions.ReporterIons.ReporterIonSaveObservedMasses Then // headerColumns.AddRange(ftmsLabelDataMz) // End If } // Write the headers to the output file, separated by tabs writer.WriteLine(string.Join(TAB_DELIMITER.ToString(), headerColumns)); UpdateProgress(0, "Searching for reporter ions"); for (var masterOrderIndex = 0; masterOrderIndex < scanList.MasterScanOrderCount; masterOrderIndex++) { var scanPointer = scanList.MasterScanOrder[masterOrderIndex].ScanIndexPointer; if (scanList.MasterScanOrder[masterOrderIndex].ScanType == clsScanList.eScanTypeConstants.SurveyScan) { // Skip Survey Scans continue; } FindReporterIonsWork( rawFileReader, dataAggregation, includeFtmsColumns, mOptions.SICOptions, scanList, spectraCache, scanList.FragScans[scanPointer], writer, reporterIons, TAB_DELIMITER, saveUncorrectedIntensities, mOptions.ReporterIons.ReporterIonSaveObservedMasses); if (scanList.MasterScanOrderCount > 1) { UpdateProgress((short)(masterOrderIndex / (double)(scanList.MasterScanOrderCount - 1) * 100)); } else { UpdateProgress(0); } UpdateCacheStats(spectraCache); if (mOptions.AbortProcessing) { break; } } } if (includeFtmsColumns) { // Close the handle to the data file rawFileReader.CloseRawFile(); } return(true); } catch (Exception ex) { ReportError("Error writing the reporter ions to: " + outputFilePath, ex, clsMASIC.eMasicErrorCodes.OutputFileWriteError); return(false); } }
/// <summary> /// Looks for the reporter ion m/z values, +/- a tolerance /// Calls AggregateIonsInRange with returnMax = True, meaning we're reporting the maximum ion abundance for each reporter ion m/z /// </summary> /// <param name="rawFileReader"></param> /// <param name="dataAggregation"></param> /// <param name="includeFtmsColumns"></param> /// <param name="sicOptions"></param> /// <param name="scanList"></param> /// <param name="spectraCache"></param> /// <param name="currentScan"></param> /// <param name="writer"></param> /// <param name="reporterIons"></param> /// <param name="delimiter"></param> /// <param name="saveUncorrectedIntensities"></param> /// <param name="saveObservedMasses"></param> /// <remarks></remarks> private void FindReporterIonsWork( XRawFileIO rawFileReader, clsDataAggregation dataAggregation, bool includeFtmsColumns, clsSICOptions sicOptions, clsScanList scanList, clsSpectraCache spectraCache, clsScanInfo currentScan, TextWriter writer, IList <clsReporterIonInfo> reporterIons, char delimiter, bool saveUncorrectedIntensities, bool saveObservedMasses) { const bool USE_MAX_ABUNDANCE_IN_WINDOW = true; // The following will be a value between 0 and 100 // Using Absolute Value of percent change to avoid averaging both negative and positive values double parentIonMZ; if (currentScan.FragScanInfo.ParentIonInfoIndex >= 0 && currentScan.FragScanInfo.ParentIonInfoIndex < scanList.ParentIons.Count) { parentIonMZ = scanList.ParentIons[currentScan.FragScanInfo.ParentIonInfoIndex].MZ; } else { parentIonMZ = 0; } if (!spectraCache.GetSpectrum(currentScan.ScanNumber, out var spectrum, true)) { SetLocalErrorCode(clsMASIC.eMasicErrorCodes.ErrorUncachingSpectrum); return; } // Initialize the arrays used to track the observed reporter ion values var reporterIntensities = new double[reporterIons.Count]; var reporterIntensitiesCorrected = new double[reporterIons.Count]; var closestMZ = new double[reporterIons.Count]; // Initialize the output variables var dataColumns = new List <string>() { sicOptions.DatasetID.ToString(), currentScan.ScanNumber.ToString(), currentScan.FragScanInfo.CollisionMode, StringUtilities.DblToString(parentIonMZ, 2), StringUtilities.DblToString(currentScan.BasePeakIonIntensity, 2), StringUtilities.DblToString(currentScan.BasePeakIonMZ, 4) }; var reporterIntensityList = new List <string>(reporterIons.Count); var obsMZList = new List <string>(reporterIons.Count); var uncorrectedIntensityList = new List <string>(reporterIons.Count); var ftmsSignalToNoise = new List <string>(reporterIons.Count); var ftmsResolution = new List <string>(reporterIons.Count); //var ftmsLabelDataMz = new List<string>(reporterIons.Count); double reporterIntensityMax = 0; // Find the reporter ion intensities // Also keep track of the closest m/z for each reporter ion // Note that we're using the maximum intensity in the range (not the sum) for (var reporterIonIndex = 0; reporterIonIndex < reporterIons.Count; reporterIonIndex++) { var ion = reporterIons[reporterIonIndex]; // Search for the reporter ion MZ in this mass spectrum reporterIntensities[reporterIonIndex] = dataAggregation.AggregateIonsInRange( spectrum, ion.MZ, ion.MZToleranceDa, out _, out closestMZ[reporterIonIndex], USE_MAX_ABUNDANCE_IN_WINDOW); ion.SignalToNoise = 0; ion.Resolution = 0; ion.LabelDataMZ = 0; } if (includeFtmsColumns && currentScan.IsFTMS) { // Retrieve the label data for this spectrum rawFileReader.GetScanLabelData(currentScan.ScanNumber, out var ftLabelData); // Find each reporter ion in ftLabelData for (var reporterIonIndex = 0; reporterIonIndex < reporterIons.Count; reporterIonIndex++) { var mzToFind = reporterIons[reporterIonIndex].MZ; var mzToleranceDa = reporterIons[reporterIonIndex].MZToleranceDa; var highestIntensity = 0.0; var udtBestMatch = new udtFTLabelInfoType(); var matchFound = false; foreach (var labelItem in ftLabelData) { // Compare labelItem.Mass (which is m/z of the ion in labelItem) to the m/z of the current reporter ion if (Math.Abs(mzToFind - labelItem.Mass) > mzToleranceDa) { continue; } // m/z is within range if (labelItem.Intensity > highestIntensity) { udtBestMatch = labelItem; highestIntensity = labelItem.Intensity; matchFound = true; } } if (matchFound) { reporterIons[reporterIonIndex].SignalToNoise = udtBestMatch.SignalToNoise; reporterIons[reporterIonIndex].Resolution = udtBestMatch.Resolution; reporterIons[reporterIonIndex].LabelDataMZ = udtBestMatch.Mass; } } } // Populate reporterIntensitiesCorrected with the data in reporterIntensities Array.Copy(reporterIntensities, reporterIntensitiesCorrected, reporterIntensities.Length); if (mOptions.ReporterIons.ReporterIonApplyAbundanceCorrection) { if (mOptions.ReporterIons.ReporterIonMassMode == clsReporterIons.eReporterIonMassModeConstants.ITraqFourMZ || mOptions.ReporterIons.ReporterIonMassMode == clsReporterIons.eReporterIonMassModeConstants.ITraqEightMZHighRes || mOptions.ReporterIons.ReporterIonMassMode == clsReporterIons.eReporterIonMassModeConstants.ITraqEightMZLowRes || mOptions.ReporterIons.ReporterIonMassMode == clsReporterIons.eReporterIonMassModeConstants.TMTTenMZ || mOptions.ReporterIons.ReporterIonMassMode == clsReporterIons.eReporterIonMassModeConstants.TMTElevenMZ || mOptions.ReporterIons.ReporterIonMassMode == clsReporterIons.eReporterIonMassModeConstants.TMTSixteenMZ) { // Correct the reporter ion intensities using the Reporter Ion Intensity Corrector class if (intensityCorrector.ReporterIonMode != mOptions.ReporterIons.ReporterIonMassMode || intensityCorrector.ITraq4PlexCorrectionFactorType != mOptions.ReporterIons.ReporterIonITraq4PlexCorrectionFactorType) { intensityCorrector.UpdateReporterIonMode( mOptions.ReporterIons.ReporterIonMassMode, mOptions.ReporterIons.ReporterIonITraq4PlexCorrectionFactorType); } // Count the number of non-zero data points in reporterIntensitiesCorrected() var positiveCount = 0; for (var reporterIonIndex = 0; reporterIonIndex < reporterIons.Count; reporterIonIndex++) { if (reporterIntensitiesCorrected[reporterIonIndex] > 0) { positiveCount += 1; } } // Apply the correction if 2 or more points are non-zero if (positiveCount >= 2) { intensityCorrector.ApplyCorrection(reporterIntensitiesCorrected); } } } // Now construct the string of intensity values, delimited by delimiter // Will also compute the percent change in intensities // Initialize the variables used to compute the weighted average percent change double pctChangeSum = 0; double originalIntensitySum = 0; for (var reporterIonIndex = 0; reporterIonIndex < reporterIons.Count; reporterIonIndex++) { if (!reporterIons[reporterIonIndex].ContaminantIon) { // Update the PctChange variables and the IntensityMax variable only if this is not a Contaminant Ion originalIntensitySum += reporterIntensities[reporterIonIndex]; if (reporterIntensities[reporterIonIndex] > 0) { // Compute the percent change, then update pctChangeSum var pctChange = (reporterIntensitiesCorrected[reporterIonIndex] - reporterIntensities[reporterIonIndex]) / reporterIntensities[reporterIonIndex]; // Using Absolute Value here to prevent negative changes from cancelling out positive changes pctChangeSum += Math.Abs(pctChange * reporterIntensities[reporterIonIndex]); } if (reporterIntensitiesCorrected[reporterIonIndex] > reporterIntensityMax) { reporterIntensityMax = reporterIntensitiesCorrected[reporterIonIndex]; } } if (!reporterIons[reporterIonIndex].ContaminantIon || saveUncorrectedIntensities) { // Append the reporter ion intensity to reporterIntensityList // We skip contaminant ions, unless saveUncorrectedIntensities is True, then we include them reporterIntensityList.Add(StringUtilities.DblToString(reporterIntensitiesCorrected[reporterIonIndex], 2)); if (saveObservedMasses) { // Append the observed reporter mass value to obsMZList obsMZList.Add(StringUtilities.DblToString(closestMZ[reporterIonIndex], 3)); } if (saveUncorrectedIntensities) { // Append the original, uncorrected intensity value uncorrectedIntensityList.Add(StringUtilities.DblToString(reporterIntensities[reporterIonIndex], 2)); } if (includeFtmsColumns) { if (Math.Abs(reporterIons[reporterIonIndex].SignalToNoise) < float.Epsilon && Math.Abs(reporterIons[reporterIonIndex].Resolution) < float.Epsilon && Math.Abs(reporterIons[reporterIonIndex].LabelDataMZ) < float.Epsilon) { // A match was not found in the label data; display blanks (not zeroes) ftmsSignalToNoise.Add(string.Empty); ftmsResolution.Add(string.Empty); //ftmsLabelDataMz.Add(string.Empty); } else { ftmsSignalToNoise.Add(StringUtilities.DblToString(reporterIons[reporterIonIndex].SignalToNoise, 2)); ftmsResolution.Add(StringUtilities.DblToString(reporterIons[reporterIonIndex].Resolution, 2)); //ftmsLabelDataMz.Add(StringUtilities.DblToString(reporterIons(reporterIonIndex).LabelDataMZ, 4)); } } } } // Compute the weighted average percent intensity correction value float weightedAvgPctIntensityCorrection; if (originalIntensitySum > 0) { weightedAvgPctIntensityCorrection = (float)(pctChangeSum / originalIntensitySum * 100); } else { weightedAvgPctIntensityCorrection = 0; } // Resize the target list capacity to large enough to hold all data. dataColumns.Capacity = reporterIntensityList.Count + 3 + obsMZList.Count + uncorrectedIntensityList.Count + ftmsSignalToNoise.Count + ftmsResolution.Count; // Append the maximum reporter ion intensity then the individual reporter ion intensities dataColumns.Add(StringUtilities.DblToString(reporterIntensityMax, 2)); dataColumns.AddRange(reporterIntensityList); // Append the weighted average percent intensity correction if (weightedAvgPctIntensityCorrection < float.Epsilon) { dataColumns.Add("0"); } else { dataColumns.Add(StringUtilities.DblToString(weightedAvgPctIntensityCorrection, 1)); } if (saveObservedMasses) { dataColumns.AddRange(obsMZList); } if (saveUncorrectedIntensities) { dataColumns.AddRange(uncorrectedIntensityList); } if (includeFtmsColumns) { dataColumns.AddRange(ftmsSignalToNoise); dataColumns.AddRange(ftmsResolution); // Uncomment to include the label data m/z value in the _ReporterIons.txt file //if (saveObservedMasses) // dataColumns.AddRange(ftmsLabelDataMz) } writer.WriteLine(string.Join(delimiter.ToString(), dataColumns)); }
/// <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; } }
private float CompareFragSpectraForParentIons( clsScanList scanList, clsSpectraCache spectraCache, int parentIonIndex1, int parentIonIndex2, clsBinningOptions binningOptions, MASICPeakFinder.clsBaselineNoiseOptions noiseThresholdOptions, DataInput.clsDataImport dataImportUtilities) { // Compare the fragmentation spectra for the two parent ions // Returns the highest similarity score (ranging from 0 to 1) // Returns 0 if no similarity or no spectra to compare // Returns -1 if an error float highestSimilarityScore; try { if (scanList.ParentIons[parentIonIndex1].CustomSICPeak || scanList.ParentIons[parentIonIndex2].CustomSICPeak) { // Custom SIC values do not have fragmentation spectra; nothing to compare highestSimilarityScore = 0; } else if (scanList.ParentIons[parentIonIndex1].MRMDaughterMZ > 0 || scanList.ParentIons[parentIonIndex2].MRMDaughterMZ > 0) { // MRM Spectra should not be compared highestSimilarityScore = 0; } else { highestSimilarityScore = 0; foreach (var fragSpectrumIndex1 in scanList.ParentIons[parentIonIndex1].FragScanIndices) { if (!spectraCache.GetSpectrum(scanList.FragScans[fragSpectrumIndex1].ScanNumber, out var spectrum1, false)) { SetLocalErrorCode(clsMASIC.eMasicErrorCodes.ErrorUncachingSpectrum); return(-1); } // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (!clsMASIC.DISCARD_LOW_INTENSITY_MSMS_DATA_ON_LOAD) #pragma warning disable 162 // ReSharper disable HeuristicUnreachableCode { dataImportUtilities.DiscardDataBelowNoiseThreshold(spectrum1, scanList.FragScans[fragSpectrumIndex1].BaselineNoiseStats.NoiseLevel, 0, 0, noiseThresholdOptions); } // ReSharper restore HeuristicUnreachableCode #pragma warning restore 162 foreach (var fragSpectrumIndex2 in scanList.ParentIons[parentIonIndex2].FragScanIndices) { if (!spectraCache.GetSpectrum(scanList.FragScans[fragSpectrumIndex2].ScanNumber, out var spectrum2, false)) { SetLocalErrorCode(clsMASIC.eMasicErrorCodes.ErrorUncachingSpectrum); return(-1); } // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (!clsMASIC.DISCARD_LOW_INTENSITY_MSMS_DATA_ON_LOAD) #pragma warning disable 162 // ReSharper disable HeuristicUnreachableCode { dataImportUtilities.DiscardDataBelowNoiseThreshold(spectrum2, scanList.FragScans[fragSpectrumIndex2].BaselineNoiseStats.NoiseLevel, 0, 0, noiseThresholdOptions); } // ReSharper restore HeuristicUnreachableCode #pragma warning restore 162 var similarityScore = CompareSpectra(spectrum1, spectrum2, binningOptions); if (similarityScore > highestSimilarityScore) { highestSimilarityScore = similarityScore; } } } } } catch (Exception ex) { ReportError("Error in CompareFragSpectraForParentIons", ex); return(-1); } return(highestSimilarityScore); }
public bool ProcessMRMList( clsScanList scanList, clsSpectraCache spectraCache, clsSICProcessing sicProcessor, clsXMLResultsWriter xmlResultsWriter, clsMASICPeakFinder peakFinder, ref int parentIonsProcessed) { try { // Initialize sicDetails var sicDetails = new clsSICDetails(); sicDetails.Reset(); sicDetails.SICScanType = clsScanList.eScanTypeConstants.FragScan; for (var parentIonIndex = 0; parentIonIndex < scanList.ParentIons.Count; parentIonIndex++) { if (scanList.ParentIons[parentIonIndex].MRMDaughterMZ <= 0) { continue; } // Step 1: Create the SIC for this MRM Parent/Daughter pair var parentIonMZ = scanList.ParentIons[parentIonIndex].MZ; var mrmDaughterMZ = scanList.ParentIons[parentIonIndex].MRMDaughterMZ; var searchToleranceHalfWidth = scanList.ParentIons[parentIonIndex].MRMToleranceHalfWidth; // Reset SICData sicDetails.SICData.Clear(); // Step through the fragmentation spectra, finding those that have matching parent and daughter ion m/z values for (var scanIndex = 0; scanIndex < scanList.FragScans.Count; scanIndex++) { if (scanList.FragScans[scanIndex].MRMScanType != MRMScanTypeConstants.SRM) { continue; } var fragScan = scanList.FragScans[scanIndex]; var useScan = false; for (var mrmMassIndex = 0; mrmMassIndex < fragScan.MRMScanInfo.MRMMassCount; mrmMassIndex++) { if (MRMParentDaughterMatch(fragScan.MRMScanInfo.ParentIonMZ, fragScan.MRMScanInfo.MRMMassList[mrmMassIndex].CentralMass, parentIonMZ, mrmDaughterMZ)) { useScan = true; break; } } if (!useScan) { continue; } // Include this scan in the SIC for this parent ion mDataAggregation.FindMaxValueInMZRange(spectraCache, scanList.FragScans[scanIndex], mrmDaughterMZ - searchToleranceHalfWidth, mrmDaughterMZ + searchToleranceHalfWidth, out var closestMZ, out var matchIntensity); sicDetails.AddData(fragScan.ScanNumber, matchIntensity, closestMZ, scanIndex); } // Step 2: Find the largest peak in the SIC // Compute the noise level; the noise level may change with increasing index number if the background is increasing for a given m/z var success = peakFinder.ComputeDualTrimmedNoiseLevelTTest(sicDetails.SICIntensities, 0, sicDetails.SICDataCount - 1, mOptions.SICOptions.SICPeakFinderOptions. SICBaselineNoiseOptions, out var noiseStatsSegments); if (!success) { SetLocalErrorCode(clsMASIC.eMasicErrorCodes.FindSICPeaksError, true); return(false); } // Initialize the peak scanList.ParentIons[parentIonIndex].SICStats.Peak = new clsSICStatsPeak(); // Find the data point with the maximum intensity double maximumIntensity = 0; scanList.ParentIons[parentIonIndex].SICStats.Peak.IndexObserved = 0; for (var scanIndex = 0; scanIndex < sicDetails.SICDataCount; scanIndex++) { var intensity = sicDetails.SICIntensities[scanIndex]; if (intensity > maximumIntensity) { maximumIntensity = intensity; scanList.ParentIons[parentIonIndex].SICStats.Peak.IndexObserved = scanIndex; } } // Compute the minimum potential peak area in the entire SIC, populating udtSICPotentialAreaStatsInFullSIC peakFinder.FindPotentialPeakArea(sicDetails.SICData, out var potentialAreaStatsInFullSIC, mOptions.SICOptions.SICPeakFinderOptions); // Update .BaselineNoiseStats in scanList.ParentIons(parentIonIndex).SICStats.Peak scanList.ParentIons[parentIonIndex].SICStats.Peak.BaselineNoiseStats = peakFinder.LookupNoiseStatsUsingSegments( scanList.ParentIons[parentIonIndex].SICStats.Peak.IndexObserved, noiseStatsSegments); var parentIon = scanList.ParentIons[parentIonIndex]; // Clear udtSICPotentialAreaStatsForPeak parentIon.SICStats.SICPotentialAreaStatsForPeak = new clsSICPotentialAreaStats(); var peakIsValid = peakFinder.FindSICPeakAndArea(sicDetails.SICData, out var potentialAreaStatsForPeakOut, parentIon.SICStats.Peak, out var smoothedYDataSubset, mOptions.SICOptions.SICPeakFinderOptions, potentialAreaStatsInFullSIC, false, scanList.SIMDataPresent, false); parentIon.SICStats.SICPotentialAreaStatsForPeak = potentialAreaStatsForPeakOut; sicProcessor.StorePeakInParentIon(scanList, parentIonIndex, sicDetails, parentIon.SICStats.SICPotentialAreaStatsForPeak, parentIon.SICStats.Peak, peakIsValid); // Step 3: store the results // Possibly save the stats for this SIC to the SICData file mDataOutputHandler.SaveSICDataToText(mOptions.SICOptions, scanList, parentIonIndex, sicDetails); // Save the stats for this SIC to the XML file xmlResultsWriter.SaveDataToXML(scanList, parentIonIndex, sicDetails, smoothedYDataSubset, mDataOutputHandler); parentIonsProcessed += 1; // --------------------------------------------------------- // Update progress // --------------------------------------------------------- try { if (scanList.ParentIons.Count > 1) { UpdateProgress((short)(parentIonsProcessed / (double)(scanList.ParentIons.Count - 1) * 100)); } else { UpdateProgress(0); } UpdateCacheStats(spectraCache); if (mOptions.AbortProcessing) { scanList.ProcessingIncomplete = true; break; } if (parentIonsProcessed % 100 == 0) { if (DateTime.UtcNow.Subtract(mOptions.LastParentIonProcessingLogTime).TotalSeconds >= 10 || parentIonsProcessed % 500 == 0) { ReportMessage("Parent Ions Processed: " + parentIonsProcessed.ToString()); Console.Write("."); mOptions.LastParentIonProcessingLogTime = DateTime.UtcNow; } } } catch (Exception ex) { ReportError("Error updating progress", ex, clsMASIC.eMasicErrorCodes.CreateSICsError); } } return(true); } catch (Exception ex) { ReportError("Error creating SICs for MRM spectra", ex, clsMASIC.eMasicErrorCodes.CreateSICsError); return(false); } }
/// <summary> /// Export MRM data to disk /// </summary> /// <param name="scanList"></param> /// <param name="spectraCache"></param> /// <param name="mrmSettings"></param> /// <param name="srmList"></param> /// <param name="inputFileName"></param> /// <param name="outputDirectoryPath"></param> /// <returns>True if the MRM data is successfully written to disk, or if the mrmSettings list is empty</returns> private bool ExportMRMDataToDisk( clsScanList scanList, clsSpectraCache spectraCache, IReadOnlyList <clsMRMScanInfo> mrmSettings, IReadOnlyList <udtSRMListType> srmList, string inputFileName, string outputDirectoryPath) { const char TAB_DELIMITER = '\t'; StreamWriter dataWriter = null; StreamWriter crosstabWriter = null; bool success; try { // Only write this data if 1 or more fragmentation spectra are of type SRM if (mrmSettings == null || mrmSettings.Count == 0) { return(true); } UpdateProgress(0, "Exporting MRM data"); // Write out the MRM Settings var mrmSettingsFilePath = clsDataOutput.ConstructOutputFilePath( inputFileName, outputDirectoryPath, clsDataOutput.eOutputFileTypeConstants.MRMSettingsFile); using (var settingsWriter = new StreamWriter(mrmSettingsFilePath)) { settingsWriter.WriteLine(mDataOutputHandler.GetHeadersForOutputFile(scanList, clsDataOutput.eOutputFileTypeConstants.MRMSettingsFile)); var dataColumns = new List <string>(7); for (var mrmInfoIndex = 0; mrmInfoIndex < mrmSettings.Count; mrmInfoIndex++) { var mrmSetting = mrmSettings[mrmInfoIndex]; for (var mrmMassIndex = 0; mrmMassIndex < mrmSetting.MRMMassCount; mrmMassIndex++) { dataColumns.Clear(); dataColumns.Add(mrmInfoIndex.ToString()); dataColumns.Add(mrmSetting.ParentIonMZ.ToString("0.000")); dataColumns.Add(mrmSetting.MRMMassList[mrmMassIndex].CentralMass.ToString("0.000")); dataColumns.Add(mrmSetting.MRMMassList[mrmMassIndex].StartMass.ToString("0.000")); dataColumns.Add(mrmSetting.MRMMassList[mrmMassIndex].EndMass.ToString("0.000")); dataColumns.Add(mrmSetting.ScanCount.ToString()); settingsWriter.WriteLine(string.Join(TAB_DELIMITER.ToString(), dataColumns)); } } if (mOptions.WriteMRMDataList || mOptions.WriteMRMIntensityCrosstab) { // Populate srmKeyToIndexMap var srmKeyToIndexMap = new Dictionary <string, int>(); for (var srmIndex = 0; srmIndex < srmList.Count; srmIndex++) { srmKeyToIndexMap.Add(ConstructSRMMapKey(srmList[srmIndex]), srmIndex); } if (mOptions.WriteMRMDataList) { // Write out the raw MRM Data var dataFilePath = clsDataOutput.ConstructOutputFilePath(inputFileName, outputDirectoryPath, clsDataOutput.eOutputFileTypeConstants.MRMDatafile); dataWriter = new StreamWriter(dataFilePath); // Write the file headers dataWriter.WriteLine(mDataOutputHandler.GetHeadersForOutputFile(scanList, clsDataOutput.eOutputFileTypeConstants.MRMDatafile)); } if (mOptions.WriteMRMIntensityCrosstab) { // Write out the raw MRM Data var crosstabFilePath = clsDataOutput.ConstructOutputFilePath(inputFileName, outputDirectoryPath, clsDataOutput.eOutputFileTypeConstants.MRMCrosstabFile); crosstabWriter = new StreamWriter(crosstabFilePath); // Initialize the crosstab header variable using the data in udtSRMList() var headerNames = new List <string>(srmList.Count + 2) { "Scan_First", "ScanTime" }; for (var srmIndex = 0; srmIndex < srmList.Count; srmIndex++) { headerNames.Add(ConstructSRMMapKey(srmList[srmIndex])); } crosstabWriter.WriteLine(string.Join(TAB_DELIMITER.ToString(), headerNames)); } var scanFirst = int.MinValue; float scanTimeFirst = 0; var srmIndexLast = 0; var crosstabColumnValue = new double[srmList.Count]; var crosstabColumnFlag = new bool[srmList.Count]; // For scanIndex = 0 To scanList.FragScanCount - 1 foreach (var fragScan in scanList.FragScans) { if (fragScan.MRMScanType != MRMScanTypeConstants.SRM) { continue; } if (scanFirst == int.MinValue) { scanFirst = fragScan.ScanNumber; scanTimeFirst = fragScan.ScanTime; } // Look for each of the m/z values specified in fragScan.MRMScanInfo.MRMMassList for (var mrmMassIndex = 0; mrmMassIndex < fragScan.MRMScanInfo.MRMMassCount; mrmMassIndex++) { // Find the maximum value between fragScan.StartMass and fragScan.EndMass // Need to define a tolerance to account for numeric rounding artifacts in the variables var mzStart = fragScan.MRMScanInfo.MRMMassList[mrmMassIndex].StartMass; var mzEnd = fragScan.MRMScanInfo.MRMMassList[mrmMassIndex].EndMass; var mrmToleranceHalfWidth = Math.Round((mzEnd - mzStart) / 2, 6); if (mrmToleranceHalfWidth < 0.001) { mrmToleranceHalfWidth = 0.001; } var matchFound = mDataAggregation.FindMaxValueInMZRange( spectraCache, fragScan, mzStart - mrmToleranceHalfWidth, mzEnd + mrmToleranceHalfWidth, out _, out var matchIntensity); if (mOptions.WriteMRMDataList) { dataColumns.Clear(); dataColumns.Add(fragScan.ScanNumber.ToString()); dataColumns.Add(fragScan.MRMScanInfo.ParentIonMZ.ToString("0.000")); if (matchFound) { dataColumns.Add(fragScan.MRMScanInfo.MRMMassList[mrmMassIndex].CentralMass.ToString("0.000")); dataColumns.Add(matchIntensity.ToString("0.000")); } else { dataColumns.Add(fragScan.MRMScanInfo.MRMMassList[mrmMassIndex].CentralMass.ToString("0.000")); dataColumns.Add("0"); } dataWriter?.WriteLine(string.Join(TAB_DELIMITER.ToString(), dataColumns)); } if (!mOptions.WriteMRMIntensityCrosstab) { continue; } var srmMapKey = ConstructSRMMapKey(fragScan.MRMScanInfo.ParentIonMZ, fragScan.MRMScanInfo.MRMMassList[mrmMassIndex].CentralMass); // Use srmKeyToIndexMap to determine the appropriate column index for srmMapKey if (srmKeyToIndexMap.TryGetValue(srmMapKey, out var srmIndex)) { if (crosstabColumnFlag[srmIndex] || srmIndex == 0 && srmIndexLast == srmList.Count - 1) { // Either the column is already populated, or the SRMIndex has cycled back to zero // Write out the current crosstab line and reset the crosstab column arrays ExportMRMDataWriteLine(crosstabWriter, scanFirst, scanTimeFirst, crosstabColumnValue, crosstabColumnFlag, TAB_DELIMITER, true); scanFirst = fragScan.ScanNumber; scanTimeFirst = fragScan.ScanTime; } if (matchFound) { crosstabColumnValue[srmIndex] = matchIntensity; } crosstabColumnFlag[srmIndex] = true; srmIndexLast = srmIndex; } else { // Unknown combination of parent ion m/z and daughter m/z; this is unexpected // We won't write this entry out } } UpdateCacheStats(spectraCache); if (mOptions.AbortProcessing) { break; } } if (mOptions.WriteMRMIntensityCrosstab) { // Write out any remaining crosstab values ExportMRMDataWriteLine(crosstabWriter, scanFirst, scanTimeFirst, crosstabColumnValue, crosstabColumnFlag, TAB_DELIMITER, false); } } } success = true; } catch (Exception ex) { ReportError("Error writing the SRM data to disk", ex, clsMASIC.eMasicErrorCodes.OutputFileWriteError); success = false; } finally { dataWriter?.Close(); crosstabWriter?.Close(); } return(success); }
public bool ProcessAndStoreSpectrum( clsScanInfo scanInfo, DataInput.clsDataImport dataImportUtilities, clsSpectraCache spectraCache, clsMSSpectrum msSpectrum, clsBaselineNoiseOptions noiseThresholdOptions, bool discardLowIntensityData, bool compressData, double msDataResolution, bool keepRawSpectrum) { var lastKnownLocation = "Start"; try { // Determine the noise threshold intensity for this spectrum // Stored in scanInfo.BaselineNoiseStats lastKnownLocation = "Call ComputeNoiseLevelForMassSpectrum"; ComputeNoiseLevelForMassSpectrum(scanInfo, msSpectrum, noiseThresholdOptions); if (!keepRawSpectrum) { return(true); } // Discard low intensity data, but not for MRM scans if (discardLowIntensityData && scanInfo.MRMScanType == ThermoRawFileReader.MRMScanTypeConstants.NotMRM) { // Discard data below the noise level or below the minimum S/N level // If we are searching for Reporter ions, then it is important to not discard any of the ions in the region of the reporter ion m/z values lastKnownLocation = "Call DiscardDataBelowNoiseThreshold"; dataImportUtilities.DiscardDataBelowNoiseThreshold(msSpectrum, scanInfo.BaselineNoiseStats.NoiseLevel, mReporterIons.MZIntensityFilterIgnoreRangeStart, mReporterIons.MZIntensityFilterIgnoreRangeEnd, noiseThresholdOptions); scanInfo.IonCount = msSpectrum.IonCount; } if (compressData) { lastKnownLocation = "Call CompressSpectraData"; // Again, if we are searching for Reporter ions, then it is important to not discard any of the ions in the region of the reporter ion m/z values CompressSpectraData(msSpectrum, msDataResolution, mReporterIons.MZIntensityFilterIgnoreRangeStart, mReporterIons.MZIntensityFilterIgnoreRangeEnd); } if (msSpectrum.IonCount > MAX_ALLOWABLE_ION_COUNT) { // Do not keep more than 50,000 ions lastKnownLocation = "Call DiscardDataToLimitIonCount"; mSpectraFoundExceedingMaxIonCount += 1; // Display a message at the console the first 10 times we encounter spectra with over MAX_ALLOWABLE_ION_COUNT ions // In addition, display a new message every time a new max value is encountered if (mSpectraFoundExceedingMaxIonCount <= 10 || msSpectrum.IonCount > mMaxIonCountReported) { Console.WriteLine(); Console.WriteLine( "Note: Scan " + scanInfo.ScanNumber + " has " + msSpectrum.IonCount + " ions; " + "will only retain " + MAX_ALLOWABLE_ION_COUNT + " (trimmed " + mSpectraFoundExceedingMaxIonCount.ToString() + " spectra)"); mMaxIonCountReported = msSpectrum.IonCount; } dataImportUtilities.DiscardDataToLimitIonCount(msSpectrum, mReporterIons.MZIntensityFilterIgnoreRangeStart, mReporterIons.MZIntensityFilterIgnoreRangeEnd, MAX_ALLOWABLE_ION_COUNT); scanInfo.IonCount = msSpectrum.IonCount; } lastKnownLocation = "Call AddSpectrumToPool"; var success = spectraCache.AddSpectrumToPool(msSpectrum, scanInfo.ScanNumber); return(success); } catch (Exception ex) { ReportError("Error in ProcessAndStoreSpectrum (LastKnownLocation: " + lastKnownLocation + ")", ex, clsMASIC.eMasicErrorCodes.InputFileDataReadError); return(false); } }