public DiffusionProfileDescriptor(ObservedPeak observation) { this.ArrivalTimeCenterLocation = observation.Peak.PeakCenterLocationOnArrivalTime(); this.ArrivalTimeDiffusionWidthInMs = observation.Peak.PeakApex.DriftTimeFullWidthHalfMaxHigherBondInMs - observation.Peak.PeakApex.DriftTimeFullWidthHalfMaxLowerBondInMs; this.MzCenterLocation = observation.Peak.PeakCenterLocationOnMz(); this.MzDiffusionWidthInPpm = Metrics.DaltonToPpm(observation.Peak.PeakApex.MzFullWidthHalfMaxHigh - observation.Peak.PeakApex.MzFullWidthHalfMaxLow, observation.Peak.PeakApex.MzCenterInDalton); }
/// <summary> /// Binary increment the selected peaks /// </summary> /// <param name="groups"> /// The groups. /// </param> /// <param name="selectedPeaks"> /// The selected peaks. /// </param> /// <param name="onIndex"> /// The on index. /// </param> /// <param name="graph"> /// The base peak map. /// </param> /// <param name="minFitPoints"> /// The min Fit Points. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> /// If incrementing overflows occurs/ all combinations were gone through. /// <exception cref="Exception"> /// </exception> private bool IncrementCombination(VoltageGroup[] groups, ref ObservedPeak[] selectedPeaks, int onIndex, ObservationTransitionGraph<IonTransition> graph) { int peakIndex; if (groups.Length != selectedPeaks.Length) { throw new ArgumentException("Votlage group does not match selected peaks"); } int length = selectedPeaks.Length; // Base case if (onIndex >= length) { return true; } ObservedPeak incrementPeak = selectedPeaks[onIndex]; VoltageGroup group = groups[onIndex]; IList<ObservedPeak> candidates = graph.FindPeaksInVoltageGroup(group).ToList(); // Empty peak if (incrementPeak == null) { peakIndex = -1; } else { peakIndex = candidates.IndexOf(incrementPeak); if (peakIndex < 0) { throw new Exception("The combination to be incremented is not in the base peak map space."); } } if (peakIndex + 1 >= candidates.Count) { if (selectedPeaks[onIndex] != null) { this.trackPointsCounter--; } selectedPeaks[onIndex] = null; return this.IncrementCombination(groups, ref selectedPeaks, onIndex + 1, graph); } else { if (peakIndex + 1 == 0) { this.trackPointsCounter++; } selectedPeaks[onIndex] = candidates[peakIndex + 1]; return false; } }
/// <summary> /// The increment combination. /// </summary> /// <param name="groups"> /// The groups. /// </param> /// <param name="selectedPeaks"> /// The selected peaks. /// </param> /// <param name="basePeakMap"> /// The base peak map. /// </param> /// <param name="minFitPoints"> /// The min Fit Points. /// </param> /// <returns> /// If the increment overflows the "counter"<see cref="bool"/>. /// </returns> private bool IncrementCombination(VoltageGroup[] groups, ref ObservedPeak[] selectedPeaks, ObservationTransitionGraph<IonTransition> graph, int minFitPoints) { bool overflow = false; // So-called do while loop overflow = this.IncrementCombination(groups, ref selectedPeaks, 0, graph); while (!overflow && this.trackPointsCounter < minFitPoints) { overflow = this.IncrementCombination(groups, ref selectedPeaks, 0, graph); } return overflow; }
/// <summary> /// The extract arrival time snap shot. /// </summary> /// <param name="peak"> /// The peak. /// </param> /// <returns> /// The <see cref="ArrivalTimeSnapShot"/>. /// </returns> private ArrivalTimeSnapShot ExtractArrivalTimeSnapShot(ObservedPeak peak) { ArrivalTimeSnapShot snapShot; snapShot.MeasuredArrivalTimeInMs = peak.Peak.PeakApex.DriftTimeCenterInMs; snapShot.PressureInTorr = peak.VoltageGroup.MeanPressureInTorr; snapShot.TemperatureInKelvin = peak.VoltageGroup.MeanTemperatureInKelvin; snapShot.DriftTubeVoltageInVolt = peak.VoltageGroup.MeanVoltageInVolts; return snapShot; }
/// <summary> /// The get drift time error in seconds for obsevation. /// </summary> /// <param name="peak"> /// The peak. /// </param> /// <returns> /// The <see cref="double"/>. /// </returns> public double GetDriftTimeErrorInSecondsForObsevation(ObservedPeak peak) { ContinuousXYPoint observationPoint = peak.ToContinuousXyPoint(this.useMeanTemperature, this.GetGlobalMeanTemperature()); double actualDriftTimeInSeconds = observationPoint.Y / 1000; double predictedDriftTimeInSeconds = this.fitLine.ModelPredictX2Y(observationPoint.X) / 1000; double error = Math.Abs(actualDriftTimeInSeconds - predictedDriftTimeInSeconds); return error; }
/// <summary> /// The add observation. /// </summary> /// <param name="peak"> /// The peak. /// </param> /// <returns> /// The <see cref="double"/>. /// </returns> public void AddObservation(ObservedPeak peak) { this.observedPeaks.Add(peak); this.fitLineNotComputed = true; if (!this.definedVoltageGroups.Contains(peak.VoltageGroup)) { this.definedVoltageGroups.Add(peak.VoltageGroup); } else { throw new ArgumentException("Voltage group is already defined track"); } }
/// <summary> /// The more likely than. /// </summary> /// <param name="a"> /// The a. /// </param> /// <param name="b"> /// The b. /// </param> /// <param name="likelihoodFunc"> /// The likelyhood func. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> public static bool MoreLikelyThanUntargeted(ObservedPeak a, ObservedPeak b, LikelihoodFunc likelihoodFunc) { int result = CompareFeatureScore(a.Statistics, b.Statistics, likelihoodFunc); return result > 0; }
/// <summary> /// The more likely than targeted. /// </summary> /// <param name="a"> /// The a. /// </param> /// <param name="b"> /// The b. /// </param> /// <param name="likelihoodFunc"> /// The likelihood func. /// </param> /// <param name="target"> /// The target. /// </param> /// <returns> /// The <see cref="bool"/>. /// </returns> /// <exception cref="NotImplementedException"> /// </exception> public static bool MoreLikelyThanTargeted(ObservedPeak a, ObservedPeak b, LikelihoodFunc likelihoodFunc, IImsTarget target) { throw new NotImplementedException(); }
private static double MapToPointSize(ObservedPeak observation) { double size = 8 * observation.Statistics.IntensityScore; return size < 2 ? 2 : size; }
/// <summary> /// The run molecule informed work flow. /// </summary> /// <param name="target"> /// The Target. /// </param> /// <param name="detailedVerbose"> /// </param> /// <returns> /// The <see cref="CrossSectionWorkflowResult"/>. /// </returns> public CrossSectionWorkflowResult RunCrossSectionWorkFlow(IImsTarget target, bool detailedVerbose = true) { // Reassign trace listener to print to console as well as the target directory. using (this.ResetTraceListenerToTarget(target, this.DatasetName)) { try { // Get the monoisotopic mass for viper, which is different from anything else. double viperFriendlyMass = target.MassWithAdduct; if (target.ChargeState < 0) { viperFriendlyMass = viperFriendlyMass + new Composition(0, target.ChargeState, 0, 0, 0).Mass; } else { viperFriendlyMass = viperFriendlyMass - new Composition(0, Math.Abs(target.ChargeState), 0, 0, 0).Mass; } // Generate Theoretical Isotopic Profile List<Peak> theoreticalIsotopicProfilePeakList = null; if (target.HasCompositionInfo) { int chargeStateAbs = Math.Abs(target.ChargeState); // Again this isotopic profile generator auto adds hydrogen for you depending on charge states. So here take it out. Composition compensatedComposition = target.CompositionWithAdduct - new Composition(0, chargeStateAbs, 0, 0, 0); string empiricalFormula = compensatedComposition.ToPlainString(); IsotopicProfile theoreticalIsotopicProfile = this.theoreticalFeatureGenerator.GenerateTheorProfile(empiricalFormula, chargeStateAbs); theoreticalIsotopicProfilePeakList = theoreticalIsotopicProfile.Peaklist.Cast<Peak>().ToList(); } Trace.WriteLine(string.Format("Dataset: {0}", this.uimfReader.UimfFilePath)); ReportTargetInfo(target, detailedVerbose); double targetMz = Math.Abs(target.MassWithAdduct / target.ChargeState); // Voltage grouping. Note that we only accumulate frames as needed. Accumulate frames globally is too costly. // Here we accumulate the XICs around target MZ. VoltageSeparatedAccumulatedXiCs accumulatedXiCs = new VoltageSeparatedAccumulatedXiCs(this.uimfReader, targetMz, this.Parameters.MzWindowHalfWidthInPpm, this.Parameters.DriftTubeLengthInCm); // Remove voltage groups that don't have sufficient frame accumulations IEnumerable<VoltageGroup> remainingVGs = accumulatedXiCs.Keys; IEnumerable<VoltageGroup> toBeRemoved = VoltageGroupFilters.RemoveVoltageGroupsWithInsufficentFrames(remainingVGs, this.Parameters.InsufficientFramesFraction); foreach (VoltageGroup voltageGroup in toBeRemoved) { accumulatedXiCs.Remove(voltageGroup); } // Perform feature detection and scoring and the given MzInDalton range on the accumulated XICs to get the base peaks. if (detailedVerbose) { Trace.WriteLine("Feature detection and scoring: "); } IList<VoltageGroup> rejectedVoltageGroups = new List<VoltageGroup>(); IList<ObservedPeak> filteredObservations = new List<ObservedPeak>(); IList<ObservedPeak> allObservations = new List<ObservedPeak>(); IDictionary<string, IList<ObservedPeak>> rejectedObservations = new Dictionary<string, IList<ObservedPeak>>(); var numberOfParticipatingVGs = accumulatedXiCs.Keys.Count; // Iterate through the features and perform filtering on isotopic affinity, intensity, drift time and peak shape. foreach (VoltageGroup voltageGroup in accumulatedXiCs.Keys) { double globalMaxIntensity = IMSUtil.MaxIntensityAfterFrameAccumulation(voltageGroup, this.uimfReader); List<StandardImsPeak> standardPeaks = this.FindPeaksBasedOnXIC(voltageGroup, accumulatedXiCs[voltageGroup], target); // Score features IDictionary<StandardImsPeak, PeakScores> scoresTable = new Dictionary<StandardImsPeak, PeakScores>(); if (detailedVerbose) { Trace.WriteLine( string.Format( " Voltage group: {0:F2} V, Frame {1}-{2}, {3:F2}K, {4:F2}Torr", voltageGroup.MeanVoltageInVolts, voltageGroup.FirstFrameNumber, voltageGroup.LastFrameNumber, voltageGroup.MeanTemperatureInKelvin, voltageGroup.MeanPressureInTorr)); } foreach (StandardImsPeak peak in standardPeaks) { PeakScores currentStatistics = FeatureScoreUtilities.ScoreFeature( peak, globalMaxIntensity, this.uimfReader, this.Parameters.MzWindowHalfWidthInPpm, this.Parameters.DriftTimeToleranceInMs, voltageGroup, this.NumberOfScans, target, IsotopicScoreMethod.Angle, theoreticalIsotopicProfilePeakList); scoresTable.Add(peak, currentStatistics); } double maxIntensity = standardPeaks.Max(x => x.SummedIntensities); Predicate<StandardImsPeak> relativeIntensityThreshold = imsPeak => FeatureFilters.FilterOnRelativeIntesity(imsPeak, maxIntensity, this.Parameters.RelativeIntensityPercentageThreshold); // filter out non Target peaks and noise. Predicate<StandardImsPeak> absoluteIntensityThreshold = imsPeak => FeatureFilters.FilterOnAbsoluteIntensity(imsPeak, scoresTable[imsPeak].IntensityScore, this.Parameters.AbsoluteIntensityThreshold); // filter out features with Ims scans at 1% left or right. Predicate<StandardImsPeak> scanPredicate = imsPeak => FeatureFilters.FilterExtremeDriftTime(imsPeak, this.NumberOfScans); // filter out features with bad peak shapes. Predicate<StandardImsPeak> shapeThreshold = imsPeak => FeatureFilters.FilterBadPeakShape(imsPeak, scoresTable[imsPeak].PeakShapeScore, this.Parameters.PeakShapeThreshold); // filter out features with distant isotopic profile. Predicate<StandardImsPeak> isotopeThreshold = imsPeak => FeatureFilters.FilterBadIsotopicProfile(imsPeak, scoresTable[imsPeak].IsotopicScore, this.Parameters.IsotopicThreshold); // Print out candidate features and how they were rejected. foreach (StandardImsPeak peak in standardPeaks) { PeakScores currentStatistics = scoresTable[peak]; string rejectionReason = ReportFeatureEvaluation( peak, currentStatistics, detailedVerbose, target, scanPredicate(peak), absoluteIntensityThreshold(peak), relativeIntensityThreshold(peak), shapeThreshold(peak), isotopeThreshold(peak)); bool pass = string.IsNullOrEmpty(rejectionReason); ObservedPeak analyzedPeak = new ObservedPeak(voltageGroup, peak, currentStatistics); if (pass) { filteredObservations.Add(analyzedPeak); } else { if (rejectedObservations.ContainsKey(rejectionReason)) { rejectedObservations[rejectionReason].Add(analyzedPeak); } else { rejectedObservations.Add(rejectionReason, new List<ObservedPeak>(){analyzedPeak}); } } allObservations.Add(analyzedPeak); } standardPeaks.RemoveAll(scanPredicate); standardPeaks.RemoveAll(absoluteIntensityThreshold); standardPeaks.RemoveAll(relativeIntensityThreshold); standardPeaks.RemoveAll(shapeThreshold); if (target.HasCompositionInfo) { standardPeaks.RemoveAll(isotopeThreshold); } if (standardPeaks.Count == 0) { if (detailedVerbose) { Trace.WriteLine(string.Format(" (All features were rejected in voltage group {0:F4} V)", voltageGroup.MeanVoltageInVolts)); Trace.WriteLine(string.Empty); Trace.WriteLine(string.Empty); } rejectedVoltageGroups.Add(voltageGroup); } // Rate the feature's VoltageGroupScoring score. VoltageGroupScoring score measures how likely the voltage group contains and detected the Target ion. voltageGroup.VoltageGroupScore = VoltageGroupScoring.ComputeVoltageGroupStabilityScore(voltageGroup); } // Remove voltage groups that were rejected foreach (VoltageGroup voltageGroup in rejectedVoltageGroups) { accumulatedXiCs.Remove(voltageGroup); } IEnumerable<ObservedPeak> rejectedPeaks = rejectedObservations.Values.SelectMany(x => x); // Report analysis as negative if (accumulatedXiCs.Keys.Count == 0) { CrossSectionWorkflowResult informedResult = CrossSectionWorkflowResult.CreateNegativeResult(rejectedPeaks, rejectedVoltageGroups, target, this.DatasetPath, this.OutputPath, this.SampleCollectionDate); ReportAnslysisResultAndMetrics(informedResult, detailedVerbose); return informedResult; } else { // Perform the data association algorithm. IIonTracker tracker = new CombinatorialIonTracker(3000); // Because for somereason we are not keeping track of drift tube length in UIMF...so we kind of have to go ask the instrument operator.. double driftTubeLength = this.Parameters.DriftTubeLengthInCm; AssociationHypothesis optimalAssociationHypothesis = tracker.FindOptimumHypothesis(filteredObservations, driftTubeLength, target, this.Parameters, numberOfParticipatingVGs); if (optimalAssociationHypothesis == null) { CrossSectionWorkflowResult negResult = CrossSectionWorkflowResult.CreateNegativeResult(rejectedPeaks, rejectedVoltageGroups, target, this.DatasetPath, this.OutputPath, this.SampleCollectionDate); ReportAnslysisResultAndMetrics(negResult, detailedVerbose); return negResult; } if (detailedVerbose) { Console.WriteLine("Writes QC plot of fitline to " + this.OutputPath); Trace.WriteLine(string.Empty); } string extension = (this.Parameters.GraphicsExtension.StartsWith(".")) ? this.Parameters.GraphicsExtension : "." + this.Parameters.GraphicsExtension; string outputPath = string.Format("{0}target_{1}_in_{2}_QA{3}", this.OutputPath, target.TargetDescriptor, this.DatasetName, extension); ImsInformedPlotter plotter = new ImsInformedPlotter(); plotter.PlotAssociationHypothesis(optimalAssociationHypothesis, outputPath, this.DatasetName, target, rejectedObservations); // Printout results if (detailedVerbose) { Trace.WriteLine("Target Data Association"); int count = 0; foreach (IsomerTrack track in optimalAssociationHypothesis.Tracks) { Trace.WriteLine(string.Format(" T{0}: ", count)); foreach (ObservedPeak peak in track.ObservedPeaks) { Trace.WriteLine(string.Format(" [td: {0:F4}ms, V: {1:F4}V, T: {2:F4}K, P: {3:F4}Torr]", peak.Peak.PeakApex.DriftTimeCenterInMs, peak.VoltageGroup.MeanVoltageInVolts, peak.VoltageGroup.MeanTemperatureInKelvin, peak.VoltageGroup.MeanPressureInTorr)); } count++; Trace.WriteLine(""); } Trace.WriteLine(""); } // Remove outliers with high influence. // FitLine.RemoveOutliersAboveThreshold(3, minFitPoints); // Remove outliers until min fit point is reached or good R2 is achieved. // while (TrackFilter.IsLowR2(FitLine.RSquared) && FitLine.FitPointCollection.Count > minFitPoints) // { // FitLine.RemoveOutlierWithHighestCookDistance(minFitPoints); // } // Remove the voltage considered outliers // foreach (VoltageGroup voltageGroup in accumulatedXiCs.Keys.Where(p => FitLine.OutlierCollection.Contains(p.FitPoint)).ToList()) // { // accumulatedXiCs.Remove(voltageGroup); // } CrossSectionWorkflowResult informedResult = CrossSectionWorkflowResult.CreateResultFromAssociationHypothesis( this.Parameters, optimalAssociationHypothesis, target, accumulatedXiCs.Keys, allObservations, this.DatasetPath, this.OutputPath, this.SampleCollectionDate, viperFriendlyMass ); ReportAnslysisResultAndMetrics(informedResult, detailedVerbose); Trace.Listeners.Clear(); return informedResult; } } catch (Exception e) { // Print result Trace.WriteLine(e.Message); Trace.WriteLine(e.StackTrace); Trace.Listeners.Clear(); Trace.Close(); Trace.Listeners.Clear(); // create the error result return CrossSectionWorkflowResult.CreateErrorResult(target, this.DatasetName, this.DatasetPath, this.OutputPath, this.SampleCollectionDate); } } }