Esempio n. 1
0
        public override bool TryLoadSpectrum(LibKey key, out SpectrumPeaksInfo spectrum)
        {
            spectrum = null;
            DbSpectrum[] spectra;
            if (!key.IsPrecursorKey)
            {
                spectra = GetSpectraByPeptide(null, key).ToArray();
            }
            else
            {
                spectra = GetSpectraByPrecursor(null, key.PrecursorMz.GetValueOrDefault()).ToArray();
                var keyRt = key.RetentionTime;
                if (keyRt.HasValue)
                {
                    spectra = spectra.Where(s => Equals(keyRt.Value, s.RetentionTime)).ToArray();
                }
            }
            if (!spectra.Any())
            {
                return(false);
            }

            var spec = spectra.First();
            var mi   = spec.Mzs.Select((t, i) => new SpectrumPeaksInfo.MI {
                Mz = spec.Mzs[i], Intensity = (float)spec.Intensities[i]
            });                                                                                                                        // CONSIDER(bspratt): annotation?

            spectrum = new SpectrumPeaksInfo(mi.ToArray());
            return(true);
        }
Esempio n. 2
0
        /// <summary>
        /// Returns a LibraryRankedSpectrumInfo
        /// </summary>
        /// <param name="info">The spectrum to be ranked</param>
        /// <param name="labelType">The IsotopeLabelType for the peptide in the library. This might be different from the
        /// LabelType on the GroupDocNode.</param>
        /// <param name="groupDocNode">The Transition Group from the user's document</param>
        /// <param name="settings"></param>
        /// <param name="lookupSequence"></param>
        /// <param name="lookupMods"></param>
        /// <param name="charges">The set of charges that the user is choosing to show in the spectrum viewer.</param>
        /// <param name="types">The set of ion types to be displayed</param>
        /// <param name="rankCharges">The set of charges that are enabled in the document's Transition Settings</param>
        /// <param name="rankTypes">The set of ion types in the user's transition settings</param>
        /// <param name="score">the score to assign to the spectrum. If it is null, then the spectrum gets the score from transition group's LibInfo</param>
        /// <param name="useFilter">true if this list is being generated in order to show the filtered list of potential transitions</param>
        /// <param name="matchAll">true if peaks matched peaks should be given a list of all of the ion types that they match, instead
        /// of only being annotated with the first matching one</param>
        /// <param name="minPeaks">The minimum number of peaks to match, or -1 to match as many as possible</param>
        /// <returns></returns>
        public static LibraryRankedSpectrumInfo RankSpectrum(SpectrumPeaksInfo info,
                                                             IsotopeLabelType labelType,
                                                             TransitionGroupDocNode groupDocNode, SrmSettings settings,
                                                             Target lookupSequence, ExplicitMods lookupMods,
                                                             IEnumerable <Adduct> charges, IEnumerable <IonType> types,
                                                             IEnumerable <Adduct> rankCharges, IEnumerable <IonType> rankTypes,
                                                             double?score, bool useFilter, bool matchAll, int minPeaks)
        {
            var targetInfo     = new TargetInfo(labelType, groupDocNode, lookupSequence, lookupMods);
            var fragmentFilter = new FragmentFilter(settings.TransitionSettings, rankCharges, rankTypes).ChangeMatchAll(matchAll);

            if (!useFilter)
            {
                bool isProteomic = groupDocNode.TransitionGroup.IsProteomic;
                fragmentFilter = fragmentFilter.ChangeUseFilter(false);
                fragmentFilter = fragmentFilter
                                 .ChangeAdductsToDisplay(charges ?? GetRanked(fragmentFilter.RankedAdducts,
                                                                              isProteomic
                                                    ? Transition.DEFAULT_PEPTIDE_CHARGES
                                                    : Transition.DEFAULT_MOLECULE_CHARGES));
                fragmentFilter = fragmentFilter.ChangeIonTypesToDisplay(
                    types ?? GetRanked(fragmentFilter.RankedIonTypes,
                                       isProteomic ? Transition.PEPTIDE_ION_TYPES : Transition.MOLECULE_ION_TYPES));
                fragmentFilter = fragmentFilter.ChangeMatchAll(true);
            }
            else
            {
                if (null != charges)
                {
                    fragmentFilter = fragmentFilter.ChangeAdductsToDisplay(charges);
                }

                if (null != types)
                {
                    fragmentFilter = fragmentFilter.ChangeIonTypesToDisplay(types);
                }
            }
            bool limitRanks =
                groupDocNode.IsCustomIon && // For small molecules, cap the number of ranked ions displayed if we don't have any peak metadata
                groupDocNode.Transitions.Any(t => string.IsNullOrEmpty(t.FragmentIonName));

            if (limitRanks)
            {
                fragmentFilter = fragmentFilter.ChangeRankLimit(settings.TransitionSettings.Libraries.IonCount);
            }
            // If no library filtering will happen, return all rankings for view in the UI
            if (!useFilter || fragmentFilter.LibraryPick == TransitionLibraryPick.none)
            {
                if (fragmentFilter.LibraryPick == TransitionLibraryPick.none)
                {
                    fragmentFilter = fragmentFilter.ChangeLibraryPick(TransitionLibraryPick.all);
                }
                fragmentFilter = fragmentFilter.ChangeFragmentMatchCount(null);
            }

            var spectrumRanker = new SpectrumRanker(targetInfo, settings, fragmentFilter);

            return(spectrumRanker.RankSpectrum(info, minPeaks, score));
        }
 public LibraryRankedSpectrumInfo(SpectrumPeaksInfo info,
                                  IsotopeLabelType labelType, TransitionGroup group,
                                  SrmSettings settings, string lookupSequence, ExplicitMods lookupMods,
                                  IEnumerable <int> charges, IEnumerable <IonType> types,
                                  IEnumerable <int> rankCharges, IEnumerable <IonType> rankTypes)
     : this(info, labelType, group, settings, lookupSequence, lookupMods,
            charges, types, rankCharges, rankTypes, false, true, -1)
 {
 }
Esempio n. 4
0
 public LibraryRankedSpectrumInfo(SpectrumPeaksInfo info,
                                  IsotopeLabelType labelType, TransitionGroupDocNode group,
                                  SrmSettings settings, Target lookupSequence, ExplicitMods lookupMods,
                                  IEnumerable <Adduct> charges, IEnumerable <IonType> types,
                                  IEnumerable <Adduct> rankCharges, IEnumerable <IonType> rankTypes, double?score)
     : this(info, labelType, group, settings, lookupSequence, lookupMods,
            charges, types, rankCharges, rankTypes, score, false, true, -1)
 {
 }
