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);
                }
            }
        }