Example #1
0
        public bool ExtractScanInfoFromMGFandCDF(
            string filePath,
            clsScanList scanList,
            clsSpectraCache spectraCache,
            DataOutput.clsDataOutput dataOutputHandler,
            bool keepRawSpectra,
            bool keepMSMSSpectra)
        {
            // Returns True if Success, False if failure
            // Note: This function assumes filePath exists
            //
            // This function can be used to read a pair of MGF and NetCDF files that contain MS/MS and MS-only parent ion scans, respectively
            // Typically, this will apply to LC-MS/MS analyses acquired using an Agilent mass spectrometer running DataAnalysis software
            // filePath can contain the path to the MGF or to the CDF file; the extension will be removed in order to determine the base file name,
            // then the two files will be looked for separately

            var scanTime = 0.0;

            var cdfReader = new NetCDFReader.clsMSNetCdf();
            var mgfReader = new MSDataFileReader.clsMGFFileReader();

            try
            {
                Console.Write("Reading CDF/MGF data files ");
                ReportMessage("Reading CDF/MGF data files");

                UpdateProgress(0, "Opening data file: " + Environment.NewLine + Path.GetFileName(filePath));

                // Obtain the full path to the file
                var mgfFileInfo          = new FileInfo(filePath);
                var mgfInputFilePathFull = mgfFileInfo.FullName;

                // Make sure the extension for mgfInputFilePathFull is .MGF
                mgfInputFilePathFull = Path.ChangeExtension(mgfInputFilePathFull, AGILENT_MSMS_FILE_EXTENSION);
                var cdfInputFilePathFull = Path.ChangeExtension(mgfInputFilePathFull, AGILENT_MS_FILE_EXTENSION);

                var datasetID  = mOptions.SICOptions.DatasetID;
                var sicOptions = mOptions.SICOptions;

                var success = UpdateDatasetFileStats(mgfFileInfo, datasetID);
                mDatasetFileInfo.ScanCount = 0;

                // Open a handle to each data file
                if (!cdfReader.OpenMSCdfFile(cdfInputFilePathFull))
                {
                    ReportError("Error opening input data file: " + cdfInputFilePathFull);
                    SetLocalErrorCode(clsMASIC.eMasicErrorCodes.InputFileAccessError);
                    return(false);
                }

                if (!mgfReader.OpenFile(mgfInputFilePathFull))
                {
                    ReportError("Error opening input data file: " + mgfInputFilePathFull);
                    SetLocalErrorCode(clsMASIC.eMasicErrorCodes.InputFileAccessError);
                    return(false);
                }

                var msScanCount = cdfReader.GetScanCount();
                mDatasetFileInfo.ScanCount = msScanCount;

                if (msScanCount <= 0)
                {
                    // No scans found
                    ReportError("No scans found in the input file: " + cdfInputFilePathFull);
                    SetLocalErrorCode(clsMASIC.eMasicErrorCodes.InputFileAccessError);
                    return(false);
                }

                // Reserve memory for all of the Survey Scan data
                scanList.Initialize();
                var scanCount = mOptions.SICOptions.ScanRangeCount;
                if (scanCount <= 0)
                {
                    scanCount = msScanCount * 3;
                }
                scanList.ReserveListCapacity(scanCount, Math.Min(msScanCount, scanCount / 3)); // Assume there are 2 frag scans for every survey scan
                mScanTracking.ReserveListCapacity(scanCount);
                spectraCache.SpectrumCount = Math.Max(spectraCache.SpectrumCount, scanCount);

                UpdateProgress("Reading CDF/MGF data (" + msScanCount.ToString() + " scans)" + Environment.NewLine + Path.GetFileName(filePath));
                ReportMessage("Reading CDF/MGF data; Total MS scan count: " + msScanCount.ToString());

                // Read all of the Survey scans from the CDF file
                // CDF files created by the Agilent XCT list the first scan number as 0; use scanNumberCorrection to correct for this
                var scanNumberCorrection = 0;
                for (var msScanIndex = 0; msScanIndex < msScanCount; msScanIndex++)
                {
                    success = cdfReader.GetScanInfo(msScanIndex, out var scanNumber, out var scanTotalIntensity, out scanTime, out _, out _);

                    if (msScanIndex == 0 && scanNumber == 0)
                    {
                        scanNumberCorrection = 1;
                    }

                    if (!success)
                    {
                        // Error reading CDF file
                        ReportError("Error obtaining data from CDF file: " + cdfInputFilePathFull);
                        SetLocalErrorCode(clsMASIC.eMasicErrorCodes.InputFileDataReadError);
                        return(false);
                    }

                    if (scanNumberCorrection > 0)
                    {
                        scanNumber += scanNumberCorrection;
                    }

                    if (mScanTracking.CheckScanInRange(scanNumber, scanTime, sicOptions))
                    {
                        var newSurveyScan = new clsScanInfo
                        {
                            ScanNumber        = scanNumber,
                            TotalIonIntensity = (float)scanTotalIntensity,    // Copy the Total Scan Intensity to .TotalIonIntensity
                            ScanHeaderText    = string.Empty,
                            ScanTypeName      = "MS",
                        };

                        if (mOptions.CDFTimeInSeconds)
                        {
                            newSurveyScan.ScanTime = (float)(scanTime / 60);
                        }
                        else
                        {
                            newSurveyScan.ScanTime = (float)scanTime;
                        }

                        // Survey scans typically lead to multiple parent ions; we do not record them here
                        newSurveyScan.FragScanInfo.ParentIonInfoIndex = -1;

                        scanList.SurveyScans.Add(newSurveyScan);

                        success = cdfReader.GetMassSpectrum(msScanIndex, out var mzData,
                                                            out var intensityData,
                                                            out var intIonCount, out _);

                        if (success && intIonCount > 0)
                        {
                            var msSpectrum = new clsMSSpectrum(scanNumber, mzData, intensityData, intIonCount);

                            double msDataResolution;

                            newSurveyScan.IonCount    = msSpectrum.IonCount;
                            newSurveyScan.IonCountRaw = newSurveyScan.IonCount;

                            // Find the base peak ion mass and intensity
                            newSurveyScan.BasePeakIonMZ = FindBasePeakIon(msSpectrum.IonsMZ,
                                                                          msSpectrum.IonsIntensity,
                                                                          out var basePeakIonIntensity,
                                                                          out var mzMin, out var mzMax);
                            newSurveyScan.BasePeakIonIntensity = basePeakIonIntensity;

                            // Determine the minimum positive intensity in this scan
                            newSurveyScan.MinimumPositiveIntensity = mPeakFinder.FindMinimumPositiveValue(msSpectrum.IonsIntensity, 0);

                            if (sicOptions.SICToleranceIsPPM)
                            {
                                // Define MSDataResolution based on the tolerance value that will be used at the lowest m/z in this spectrum, divided by COMPRESS_TOLERANCE_DIVISOR
                                // However, if the lowest m/z value is < 100, then use 100 m/z
                                if (mzMin < 100)
                                {
                                    msDataResolution = clsParentIonProcessing.GetParentIonToleranceDa(sicOptions, 100) /
                                                       sicOptions.CompressToleranceDivisorForPPM;
                                }
                                else
                                {
                                    msDataResolution = clsParentIonProcessing.GetParentIonToleranceDa(sicOptions, mzMin) /
                                                       sicOptions.CompressToleranceDivisorForPPM;
                                }
                            }
                            else
                            {
                                msDataResolution = sicOptions.SICTolerance / sicOptions.CompressToleranceDivisorForDa;
                            }

                            mScanTracking.ProcessAndStoreSpectrum(
                                newSurveyScan, this,
                                spectraCache, msSpectrum,
                                sicOptions.SICPeakFinderOptions.MassSpectraNoiseThresholdOptions,
                                clsMASIC.DISCARD_LOW_INTENSITY_MS_DATA_ON_LOAD,
                                sicOptions.CompressMSSpectraData,
                                msDataResolution,
                                keepRawSpectra);
                        }
                        else
                        {
                            newSurveyScan.IonCount    = 0;
                            newSurveyScan.IonCountRaw = 0;
                        }

                        // Note: Since we're reading all of the Survey Scan data, we cannot update .MasterScanOrder() at this time
                    }

                    // Note: We need to take msScanCount * 2 since we have to read two different files
                    if (msScanCount > 1)
                    {
                        UpdateProgress((short)(msScanIndex / (double)(msScanCount * 2 - 1) * 100));
                    }
                    else
                    {
                        UpdateProgress(0);
                    }

                    UpdateCacheStats(spectraCache);
                    if (mOptions.AbortProcessing)
                    {
                        scanList.ProcessingIncomplete = true;
                        break;
                    }

                    if (msScanIndex % 100 == 0)
                    {
                        ReportMessage("Reading MS scan index: " + msScanIndex.ToString());
                        Console.Write(".");
                    }
                }

                // Record the current memory usage (before we close the .CDF file)
                OnUpdateMemoryUsage();

                cdfReader.CloseMSCdfFile();

                // We loaded all of the survey scan data above
                // We can now initialize .MasterScanOrder()
                var lastSurveyScanIndex = 0;

                scanList.AddMasterScanEntry(clsScanList.eScanTypeConstants.SurveyScan, lastSurveyScanIndex);

                var surveyScansRecorded = new SortedSet <int>()
                {
                    lastSurveyScanIndex
                };

                // Reset scanNumberCorrection; we might also apply it to MS/MS data
                scanNumberCorrection = 0;

                // Now read the MS/MS data from the MGF file
                do
                {
                    var fragScanFound = mgfReader.ReadNextSpectrum(out var spectrumInfo);
                    if (!fragScanFound)
                    {
                        break;
                    }

                    mDatasetFileInfo.ScanCount += 1;

                    while (spectrumInfo.ScanNumber < scanList.SurveyScans[lastSurveyScanIndex].ScanNumber)
                    {
                        // The scan number for the current MS/MS spectrum is less than the last survey scan index scan number
                        // This can happen, due to oddities with combining scans when creating the .MGF file
                        // Need to decrement lastSurveyScanIndex until we find the appropriate survey scan

                        lastSurveyScanIndex -= 1;
                        if (lastSurveyScanIndex == 0)
                        {
                            break;
                        }
                    }

                    if (scanNumberCorrection == 0)
                    {
                        // See if udtSpectrumHeaderInfo.ScanNumberStart is equivalent to one of the survey scan numbers, yielding conflicting scan numbers
                        // If it is, then there is an indexing error in the .MGF file; this error was present in .MGF files generated with
                        // an older version of Agilent Chemstation.  These files typically have lines like ###MSMS: #13-29 instead of ###MSMS: #13/29/
                        // If this indexing error is found, then we'll set scanNumberCorrection = 1 and apply it to all subsequent MS/MS scans;
                        // we'll also need to correct prior MS/MS scans
                        for (var surveyScanIndex = lastSurveyScanIndex; surveyScanIndex < scanList.SurveyScans.Count; surveyScanIndex++)
                        {
                            if (scanList.SurveyScans[surveyScanIndex].ScanNumber == spectrumInfo.ScanNumber)
                            {
                                // Conflicting scan numbers were found
                                scanNumberCorrection = 1;

                                // Need to update prior MS/MS scans
                                foreach (var fragScan in scanList.FragScans)
                                {
                                    fragScan.ScanNumber += scanNumberCorrection;
                                    var scanTimeInterpolated = InterpolateRTandFragScanNumber(
                                        scanList.SurveyScans, 0, fragScan.ScanNumber, out var fragScanIterationOut);
                                    fragScan.FragScanInfo.FragScanNumber = fragScanIterationOut;

                                    fragScan.ScanTime = scanTimeInterpolated;
                                }

                                break;
                            }

                            if (scanList.SurveyScans[surveyScanIndex].ScanNumber > spectrumInfo.ScanNumber)
                            {
                                break;
                            }
                        }
                    }

                    if (scanNumberCorrection > 0)
                    {
                        spectrumInfo.ScanNumber    += scanNumberCorrection;
                        spectrumInfo.ScanNumberEnd += scanNumberCorrection;
                    }

                    scanTime = InterpolateRTandFragScanNumber(
                        scanList.SurveyScans, lastSurveyScanIndex, spectrumInfo.ScanNumber, out var fragScanIteration);

                    // Make sure this fragmentation scan isn't present yet in scanList.FragScans
                    // This can occur in Agilent .MGF files if the scan is listed both singly and grouped with other MS/MS scans
                    var validFragScan = true;
                    foreach (var fragScan in scanList.FragScans)
                    {
                        if (fragScan.ScanNumber == spectrumInfo.ScanNumber)
                        {
                            // Duplicate found
                            validFragScan = false;
                            break;
                        }
                    }

                    if (!(validFragScan && mScanTracking.CheckScanInRange(spectrumInfo.ScanNumber, scanTime, sicOptions)))
                    {
                        continue;
                    }

                    // See if lastSurveyScanIndex needs to be updated
                    // At the same time, populate .MasterScanOrder
                    while (lastSurveyScanIndex < scanList.SurveyScans.Count - 1 &&
                           spectrumInfo.ScanNumber > scanList.SurveyScans[lastSurveyScanIndex + 1].ScanNumber)
                    {
                        lastSurveyScanIndex += 1;

                        // Add the given SurveyScan to .MasterScanOrder, though only if it hasn't yet been added
                        if (!surveyScansRecorded.Contains(lastSurveyScanIndex))
                        {
                            surveyScansRecorded.Add(lastSurveyScanIndex);
                            scanList.AddMasterScanEntry(clsScanList.eScanTypeConstants.SurveyScan,
                                                        lastSurveyScanIndex);
                        }
                    }

                    scanList.AddMasterScanEntry(clsScanList.eScanTypeConstants.FragScan, scanList.FragScans.Count,
                                                spectrumInfo.ScanNumber, (float)scanTime);

                    var newFragScan = new clsScanInfo
                    {
                        ScanNumber     = spectrumInfo.ScanNumber,
                        ScanTime       = (float)scanTime,
                        ScanHeaderText = string.Empty,
                        ScanTypeName   = "MSn",
                    };

                    newFragScan.FragScanInfo.FragScanNumber = fragScanIteration;
                    newFragScan.FragScanInfo.MSLevel        = 2;
                    newFragScan.MRMScanInfo.MRMMassCount    = 0;

                    scanList.FragScans.Add(newFragScan);

                    var msSpectrum = new clsMSSpectrum(newFragScan.ScanNumber, spectrumInfo.MZList, spectrumInfo.IntensityList, spectrumInfo.DataCount);

                    if (spectrumInfo.DataCount > 0)
                    {
                        newFragScan.IonCount    = msSpectrum.IonCount;
                        newFragScan.IonCountRaw = newFragScan.IonCount;

                        // Find the base peak ion mass and intensity
                        newFragScan.BasePeakIonMZ = FindBasePeakIon(msSpectrum.IonsMZ, msSpectrum.IonsIntensity,
                                                                    out var basePeakIonIntensity,
                                                                    out _, out _);

                        newFragScan.BasePeakIonIntensity = basePeakIonIntensity;

                        // Compute the total scan intensity
                        newFragScan.TotalIonIntensity = 0;
                        for (var ionIndex = 0; ionIndex < newFragScan.IonCount; ionIndex++)
                        {
                            newFragScan.TotalIonIntensity += msSpectrum.IonsIntensity[ionIndex];
                        }

                        // Determine the minimum positive intensity in this scan
                        newFragScan.MinimumPositiveIntensity = mPeakFinder.FindMinimumPositiveValue(msSpectrum.IonsIntensity, 0);

                        var msDataResolution = mOptions.BinningOptions.BinSize / sicOptions.CompressToleranceDivisorForDa;
                        var keepRawSpectrum  = keepRawSpectra && keepMSMSSpectra;

                        mScanTracking.ProcessAndStoreSpectrum(
                            newFragScan, this,
                            spectraCache, msSpectrum,
                            sicOptions.SICPeakFinderOptions.MassSpectraNoiseThresholdOptions,
                            clsMASIC.DISCARD_LOW_INTENSITY_MSMS_DATA_ON_LOAD,
                            sicOptions.CompressMSMSSpectraData,
                            msDataResolution,
                            keepRawSpectrum);
                    }
                    else
                    {
                        newFragScan.IonCount          = 0;
                        newFragScan.IonCountRaw       = 0;
                        newFragScan.TotalIonIntensity = 0;
                    }

                    mParentIonProcessor.AddUpdateParentIons(scanList, lastSurveyScanIndex, spectrumInfo.ParentIonMZ,
                                                            scanList.FragScans.Count - 1, spectraCache, sicOptions);

                    // Note: We need to take msScanCount * 2, in addition to adding msScanCount to lastSurveyScanIndex, since we have to read two different files
                    if (msScanCount > 1)
                    {
                        UpdateProgress((short)((lastSurveyScanIndex + msScanCount) / (double)(msScanCount * 2 - 1) * 100));
                    }
                    else
                    {
                        UpdateProgress(0);
                    }

                    UpdateCacheStats(spectraCache);
                    if (mOptions.AbortProcessing)
                    {
                        scanList.ProcessingIncomplete = true;
                        break;
                    }

                    if (scanList.FragScans.Count % 100 == 0)
                    {
                        ReportMessage("Reading MSMS scan index: " + scanList.FragScans.Count);
                        Console.Write(".");
                    }
                }while (true);

                // Record the current memory usage (before we close the .MGF file)
                OnUpdateMemoryUsage();

                mgfReader.CloseFile();

                // Check for any other survey scans that need to be added to MasterScanOrder

                // See if lastSurveyScanIndex needs to be updated
                // At the same time, populate .MasterScanOrder
                while (lastSurveyScanIndex < scanList.SurveyScans.Count - 1)
                {
                    lastSurveyScanIndex += 1;

                    // Note that scanTime is the scan time of the most recent survey scan processed in the above Do loop, so it's not accurate
                    if (mScanTracking.CheckScanInRange(scanList.SurveyScans[lastSurveyScanIndex].ScanNumber, scanTime, sicOptions))
                    {
                        // Add the given SurveyScan to .MasterScanOrder, though only if it hasn't yet been added
                        if (!surveyScansRecorded.Contains(lastSurveyScanIndex))
                        {
                            surveyScansRecorded.Add(lastSurveyScanIndex);

                            scanList.AddMasterScanEntry(clsScanList.eScanTypeConstants.SurveyScan, lastSurveyScanIndex);
                        }
                    }
                }

                // Make sure that MasterScanOrder really is sorted by scan number
                ValidateMasterScanOrderSorting(scanList);

                // Now that all of the data has been read, write out to the scan stats file, in order of scan number
                for (var scanIndex = 0; scanIndex < scanList.MasterScanOrderCount; scanIndex++)
                {
                    var         eScanType = scanList.MasterScanOrder[scanIndex].ScanType;
                    clsScanInfo currentScan;

                    if (eScanType == clsScanList.eScanTypeConstants.SurveyScan)
                    {
                        // Survey scan
                        currentScan = scanList.SurveyScans[scanList.MasterScanOrder[scanIndex].ScanIndexPointer];
                    }
                    else
                    {
                        // Frag Scan
                        currentScan = scanList.FragScans[scanList.MasterScanOrder[scanIndex].ScanIndexPointer];
                    }

                    SaveScanStatEntry(dataOutputHandler.OutputFileHandles.ScanStats, eScanType, currentScan, sicOptions.DatasetID);
                }

                Console.WriteLine();

                scanList.SetListCapacityToCount();
                mScanTracking.SetListCapacityToCount();

                return(success);
            }
            catch (Exception ex)
            {
                ReportError("Error in ExtractScanInfoFromMGFandCDF", ex, clsMASIC.eMasicErrorCodes.InputFileDataReadError);
                return(false);
            }
        }
