/// <summary> /// Assigns to each detected chromatogram peak the nearest MS1 spectrum and all related data dependent spectra and persists the spectra afterwards. /// </summary> /// <param name="spectrumDescriptors">The spectrum descriptors group by file identifier.</param> /// <param name="compoundIon2IsotopePeaksDictionary">The detected peaks for each compound ion as dictionary.</param> private void AssignAndPersistMassSpectra(IEnumerable <SpectrumDescriptor> spectrumDescriptors, IEnumerable <KeyValuePair <UnknownFeatureIonInstanceItem, List <ChromatogramPeakItem> > > compoundIon2IsotopePeaksDictionary) { var time = Stopwatch.StartNew(); SendAndLogTemporaryMessage("Assigning MS1 spectra..."); var defaultCharge = 1; if (spectrumDescriptors.First().ScanEvent.Polarity == PolarityType.Negative) { defaultCharge = -1; } var orderedSpectrumDescriptors = spectrumDescriptors .OrderBy(o => o.Header.RetentionTimeCenter) .ToList(); if (orderedSpectrumDescriptors.Any(a => a.ScanEvent.MSOrder == MSOrderType.MS1) == false) { SendAndLogErrorMessage("Exception, MS1 spectra not available (check spectrum selector node)."); return; } // Create peak details for each detected peak using the charge of the related compound ion var detectedPeakDetails = compoundIon2IsotopePeaksDictionary.SelectMany( sm => sm.Value.Select( s => new DetectedPeakDetails(s) { Charge = sm.Key.Charge == 0 ? defaultCharge : sm.Key.Charge })) .ToList(); DetectedPeakDetailsHelper.AssignMassSpectraToPeakApexes(orderedSpectrumDescriptors, detectedPeakDetails); SendAndLogMessage("Assigning MS1 spectra to chromatogram peak apexes finished after {0}", StringHelper.GetDisplayString(time.Elapsed)); time.Restart(); BuildSpectralTrees(orderedSpectrumDescriptors, detectedPeakDetails); SendAndLogTemporaryMessage("Persisting assigned spectra..."); // get spectrum ids of distinct spectra var distinctSpectrumIds = detectedPeakDetails.SelectMany(s => s.AssignedSpectrumDescriptors) .Select(s => s.Header.SpectrumID) .Distinct() .ToList(); // Divide spectrum ids into parts to reduce the memory foot print. Therefore it is necessary to interrupt the spectra reading, // because otherwise a database locked exception will be thrown when storing the spectra foreach (var spectrumIdsPartition in distinctSpectrumIds .Partition(ServerConfiguration.ProcessingPacketSize)) { // Retrieve mass spectra and create MassSpectrumItem's var distinctSpectra = ProcessingServices.SpectrumProcessingService.ReadSpectraFromCache(spectrumIdsPartition.ToList()) .Select( s => new MassSpectrumItem { ID = s.Header.SpectrumID, FileID = s.Header.FileID, Spectrum = s }) .ToList(); // Persist mass spectra PersistMassSpectra(distinctSpectra); } // Persists peak <-> mass spectrum connections var peaksToMassSpectrumConnectionList = new List <EntityConnectionItemList <ChromatogramPeakItem, MassSpectrumItem> >(detectedPeakDetails.Count); // Get connections between spectrum and chromatographic peak foreach (var item in detectedPeakDetails .Where(w => w.AssignedSpectrumDescriptors.Any())) { var connection = new EntityConnectionItemList <ChromatogramPeakItem, MassSpectrumItem>(item.Peak); peaksToMassSpectrumConnectionList.Add(connection); foreach (var spectrumDescriptor in item.AssignedSpectrumDescriptors) { connection.AddConnection(new MassSpectrumItem { ID = spectrumDescriptor.Header.SpectrumID, FileID = spectrumDescriptor.Header.FileID, // Omit mass spectrum here to reduce the memory footprint (only the IDs are required to persist the connections) }); } } // Persists peak <-> mass spectrum connections EntityDataService.ConnectItems(peaksToMassSpectrumConnectionList); SendAndLogMessage("Persisting spectra finished after {0}", StringHelper.GetDisplayString(time.Elapsed)); m_currentStep += 1; ReportTotalProgress((double)m_currentStep / m_numSteps); time.Stop(); }
/// <summary> /// Creates and persists XIC traces for all compound ion items. /// </summary> private void RebuildAndPersistCompoundIonTraces( int fileID, IEnumerable <SpectrumDescriptor> spectrumDescriptors, Dictionary <UnknownFeatureIonInstanceItem, List <ChromatogramPeakItem> > ionInstanceToPeaksMap) { SendAndLogTemporaryMessage("Re-creating XIC traces..."); var time = Stopwatch.StartNew(); // init XICPattern builder var xicPatternBuilder = new Func <List <ChromatogramPeakItem>, XICPattern>( peaks => { var masks = peaks.Select( peak => new XICMask( peak.IsotopeNumber, peak.Mass, MassTolerance.Value) ).ToList(); return(new XICPattern(masks)); }); // make XIC patterns var xicPatterns = ionInstanceToPeaksMap.ToDictionary(item => item.Key, item => xicPatternBuilder(item.Value)); // init XIC tracer var tracer = new XICTraceGenerator <UnknownFeatureIonInstanceItem>(xicPatterns); // get sprectrum IDs var spectrumIds = spectrumDescriptors .Where(s => s.ScanEvent.MSOrder == MSOrderType.MS1) .OrderBy(o => o.Header.RetentionTimeRange.LowerLimit) .Select(s => s.Header.SpectrumID) .ToList(); // add spectrum to tracer foreach (var spectrum in ProcessingServices.SpectrumProcessingService.ReadSpectraFromCache(spectrumIds)) { tracer.AddSpectrum(spectrum); } // make trace items var ionInstanceToTraceMap = new Dictionary <UnknownFeatureIonInstanceItem, XicTraceItem>(); foreach (var item in ionInstanceToPeaksMap) { // get trace var trace = tracer.GetXICTrace(item.Key, useFullRange: true, useFullRaster: false); // make XicTraceItem ionInstanceToTraceMap.Add( item.Key, new XicTraceItem { ID = EntityDataService.NextId <XicTraceItem>(), FileID = fileID, Trace = new TraceData(trace), }); } // make raster var rasterItem = MakeRetentionTimeRasterItem(tracer, fileID); // persist traces EntityDataService.InsertItems(ionInstanceToTraceMap.Values); EntityDataService.ConnectItems(ionInstanceToTraceMap.Select(s => Tuple.Create(s.Key, s.Value))); // persist raster EntityDataService.InsertItems(new[] { rasterItem }); EntityDataService.ConnectItems(ionInstanceToTraceMap.Select(s => Tuple.Create(s.Value, rasterItem))); time.Stop(); SendAndLogVerboseMessage("Re-creating and persisting {0} XIC traces took {1:F2} s.", ionInstanceToTraceMap.Values.Count, time.Elapsed.TotalSeconds); m_currentStep += 4; ReportTotalProgress((double)m_currentStep / m_numSteps); }