Esempio n. 5
0
 private static LibraryRankedSpectrumInfo MakeLibraryRankedSpectrumInfo(SpectrumPeaksInfo info,
                                                                        IsotopeLabelType labelType,
                                                                        TransitionGroupDocNode groupDocNode, SrmSettings settings,
                                                                        Target lookupSequence, ExplicitMods lookupMods,
                                                                        IEnumerable <Adduct> charges, IEnumerable <IonType> types,
                                                                        IEnumerable <Adduct> rankCharges, IEnumerable <IonType> rankTypes,
                                                                        double?score, bool useFilter, bool matchAll, int minPeaks)
 {
     return(SpectrumRanker.RankSpectrum(info, labelType, groupDocNode, settings, lookupSequence, lookupMods,
                                        charges, types, rankCharges, rankTypes, score, useFilter, matchAll, minPeaks));
 }
 public LibraryRankedSpectrumInfo(SpectrumPeaksInfo info, IsotopeLabelType labelType,
                                  TransitionGroup group, SrmSettings settings, ExplicitMods lookupMods,
                                  bool useFilter, int minPeaks)
     : this(info, labelType, group, settings, group.Peptide.Sequence, lookupMods,
            null, // charges
            null, // types
            // ReadOnlyCollection enumerators are too slow, and show under a profiler
            settings.TransitionSettings.Filter.ProductCharges.ToArray(),
            settings.TransitionSettings.Filter.IonTypes.ToArray(),
            useFilter, false, minPeaks)
 {
 }
Esempio n. 7
0
 public LibraryRankedSpectrumInfo(SpectrumPeaksInfo info, IsotopeLabelType labelType,
                                  TransitionGroupDocNode group, SrmSettings settings, ExplicitMods lookupMods,
                                  bool useFilter, int minPeaks)
     : this(info, labelType, group, settings, group.Peptide.Target, lookupMods,
            null, // charges
            null, // types
            // ReadOnlyCollection enumerators are too slow, and show under a profiler
            group.IsCustomIon ? settings.TransitionSettings.Filter.SmallMoleculeFragmentAdducts.ToArray() : settings.TransitionSettings.Filter.PeptideProductCharges.ToArray(),
            group.IsCustomIon ? settings.TransitionSettings.Filter.SmallMoleculeIonTypes.ToArray() : settings.TransitionSettings.Filter.PeptideIonTypes.ToArray(),
            null, useFilter, false, minPeaks)
 {
 }
Esempio n. 8
0
        public override bool TryLoadSpectrum(LibKey key, out SpectrumPeaksInfo spectrum)
        {
            BiblioSpectrumInfo info;

            if (_dictLibrary != null && _dictLibrary.TryGetValue(key, out info))
            {
                spectrum = new SpectrumPeaksInfo(ReadSpectrum(info));
                return(true);
            }

            spectrum = null;
            return(false);
        }
Esempio n. 9
0
        public override bool TryLoadSpectrum(LibKey key, out SpectrumPeaksInfo spectrum)
        {
            if (_dictLibrary != null)
            {
                foreach (var item in _dictLibrary.ItemsMatching(key.LibraryKey, true))
                {
                    spectrum = new SpectrumPeaksInfo(ReadSpectrum(item));
                    return(true);
                }
            }

            spectrum = null;
            return(false);
        }
Esempio n. 10
0
        public PrositMS2Spectrum(SrmSettings settings, PrositIntensityModel.PeptidePrecursorNCE peptidePrecursorNCE,
                                 int precursorIndex, PrositIntensityModel.PrositIntensityOutput prositIntensityOutput, IsotopeLabelType labelTypeOverride = null)
        {
            PeptidePrecursorNCE = peptidePrecursorNCE;
            Settings            = settings;
            var precursor = peptidePrecursorNCE.NodeGroup;
            var peptide   = peptidePrecursorNCE.NodePep;

            var calc     = settings.GetFragmentCalc(IsotopeLabelType.light, peptide.ExplicitMods);
            var ionTable = calc.GetFragmentIonMasses(peptide.Target); // TODO: get mods and pass them as explicit mods above?
            var ions     = ionTable.GetLength(1);

            var mis = new List <SpectrumPeaksInfo.MI>(ions * PrositConstants.IONS_PER_RESIDUE);

            for (int i = 0; i < ions; ++i)
            {
                var intensities = prositIntensityOutput.OutputRows[precursorIndex].Intensities[i].Intensities
                                  .Select(ReLu).ToArray();
                var yMIs = CalcMIs(ionTable[IonType.y, ions - i - 1], intensities, 0);
                var bMIs = CalcMIs(ionTable[IonType.b, i], intensities, PrositConstants.IONS_PER_RESIDUE / 2);
                mis.AddRange(yMIs);
                mis.AddRange(bMIs);
            }

            var maxIntensity = mis.Max(mi => mi.Intensity);

            // Max Norm
            for (int i = 0; i < mis.Count; ++i)
            {
                mis[i] = new SpectrumPeaksInfo.MI {
                    Mz = mis[i].Mz, Intensity = mis[i].Intensity / maxIntensity
                }
            }
            ;

            SpectrumPeaks = new SpectrumPeaksInfo(mis.ToArray());
        }
Esempio n. 11
0
        public bool TryLoadSpectrum(string sequence, int charge, ExplicitMods mods,
            out IsotopeLabelType type, out SpectrumPeaksInfo spectrum)
        {
            var libraries = PeptideSettings.Libraries;
            foreach (var typedSequence in GetTypedSequences(sequence, mods))
            {
                var key = new LibKey(typedSequence.ModifiedSequence, charge);
                if (libraries.TryLoadSpectrum(key, out spectrum))
                {
                    type = typedSequence.LabelType;
                    return true;
                }
            }

            type = IsotopeLabelType.light;
            spectrum = null;
            return false;
        }
        private void GetSmallMoleculeFragments(LibKey key, TransitionGroupDocNode nodeGroupMatched, SpectrumPeaksInfo spectrum,
                                               IList <DocNode> transitionDocNodes)
        {
            // We usually don't know actual charge of fragments in the library, so just note + or - if
            // there are no peak annotations containing that info
            var fragmentCharge = key.Adduct.AdductCharge < 0 ? Adduct.M_MINUS : Adduct.M_PLUS;
            // Get list of possible transitions based on library spectrum
            var transitionsUnranked = new List <DocNode>();

            foreach (var peak in spectrum.Peaks)
            {
                transitionsUnranked.Add(TransitionFromPeakAndAnnotations(key, nodeGroupMatched, fragmentCharge, peak, null));
            }
            var nodeGroupUnranked = (TransitionGroupDocNode)nodeGroupMatched.ChangeChildren(transitionsUnranked);
            // Filter again, retain only those with rank info,  or at least an interesting name
            SpectrumHeaderInfo groupLibInfo = null;
            var transitionRanks             = new Dictionary <double, LibraryRankedSpectrumInfo.RankedMI>();

            nodeGroupUnranked.GetLibraryInfo(Settings, ExplicitMods.EMPTY, true, ref groupLibInfo, transitionRanks);
            foreach (var ranked in transitionRanks)
            {
                transitionDocNodes.Add(TransitionFromPeakAndAnnotations(key, nodeGroupMatched, fragmentCharge, ranked.Value.MI, ranked.Value.Rank));
            }
            // And add any unranked that have names to display
            foreach (var unrankedT in nodeGroupUnranked.Transitions)
            {
                var unranked = unrankedT;
                if (!string.IsNullOrEmpty(unranked.Transition.CustomIon.Name) &&
                    !transitionDocNodes.Any(t => t is TransitionDocNode && unranked.Transition.Equivalent(((TransitionDocNode)t).Transition)))
                {
                    transitionDocNodes.Add(unranked);
                }
            }
        }