Example #2
0
        private bool ExtractXcaliburFragmentationScan(
            XRawFileIO xcaliburAccessor,
            clsScanList scanList,
            clsSpectraCache spectraCache,
            clsDataOutput dataOutputHandler,
            clsSICOptions sicOptions,
            clsBinningOptions binningOptions,
            ThermoRawFileReader.clsScanInfo thermoScanInfo)
        {
            // Note that MinimumPositiveIntensity will be determined in LoadSpectraForThermoRawFile

            var scanInfo = new clsScanInfo(thermoScanInfo.ParentIonMZ)
            {
                ScanNumber               = thermoScanInfo.ScanNumber,
                ScanTime                 = (float)thermoScanInfo.RetentionTime,
                ScanHeaderText           = XRawFileIO.MakeGenericThermoScanFilter(thermoScanInfo.FilterText),
                ScanTypeName             = XRawFileIO.GetScanTypeNameFromThermoScanFilterText(thermoScanInfo.FilterText),
                BasePeakIonMZ            = thermoScanInfo.BasePeakMZ,
                BasePeakIonIntensity     = thermoScanInfo.BasePeakIntensity,
                TotalIonIntensity        = thermoScanInfo.TotalIonCurrent,
                MinimumPositiveIntensity = 0,
                ZoomScan                 = thermoScanInfo.ZoomScan,
                SIMScan     = thermoScanInfo.SIMScan,
                MRMScanType = thermoScanInfo.MRMScanType
            };

            // Typically .EventNumber is 1 for the parent-ion scan; 2 for 1st frag scan, 3 for 2nd frag scan, etc.
            // This resets for each new parent-ion scan
            scanInfo.FragScanInfo.FragScanNumber = thermoScanInfo.EventNumber - 1;

            // The .EventNumber value is sometimes wrong; need to check for this
            // For example, if the dataset only has MS2 scans and no parent-ion scan, .EventNumber will be 2 for every MS2 scan
            if (scanList.FragScans.Count > 0)
            {
                var prevFragScan = scanList.FragScans[scanList.FragScans.Count - 1];
                if (prevFragScan.ScanNumber == scanInfo.ScanNumber - 1)
                {
                    if (scanInfo.FragScanInfo.FragScanNumber <= prevFragScan.FragScanInfo.FragScanNumber)
                    {
                        scanInfo.FragScanInfo.FragScanNumber = prevFragScan.FragScanInfo.FragScanNumber + 1;
                    }
                }
            }

            scanInfo.FragScanInfo.MSLevel = thermoScanInfo.MSLevel;

            if (scanInfo.MRMScanType != MRMScanTypeConstants.NotMRM)
            {
                // This is an MRM scan
                scanList.MRMDataPresent = true;

                scanInfo.MRMScanInfo = clsMRMProcessing.DuplicateMRMInfo(thermoScanInfo.MRMInfo, thermoScanInfo.ParentIonMZ);

                if (scanList.SurveyScans.Count == 0)
                {
                    // Need to add a "fake" survey scan that we can map this parent ion to
                    mLastNonZoomSurveyScanIndex = scanList.AddFakeSurveyScan();
                }
            }
            else
            {
                scanInfo.MRMScanInfo.MRMMassCount = 0;
            }

            scanInfo.LowMass  = thermoScanInfo.LowMass;
            scanInfo.HighMass = thermoScanInfo.HighMass;
            scanInfo.IsFTMS   = thermoScanInfo.IsFTMS;

            // Store the ScanEvent values in .ExtendedHeaderInfo
            StoreExtendedHeaderInfo(dataOutputHandler, scanInfo, thermoScanInfo.ScanEvents);

            // Store the collision mode and possibly the scan filter text
            scanInfo.FragScanInfo.CollisionMode = thermoScanInfo.CollisionMode;
            StoreExtendedHeaderInfo(dataOutputHandler, scanInfo, clsExtendedStatsWriter.EXTENDED_STATS_HEADER_COLLISION_MODE, thermoScanInfo.CollisionMode);
            if (mOptions.WriteExtendedStatsIncludeScanFilterText)
            {
                StoreExtendedHeaderInfo(dataOutputHandler, scanInfo, clsExtendedStatsWriter.EXTENDED_STATS_HEADER_SCAN_FILTER_TEXT, thermoScanInfo.FilterText);
            }

            if (mOptions.WriteExtendedStatsStatusLog)
            {
                // Store the StatusLog values in .ExtendedHeaderInfo
                StoreExtendedHeaderInfo(dataOutputHandler, scanInfo, thermoScanInfo.StatusLog, mOptions.StatusLogKeyNameFilterList);
            }

            scanList.FragScans.Add(scanInfo);
            var fragScanIndex = scanList.FragScans.Count - 1;

            scanList.AddMasterScanEntry(clsScanList.eScanTypeConstants.FragScan, fragScanIndex);

            // Note: Even if keepRawSpectra = False, we still need to load the raw data so that we can compute the noise level for the spectrum
            var msDataResolution = binningOptions.BinSize / sicOptions.CompressToleranceDivisorForDa;

            var success = LoadSpectraForThermoRawFile(
                xcaliburAccessor,
                spectraCache,
                scanInfo,
                sicOptions.SICPeakFinderOptions.MassSpectraNoiseThresholdOptions,
                clsMASIC.DISCARD_LOW_INTENSITY_MSMS_DATA_ON_LOAD,
                sicOptions.CompressMSMSSpectraData,
                msDataResolution,
                mKeepRawSpectra && mKeepMSMSSpectra);

            if (!success)
            {
                return(false);
            }

            SaveScanStatEntry(dataOutputHandler.OutputFileHandles.ScanStats, clsScanList.eScanTypeConstants.FragScan, scanInfo, sicOptions.DatasetID);

            if (thermoScanInfo.MRMScanType == MRMScanTypeConstants.NotMRM)
            {
                // This is not an MRM scan
                mParentIonProcessor.AddUpdateParentIons(scanList, mLastNonZoomSurveyScanIndex, thermoScanInfo.ParentIonMZ,
                                                        fragScanIndex, spectraCache, sicOptions);
            }
            else
            {
                // This is an MRM scan
                mParentIonProcessor.AddUpdateParentIons(scanList, mLastNonZoomSurveyScanIndex, thermoScanInfo.ParentIonMZ,
                                                        scanInfo.MRMScanInfo, spectraCache, sicOptions);
            }

            if (mLastNonZoomSurveyScanIndex >= 0)
            {
                var precursorScanNumber = scanList.SurveyScans[mLastNonZoomSurveyScanIndex].ScanNumber;

                // Compute the interference of the parent ion in the MS1 spectrum for this frag scan
                scanInfo.FragScanInfo.InterferenceScore = ComputeInterference(xcaliburAccessor, thermoScanInfo, precursorScanNumber);
            }

            return(true);
        }
