/// <summary>
        /// The run libray match workflow.
        /// </summary>
        /// <param name="target">
        /// The Target.
        /// </param>
        /// <returns>
        /// The <see cref="LibraryMatchResult"/>.
        /// </returns>
        private LibraryMatchResult RunLibrayMatchWorkflow(DriftTimeTarget target)
        {
            Trace.WriteLine("    Target: " + target.CorrespondingChemical);
            Trace.WriteLine("    Target Info: " + target.TargetDescriptor);

            // Generate Theoretical Isotopic Profile
            List<Peak> theoreticalIsotopicProfilePeakList = null;
            string empiricalFormula = target.CompositionWithAdduct.ToPlainString();
            ITheorFeatureGenerator featureGenerator = new JoshTheorFeatureGenerator();
            IsotopicProfile theoreticalIsotopicProfile = featureGenerator.GenerateTheorProfile(empiricalFormula, 1);
            theoreticalIsotopicProfilePeakList = theoreticalIsotopicProfile.Peaklist.Cast<Peak>().ToList();

            // Voltage grouping
            VoltageSeparatedAccumulatedXiCs accumulatedXiCs = new VoltageSeparatedAccumulatedXiCs(this.uimfReader, target.MassWithAdduct, this.Parameters.InitialSearchMassToleranceInPpm, target.NormalizedDriftTimeInMs, this.Parameters.DriftTimeToleranceInMs, this.Parameters.DriftTubeLengthInCm);

            foreach (VoltageGroup voltageGroup in accumulatedXiCs.Keys)
            {
                // TODO Verify the temperature, pressure and drift tube voltage of the voltage group
                // Because we don't record TVP info in the AMT library. we can't verify it yet, so stick with last voltage group.
                if (IMSUtil.IsLastVoltageGroup(voltageGroup, this.NumberOfFrames))
                {
                    double globalMaxIntensity = IMSUtil.MaxIntensityAfterFrameAccumulation(voltageGroup, this.uimfReader);

                    Trace.WriteLine(string.Format("    Temperature/Pressure/Voltage Adjusted Drift time: {0:F4} ms", IMSUtil.DeNormalizeDriftTime(target.NormalizedDriftTimeInMs, voltageGroup)));

                    // Find peaks using multidimensional peak finder.
                    List<IntensityPoint> intensityPoints = accumulatedXiCs[voltageGroup].IntensityPoints;
                    List<FeatureBlob> featureBlobs = PeakFinding.FindPeakUsingWatershed(intensityPoints, this.smoother, this.Parameters.FeatureFilterLevel);
                    List<StandardImsPeak> standardPeaks = featureBlobs.Select(featureBlob => new StandardImsPeak(featureBlob, this.uimfReader, voltageGroup, target.MassWithAdduct, this.Parameters.InitialSearchMassToleranceInPpm)).ToList();

                    // Score features
                    IDictionary<StandardImsPeak, PeakScores> scoresTable = new Dictionary<StandardImsPeak, PeakScores>();
                    Trace.WriteLine(string.Format("    Voltage Group: {0:F4} V, [{1}-{2}]", voltageGroup.MeanVoltageInVolts, voltageGroup.FirstFrameNumber, voltageGroup.LastFrameNumber));

                    foreach (StandardImsPeak peak in standardPeaks)
                    {
                        PeakScores currentStatistics = FeatureScoreUtilities.ScoreFeature(
                            peak,
                            globalMaxIntensity,
                            this.uimfReader,
                            this.Parameters.InitialSearchMassToleranceInPpm,
                            this.Parameters.DriftTimeToleranceInMs,
                            voltageGroup,
                            this.NumberOfScans,
                            target,
                            IsotopicScoreMethod.Angle,
                            theoreticalIsotopicProfilePeakList);
                        scoresTable.Add(peak, currentStatistics);
                    }

                    // filter out features with Ims scans at 1% left or right.
                    Predicate<StandardImsPeak> scanPredicate = blob => FeatureFilters.FilterExtremeDriftTime(blob, (int)this.NumberOfScans);
                    Predicate<StandardImsPeak> shapeThreshold = blob => FeatureFilters.FilterBadPeakShape(blob, scoresTable[blob].PeakShapeScore, this.Parameters.PeakShapeThreshold);
                    Predicate<StandardImsPeak> isotopeThreshold = blob => FeatureFilters.FilterBadIsotopicProfile(blob, scoresTable[blob].IsotopicScore, this.Parameters.IsotopicThreshold);
                    Predicate<StandardImsPeak> massDistanceThreshold = blob => FeatureFilters.FilterHighMzDistance(blob, target, this.Parameters.MatchingMassToleranceInPpm);

                    // Print out candidate features that pass the intensity threshold.
                    foreach (StandardImsPeak peak in standardPeaks)
                    {
                        DriftTimeFeatureDistance distance = new DriftTimeFeatureDistance(target, peak, voltageGroup);

                        bool badScanRange = scanPredicate(peak);
                        bool badPeakShape = shapeThreshold(peak);
                        bool lowIsotopicAffinity = isotopeThreshold(peak);
                        bool lowMzAffinity = massDistanceThreshold(peak);

                        PeakScores currentStatistics = scoresTable[peak];
                        Trace.WriteLine(string.Empty);
                        Trace.WriteLine(string.Format("        Candidate feature found at drift time {0:F2} ms (scan number {1})",
                            peak.PeakApex.DriftTimeCenterInMs,
                            peak.PeakApex.DriftTimeCenterInScanNumber));
                        Trace.WriteLine(
                            string.Format(
                                "            M/Z: {0:F2} Dalton", peak.PeakApex.MzCenterInDalton));
                        Trace.WriteLine(
                            string.Format(
                                "            Drift time: {0:F2} ms (scan number {1})",
                                peak.PeakApex.DriftTimeCenterInMs,
                                peak.PeakApex.DriftTimeCenterInScanNumber));
                        Trace.WriteLine(
                            string.Format("            MzInDalton difference: {0:F2} ppm", distance.MassDifferenceInPpm));

                        Trace.WriteLine(
                            string.Format("            Drift time difference: {0:F4} ms", distance.DriftTimeDifferenceInMs));

                        Trace.WriteLine(string.Format("            Intensity Score: {0:F4}", currentStatistics.IntensityScore));
                        Trace.WriteLine(string.Format("            Peak Shape Score: {0:F4}", currentStatistics.PeakShapeScore));
                        Trace.WriteLine(string.Format("            Isotopic Score:  {0:F4}", currentStatistics.IsotopicScore));
                        Trace.WriteLine(string.Format("            AveragedPeakIntensities:  {0:F4}", peak.SummedIntensities));

                        string rejectionReason = badScanRange ? "        [Bad scan range] " : "        ";
                        rejectionReason += badPeakShape ? "[Bad Peak Shape] " : string.Empty;
                        rejectionReason += lowMzAffinity ? "[Inaccurate Mass] " : string.Empty;
                        rejectionReason += lowIsotopicAffinity ? "[Different Isotopic Profile] " : string.Empty;

                        if (badScanRange || lowIsotopicAffinity || badPeakShape)
                        {
                            Trace.WriteLine(rejectionReason);
                        }
                        else
                        {
                            Trace.WriteLine("        [Pass]");
                        }
                    }

                    standardPeaks.RemoveAll(scanPredicate);

                    standardPeaks.RemoveAll(massDistanceThreshold);

                    standardPeaks.RemoveAll(shapeThreshold);

                    standardPeaks.RemoveAll(isotopeThreshold);

                    if (standardPeaks.Count == 0)
                    {
                        Trace.WriteLine(string.Format("    [No Match]"));
                        Trace.WriteLine(string.Empty);
                        return new LibraryMatchResult(null, AnalysisStatus.Negative, null);
                    }

                    StandardImsPeak closestPeak = standardPeaks.First();
                    DriftTimeFeatureDistance shortestDistance = new DriftTimeFeatureDistance(target, standardPeaks.First(), voltageGroup);
                    foreach (var peak in standardPeaks)
                    {
                        DriftTimeFeatureDistance distance = new DriftTimeFeatureDistance(target, peak, voltageGroup);
                        if (distance.CompareTo(shortestDistance) < 0)
                        {
                            closestPeak = peak;
                        }
                    }

                    Trace.WriteLine(string.Format("    [Match]"));
                    Trace.WriteLine(string.Empty);
                    return new LibraryMatchResult(closestPeak, AnalysisStatus.Positive, shortestDistance);
                }
            }

            throw new Exception("No voltage groups in the Dataset match temerature, pressure, or drift tube voltage setting from the library. Matching failed.");
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="LibraryMatchResult"/> class.
 /// </summary>
 /// <param name="peak">
 /// The peak.
 /// </param>
 /// <param name="conlusion">
 /// The conlusion.
 /// </param>
 /// <param name="distance">
 /// The distance.
 /// </param>
 public LibraryMatchResult(StandardImsPeak peak, AnalysisStatus conlusion, DriftTimeFeatureDistance distance)
 {
     this.AnalysisStatus = conlusion;
     this.ImsFeature = peak;
     this.distance = distance;
 }