Esempio n. 13
0
        public override bool TryLoadSpectrum(LibKey key, out SpectrumPeaksInfo spectrum)
        {
            BiblioSpectrumInfo info;
            if (_dictLibrary != null && _dictLibrary.TryGetValue(key, out info))
            {
                spectrum = new SpectrumPeaksInfo(ReadSpectrum(info));
                return true;
            }

            spectrum = null;
            return false;
        }
Esempio n. 14
0
        public static ICollection <DbRefSpectraPeakAnnotations> Create(DbRefSpectra refSpectra, SpectrumPeaksInfo peaks)
        {
            List <DbRefSpectraPeakAnnotations> resultList = null;

            // Each peak may have more than one annotation
            if (peaks.Peaks.Any(p => p.Annotations != null && p.Annotations.Any(a => !SpectrumPeakAnnotation.IsNullOrEmpty(a))))
            {
                resultList = new List <DbRefSpectraPeakAnnotations>();
                var i = 0;
                foreach (var peak in peaks.Peaks)
                {
                    if (peak.Annotations != null)
                    {
                        foreach (var annotation in peak.Annotations.Where(a => !SpectrumPeakAnnotation.IsNullOrEmpty(a)))
                        {
                            var result = new DbRefSpectraPeakAnnotations
                            {
                                RefSpectra    = refSpectra,
                                PeakIndex     = i,
                                Name          = annotation.Ion.Name,
                                Formula       = annotation.Ion.NeutralFormula,
                                InchiKey      = annotation.Ion.AccessionNumbers.GetInChiKey(),
                                OtherKeys     = annotation.Ion.AccessionNumbers.GetNonInChiKeys(),
                                Adduct        = annotation.Ion.Adduct.ToString(),
                                Charge        = annotation.Ion.Adduct.AdductCharge,
                                Comment       = annotation.Comment,
                                mzTheoretical = annotation.Ion.MonoisotopicMassMz,
                                mzObserved    = peak.Mz
                            };
                            resultList.Add(result);
                        }
                    }
                    i++;
                }
            }
            return(resultList);
        }
Esempio n. 15
0
 private static byte[] MZsToBytes(SpectrumPeaksInfo.MI[] peaks)
 {
     const int sizeMz = sizeof(double);
     byte[] peakMZs = new byte[peaks.Length * sizeMz];
     for (int i = 0; i < peaks.Length; i++)
     {
         int offset = i * sizeMz;
         Array.Copy(BitConverter.GetBytes(peaks[i].Mz), 0, peakMZs, offset, sizeMz);
     }
     return peakMZs.Compress();
 }
Esempio n. 16
0
 private static byte[] IntensitiesToBytes(SpectrumPeaksInfo.MI[] peaks)
 {
     const int sizeInten = sizeof(float);
     byte[] peakIntens = new byte[peaks.Length * sizeInten];
     for (int i = 0; i < peaks.Length; i++)
     {
         int offset = i*sizeInten;
         Array.Copy(BitConverter.GetBytes(peaks[i].Intensity), 0, peakIntens, offset, sizeInten);
     }
     return peakIntens.Compress();
 }
Esempio n. 17
0
        public bool TryLoadSpectrum(LibKey key, out SpectrumPeaksInfo spectrum)
        {
            Assume.IsTrue(IsLoaded);

            foreach (Library lib in _libraries)
            {
                if (lib != null && lib.TryLoadSpectrum(key, out spectrum))
                    return true;
            }
            spectrum = null;
            return false;
        }