Example #3
0
        public void TestScanConversions()
        {
            const double MZ_MINIMUM        = 100;
            const float  INTENSITY_MINIMUM = 10000;
            const float  SCAN_TIME_SCALAR  = 10;

            var scanList = new clsScanList();
            var oRand    = new Random();

            var intLastSurveyScanIndexInMasterSeqOrder = -1;

            // Populate scanList with example scan data

            for (var scanNumber = 1; scanNumber <= 1750; scanNumber++)
            {
                if (scanNumber % 10 == 0)
                {
                    // Add a survey scan
                    // If this is a mzXML file that was processed with ReadW, .ScanHeaderText and .ScanTypeName will get updated by UpdateMSXMLScanType
                    var newSurveyScan = new MASIC.clsScanInfo
                    {
                        ScanNumber           = scanNumber,
                        ScanTime             = scanNumber / SCAN_TIME_SCALAR,
                        ScanHeaderText       = string.Empty,
                        ScanTypeName         = "MS",
                        BasePeakIonMZ        = MZ_MINIMUM + oRand.NextDouble() * 1000,
                        BasePeakIonIntensity = INTENSITY_MINIMUM + (float)oRand.NextDouble() * 1000
                    };

                    // Survey scans typically lead to multiple parent ions; we do not record them here
                    newSurveyScan.FragScanInfo.ParentIonInfoIndex = -1;
                    newSurveyScan.TotalIonIntensity = newSurveyScan.BasePeakIonIntensity * (float)(0.25 + oRand.NextDouble() * 5);

                    // Determine the minimum positive intensity in this scan
                    newSurveyScan.MinimumPositiveIntensity = INTENSITY_MINIMUM;

                    // If this is a mzXML file that was processed with ReadW, then these values will get updated by UpdateMSXMLScanType
                    newSurveyScan.ZoomScan    = false;
                    newSurveyScan.SIMScan     = false;
                    newSurveyScan.MRMScanType = MRMScanTypeConstants.NotMRM;

                    newSurveyScan.LowMass  = MZ_MINIMUM;
                    newSurveyScan.HighMass = Math.Max(newSurveyScan.BasePeakIonMZ * 1.1, MZ_MINIMUM * 10);
                    newSurveyScan.IsFTMS   = false;

                    scanList.SurveyScans.Add(newSurveyScan);

                    var intLastSurveyScanIndex = scanList.SurveyScans.Count - 1;

                    scanList.AddMasterScanEntry(clsScanList.eScanTypeConstants.SurveyScan, intLastSurveyScanIndex);
                    intLastSurveyScanIndexInMasterSeqOrder = scanList.MasterScanOrderCount - 1;
                }
                else
                {
                    // If this is a mzXML file that was processed with ReadW, .ScanHeaderText and .ScanTypeName will get updated by UpdateMSXMLScanType
                    var newFragScan = new MASIC.clsScanInfo
                    {
                        ScanNumber           = scanNumber,
                        ScanTime             = scanNumber / SCAN_TIME_SCALAR,
                        ScanHeaderText       = string.Empty,
                        ScanTypeName         = "MSn",
                        BasePeakIonMZ        = MZ_MINIMUM + oRand.NextDouble() * 1000,
                        BasePeakIonIntensity = INTENSITY_MINIMUM + (float)oRand.NextDouble() * 1000
                    };

                    // 1 for the first MS/MS scan after the survey scan, 2 for the second one, etc.
                    newFragScan.FragScanInfo.FragScanNumber = (scanList.MasterScanOrderCount - 1) - intLastSurveyScanIndexInMasterSeqOrder;
                    newFragScan.FragScanInfo.MSLevel        = 2;

                    newFragScan.TotalIonIntensity = newFragScan.BasePeakIonIntensity * (float)(0.25 + oRand.NextDouble() * 2);

                    // Determine the minimum positive intensity in this scan
                    newFragScan.MinimumPositiveIntensity = INTENSITY_MINIMUM;

                    // If this is a mzXML file that was processed with ReadW, then these values will get updated by UpdateMSXMLScanType
                    newFragScan.ZoomScan    = false;
                    newFragScan.SIMScan     = false;
                    newFragScan.MRMScanType = MRMScanTypeConstants.NotMRM;

                    newFragScan.MRMScanInfo.MRMMassCount = 0;

                    newFragScan.LowMass  = MZ_MINIMUM;
                    newFragScan.HighMass = Math.Max(newFragScan.BasePeakIonMZ * 1.1, MZ_MINIMUM * 10);
                    newFragScan.IsFTMS   = false;

                    scanList.FragScans.Add(newFragScan);
                    scanList.AddMasterScanEntry(clsScanList.eScanTypeConstants.FragScan, scanList.FragScans.Count - 1);
                }
            }

            var scanNumScanConverter = new clsScanNumScanTimeConversion();

            RegisterEvents(scanNumScanConverter);

            // Convert absolute values
            // Scan 500, relative scan 0.5, and the scan at 30 minutes
            TestScanConversionToAbsolute(
                scanList, scanNumScanConverter,
                new KeyValuePair <int, int>(500, 500),
                new KeyValuePair <float, float>(0.5F, 876),
                new KeyValuePair <float, float>(30, 300));

            TestScanConversionToTime(
                scanList, scanNumScanConverter,
                new KeyValuePair <int, int>(500, 50),
                new KeyValuePair <float, float>(0.5F, 87.55F),
                new KeyValuePair <float, float>(30, 30));

            // Convert ranges
            // 50 scans wide, 10% of the run, and 5 minutes
            TestScanConversionToAbsolute(
                scanList, scanNumScanConverter,
                new KeyValuePair <int, int>(50, 50),
                new KeyValuePair <float, float>(0.1F, 176),
                new KeyValuePair <float, float>(5, 50));

            TestScanConversionToTime(
                scanList, scanNumScanConverter,
                new KeyValuePair <int, int>(50, 5),
                new KeyValuePair <float, float>(0.1F, 17.59F),
                new KeyValuePair <float, float>(5, 5));
        }