Esempio n. 18
0
        private LibraryRankedSpectrumInfo(SpectrumPeaksInfo info, IsotopeLabelType labelType,
                                          TransitionGroupDocNode groupDocNode, SrmSettings settings,
                                          Target lookupSequence, ExplicitMods lookupMods,
                                          IEnumerable <Adduct> charges, IEnumerable <IonType> types,
                                          IEnumerable <Adduct> rankCharges, IEnumerable <IonType> rankTypes,
                                          double?score, bool useFilter, bool matchAll, int minPeaks)
        {
            LabelType = labelType;

            // Avoid ReSharper multiple enumeration warning
            var rankChargesArray = rankCharges.ToArray();
            var rankTypesArray   = rankTypes.ToArray();

            TransitionGroup group       = groupDocNode.TransitionGroup;
            bool            isProteomic = group.IsProteomic;

            if (score == null && groupDocNode.HasLibInfo && groupDocNode.LibInfo is BiblioSpecSpectrumHeaderInfo libInfo)
            {
                Score = libInfo.Score;
            }
            else
            {
                Score = score;
            }

            if (!useFilter)
            {
                if (charges == null)
                {
                    charges = GetRanked(rankChargesArray, isProteomic ? Transition.DEFAULT_PEPTIDE_CHARGES : Transition.DEFAULT_MOLECULE_CHARGES);
                }
                if (types == null)
                {
                    types = GetRanked(rankTypesArray, isProteomic ? Transition.PEPTIDE_ION_TYPES : Transition.MOLECULE_ION_TYPES);
                }
                matchAll = true;
            }

            bool limitRanks =
                groupDocNode.IsCustomIon && // For small molecules, cap the number of ranked ions displayed if we don't have any peak metadata
                groupDocNode.Transitions.Any(t => string.IsNullOrEmpty(t.FragmentIonName));

            RankParams rp = new RankParams
            {
                sequence        = lookupSequence,
                precursorAdduct = group.PrecursorAdduct,
                adducts         = charges ?? rankCharges,
                types           = types ?? rankTypes,
                matchAll        = matchAll,
                rankCharges     = rankChargesArray.Select(a => Math.Abs(a.AdductCharge)).ToArray(),
                rankTypes       = rankTypesArray,
                // Precursor isotopes will not be included in MS/MS, if they will be filtered
                // from MS1
                excludePrecursorIsotopes = settings.TransitionSettings.FullScan.IsEnabledMs,
                tranSettings             = settings.TransitionSettings,
                rankLimit = limitRanks ? settings.TransitionSettings.Libraries.IonCount : (int?)null
            };

            // Get necessary mass calculators and masses
            var calcMatchPre = settings.GetPrecursorCalc(labelType, lookupMods);
            var calcMatch    = isProteomic ? settings.GetFragmentCalc(labelType, lookupMods) : settings.GetDefaultFragmentCalc();
            var calcPredict  = isProteomic ? settings.GetFragmentCalc(group.LabelType, lookupMods) : calcMatch;

            if (isProteomic && rp.sequence.IsProteomic)
            {
                rp.precursorMz    = SequenceMassCalc.GetMZ(calcMatchPre.GetPrecursorMass(rp.sequence), rp.precursorAdduct);
                rp.massPreMatch   = calcMatch.GetPrecursorFragmentMass(rp.sequence);
                rp.massesMatch    = calcMatch.GetFragmentIonMasses(rp.sequence);
                rp.knownFragments = null;
            }
            else if (!isProteomic && !rp.sequence.IsProteomic)
            {
                string isotopicForumla;
                rp.precursorMz  = SequenceMassCalc.GetMZ(calcMatchPre.GetPrecursorMass(rp.sequence.Molecule, null, rp.precursorAdduct, out isotopicForumla), rp.precursorAdduct);
                rp.massPreMatch = calcMatch.GetPrecursorFragmentMass(rp.sequence);
                // rp.massesMatch = calcMatch.GetFragmentIonMasses(rp.molecule); CONSIDER, for some molecule types someday?
                // For small molecules we can't predict fragmentation, so just use those we have
                // Older Resharper code inspection implementations insist on warning here
                // Resharper disable PossibleMultipleEnumeration
                var existing = groupDocNode.Transitions.Where(tran => tran.Transition.IsNonPrecursorNonReporterCustomIon()).Select(t => t.Transition.CustomIon.GetMass(MassType.Monoisotopic)).ToArray();
                rp.massesMatch = new IonTable <TypedMass>(IonType.custom, existing.Length);
                for (var i = 0; i < existing.Length; i++)
                {
                    rp.massesMatch[IonType.custom, i] = existing[i];
                }
                // Resharper restore PossibleMultipleEnumeration
                rp.knownFragments = groupDocNode.Transitions.Where(tran => tran.Transition.IsNonPrecursorNonReporterCustomIon()).Select(t =>
                                                                                                                                        new KnownFragment
                {
                    Adduct = t.Transition.Adduct,
                    Name   = t.GetFragmentIonName(CultureInfo.CurrentCulture, settings.TransitionSettings.Libraries.IonMatchTolerance),
                    Mz     = t.Mz
                }).ToList();
            }
            else
            {
                rp.precursorMz    = 0.0;
                rp.massPreMatch   = TypedMass.ZERO_MONO_MASSH;
                rp.massesMatch    = IonTable <TypedMass> .EMPTY;
                rp.knownFragments = null;
            }
            rp.massPrePredict = rp.massPreMatch;
            rp.massesPredict  = rp.massesMatch;
            if (!ReferenceEquals(calcPredict, calcMatch))
            {
                rp.massPrePredict = calcPredict.GetPrecursorFragmentMass(rp.sequence);
                if (rp.sequence.IsProteomic) // CONSIDER - eventually we may be able to predict fragments for small molecules?
                {
                    rp.massesPredict = calcPredict.GetFragmentIonMasses(rp.sequence);
                }
            }

            // Get values of interest from the settings.
            var tranSettings = settings.TransitionSettings;
            var predict      = tranSettings.Prediction;
            var filter       = tranSettings.Filter;
            var libraries    = tranSettings.Libraries;
            var instrument   = tranSettings.Instrument;

            // Get potential losses to all fragments in this peptide
            rp.massType        = predict.FragmentMassType;
            rp.potentialLosses = TransitionGroup.CalcPotentialLosses(rp.sequence,
                                                                     settings.PeptideSettings.Modifications, lookupMods,
                                                                     rp.massType);

            // Create arrays because ReadOnlyCollection enumerators are too slow
            // In some cases these collections must be enumerated for every ion
            // allowed in the library specturm.
            rp.startFinder = filter.FragmentRangeFirst;
            rp.endFinder   = filter.FragmentRangeLast;

            // Get library settings
            Tolerance    = libraries.IonMatchTolerance;
            rp.tolerance = Tolerance;
            rp.pick      = tranSettings.Libraries.Pick;
            int ionMatchCount = libraries.IonCount;

            // If no library filtering will happen, return all rankings for view in the UI
            if (!useFilter || rp.pick == TransitionLibraryPick.none)
            {
                if (rp.pick == TransitionLibraryPick.none)
                {
                    rp.pick = TransitionLibraryPick.all;
                }
                ionMatchCount = -1;
            }

            // Get instrument settings
            rp.minMz = instrument.MinMz;
            rp.maxMz = instrument.MaxMz;

            // Get the library spectrum mass-intensity pairs
            IList <SpectrumPeaksInfo.MI> listMI = info.Peaks;

            // Because sorting and matching observed ions with predicted
            // ions appear as bottlenecks in a profiler, a minimum number
            // of peaks may be supplied to allow the use of a 2-phase linear
            // filter that can significantly reduce the number of peaks
            // needing the O(n*log(n)) sorting and the O(n*m) matching.

            int   len             = listMI.Count;
            float intensityCutoff = 0;

            if (minPeaks != -1)
            {
                // Start searching for good cut-off at mean intensity.
                double totalIntensity = info.Intensities.Sum();

                FindIntensityCutoff(listMI, 0, (float)(totalIntensity / len) * 2, minPeaks, 1, ref intensityCutoff, ref len);
            }

            // Create filtered peak array storing original index for m/z ordering
            // to avoid needing to sort to return to this order.
            RankedMI[] arrayRMI = new RankedMI[len];
            // Detect when m/z values are out of order, and use the expensive sort
            // by m/z to correct this.
            double lastMz = double.MinValue;
            bool   sortMz = false;

            for (int i = 0, j = 0, lenOrig = listMI.Count; i < lenOrig; i++)
            {
                SpectrumPeaksInfo.MI mi = listMI[i];
                if (mi.Intensity >= intensityCutoff || intensityCutoff == 0)
                {
                    arrayRMI[j] = new RankedMI(mi, j);
                    j++;
                }
                if (ionMatchCount == -1)
                {
                    if (mi.Mz < lastMz)
                    {
                        sortMz = true;
                    }
                    lastMz = mi.Mz;
                }
            }

            // The one expensive sort is used to determine rank order
            // by intensity, or m/z in case of a tie.
            Array.Sort(arrayRMI, OrderIntensityDesc);

            RankedMI[] arrayResult = new RankedMI[ionMatchCount != -1 ? ionMatchCount : arrayRMI.Length];

            foreach (RankedMI rmi in arrayRMI)
            {
                rmi.CalculateRank(rp);

                // If not filtering for only the highest ionMatchCount ranks
                if (ionMatchCount == -1)
                {
                    // Put the ranked record back where it started in the
                    // m/z ordering to avoid a second sort.
                    arrayResult[rmi.IndexMz] = rmi;
                }
                // Otherwise, if this ion was ranked, add it to the result array
                else if (rmi.Rank > 0)
                {
                    int countRanks = rmi.Rank;
                    arrayResult[countRanks - 1] = rmi;
                    // And stop when the array is full
                    if (countRanks == ionMatchCount)
                    {
                        break;
                    }
                }
            }

            // Is this a theoretical library with no intensity variation? If so it can't be ranked.
            // If it has any interesting peak annotations, pass those through
            if (rp.Ranked == 0 && arrayRMI.All(rmi => rmi.Intensity == arrayRMI[0].Intensity))
            {
                // Only do this if we have been asked to limit the ions matched, and there are any annotations
                if (ionMatchCount != -1 && arrayRMI.Any(rmi => rmi.HasAnnotations))
                {
                    // Pass through anything with an annotation as being of probable interest
                    arrayResult   = arrayRMI.Where(rmi => rmi.HasAnnotations).ToArray();
                    ionMatchCount = -1;
                }
            }

            // If not enough ranked ions were found, fill the rest of the results array
            if (ionMatchCount != -1)
            {
                for (int i = rp.Ranked; i < ionMatchCount; i++)
                {
                    arrayResult[i] = RankedMI.EMPTY;
                }
            }
            // If all ions are to be included, and some were found out of order, then
            // the expensive full sort by m/z is necesary.
            else if (sortMz)
            {
                Array.Sort(arrayResult, OrderMz);
            }

            _spectrum = MakeReadOnly(arrayResult);
        }
        private LibraryRankedSpectrumInfo(SpectrumPeaksInfo info, IsotopeLabelType labelType,
                                          TransitionGroup group, SrmSettings settings,
                                          string lookupSequence, ExplicitMods lookupMods,
                                          IEnumerable <int> charges, IEnumerable <IonType> types,
                                          IEnumerable <int> rankCharges, IEnumerable <IonType> rankTypes,
                                          bool useFilter, bool matchAll, int minPeaks)
        {
            LabelType = labelType;

            // Avoid ReSharper multiple enumeration warning
            var rankChargesArray = rankCharges.ToArray();
            var rankTypesArray   = rankTypes.ToArray();

            if (!useFilter)
            {
                if (charges == null)
                {
                    charges = GetRanked(rankChargesArray, Transition.ALL_CHARGES);
                }
                if (types == null)
                {
                    types = GetRanked(rankTypesArray, Transition.ALL_TYPES);
                }
                matchAll = true;
            }

            RankParams rp = new RankParams
            {
                sequence        = lookupSequence,
                precursorCharge = group.PrecursorCharge,
                charges         = charges ?? rankCharges,
                types           = types ?? rankTypes,
                matchAll        = matchAll,
                rankCharges     = rankChargesArray,
                rankTypes       = rankTypesArray,
                // Precursor isotopes will not be included in MS/MS, if they will be filtered
                // from MS1
                excludePrecursorIsotopes = settings.TransitionSettings.FullScan.IsEnabledMs,
                tranSettings             = settings.TransitionSettings
            };

            // Get necessary mass calculators and masses
            var calcMatchPre = settings.GetPrecursorCalc(labelType, lookupMods);
            var calcMatch    = settings.GetFragmentCalc(labelType, lookupMods);
            var calcPredict  = settings.GetFragmentCalc(group.LabelType, lookupMods);

            if (!string.IsNullOrEmpty(rp.sequence))
            {
                rp.precursorMz  = SequenceMassCalc.GetMZ(calcMatchPre.GetPrecursorMass(rp.sequence), rp.precursorCharge);
                rp.massPreMatch = calcMatch.GetPrecursorFragmentMass(rp.sequence);
                rp.massesMatch  = calcMatch.GetFragmentIonMasses(rp.sequence);
            }
            else
            {
                rp.precursorMz  = 0.0;
                rp.massPreMatch = 0.0;
                rp.massesMatch  = new double[0, 0];
            }
            rp.massPrePredict = rp.massPreMatch;
            rp.massesPredict  = rp.massesMatch;
            if (!ReferenceEquals(calcPredict, calcMatch) && !string.IsNullOrEmpty(rp.sequence))
            {
                rp.massPrePredict = calcPredict.GetPrecursorFragmentMass(rp.sequence);
                rp.massesPredict  = calcPredict.GetFragmentIonMasses(rp.sequence);
            }

            // Get values of interest from the settings.
            var tranSettings = settings.TransitionSettings;
            var predict      = tranSettings.Prediction;
            var filter       = tranSettings.Filter;
            var libraries    = tranSettings.Libraries;
            var instrument   = tranSettings.Instrument;

            // Get potential losses to all fragments in this peptide
            rp.massType        = predict.FragmentMassType;
            rp.potentialLosses = TransitionGroup.CalcPotentialLosses(rp.sequence,
                                                                     settings.PeptideSettings.Modifications, lookupMods,
                                                                     rp.massType);

            // Create arrays because ReadOnlyCollection enumerators are too slow
            // In some cases these collections must be enumerated for every ion
            // allowed in the library specturm.
            rp.startFinder = filter.FragmentRangeFirst;
            rp.endFinder   = filter.FragmentRangeLast;

            // Get library settings
            Tolerance    = libraries.IonMatchTolerance;
            rp.tolerance = Tolerance;
            rp.pick      = tranSettings.Libraries.Pick;
            int ionMatchCount = libraries.IonCount;

            // If no library filtering will happen, return all rankings for view in the UI
            if (!useFilter || rp.pick == TransitionLibraryPick.none)
            {
                if (rp.pick == TransitionLibraryPick.none)
                {
                    rp.pick = TransitionLibraryPick.all;
                }
                ionMatchCount = -1;
            }

            // Get instrument settings
            rp.minMz = instrument.MinMz;
            rp.maxMz = instrument.MaxMz;

            // Get the library spectrum mass-intensity pairs
            IList <SpectrumPeaksInfo.MI> listMI = info.Peaks;

            // Because sorting and matching observed ions with predicted
            // ions appear as bottlenecks in a profiler, a minimum number
            // of peaks may be supplied to allow the use of a 2-phase linear
            // filter that can significantly reduce the number of peaks
            // needing the O(n*log(n)) sorting and the O(n*m) matching.

            int   len             = listMI.Count;
            float intensityCutoff = 0;

            if (minPeaks != -1)
            {
                // Start searching for good cut-off at mean intensity.
                double totalIntensity = info.Intensities.Sum();

                FindIntensityCutoff(listMI, 0, (float)(totalIntensity / len) * 2, minPeaks, 1, ref intensityCutoff, ref len);
            }

            // Create filtered peak array storing original index for m/z ordering
            // to avoid needing to sort to return to this order.
            RankedMI[] arrayRMI = new RankedMI[len];
            // Detect when m/z values are out of order, and use the expensive sort
            // by m/z to correct this.
            double lastMz = double.MinValue;
            bool   sortMz = false;

            for (int i = 0, j = 0, lenOrig = listMI.Count; i < lenOrig; i++)
            {
                SpectrumPeaksInfo.MI mi = listMI[i];
                if (mi.Intensity >= intensityCutoff || intensityCutoff == 0)
                {
                    arrayRMI[j] = new RankedMI(mi, j);
                    j++;
                }
                if (ionMatchCount == -1)
                {
                    if (mi.Mz < lastMz)
                    {
                        sortMz = true;
                    }
                    lastMz = mi.Mz;
                }
            }

            // The one expensive sort is used to determine rank order
            // by intensity.
            Array.Sort(arrayRMI, OrderIntensityDesc);

            RankedMI[] arrayResult = new RankedMI[ionMatchCount != -1 ? ionMatchCount : arrayRMI.Length];

            foreach (RankedMI rmi in arrayRMI)
            {
                rmi.CalculateRank(rp);

                // If not filtering for only the highest ionMatchCount ranks
                if (ionMatchCount == -1)
                {
                    // Put the ranked record back where it started in the
                    // m/z ordering to avoid a second sort.
                    arrayResult[rmi.IndexMz] = rmi;
                }
                // Otherwise, if this ion was ranked, add it to the result array
                else if (rmi.Rank > 0)
                {
                    int countRanks = rmi.Rank;
                    arrayResult[countRanks - 1] = rmi;
                    // And stop when the array is full
                    if (countRanks == ionMatchCount)
                    {
                        break;
                    }
                }
            }

            // If not enough ranked ions were found, fill the rest of the results array
            if (ionMatchCount != -1)
            {
                for (int i = rp.Ranked; i < ionMatchCount; i++)
                {
                    arrayResult[i] = RankedMI.EMPTY;
                }
            }
            // If all ions are to be included, and some were found out of order, then
            // the expensive full sort by m/z is necesary.
            else if (sortMz)
            {
                Array.Sort(arrayResult, OrderMz);
            }

            _spectrum = MakeReadOnly(arrayResult);
        }
Esempio n. 20
0
        public void UpdateUI(bool selectionChanged = true)
        {
            // Only worry about updates, if the graph is visible
            // And make sure it is not disposed, since rendering happens on a timer
            if (!Visible || IsDisposed)
            {
                return;
            }

            // Clear existing data from the graph pane
            var graphPane = (MSGraphPane)graphControl.MasterPane[0];

            graphPane.CurveList.Clear();
            graphPane.GraphObjList.Clear();
            GraphItem = null;

            GraphHelper.FormatGraphPane(graphControl.GraphPane);
            GraphHelper.FormatFontSize(graphControl.GraphPane, Settings.Default.SpectrumFontSize);
            // Try to find a tree node with spectral library info associated
            // with the current selection.
            var nodeTree      = _stateProvider.SelectedNode as SrmTreeNode;
            var nodeGroupTree = nodeTree as TransitionGroupTreeNode;
            var nodeTranTree  = nodeTree as TransitionTreeNode;

            if (nodeTranTree != null)
            {
                nodeGroupTree = nodeTranTree.Parent as TransitionGroupTreeNode;
            }

            var             nodeGroup = (nodeGroupTree != null ? nodeGroupTree.DocNode : null);
            PeptideTreeNode nodePepTree;

            if (nodeGroup == null)
            {
                nodePepTree = nodeTree as PeptideTreeNode;
                if (nodePepTree != null)
                {
                    var listInfoGroups = GetLibraryInfoChargeGroups(nodePepTree);
                    if (listInfoGroups.Length == 1)
                    {
                        nodeGroup = listInfoGroups[0];
                    }
                    else if (listInfoGroups.Length > 1)
                    {
                        _nodeGroup      = null;
                        toolBar.Visible = false;
                        _graphHelper.SetErrorGraphItem(new NoDataMSGraphItem(
                                                           Resources.GraphSpectrum_UpdateUI_Multiple_charge_states_with_library_spectra));
                        return;
                    }
                }
            }
            else
            {
                nodePepTree = nodeGroupTree.Parent as PeptideTreeNode;
            }

            // Check for appropriate spectrum to load
            SrmSettings      settings  = DocumentUI.Settings;
            PeptideLibraries libraries = settings.PeptideSettings.Libraries;
            bool             available = false;

            if (nodeGroup == null || (!nodeGroup.HasLibInfo && !libraries.HasMidasLibrary))
            {
                _spectra = null;
            }
            else
            {
                TransitionGroup   group      = nodeGroup.TransitionGroup;
                TransitionDocNode transition = (nodeTranTree == null ? null : nodeTranTree.DocNode);
                var          lookupSequence  = group.Peptide.Target;// Sequence or custom ion id
                ExplicitMods lookupMods      = null;
                if (nodePepTree != null)
                {
                    lookupSequence = nodePepTree.DocNode.SourceUnmodifiedTarget;
                    lookupMods     = nodePepTree.DocNode.SourceExplicitMods;
                }
                try
                {
                    // Try to load a list of spectra matching the criteria for
                    // the current node group.
                    if (libraries.HasLibraries && libraries.IsLoaded)
                    {
                        if (NodeGroupChanged(nodeGroup))
                        {
                            try
                            {
                                UpdateSpectra(nodeGroup, lookupSequence, lookupMods);
                                UpdateToolbar();
                            }
                            catch (Exception)
                            {
                                _spectra = null;
                                UpdateToolbar();
                                throw;
                            }

                            _nodeGroup = nodeGroup;
                            if (settings.TransitionSettings.Instrument.IsDynamicMin)
                            {
                                ZoomSpectrumToSettings();
                            }
                        }

                        var spectrum = SelectedSpectrum;
                        if (spectrum != null)
                        {
                            IsotopeLabelType typeInfo = spectrum.LabelType;
                            var types   = _stateProvider.ShowIonTypes(group.IsProteomic);
                            var adducts = (group.IsProteomic ?
                                           Transition.DEFAULT_PEPTIDE_LIBRARY_CHARGES :
                                           nodeGroup.InUseAdducts).ToArray();
                            var charges     = _stateProvider.ShowIonCharges(adducts);
                            var rankTypes   = group.IsProteomic ? settings.TransitionSettings.Filter.PeptideIonTypes : settings.TransitionSettings.Filter.SmallMoleculeIonTypes;
                            var rankAdducts = group.IsProteomic ? settings.TransitionSettings.Filter.PeptideProductCharges : settings.TransitionSettings.Filter.SmallMoleculeFragmentAdducts;
                            var rankCharges = Adduct.OrderedAbsoluteChargeValues(rankAdducts);
                            // Make sure the types and charges in the settings are at the head
                            // of these lists to give them top priority, and get rankings correct.
                            int i = 0;
                            foreach (IonType type in rankTypes)
                            {
                                if (types.Remove(type))
                                {
                                    types.Insert(i++, type);
                                }
                            }
                            i = 0;
                            var showAdducts = new List <Adduct>();
                            foreach (var charge in rankCharges)
                            {
                                if (charges.Remove(charge))
                                {
                                    charges.Insert(i++, charge);
                                }
                                // NB for all adducts we just look at abs value of charge
                                // CONSIDER(bspratt): we may want finer per-adduct control for small molecule use
                                showAdducts.AddRange(adducts.Where(a => charge == Math.Abs(a.AdductCharge)));
                            }
                            showAdducts.AddRange(adducts.Where(a => charges.Contains(Math.Abs(a.AdductCharge)) && !showAdducts.Contains(a)));
                            SpectrumPeaksInfo spectrumInfo = spectrum.SpectrumPeaksInfo;
                            var spectrumInfoR = new LibraryRankedSpectrumInfo(spectrumInfo,
                                                                              typeInfo,
                                                                              nodeGroup,
                                                                              settings,
                                                                              lookupSequence,
                                                                              lookupMods,
                                                                              showAdducts,
                                                                              types,
                                                                              rankAdducts,
                                                                              rankTypes);
                            GraphItem = new SpectrumGraphItem(nodeGroup, transition, spectrumInfoR, spectrum.LibName)
                            {
                                ShowTypes      = types,
                                ShowCharges    = charges,
                                ShowRanks      = Settings.Default.ShowRanks,
                                ShowMz         = Settings.Default.ShowIonMz,
                                ShowObservedMz = Settings.Default.ShowObservedMz,
                                ShowDuplicates = Settings.Default.ShowDuplicateIons,
                                FontSize       = Settings.Default.SpectrumFontSize,
                                LineWidth      = Settings.Default.SpectrumLineWidth
                            };
                            LibraryChromGroup chromatogramData = null;
                            if (Settings.Default.ShowLibraryChromatograms)
                            {
                                chromatogramData = spectrum.LoadChromatogramData();
                            }
                            if (null == chromatogramData)
                            {
                                _graphHelper.ResetForSpectrum(new[] { nodeGroup.TransitionGroup });
                                _graphHelper.AddSpectrum(GraphItem);
                                _graphHelper.ZoomSpectrumToSettings(DocumentUI, nodeGroup);
                            }
                            else
                            {
                                _graphHelper.ResetForChromatograms(new[] { nodeGroup.TransitionGroup });

                                var displayType = GraphChromatogram.GetDisplayType(DocumentUI, nodeGroup);
                                IList <TransitionDocNode> displayTransitions =
                                    GraphChromatogram.GetDisplayTransitions(nodeGroup, displayType).ToArray();
                                int numTrans      = displayTransitions.Count;
                                var allChromDatas =
                                    chromatogramData.ChromDatas.Where(
                                        chromData => DisplayTypeMatches(chromData, displayType)).ToList();
                                var chromDatas = new List <LibraryChromGroup.ChromData>();
                                for (int iTran = 0; iTran < numTrans; iTran++)
                                {
                                    var displayTransition = displayTransitions[iTran];
                                    var indexMatch        =
                                        allChromDatas.IndexOf(chromData => IonMatches(displayTransition.Transition, chromData));
                                    if (indexMatch >= 0)
                                    {
                                        chromDatas.Add(allChromDatas[indexMatch]);
                                        allChromDatas.RemoveAt(indexMatch);
                                    }
                                    else
                                    {
                                        chromDatas.Add(null);
                                    }
                                }
                                allChromDatas.Sort((chromData1, chromData2) => chromData1.Mz.CompareTo(chromData2.Mz));
                                chromDatas.AddRange(allChromDatas);
                                double maxHeight         = chromDatas.Max(chromData => null == chromData ? double.MinValue : chromData.Height);
                                int    iChromDataPrimary = chromDatas.IndexOf(chromData => null != chromData && maxHeight == chromData.Height);
                                int    colorOffset       = displayType == DisplayTypeChrom.products
                                                      ? GraphChromatogram.GetDisplayTransitions(nodeGroup,
                                                                                                DisplayTypeChrom.
                                                                                                precursors).Count()
                                                      : 0;
                                for (int iChromData = 0; iChromData < chromDatas.Count; iChromData++)
                                {
                                    var chromData = chromDatas[iChromData];
                                    if (chromData == null)
                                    {
                                        continue;
                                    }
                                    string label;
                                    var    pointAnnotation = GraphItem.AnnotatePoint(new PointPair(chromData.Mz, 1.0));
                                    if (null != pointAnnotation)
                                    {
                                        label = pointAnnotation.Label;
                                    }
                                    else
                                    {
                                        label = chromData.Mz.ToString(@"0.####");
                                    }
                                    TransitionDocNode matchingTransition;
                                    Color             color;
                                    if (iChromData < numTrans)
                                    {
                                        matchingTransition = displayTransitions[iChromData];
                                        color =
                                            GraphChromatogram.COLORS_LIBRARY[
                                                (iChromData + colorOffset) % GraphChromatogram.COLORS_LIBRARY.Count];
                                    }
                                    else
                                    {
                                        matchingTransition = null;
                                        color =
                                            GraphChromatogram.COLORS_GROUPS[
                                                iChromData % GraphChromatogram.COLORS_GROUPS.Count];
                                    }

                                    TransitionChromInfo tranPeakInfo;
                                    ChromatogramInfo    chromatogramInfo;
                                    MakeChromatogramInfo(nodeGroup.PrecursorMz, chromatogramData, chromData, out chromatogramInfo, out tranPeakInfo);
                                    var graphItem = new ChromGraphItem(nodeGroup, matchingTransition, chromatogramInfo, iChromData == iChromDataPrimary ? tranPeakInfo : null, null,
                                                                       new[] { iChromData == iChromDataPrimary }, null, 0, false, false, null, 0,
                                                                       color, Settings.Default.ChromatogramFontSize, 1);
                                    LineItem curve = (LineItem)_graphHelper.AddChromatogram(PaneKey.DEFAULT, graphItem);
                                    if (matchingTransition == null)
                                    {
                                        curve.Label.Text = label;
                                    }
                                    curve.Line.Width = Settings.Default.ChromatogramLineWidth;
                                    if (null != transition)
                                    {
                                        if (IonMatches(transition.Transition, chromData))
                                        {
                                            color = ChromGraphItem.ColorSelected;
                                        }
                                    }
                                    curve.Color = color;
                                }
                                graphPane.Title.IsVisible  = false;
                                graphPane.Legend.IsVisible = true;
                                _graphHelper.FinishedAddingChromatograms(chromatogramData.StartTime, chromatogramData.EndTime, false);
                                graphControl.Refresh();
                            }
                            graphControl.IsEnableVPan = graphControl.IsEnableVZoom =
                                !Settings.Default.LockYAxis;
                            available = true;
                        }
                    }
                }
                catch (Exception)
                {
                    _graphHelper.SetErrorGraphItem(new NoDataMSGraphItem(
                                                       Resources.GraphSpectrum_UpdateUI_Failure_loading_spectrum__Library_may_be_corrupted));
                    return;
                }
            }
            // Show unavailable message, if no spectrum loaded
            if (!available)
            {
                UpdateToolbar();
                _nodeGroup = null;
                _graphHelper.SetErrorGraphItem(new UnavailableMSGraphItem());
            }
        }