Example #4
0
        private bool ExtractXcaliburSurveyScan(
            XRawFileIO xcaliburAccessor,
            clsScanList scanList,
            clsSpectraCache spectraCache,
            clsDataOutput dataOutputHandler,
            clsSICOptions sicOptions,
            ThermoRawFileReader.clsScanInfo thermoScanInfo)
        {
            var scanInfo = new clsScanInfo()
            {
                ScanNumber               = thermoScanInfo.ScanNumber,
                ScanTime                 = (float)thermoScanInfo.RetentionTime,
                ScanHeaderText           = XRawFileIO.MakeGenericThermoScanFilter(thermoScanInfo.FilterText),
                ScanTypeName             = XRawFileIO.GetScanTypeNameFromThermoScanFilterText(thermoScanInfo.FilterText),
                BasePeakIonMZ            = thermoScanInfo.BasePeakMZ,
                BasePeakIonIntensity     = thermoScanInfo.BasePeakIntensity,
                TotalIonIntensity        = thermoScanInfo.TotalIonCurrent,
                MinimumPositiveIntensity = 0,        // This will be determined in LoadSpectraForThermoRawFile
                ZoomScan                 = thermoScanInfo.ZoomScan,
                SIMScan     = thermoScanInfo.SIMScan,
                MRMScanType = thermoScanInfo.MRMScanType,
                LowMass     = thermoScanInfo.LowMass,
                HighMass    = thermoScanInfo.HighMass,
                IsFTMS      = thermoScanInfo.IsFTMS
            };

            // Survey scans typically lead to multiple parent ions; we do not record them here
            scanInfo.FragScanInfo.ParentIonInfoIndex = -1;

            if (scanInfo.MRMScanType != MRMScanTypeConstants.NotMRM)
            {
                // This is an MRM scan
                scanList.MRMDataPresent = true;
            }

            if (scanInfo.SIMScan)
            {
                scanList.SIMDataPresent = true;
                var simKey = scanInfo.LowMass + "_" + scanInfo.HighMass;

                if (mSIMScanMapping.TryGetValue(simKey, out var simIndex))
                {
                    scanInfo.SIMIndex = simIndex;
                }
                else
                {
                    scanInfo.SIMIndex = mSIMScanMapping.Count;
                    mSIMScanMapping.Add(simKey, mSIMScanMapping.Count);
                }
            }

            // Store the ScanEvent values in .ExtendedHeaderInfo
            StoreExtendedHeaderInfo(dataOutputHandler, scanInfo, thermoScanInfo.ScanEvents);

            // Store the collision mode and possibly the scan filter text
            scanInfo.FragScanInfo.CollisionMode = thermoScanInfo.CollisionMode;
            StoreExtendedHeaderInfo(dataOutputHandler, scanInfo, clsExtendedStatsWriter.EXTENDED_STATS_HEADER_COLLISION_MODE, thermoScanInfo.CollisionMode);
            if (mOptions.WriteExtendedStatsIncludeScanFilterText)
            {
                StoreExtendedHeaderInfo(dataOutputHandler, scanInfo, clsExtendedStatsWriter.EXTENDED_STATS_HEADER_SCAN_FILTER_TEXT, thermoScanInfo.FilterText);
            }

            if (mOptions.WriteExtendedStatsStatusLog)
            {
                // Store the StatusLog values in .ExtendedHeaderInfo
                StoreExtendedHeaderInfo(dataOutputHandler, scanInfo, thermoScanInfo.StatusLog, mOptions.StatusLogKeyNameFilterList);
            }

            scanList.SurveyScans.Add(scanInfo);

            if (!scanInfo.ZoomScan)
            {
                mLastNonZoomSurveyScanIndex = scanList.SurveyScans.Count - 1;
            }

            scanList.AddMasterScanEntry(clsScanList.eScanTypeConstants.SurveyScan, scanList.SurveyScans.Count - 1);

            double msDataResolution;

            if (sicOptions.SICToleranceIsPPM)
            {
                // Define MSDataResolution based on the tolerance value that will be used at the lowest m/z in this spectrum, divided by sicOptions.CompressToleranceDivisorForPPM
                // However, if the lowest m/z value is < 100, then use 100 m/z
                if (thermoScanInfo.LowMass < 100)
                {
                    msDataResolution = clsParentIonProcessing.GetParentIonToleranceDa(sicOptions, 100) /
                                       sicOptions.CompressToleranceDivisorForPPM;
                }
                else
                {
                    msDataResolution = clsParentIonProcessing.GetParentIonToleranceDa(sicOptions, thermoScanInfo.LowMass) /
                                       sicOptions.CompressToleranceDivisorForPPM;
                }
            }
            else
            {
                msDataResolution = sicOptions.SICTolerance / sicOptions.CompressToleranceDivisorForDa;
            }

            // Note: Even if mKeepRawSpectra = False, we still need to load the raw data so that we can compute the noise level for the spectrum
            var success = LoadSpectraForThermoRawFile(
                xcaliburAccessor,
                spectraCache,
                scanInfo,
                sicOptions.SICPeakFinderOptions.MassSpectraNoiseThresholdOptions,
                clsMASIC.DISCARD_LOW_INTENSITY_MS_DATA_ON_LOAD,
                sicOptions.CompressMSSpectraData,
                msDataResolution,
                mKeepRawSpectra);

            if (!success)
            {
                return(false);
            }

            SaveScanStatEntry(dataOutputHandler.OutputFileHandles.ScanStats, clsScanList.eScanTypeConstants.SurveyScan, scanInfo, sicOptions.DatasetID);

            return(true);
        }