Esempio n. 21
0
        public LibraryRankedSpectrumInfo RankSpectrum(SpectrumPeaksInfo info, int minPeaks, double?score)
        {
            var          ionsToReturn = FragmentFilterObj.FragmentMatchCount;
            RankingState rankingState = new RankingState()
            {
                matchAll = MatchAll,
            };
            // Get the library spectrum mass-intensity pairs
            IList <SpectrumPeaksInfo.MI> listMI = info.Peaks;

            // Because sorting and matching observed ions with predicted
            // ions appear as bottlenecks in a profiler, a minimum number
            // of peaks may be supplied to allow the use of a 2-phase linear
            // filter that can significantly reduce the number of peaks
            // needing the O(n*log(n)) sorting and the O(n*m) matching.

            int   len             = listMI.Count;
            float intensityCutoff = 0;

            if (minPeaks != -1)
            {
                // Start searching for good cut-off at mean intensity.
                double totalIntensity = info.Intensities.Sum();

                FindIntensityCutoff(listMI, 0, (float)(totalIntensity / len) * 2, minPeaks, 1, ref intensityCutoff, ref len);
            }
            // Create filtered peak array storing original index for m/z ordering
            // to avoid needing to sort to return to this order.
            RankedMI[] arrayRMI = new RankedMI[len];
            // Detect when m/z values are out of order, and use the expensive sort
            // by m/z to correct this.
            double lastMz = double.MinValue;
            bool   sortMz = false;

            for (int i = 0, j = 0, lenOrig = listMI.Count; i < lenOrig; i++)
            {
                SpectrumPeaksInfo.MI mi = listMI[i];
                if (mi.Intensity >= intensityCutoff || intensityCutoff == 0)
                {
                    arrayRMI[j] = new RankedMI(mi, j);
                    j++;
                }
                if (!ionsToReturn.HasValue)
                {
                    if (mi.Mz < lastMz)
                    {
                        sortMz = true;
                    }
                    lastMz = mi.Mz;
                }
            }

            // The one expensive sort is used to determine rank order
            // by intensity, or m/z in case of a tie.
            Array.Sort(arrayRMI, OrderIntensityDesc);


            RankedMI[] arrayResult = new RankedMI[ionsToReturn.HasValue ? ionsToReturn.Value : arrayRMI.Length];

            foreach (RankedMI rmi in arrayRMI)
            {
                var rankedRmi = CalculateRank(rankingState, rmi);

                // If not filtering for only the highest ionMatchCount ranks
                if (!ionsToReturn.HasValue)
                {
                    // Put the ranked record back where it started in the
                    // m/z ordering to avoid a second sort.
                    arrayResult[rmi.IndexMz] = rankedRmi;
                }
                // Otherwise, if this ion was ranked, add it to the result array
                else if (rankedRmi.Rank > 0)
                {
                    int countRanks = rankedRmi.Rank;
                    arrayResult[countRanks - 1] = rankedRmi;
                    // And stop when the array is full
                    if (countRanks == ionsToReturn.Value)
                    {
                        break;
                    }
                }
            }

            // Is this a theoretical library with no intensity variation? If so it can't be ranked.
            // If it has any interesting peak annotations, pass those through
            if (rankingState.Ranked == 0 && arrayRMI.All(rmi => rmi.Intensity == arrayRMI[0].Intensity))
            {
                // Only do this if we have been asked to limit the ions matched, and there are any annotations
                if (ionsToReturn.HasValue && arrayRMI.Any(rmi => rmi.HasAnnotations))
                {
                    // Pass through anything with an annotation as being of probable interest
                    arrayResult  = arrayRMI.Where(rmi => rmi.HasAnnotations).ToArray();
                    ionsToReturn = null;
                }
            }

            // If not enough ranked ions were found, fill the rest of the results array
            if (ionsToReturn.HasValue)
            {
                for (int i = rankingState.Ranked; i < ionsToReturn.Value; i++)
                {
                    arrayResult[i] = RankedMI.EMPTY;
                }
            }
            // If all ions are to be included, and some were found out of order, then
            // the expensive full sort by m/z is necessary.
            else if (sortMz)
            {
                Array.Sort(arrayResult, OrderMz);
            }

            double?spectrumScore;

            if (score == null && GroupDocNode.HasLibInfo && GroupDocNode.LibInfo is BiblioSpecSpectrumHeaderInfo libInfo)
            {
                spectrumScore = libInfo.Score;
            }
            else
            {
                spectrumScore = score;
            }
            return(new LibraryRankedSpectrumInfo(PredictLabelType, Libraries.IonMatchTolerance, arrayResult, spectrumScore));
        }