Example #1
0
        private MatchedFragmentIon MakeMatchedFragmentIon(IonType ionType, int ionIndex, Adduct adduct, TransitionLosses transitionLosses, out double matchMz)
        {
            var       moleculeMasses = MoleculeMassesObj;
            int       ordinal;
            int       peptideLength = TargetInfoObj.LookupSequence.Sequence?.Length ?? 0;
            TypedMass matchMass, predictedMass;

            if (ionType == IonType.precursor)
            {
                matchMass     = moleculeMasses.MatchIonMasses.PrecursorMass;
                predictedMass = moleculeMasses.PredictIonMasses.PrecursorMass;
                ordinal       = peptideLength;
            }
            else
            {
                matchMass     = moleculeMasses.MatchIonMasses.GetIonMass(ionType, ionIndex);
                predictedMass = moleculeMasses.PredictIonMasses.GetIonMass(ionType, ionIndex);
                ordinal       = Transition.OffsetToOrdinal(ionType, ionIndex, peptideLength);
            }

            if (transitionLosses != null)
            {
                matchMass     -= transitionLosses.Mass;
                predictedMass -= transitionLosses.Mass;
            }

            double predictedMz = SequenceMassCalc.GetMZ(predictedMass, adduct);

            matchMz = SequenceMassCalc.GetMZ(matchMass, adduct);
            return(new MatchedFragmentIon(ionType, ordinal, adduct, null, transitionLosses, predictedMz));
        }
Example #2
0
 public TransitionDocNode(Transition id,
                          Annotations annotations,
                          TransitionLosses losses,
                          double massH,
                          TransitionIsotopeDistInfo isotopeDistInfo,
                          TransitionLibInfo libInfo,
                          Results <TransitionChromInfo> results)
     : base(id, annotations)
 {
     Losses = losses;
     if (losses != null)
     {
         massH -= losses.Mass;
     }
     if (id.IsCustom())
     {
         Mz = new SignedMz(BioMassCalc.CalculateIonMz(massH, id.Charge), id.IsNegative());
     }
     else
     {
         Mz = new SignedMz(SequenceMassCalc.GetMZ(massH, id.Charge) + SequenceMassCalc.GetPeptideInterval(id.DecoyMassShift), id.IsNegative());
     }
     IsotopeDistInfo = isotopeDistInfo;
     LibInfo         = libInfo;
     Results         = results;
 }
Example #3
0
        private static CellDesc CreateIon(IonType type, int ordinal, TypedMass massH, Adduct charge,
                                          IEnumerable <DocNode> choices, ICollection <DocNode> chosen, Transition tranSelected,
                                          RenderTools rt)
        {
            double   mz   = SequenceMassCalc.GetMZ(massH, charge);
            CellDesc cell = CreateData(string.Format(@"{0:F02}", mz), rt);

            foreach (TransitionDocNode nodeTran in choices)
            {
                Transition tran = nodeTran.Transition;
                if (tran.IonType == type &&
                    tran.Ordinal == ordinal &&
                    tran.Adduct == charge)
                {
                    cell.Font = rt.FontBold;
                    if (Equals(tran, tranSelected))
                    {
                        cell.Brush = rt.BrushSelected; // Stop after selected
                        break;
                    }
                    if (!chosen.Contains(nodeTran))
                    {
                        cell.Brush = rt.BrushChoice;  // Keep looking
                    }
                    else
                    {
                        cell.Brush = rt.BrushChosen;  // Stop after chosen
                        break;
                    }
                }
            }

            return(cell);
        }
Example #4
0
 public static TransitionLibInfo GetLibInfo(Transition transition, double massH,
                                            IDictionary <double, LibraryRankedSpectrumInfo.RankedMI> ranks)
 {
     LibraryRankedSpectrumInfo.RankedMI rmi;
     if (ranks == null || !ranks.TryGetValue(SequenceMassCalc.GetMZ(massH, transition.Charge), out rmi))
     {
         return(null);
     }
     return(new TransitionLibInfo(rmi.Rank, rmi.Intensity));
 }
Example #5
0
        /// <summary>
        /// Calculates the matching charge within a tolerance for a mass, assuming (de)protonation.
        /// </summary>
        /// <param name="mass">The mass to calculate charge for (actually massH if !IsCustomIon)</param>
        /// <param name="mz">The desired m/z value the charge should produce</param>
        /// <param name="tolerance">How far off the actual m/z is allowed to be</param>
        /// <param name="isCustomIon">Is this a custom ion formula?</param>
        /// <param name="minCharge">Minimum charge to consider</param>
        /// <param name="maxCharge">Maximum charge to consider</param>
        /// <param name="massShifts">Possible mass shifts that may have been applied to decoys</param>
        /// <param name="massShiftType"></param>
        /// <param name="massShift">Mass shift required to to achieve this charge state or zero</param>
        /// <param name="nearestCharge">closest matching charge, useful when return value is null</param>
        /// <returns>A matching charge or null, in which case the closest non-matching charge can be found in the nearestCharge value.</returns>
        public static Adduct CalcCharge(TypedMass mass, double mz, double tolerance, bool isCustomIon, int minCharge, int maxCharge,
                                        ICollection <int> massShifts, MassShiftType massShiftType, out int massShift, out int nearestCharge)
        {
            Assume.IsTrue(minCharge <= maxCharge);

            massShift = 0;

            nearestCharge = 0;
            double nearestDelta = double.MaxValue;

            for (int i = minCharge; i <= maxCharge; i++)
            {
                if (i != 0) // Avoid z=0 if we're entertaining negative charge states
                {
                    double calculatedMz = isCustomIon
                        ? Adduct.FromChargeProtonated(i).MzFromNeutralMass(mass)
                        : SequenceMassCalc.GetMZ(mass, i);
                    double delta           = mz - calculatedMz;
                    double deltaAbs        = Math.Abs(delta);
                    int    potentialShift  = (int)Math.Round(deltaAbs);
                    double fractionalDelta = deltaAbs - potentialShift;
                    if (MatchMz(fractionalDelta, tolerance) && MatchMassShift(potentialShift, massShifts, massShiftType))
                    {
                        massShift = potentialShift;
                        if (delta < 0)
                        {
                            massShift = -massShift;
                        }
                        var result = i;
                        nearestCharge = i;
                        return(Adduct.FromCharge(result, isCustomIon ? Adduct.ADDUCT_TYPE.non_proteomic : Adduct.ADDUCT_TYPE.proteomic));
                    }
                    if (deltaAbs < nearestDelta)
                    {
                        nearestDelta  = deltaAbs;
                        nearestCharge = i;
                    }
                    // If the charge is positive and the calculated m/z is smaller than the desired m/z
                    // increasing the charge further cannot possibly produce a match
                    if (massShiftType == MassShiftType.none && minCharge > 0 && delta > 0)
                    {
                        break;
                    }
                }
            }

            Debug.Assert(nearestCharge != 0);   // Could only happen if min > max

            return(Adduct.EMPTY);
        }
Example #6
0
            public static TransitionQuantInfo GetLibTransitionQuantInfo(Transition transition, TransitionLosses losses, TypedMass massH,
                                                                        IDictionary <double, LibraryRankedSpectrumInfo.RankedMI> ranks)
            {
                LibraryRankedSpectrumInfo.RankedMI rmi = null;
                if (ranks != null)
                {
                    ranks.TryGetValue(SequenceMassCalc.GetMZ(massH, transition.Adduct), out rmi);
                }
                TransitionLibInfo transitionLibInfo = null;

                if (rmi != null)
                {
                    transitionLibInfo = new TransitionLibInfo(rmi.Rank, rmi.Intensity);
                }
                return(new TransitionQuantInfo(null, transitionLibInfo,
                                               rmi == null || rmi.Quantitative));
            }
Example #7
0
        private List <SpectrumPeaksInfo.MI> CalcMIs(TypedMass mass, float[] intensities, int offset)
        {
            var result = new List <SpectrumPeaksInfo.MI>(PrositConstants.IONS_PER_RESIDUE / 2);

            for (var c = 0; c < PrositConstants.IONS_PER_RESIDUE / 2; ++c)
            {
                // Not a possible charge
                if (PeptidePrecursorNCE.NodeGroup.PrecursorCharge <= c)
                {
                    break;
                }

                result.Add(new SpectrumPeaksInfo.MI
                {
                    Mz        = SequenceMassCalc.GetMZ(mass, c + 1),
                    Intensity = intensities[c + offset]
                });
            }

            return(result);
        }
Example #8
0
 public TransitionDocNode(Transition id,
                          Annotations annotations,
                          TransitionLosses losses,
                          TypedMass mass,
                          TransitionQuantInfo transitionQuantInfo,
                          Results <TransitionChromInfo> results)
     : base(id, annotations)
 {
     Losses = losses;
     if (losses != null)
     {
         mass -= losses.Mass;
     }
     Mz = id.IsCustom() ?
          new SignedMz(id.Adduct.MzFromNeutralMass(mass), id.IsNegative()) :
          new SignedMz(SequenceMassCalc.GetMZ(mass, id.Adduct) + SequenceMassCalc.GetPeptideInterval(id.DecoyMassShift), id.IsNegative());
     MzMassType      = mass.MassType;
     IsotopeDistInfo = transitionQuantInfo.IsotopeDistInfo;
     LibInfo         = transitionQuantInfo.LibInfo;
     Results         = results;
     Quantitative    = transitionQuantInfo.Quantititative;
 }
        public IsotopeDistInfo(MassDistribution massDistribution,
                               double monoisotopicMass,
                               bool isMassH, // Is monoisotopicMass M+H, or just M as in small molecule use?
                               int charge,
                               Func <double, double> calcFilterWindow,
                               double massResolution,
                               double minimumAbundance)
        {
            _monoisotopicMass = monoisotopicMass;
            _charge           = charge;
            _isMassH          = isMassH;

            // Get peak center of mass values for the given resolution
            var q1FilterValues = MassDistribution.NewInstance(massDistribution, massResolution, 0).Keys.ToList();
            // Find the monoisotopic m/z and make sure it is exactly the expected number
            double monoMz        = isMassH ? SequenceMassCalc.GetMZ(_monoisotopicMass, _charge) : BioMassCalc.CalculateIonMz(_monoisotopicMass, _charge);
            double monoMzDist    = monoMz;
            int    monoMassIndex = 0;

            for (int i = 0; i < q1FilterValues.Count; i++)
            {
                double peakCenterMz = q1FilterValues[i];
                double filterWindow = calcFilterWindow(peakCenterMz);
                double startMz      = peakCenterMz - filterWindow / 2;
                double endMz        = startMz + filterWindow;
                if (startMz < monoMz && monoMz < endMz)
                {
                    monoMzDist        = q1FilterValues[i];
                    q1FilterValues[i] = monoMz;
                    monoMassIndex     = i;
                    break;
                }
            }
            // Insert a M-1 peak, even if it is not expected in the isotope mass distribution
            if (monoMassIndex == 0 && q1FilterValues.Count > 1)
            {
                // Use the delta from the original distribution monoMz to the next peak
                q1FilterValues.Insert(0, monoMz + monoMzDist - q1FilterValues[1]);
                monoMassIndex++;
            }

            if (!q1FilterValues.Any())  // As is small molecule docs with mz values only, no formulas
            {
                return;
            }

            // Use the filtering algorithm that will be used on real data to determine the
            // expected proportions of the mass distribution that will end up filtered into
            // peaks
            // CONSIDER: Mass accuracy information is not calculated here
            var key    = new PrecursorTextId(q1FilterValues[monoMassIndex], null, ChromExtractor.summed);
            var filter = new SpectrumFilterPair(key, PeptideDocNode.UNKNOWN_COLOR, 0, null, null, null, null, 0, false, false);

            filter.AddQ1FilterValues(q1FilterValues, calcFilterWindow);

            var expectedSpectrum = filter.FilterQ1SpectrumList(new[] { new MsDataSpectrum
                                                                       {
                                                                           Mzs = massDistribution.Keys.ToArray(), Intensities = massDistribution.Values.ToArray()
                                                                       } });

            int startIndex = expectedSpectrum.Intensities.IndexOf(inten => inten >= minimumAbundance);

            if (startIndex == -1)
            {
                throw new InvalidOperationException(
                          string.Format(Resources.IsotopeDistInfo_IsotopeDistInfo_Minimum_abundance__0__too_high,
                                        minimumAbundance));
            }
            // Always include the M-1 peak, even if it is expected to have zero intensity
            if (startIndex > monoMassIndex - 1)
            {
                startIndex = monoMassIndex - 1;
            }
            if (startIndex < 0)
            {
                startIndex = 0;
            }
            int endIndex              = expectedSpectrum.Intensities.LastIndexOf(inten => inten >= minimumAbundance) + 1;
            int countPeaks            = endIndex - startIndex;
            var listProportionIndices = new List <KeyValuePair <float, int> >(countPeaks);

            for (int i = 0; i < countPeaks; i++)
            {
                listProportionIndices.Add(new KeyValuePair <float, int>(
                                              expectedSpectrum.Intensities[i + startIndex], i));
            }
            // Sort proportions descending.
            listProportionIndices.Sort((p1, p2) => Comparer.Default.Compare(p2.Key, p1.Key));

            // Set proportions and ranks back in the original locations
            var expectedProportionRanks = new KeyValuePair <float, int> [countPeaks];

            for (int i = 0; i < countPeaks; i++)
            {
                expectedProportionRanks[listProportionIndices[i].Value] =
                    new KeyValuePair <float, int>(listProportionIndices[i].Key, i + 1);
            }

            // TODO: Can this be discarded?
            // MassDistribution = massDistribution;

            MonoMassIndex = monoMassIndex - startIndex;

            // Find the base peak and fill in the masses and proportions
            var expectedPeaks = new List <MzRankProportion>();

            for (int i = 0; i < countPeaks; i++)
            {
                float expectedProportion = expectedProportionRanks[i].Key;
                int   rank = expectedProportionRanks[i].Value;
                expectedPeaks.Add(new MzRankProportion(q1FilterValues[i + startIndex], rank, expectedProportion));
                if (expectedProportion > expectedProportionRanks[BaseMassIndex].Key)
                {
                    BaseMassIndex = i;
                }
            }
            ExpectedPeaks = expectedPeaks;
        }
Example #10
0
            private bool MatchNext(RankParams rp, IonType type, int offset, TransitionLosses losses, int charge, int len, bool filter, int end, int start, double startMz)
            {
                bool   precursorMatch = Transition.IsPrecursor(type);
                double ionMass        = !precursorMatch ? rp.massesMatch[(int)type, offset] : rp.massPreMatch;

                if (losses != null)
                {
                    ionMass -= losses.Mass;
                }
                double ionMz = SequenceMassCalc.GetMZ(ionMass, charge);

                // Unless trying to match everything, stop looking outside the instrument range
                if (!rp.matchAll && !rp.HasLosses && ionMz > rp.maxMz)
                {
                    return(false);
                }
                // Check filter properties, if apropriate
                if ((rp.matchAll || ionMz >= rp.minMz) && Math.Abs(ionMz - ObservedMz) < rp.tolerance)
                {
                    // Make sure each m/z value is only used for the most intense peak
                    // that is within the tolerance range.
                    if (rp.IsSeen(ionMz))
                    {
                        return(true); // Keep looking
                    }
                    rp.Seen(ionMz);

                    int ordinal = Transition.OffsetToOrdinal(type, offset, len + 1);
                    // If this m/z aready matched a different ion, just remember the second ion.
                    double predictedMass = !precursorMatch ? rp.massesPredict[(int)type, offset] : rp.massPrePredict;
                    if (losses != null)
                    {
                        predictedMass -= losses.Mass;
                    }
                    double predictedMz = SequenceMassCalc.GetMZ(predictedMass, charge);
                    if (Ordinal > 0)
                    {
                        IonType2     = type;
                        Charge2      = charge;
                        Ordinal2     = ordinal;
                        Losses2      = losses;
                        PredictedMz2 = predictedMz;
                        rp.matched   = true;
                        return(false);
                    }

                    // Avoid using the same predicted m/z on two different peaks
                    if (predictedMz == ionMz || !rp.IsSeen(predictedMz))
                    {
                        rp.Seen(predictedMz);

                        // Avoid ranking precursor ions without losses, if the precursor isotopes will
                        // not be taken from product ions
                        if (!rp.excludePrecursorIsotopes || type != IonType.precursor || losses != null)
                        {
                            if (!filter || rp.tranSettings.Accept(rp.sequence, rp.precursorMz, type, offset, ionMz, start, end, startMz))
                            {
                                if (!rp.matchAll || (rp.minMz <= ionMz && ionMz <= rp.maxMz &&
                                                     rp.rankTypes.Contains(type) &&
                                                     (rp.rankCharges.Contains(charge) || type == IonType.precursor)))
                                {
                                    Rank = rp.RankNext();
                                }
                            }
                        }
                        IonType     = type;
                        Charge      = charge;
                        Ordinal     = ordinal;
                        Losses      = losses;
                        PredictedMz = predictedMz;
                        rp.matched  = (!rp.matchAll);
                        return(rp.matchAll);
                    }
                }
                // Stop looking once the mass has been passed, unless there are losses to consider
                if (rp.HasLosses)
                {
                    return(true);
                }
                return(ionMz <= ObservedMz);
            }
Example #11
0
        public IEnumerable <TransitionDocNode> GetPrecursorTransitions(SrmSettings settings,
                                                                       ExplicitMods mods,
                                                                       IPrecursorMassCalc calcPredictPre,
                                                                       IFragmentMassCalc calcPredict,
                                                                       double precursorMz,
                                                                       IsotopeDistInfo isotopeDist,
                                                                       IList <IList <ExplicitLoss> > potentialLosses,
                                                                       IDictionary <double, LibraryRankedSpectrumInfo.RankedMI> transitionRanks,
                                                                       bool libraryFilter,
                                                                       bool useFilter,
                                                                       bool ensureMassesAreMeasurable)
        {
            var      tranSettings = settings.TransitionSettings;
            var      fullScan     = tranSettings.FullScan;
            int      minMz        = tranSettings.Instrument.GetMinMz(precursorMz);
            int      maxMz        = tranSettings.Instrument.MaxMz;
            bool     precursorMS1 = fullScan.IsEnabledMs;
            MassType massType     = tranSettings.Prediction.FragmentMassType;
            MassType massTypeIon  = precursorMS1 ? tranSettings.Prediction.PrecursorMassType : massType;

            var  sequence            = Peptide.Target;
            var  ionTypes            = IsProteomic ? tranSettings.Filter.PeptideIonTypes : tranSettings.Filter.SmallMoleculeIonTypes;
            bool precursorNoProducts = precursorMS1 && !fullScan.IsEnabledMsMs &&
                                       ionTypes.Count == 1 && ionTypes[0] == IonType.precursor;
            var precursorMassPredict = precursorMS1
                ? calcPredictPre.GetPrecursorMass(sequence)
                : calcPredict.GetPrecursorFragmentMass(sequence);

            foreach (var losses in CalcTransitionLosses(IonType.precursor, 0, massType, potentialLosses))
            {
                Adduct productAdduct;
                if (losses == null)
                {
                    productAdduct = PrecursorAdduct;
                }
                else
                {
                    productAdduct = losses.GetProductAdduct(PrecursorAdduct);
                    if (productAdduct == null)
                    {
                        continue;
                    }
                }

                double ionMz = IsProteomic ?
                               SequenceMassCalc.GetMZ(Transition.CalcMass(precursorMassPredict, losses), PrecursorAdduct) :
                               PrecursorAdduct.MzFromNeutralMass(CustomMolecule.GetMass(massTypeIon), massTypeIon);

                if (losses == null)
                {
                    if (precursorMS1 && isotopeDist != null && ensureMassesAreMeasurable)
                    {
                        foreach (int i in fullScan.SelectMassIndices(isotopeDist, useFilter))
                        {
                            var precursorMS1Mass = isotopeDist.GetMassI(i, DecoyMassShift);
                            ionMz = SequenceMassCalc.GetMZ(precursorMS1Mass, PrecursorAdduct);
                            if (minMz > ionMz || ionMz > maxMz)
                            {
                                continue;
                            }
                            var isotopeDistInfo = new TransitionIsotopeDistInfo(
                                isotopeDist.GetRankI(i), isotopeDist.GetProportionI(i));
                            yield return(CreateTransitionNode(i, precursorMS1Mass, isotopeDistInfo, null, transitionRanks, productAdduct));
                        }
                        continue;
                    }
                }
                // If there was loss, it is possible (though not likely) that the ion m/z value
                // will now fall below the minimum measurable value for the instrument
                else if (ensureMassesAreMeasurable && minMz > ionMz)
                {
                    continue;
                }

                // If filtering precursors from MS1 scans, then ranking in MS/MS does not apply
                bool precursorIsProduct = !precursorMS1 || losses != null;
                // Skip product ion precursors, if the should not be included
                if (useFilter && precursorIsProduct && precursorNoProducts)
                {
                    continue;
                }
                if (!useFilter || !precursorIsProduct ||
                    !libraryFilter || IsMatched(transitionRanks, ionMz, IonType.precursor,
                                                PrecursorAdduct, losses))
                {
                    yield return(CreateTransitionNode(0, precursorMassPredict, null, losses,
                                                      precursorIsProduct ? transitionRanks : null, productAdduct));
                }
            }
        }
Example #12
0
        public IEnumerable <TransitionGroup> GetTransitionGroups(SrmSettings settings, PeptideDocNode nodePep, ExplicitMods mods, bool useFilter)
        {
            if (IsCustomMolecule)
            {
                // TODO(bspratt) WHY NOT USING TRANSITION SETTINGS FILTER PRECURSOR ADDUCTS?
                // We can't generate precursors as we do with peptides, so just filter what we do have on instrument mz range
                //var precursorAdducts = settings.TransitionSettings.Filter.SmallMoleculePrecursorAdducts;
                // TODO(bspratt) generate precursor transitions if doc has no fragments
                // CONSIDER(bspratt) could we reasonably reuse fragments with proposed precursors of suitable charge and polarity (say, add an M+Na node that mimics an existing M+H node and children)
                foreach (var group in nodePep.TransitionGroups.Where(tranGroup => tranGroup.TransitionGroup.IsCustomIon))
                {
                    if (!useFilter || settings.TransitionSettings.IsMeasurablePrecursor(group.PrecursorMz))
                    {
                        yield return(group.TransitionGroup);
                    }
                }
            }
            else
            {
                var precursorCharges = settings.TransitionSettings.Filter.PeptidePrecursorCharges;
                if (!useFilter)
                {
                    precursorCharges = new List <Adduct>();
                    for (int i = TransitionGroup.MIN_PRECURSOR_CHARGE; i < TransitionGroup.MAX_PRECURSOR_CHARGE; i++)
                    {
                        precursorCharges.Add(Adduct.FromChargeProtonated(i));
                    }
                }

                var modSettings = settings.PeptideSettings.Modifications;

                var precursorMassLight  = settings.GetPrecursorMass(IsotopeLabelType.light, Target, mods);
                var listPrecursorMasses = new List <KeyValuePair <IsotopeLabelType, TypedMass> >
                {
                    new KeyValuePair <IsotopeLabelType, TypedMass>(IsotopeLabelType.light, precursorMassLight)
                };

                foreach (var typeMods in modSettings.GetHeavyModifications())
                {
                    IsotopeLabelType labelType = typeMods.LabelType;
                    var precursorMass          = precursorMassLight;
                    if (settings.HasPrecursorCalc(labelType, mods))
                    {
                        precursorMass = settings.GetPrecursorMass(labelType, Target, mods);
                    }

                    listPrecursorMasses.Add(new KeyValuePair <IsotopeLabelType, TypedMass>(labelType, precursorMass));
                }

                foreach (var adduct in precursorCharges)
                {
                    if (useFilter && !settings.Accept(settings, this, mods, adduct))
                    {
                        continue;
                    }

                    for (int i = 0; i < listPrecursorMasses.Count; i++)
                    {
                        var pair = listPrecursorMasses[i];
                        IsotopeLabelType labelType = pair.Key;
                        var precursorMass          = pair.Value;
                        // Only return a heavy group, if the precursor masses differ
                        // between the light and heavy calculators
                        if (i == 0 || precursorMass != precursorMassLight)
                        {
                            if (settings.TransitionSettings.IsMeasurablePrecursor(SequenceMassCalc.GetMZ(precursorMass, adduct)))
                            {
                                yield return(new TransitionGroup(this, adduct, labelType));
                            }
                        }
                    }
                }
            }
        }
Example #13
0
        /// <summary>
        /// Calculates the matching charge within a tolerance for a mass.
        /// </summary>
        /// <param name="massH">The mass to calculate charge for</param>
        /// <param name="mz">The desired m/z value the charge should produce</param>
        /// <param name="tolerance">How far off the actual m/z is allowed to be</param>
        /// <param name="isCustomIon">Is this a custom ion formula?</param>
        /// <param name="minCharge">Minimum charge to consider</param>
        /// <param name="maxCharge">Maximum charge to consider</param>
        /// <param name="massShifts">Possible mass shifts that may have been applied to decoys</param>
        /// <param name="massShiftType"></param>
        /// <param name="massShift">Mass shift required to to achieve this charge state or zero</param>
        /// <param name="nearestCharge">closest matching charge, useful when return value is null</param>
        /// <returns>A matching charge or null, in which case the closest non-matching charge can be found in the nearestCharge value.</returns>
        public static int?CalcCharge(double massH, double mz, double tolerance, bool isCustomIon, int minCharge, int maxCharge,
                                     ICollection <int> massShifts, MassShiftType massShiftType, out int massShift, out int nearestCharge)
        {
            Assume.IsTrue(minCharge <= maxCharge);

            massShift = 0;

            nearestCharge = 0;
            double nearestDelta = double.MaxValue;

            for (int i = minCharge; i <= maxCharge; i++)
            {
                if (i != 0) // Avoid z=0 if we're entertaining negative charge states
                {
                    double delta           = mz - (isCustomIon ? BioMassCalc.CalculateIonMz(massH, i) : SequenceMassCalc.GetMZ(massH, i));
                    double deltaAbs        = Math.Abs(delta);
                    int    potentialShift  = (int)Math.Round(deltaAbs);
                    double fractionalDelta = deltaAbs - potentialShift;
                    if (MatchMz(fractionalDelta, tolerance) && MatchMassShift(potentialShift, massShifts, massShiftType))
                    {
                        massShift = potentialShift;
                        if (delta < 0)
                        {
                            massShift = -massShift;
                        }
                        int?result = i;
                        nearestCharge = i;
                        return(result);
                    }
                    if (deltaAbs < nearestDelta)
                    {
                        nearestDelta  = deltaAbs;
                        nearestCharge = i;
                    }
                }
            }

            Debug.Assert(nearestCharge != 0);   // Could only happen if min > max

            return(null);
        }
Example #14
0
        public static int CalcProductCharge(double productPrecursorMass,
                                            int precursorCharge,
                                            double[,] productMasses,
                                            IList <IList <ExplicitLoss> > potentialLosses,
                                            double productMz,
                                            double tolerance,
                                            MassType massType,
                                            MassShiftType massShiftType,
                                            out IonType?ionType,
                                            out int?ordinal,
                                            out TransitionLosses losses,
                                            out int massShift)
        {
            // Get length of fragment ion mass array
            int len = productMasses.GetLength(1);

            // Check all possible ion types and offsets
            double           minDelta = double.MaxValue, minDeltaNs = double.MaxValue;
            int              bestCharge = 0, bestChargeNs = 0;
            IonType?         bestIonType = null, bestIonTypeNs = null;
            int?             bestOrdinal = null, bestOrdinalNs = null;
            TransitionLosses bestLosses = null, bestLossesNs = null;
            int              bestMassShift = 0;

            // Check to see if it is the precursor
            foreach (var lossesTrial in TransitionGroup.CalcTransitionLosses(IonType.precursor, 0, massType, potentialLosses))
            {
                double productMass = productPrecursorMass - (lossesTrial != null ? lossesTrial.Mass : 0);
                int    potentialMassShift;
                int    nearestCharge;
                int?   charge = CalcProductCharge(productMass, productMz, tolerance, false, precursorCharge,
                                                  massShiftType, out potentialMassShift, out nearestCharge);
                if (charge.HasValue && charge.Value == precursorCharge)
                {
                    double potentialMz = SequenceMassCalc.GetMZ(productMass, charge.Value) + potentialMassShift;
                    double delta       = Math.Abs(productMz - potentialMz);

                    if (potentialMassShift == 0 && minDeltaNs > delta)
                    {
                        bestChargeNs  = charge.Value;
                        bestIonTypeNs = IonType.precursor;
                        bestOrdinalNs = len + 1;
                        bestLossesNs  = lossesTrial;

                        minDeltaNs = delta;
                    }
                    else if (potentialMassShift != 0 && minDelta > delta)
                    {
                        bestCharge    = charge.Value;
                        bestIonType   = IonType.precursor;
                        bestOrdinal   = len + 1;
                        bestLosses    = lossesTrial;
                        bestMassShift = potentialMassShift;

                        minDelta = delta;
                    }
                }
            }

            foreach (IonType type in Transition.ALL_TYPES)
            {
                // Types have priorities.  If moving to a lower priority type, and there is already a
                // suitable answer stop looking.
                if ((type == Transition.ALL_TYPES[2] || type == Transition.ALL_TYPES[2]) &&
                    (MatchMz(minDelta, tolerance) || MatchMz(minDeltaNs, tolerance)))
                {
                    break;
                }

                for (int offset = 0; offset < len; offset++)
                {
                    foreach (var lossesTrial in TransitionGroup.CalcTransitionLosses(type, offset, massType, potentialLosses))
                    {
                        // Look for the closest match.
                        double productMass = productMasses[(int)type, offset];
                        if (lossesTrial != null)
                        {
                            productMass -= lossesTrial.Mass;
                        }
                        int potentialMassShift;
                        int nearestCharge;
                        int?chargeFound = CalcProductCharge(productMass, productMz, tolerance, false, precursorCharge,
                                                            massShiftType, out potentialMassShift, out nearestCharge);
                        if (chargeFound.HasValue)
                        {
                            int    charge      = chargeFound.Value;
                            double potentialMz = SequenceMassCalc.GetMZ(productMass, charge) + potentialMassShift;
                            double delta       = Math.Abs(productMz - potentialMz);
                            if (potentialMassShift == 0 && minDeltaNs > delta)
                            {
                                bestChargeNs  = charge;
                                bestIonTypeNs = type;
                                // The peptide length is 1 longer than the mass array
                                bestOrdinalNs = Transition.OffsetToOrdinal(type, offset, len + 1);
                                bestLossesNs  = lossesTrial;

                                minDeltaNs = delta;
                            }
                            else if (potentialMassShift != 0 && minDelta > delta)
                            {
                                bestCharge  = charge;
                                bestIonType = type;
                                // The peptide length is 1 longer than the mass array
                                bestOrdinal   = Transition.OffsetToOrdinal(type, offset, len + 1);
                                bestLosses    = lossesTrial;
                                bestMassShift = potentialMassShift;

                                minDelta = delta;
                            }
                        }
                    }
                }
            }

            // Pefer no-shift to shift, even if the shift value is closer
            if (MatchMz(minDelta, tolerance) && !MatchMz(minDeltaNs, tolerance))
            {
                ionType   = bestIonType;
                ordinal   = bestOrdinal;
                losses    = bestLosses;
                massShift = bestMassShift;
                return(bestCharge);
            }

            ionType   = bestIonTypeNs;
            ordinal   = bestOrdinalNs;
            losses    = bestLossesNs;
            massShift = 0;
            return(bestChargeNs);
        }
Example #15
0
        /// <summary>
        /// Test only method for creating a <see cref="BiblioSpecLibrary"/> file
        /// from another loaded <see cref="Library"/>.  Should this move into test project?
        /// </summary>
        /// <param name="streamManager">Provides access to the file system</param>
        /// <param name="path">Path to write to</param>
        /// <param name="library">The loaded library to use as a data source</param>
        public static void Write(IStreamManager streamManager, string path, Library library)
        {
            using (FileSaver fs = new FileSaver(path, streamManager))
                using (Stream outStream = streamManager.CreateStream(fs.SafeName, FileMode.Create, true))
                {
                    outStream.Write(BitConverter.GetBytes(library.SpectrumCount), 0, sizeof(int)); // num_spectra
                    outStream.Write(BitConverter.GetBytes(0), 0, sizeof(int));                     // filtered
                    outStream.Write(BitConverter.GetBytes(1), 0, sizeof(int));                     // version1
                    outStream.Write(BitConverter.GetBytes(1), 0, sizeof(int));                     // version2
                    outStream.Write(BitConverter.GetBytes(0), 0, sizeof(int));                     // next_id

                    SequenceMassCalc calc = new SequenceMassCalc(MassType.Monoisotopic);

                    byte[] seqBuffer = new byte[1024];

                    int scanNum = 1;

                    foreach (var key in library.Keys)
                    {
                        SpectrumPeaksInfo peaksInfo;
                        if (!library.TryLoadSpectrum(key, out peaksInfo))
                        {
                            continue;
                        }

                        string sequence = key.Sequence;
                        // Only works for unmodified sequence
                        Debug.Assert(!key.IsModified);
                        double precursorMH = calc.GetPrecursorMass(sequence);
                        int    charge      = key.Charge;
                        float  precursorMz = (float)SequenceMassCalc.GetMZ(precursorMH, charge);

                        outStream.Write(BitConverter.GetBytes(scanNum), 0, sizeof(int));                // scan_num
                        outStream.Write(BitConverter.GetBytes(2), 0, sizeof(int));                      // scan_type
                        outStream.Write(BitConverter.GetBytes(precursorMz), 0, sizeof(float));          // pre_mz
                        outStream.Write(BitConverter.GetBytes(charge), 0, sizeof(int));                 // scan_type
                        outStream.Write(BitConverter.GetBytes(0f), 0, sizeof(int));                     // r_time
                        outStream.Write(BitConverter.GetBytes(peaksInfo.Peaks.Length), 0, sizeof(int)); // num_peaks
                        outStream.Write(BitConverter.GetBytes(0), 0, sizeof(int));                      // 32-bit peak_ptr
                        outStream.Write(BitConverter.GetBytes(sequence.Length), 0, sizeof(int));        // seq_len
                        outStream.Write(BitConverter.GetBytes(0), 0, sizeof(int));                      // annot
                        outStream.Write(BitConverter.GetBytes(scanNum), 0, sizeof(int));                // copies (bogus value for ranking)
                        outStream.Write(BitConverter.GetBytes(0), 0, sizeof(int));                      // lib_id
                        scanNum++;

                        // Sequence
                        int len = sequence.Length;
                        seqBuffer[len] = 0;
                        Encoding.UTF8.GetBytes(sequence, 0, len, seqBuffer, 0);
                        outStream.Write(seqBuffer, 0, len + 1);
                        // Modifications
                        const string zeros = "000000000000000000000000000000000000000000000000000"; // Not L10N
                        Encoding.UTF8.GetBytes(zeros.Substring(0, len), 0, len, seqBuffer, 0);
                        outStream.Write(seqBuffer, 0, len + 1);
                        // Peaks
                        foreach (var mi in peaksInfo.Peaks)
                        {
                            outStream.Write(BitConverter.GetBytes((float)mi.Mz), 0, sizeof(float));
                            outStream.Write(BitConverter.GetBytes(mi.Intensity), 0, sizeof(float));
                        }
                    }

                    streamManager.Finish(outStream);
                    fs.Commit();
                }
        }
Example #16
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);
        }
Example #17
0
        public static Adduct CalcProductCharge(TypedMass productPrecursorMass,
                                               int?productZ,
                                               Adduct precursorCharge,
                                               IList <IonType> acceptedIonTypes,
                                               IonTable <TypedMass> productMasses,
                                               IList <IList <ExplicitLoss> > potentialLosses,
                                               double productMz,
                                               double tolerance,
                                               MassType massType,
                                               MassShiftType massShiftType,
                                               out IonType?ionType,
                                               out int?ordinal,
                                               out TransitionLosses losses,
                                               out int massShift)
        {
            // Get length of fragment ion mass array
            int len = productMasses.GetLength(1);

            // Check all possible ion types and offsets
            double?minDelta = null;
            double?minFragmentMass = null, maxFragmentMass = null, maxLoss = null;

            if (massShiftType == MassShiftType.none)
            {
                if (!productZ.HasValue)
                {
                    minFragmentMass = productMz - tolerance;
                }
                else
                {
                    minFragmentMass = SequenceMassCalc.GetMH(productMz - tolerance, productZ.Value);
                    maxFragmentMass = SequenceMassCalc.GetMH(productMz + tolerance, productZ.Value);
                }
            }

            var              bestCharge    = Adduct.EMPTY;
            IonType?         bestIonType   = null;
            int?             bestOrdinal   = null;
            TransitionLosses bestLosses    = null;
            int              bestMassShift = 0;

            // Check to see if it is the precursor
            foreach (var lossesTrial in TransitionGroup.CalcTransitionLosses(IonType.precursor, 0, massType, potentialLosses))
            {
                var productMass = productPrecursorMass;
                if (lossesTrial != null)
                {
                    productMass -= lossesTrial.Mass;
                    maxLoss      = Math.Max(maxLoss ?? 0, lossesTrial.Mass);
                }
                int potentialMassShift;
                int nearestCharge;
                var charge = CalcProductCharge(productMass, productZ, productMz, tolerance, false, precursorCharge,
                                               massShiftType, out potentialMassShift, out nearestCharge);
                if (Equals(charge, precursorCharge))
                {
                    double potentialMz = SequenceMassCalc.GetMZ(productMass, charge) + potentialMassShift;
                    double delta       = Math.Abs(productMz - potentialMz);

                    if (CompareIonMatch(delta, lossesTrial, potentialMassShift, minDelta, bestLosses, bestMassShift) < 0)
                    {
                        bestCharge    = charge;
                        bestIonType   = IonType.precursor;
                        bestOrdinal   = len + 1;
                        bestLosses    = lossesTrial;
                        bestMassShift = potentialMassShift;

                        minDelta = delta;
                    }
                }
            }

            if (maxLoss.HasValue)
            {
                maxFragmentMass += maxLoss.Value;
            }

            var categoryLast = -1;

            foreach (var typeAccepted in GetIonTypes(acceptedIonTypes))
            {
                var type     = typeAccepted.IonType;
                var category = typeAccepted.IonCategory;

                // Types have priorities.  If changing type category, and there is already a
                // suitable answer stop looking.
                if (category != categoryLast && minDelta.HasValue && MatchMz(minDelta.Value, tolerance))
                {
                    break;
                }
                categoryLast = category;

                // The peptide length is 1 longer than the mass array
                for (int ord = len; ord > 0; ord--)
                {
                    int offset          = Transition.OrdinalToOffset(type, ord, len + 1);
                    var productMassBase = productMasses[type, offset];
                    // Until below the maximum fragment mass no possible matches
                    if (maxFragmentMass.HasValue && productMassBase > maxFragmentMass.Value)
                    {
                        continue;
                    }
                    // Once below the minimum fragment mass no more possible matches, so stop
                    if (minFragmentMass.HasValue && productMassBase < minFragmentMass.Value)
                    {
                        break;
                    }

                    foreach (var lossesTrial in TransitionGroup.CalcTransitionLosses(type, offset, massType, potentialLosses))
                    {
                        // Look for the closest match.
                        var productMass = productMassBase;
                        if (lossesTrial != null)
                        {
                            productMass -= lossesTrial.Mass;
                        }
                        int potentialMassShift;
                        int nearestCharge;
                        var chargeFound = CalcProductCharge(productMass, productZ, productMz, tolerance, false, precursorCharge,
                                                            massShiftType, out potentialMassShift, out nearestCharge);
                        if (!chargeFound.IsEmpty)
                        {
                            var    charge      = chargeFound;
                            double potentialMz = SequenceMassCalc.GetMZ(productMass, charge) + potentialMassShift;
                            double delta       = Math.Abs(productMz - potentialMz);
                            if (CompareIonMatch(delta, lossesTrial, potentialMassShift, minDelta, bestLosses, bestMassShift) < 0)
                            {
                                bestCharge    = charge;
                                bestIonType   = type;
                                bestOrdinal   = ord;
                                bestLosses    = lossesTrial;
                                bestMassShift = potentialMassShift;

                                minDelta = delta;
                            }
                        }
                    }
                }
            }

            ionType   = bestIonType;
            ordinal   = bestOrdinal;
            losses    = bestLosses;
            massShift = bestMassShift;
            return(bestCharge);
        }
Example #18
0
        public IEnumerable <TransitionDocNode> GetPrecursorTransitions(SrmSettings settings,
                                                                       ExplicitMods mods,
                                                                       IPrecursorMassCalc calcFilterPre,
                                                                       IFragmentMassCalc calcPredict,
                                                                       double precursorMz,
                                                                       IsotopeDistInfo isotopeDist,
                                                                       IList <IList <ExplicitLoss> > potentialLosses,
                                                                       IDictionary <double, LibraryRankedSpectrumInfo.RankedMI> transitionRanks,
                                                                       bool libraryFilter,
                                                                       bool useFilter)
        {
            var      tranSettings = settings.TransitionSettings;
            var      fullScan     = tranSettings.FullScan;
            MassType massType     = tranSettings.Prediction.FragmentMassType;
            int      minMz        = tranSettings.Instrument.GetMinMz(precursorMz);
            int      maxMz        = tranSettings.Instrument.MaxMz;
            bool     precursorMS1 = fullScan.IsEnabledMs;

            if (IsCustomIon)
            {
                var ionMz =
                    BioMassCalc.CalculateIonMz(
                        CustomIon.GetMass(settings.TransitionSettings.Prediction.PrecursorMassType),
                        PrecursorCharge);
                if (!useFilter ||
                    !libraryFilter ||
                    IsMatched(transitionRanks,
                              ionMz, IonType.precursor,
                              PrecursorCharge, null))
                {
                    if (precursorMS1 && isotopeDist != null)
                    {
                        foreach (int i in fullScan.SelectMassIndices(isotopeDist, useFilter))
                        {
                            double precursorMS1Mass = isotopeDist.GetMassI(i);
                            ionMz = BioMassCalc.CalculateIonMz(precursorMS1Mass, PrecursorCharge);
                            if (minMz > ionMz || ionMz > maxMz)
                            {
                                continue;
                            }
                            var isotopeDistInfo = new TransitionIsotopeDistInfo(isotopeDist.GetRankI(i), isotopeDist.GetProportionI(i));
                            yield return(CreateTransitionNode(i, precursorMS1Mass, isotopeDistInfo, null, transitionRanks, CustomIon));
                        }
                    }
                    else
                    {
                        var    transition = new Transition(this, PrecursorCharge, null, CustomIon, IonType.precursor);
                        double massH      = CustomIon.GetMass(settings.TransitionSettings.Prediction.PrecursorMassType);
                        yield return(new TransitionDocNode(transition, null, massH, null, null));
                    }
                }
                yield break;
            }

            string sequence            = Peptide.Sequence;
            bool   precursorNoProducts = precursorMS1 && !fullScan.IsEnabledMsMs &&
                                         tranSettings.Filter.IonTypes.Count == 1 && tranSettings.Filter.IonTypes[0] == IonType.precursor;
            double precursorMassPredict = calcPredict.GetPrecursorFragmentMass(sequence);

            foreach (var losses in CalcTransitionLosses(IonType.precursor, 0, massType, potentialLosses))
            {
                double ionMz = SequenceMassCalc.GetMZ(Transition.CalcMass(precursorMassPredict, losses), PrecursorCharge);
                if (losses == null)
                {
                    if (precursorMS1 && isotopeDist != null)
                    {
                        foreach (int i in fullScan.SelectMassIndices(isotopeDist, useFilter))
                        {
                            double precursorMS1Mass = isotopeDist.GetMassI(i, DecoyMassShift);
                            ionMz = SequenceMassCalc.GetMZ(precursorMS1Mass, PrecursorCharge);
                            if (minMz > ionMz || ionMz > maxMz)
                            {
                                continue;
                            }
                            var isotopeDistInfo = new TransitionIsotopeDistInfo(
                                isotopeDist.GetRankI(i), isotopeDist.GetProportionI(i));
                            yield return(CreateTransitionNode(i, precursorMS1Mass, isotopeDistInfo, null, transitionRanks));
                        }
                        continue;
                    }
                }
                // If there was loss, it is possible (though not likely) that the ion m/z value
                // will now fall below the minimum measurable value for the instrument
                else if (minMz > ionMz)
                {
                    continue;
                }

                // If filtering precursors from MS1 scans, then ranking in MS/MS does not apply
                bool precursorIsProduct = !precursorMS1 || losses != null;
                // Skip product ion precursors, if the should not be included
                if (useFilter && precursorIsProduct && precursorNoProducts)
                {
                    continue;
                }
                if (!useFilter || !precursorIsProduct ||
                    !libraryFilter || IsMatched(transitionRanks, ionMz, IonType.precursor,
                                                PrecursorCharge, losses))
                {
                    yield return(CreateTransitionNode(0, precursorMassPredict, null, losses,
                                                      precursorIsProduct ? transitionRanks : null));
                }
            }
        }
Example #19
0
        public IEnumerable <TransitionDocNode> GetTransitions(SrmSettings settings,
                                                              TransitionGroupDocNode groupDocNode,
                                                              ExplicitMods mods,
                                                              double precursorMz,
                                                              IsotopeDistInfo isotopeDist,
                                                              SpectrumHeaderInfo libInfo,
                                                              IDictionary <double, LibraryRankedSpectrumInfo.RankedMI> transitionRanks,
                                                              bool useFilter)
        {
            Assume.IsTrue(ReferenceEquals(groupDocNode.TransitionGroup, this));
            // Get necessary mass calculators and masses
            var calcFilterPre = settings.GetPrecursorCalc(IsotopeLabelType.light, mods);
            var calcFilter    = settings.GetFragmentCalc(IsotopeLabelType.light, mods);
            var calcPredict   = settings.GetFragmentCalc(LabelType, mods);

            string sequence = Peptide.Sequence;

            // Save the true precursor m/z for TranstionSettings.Accept() now that all isotope types are
            // checked.  This is more correct than just using the light precursor m/z for precursor window
            // exclusion.
            double precursorMzAccept = precursorMz;

            if (!ReferenceEquals(calcFilter, calcPredict))
            {
                // Get the normal precursor m/z for filtering, so that light and heavy ion picks will match.
                precursorMz = IsCustomIon ?
                              BioMassCalc.CalculateIonMz(calcFilterPre.GetPrecursorMass(groupDocNode.CustomIon), groupDocNode.TransitionGroup.PrecursorCharge) :
                              SequenceMassCalc.GetMZ(calcFilterPre.GetPrecursorMass(sequence), groupDocNode.TransitionGroup.PrecursorCharge);
            }
            if (!IsAvoidMismatchedIsotopeTransitions)
            {
                precursorMzAccept = precursorMz;
            }

            var      tranSettings      = settings.TransitionSettings;
            var      filter            = tranSettings.Filter;
            var      charges           = filter.ProductCharges;
            var      startFinder       = filter.FragmentRangeFirst;
            var      endFinder         = filter.FragmentRangeLast;
            double   precursorMzWindow = filter.PrecursorMzWindow;
            var      types             = filter.IonTypes;
            MassType massType          = tranSettings.Prediction.FragmentMassType;
            int      minMz             = tranSettings.Instrument.GetMinMz(precursorMzAccept);
            int      maxMz             = tranSettings.Instrument.MaxMz;

            var pepMods         = settings.PeptideSettings.Modifications;
            var potentialLosses = CalcPotentialLosses(sequence, pepMods, mods, massType);

            // A start m/z will need to be calculated if the start fragment
            // finder uses m/z and their are losses to consider.  If the filter
            // is set to only consider fragments with m/z greater than the
            // precursor, the code below needs to also prevent loss fragments
            // from being under that m/z.
            double startMz = 0;

            // Get library settings
            var pick = tranSettings.Libraries.Pick;

            if (!useFilter)
            {
                pick = TransitionLibraryPick.all;
                var listAll = Transition.ALL_CHARGES.ToList();
                listAll.AddRange(charges.Where(c => !Transition.ALL_CHARGES.Contains(c)));
                listAll.Sort();
                charges = listAll.ToArray();
                types   = Transition.ALL_TYPES;
            }
            // If there are no libraries or no library information, then
            // picking cannot use library information
            else if (!settings.PeptideSettings.Libraries.HasLibraries || libInfo == null)
            {
                pick = TransitionLibraryPick.none;
            }

            // If filtering without library picking
            if (potentialLosses != null)
            {
                if (pick == TransitionLibraryPick.none)
                {
                    // Only include loss combinations where all losses are included always
                    potentialLosses = potentialLosses.Where(losses =>
                                                            losses.All(loss => loss.TransitionLoss.Loss.Inclusion == LossInclusion.Always)).ToArray();
                }
                else if (useFilter)
                {
                    // Exclude all losses which should never be included by default
                    potentialLosses = potentialLosses.Where(losses =>
                                                            losses.All(loss => loss.TransitionLoss.Loss.Inclusion != LossInclusion.Never)).ToArray();
                }
                if (!potentialLosses.Any())
                {
                    potentialLosses = null;
                }
            }

            // Return precursor ions
            if (!useFilter || types.Contains(IonType.precursor))
            {
                bool libraryFilter = (pick == TransitionLibraryPick.all || pick == TransitionLibraryPick.filter);
                foreach (var nodeTran in GetPrecursorTransitions(settings, mods, calcFilterPre, calcPredict,
                                                                 precursorMz, isotopeDist, potentialLosses, transitionRanks, libraryFilter, useFilter))
                {
                    if (minMz <= nodeTran.Mz && nodeTran.Mz <= maxMz)
                    {
                        yield return(nodeTran);
                    }
                }
            }

            // Return special ions from settings, if this is a peptide
            if (!IsCustomIon)
            {
                // This is a peptide, but it may have custom transitions (reporter ions), check those
                foreach (var measuredIon in tranSettings.Filter.MeasuredIons.Where(m => m.IsCustom))
                {
                    if (useFilter && measuredIon.IsOptional)
                    {
                        continue;
                    }
                    var    tran     = new Transition(this, measuredIon.Charge, null, measuredIon.CustomIon);
                    double mass     = settings.GetFragmentMass(IsotopeLabelType.light, null, tran, null);
                    var    nodeTran = new TransitionDocNode(tran, null, mass, null, null);
                    if (minMz <= nodeTran.Mz && nodeTran.Mz <= maxMz)
                    {
                        yield return(nodeTran);
                    }
                }
            }

            // For small molecules we can't generate new nodes, so just mz filter those we have
            foreach (var nodeTran in groupDocNode.Transitions.Where(tran => tran.Transition.IsNonPrecursorNonReporterCustomIon()))
            {
                if (minMz <= nodeTran.Mz && nodeTran.Mz <= maxMz)
                {
                    yield return(nodeTran);
                }
            }

            if (sequence == null) // Completely custom
            {
                yield break;
            }

            // If picking relies on library information
            if (useFilter && pick != TransitionLibraryPick.none)
            {
                // If it is not yet loaded, or nothing got ranked, return an empty enumeration
                if (!settings.PeptideSettings.Libraries.IsLoaded ||
                    (transitionRanks != null && transitionRanks.Count == 0))
                {
                    yield break;
                }
            }

            double[,] massesPredict = calcPredict.GetFragmentIonMasses(sequence);
            int len = massesPredict.GetLength(1);

            if (len == 0)
            {
                yield break;
            }

            double[,] massesFilter = massesPredict;
            if (!ReferenceEquals(calcFilter, calcPredict))
            {
                // Get the normal m/z values for filtering, so that light and heavy
                // ion picks will match.
                massesFilter = calcFilter.GetFragmentIonMasses(sequence);
            }

            // Get types other than this to make sure matches are possible for all types
            var listOtherTypes = new List <Tuple <TransitionGroupDocNode, IFragmentMassCalc> >();

            foreach (var labelType in settings.PeptideSettings.Modifications.GetModificationTypes())
            {
                if (Equals(labelType, LabelType))
                {
                    continue;
                }
                var calc = settings.GetFragmentCalc(labelType, mods);
                if (calc == null)
                {
                    continue;
                }
                var tranGroupOther = new TransitionGroup(Peptide, PrecursorCharge, labelType, false, DecoyMassShift);
                var nodeGroupOther = new TransitionGroupDocNode(tranGroupOther, Annotations.EMPTY, settings, mods,
                                                                libInfo, ExplicitTransitionGroupValues.EMPTY, null, new TransitionDocNode[0], false);

                listOtherTypes.Add(new Tuple <TransitionGroupDocNode, IFragmentMassCalc>(nodeGroupOther, calc));
            }

            // Loop over potential product ions picking transitions
            foreach (IonType type in types)
            {
                // Precursor type is handled above.
                if (type == IonType.precursor)
                {
                    continue;
                }

                foreach (int charge in charges)
                {
                    // Precursor charge can never be lower than product ion charge.
                    if (Math.Abs(PrecursorCharge) < Math.Abs(charge))
                    {
                        continue;
                    }

                    int start = 0, end = 0;
                    if (pick != TransitionLibraryPick.all)
                    {
                        start = startFinder.FindStartFragment(massesFilter, type, charge,
                                                              precursorMz, precursorMzWindow, out startMz);
                        end = endFinder.FindEndFragment(type, start, len);
                        if (Transition.IsCTerminal(type))
                        {
                            Helpers.Swap(ref start, ref end);
                        }
                    }

                    for (int i = 0; i < len; i++)
                    {
                        // Get the predicted m/z that would be used in the transition
                        double massH = massesPredict[(int)type, i];
                        foreach (var losses in CalcTransitionLosses(type, i, massType, potentialLosses))
                        {
                            double ionMz = SequenceMassCalc.GetMZ(Transition.CalcMass(massH, losses), charge);

                            // Make sure the fragment m/z value falls within the valid instrument range.
                            // CONSIDER: This means that a heavy transition might excede the instrument
                            //           range where a light one is accepted, leading to a disparity
                            //           between heavy and light transtions picked.
                            if (minMz > ionMz || ionMz > maxMz)
                            {
                                continue;
                            }

                            TransitionDocNode nodeTranReturn = null;
                            bool accept = true;
                            if (pick == TransitionLibraryPick.all || pick == TransitionLibraryPick.all_plus)
                            {
                                if (!useFilter)
                                {
                                    nodeTranReturn = CreateTransitionNode(type, i, charge, massH, losses, transitionRanks);
                                    accept         = false;
                                }
                                else
                                {
                                    if (IsMatched(transitionRanks, ionMz, type, charge, losses))
                                    {
                                        nodeTranReturn = CreateTransitionNode(type, i, charge, massH, losses, transitionRanks);
                                        accept         = false;
                                    }
                                    // If allowing library or filter, check the filter to decide whether to accept
                                    else if (pick == TransitionLibraryPick.all_plus &&
                                             tranSettings.Accept(sequence, precursorMzAccept, type, i, ionMz, start, end, startMz))
                                    {
                                        nodeTranReturn = CreateTransitionNode(type, i, charge, massH, losses, transitionRanks);
                                    }
                                }
                            }
                            else if (tranSettings.Accept(sequence, precursorMzAccept, type, i, ionMz, start, end, startMz))
                            {
                                if (pick == TransitionLibraryPick.none)
                                {
                                    nodeTranReturn = CreateTransitionNode(type, i, charge, massH, losses, transitionRanks);
                                }
                                else
                                {
                                    if (IsMatched(transitionRanks, ionMz, type, charge, losses))
                                    {
                                        nodeTranReturn = CreateTransitionNode(type, i, charge, massH, losses, transitionRanks);
                                    }
                                }
                            }
                            if (nodeTranReturn != null)
                            {
                                if (IsAvoidMismatchedIsotopeTransitions &&
                                    !OtherLabelTypesAllowed(settings, minMz, maxMz, start, end, startMz, accept,
                                                            groupDocNode, nodeTranReturn, listOtherTypes))
                                {
                                    continue;
                                }
                                Assume.IsTrue(minMz <= nodeTranReturn.Mz && nodeTranReturn.Mz <= maxMz);
                                yield return(nodeTranReturn);
                            }
                        }
                    }
                }
            }
        }
Example #20
0
        public void DoFullScanSettingsTest(RefinementSettings.ConvertToSmallMoleculesMode asSmallMolecules,
                                           out List <SrmDocument> docCheckPoints)
        {
            docCheckPoints = new List <SrmDocument>();

            var doc0   = ResultsUtil.DeserializeDocument("MultiLabel.sky", GetType());
            var refine = new RefinementSettings();
            var docSM  = refine.ConvertToSmallMolecules(doc0, ".", asSmallMolecules);

            docCheckPoints.Add(docSM);
            Assert.IsFalse(docSM.MoleculeTransitionGroups.Any(nodeGroup => nodeGroup.IsotopeDist != null));
            AssertEx.Serializable(docSM, AssertEx.Cloned);

            double c13Delta = BioMassCalc.MONOISOTOPIC.GetMass(BioMassCalc.C13) -
                              BioMassCalc.MONOISOTOPIC.GetMass(BioMassCalc.C);
            double n15Delta = BioMassCalc.MONOISOTOPIC.GetMass(BioMassCalc.N15) -
                              BioMassCalc.MONOISOTOPIC.GetMass(BioMassCalc.N);

            // Verify isotope distributions calculated when MS1 filtering enabled
            var enrichments = IsotopeEnrichmentsList.DEFAULT;
            var docIsotopes = docSM.ChangeSettings(docSM.Settings.ChangeTransitionFullScan(fs =>
                                                                                           fs.ChangePrecursorIsotopes(FullScanPrecursorIsotopes.Count, 3, enrichments)));

            docCheckPoints.Add(docIsotopes);
            Assert.AreEqual(FullScanMassAnalyzerType.tof,
                            docIsotopes.Settings.TransitionSettings.FullScan.PrecursorMassAnalyzer);
            Assert.IsFalse(docIsotopes.MoleculeTransitionGroups.Any(nodeGroup => nodeGroup.IsotopeDist == null));
            foreach (var nodeGroup in docIsotopes.MoleculeTransitionGroups)
            {
                Assert.AreEqual(3, nodeGroup.Children.Count);
                var isotopePeaks = nodeGroup.IsotopeDist;
                Assert.IsNotNull(isotopePeaks);
                Assert.IsTrue(nodeGroup.HasIsotopeDist);
                // The peaks should always includ at least M-1
                Assume.IsTrue(isotopePeaks.MassIndexToPeakIndex(0) > 0);
                // Within 2.5% of 100% of the entire isotope distribution
                Assert.AreEqual(1.0, isotopePeaks.ExpectedProportions.Sum(), 0.025);

                // Precursor mass and m/z values are expected to match exactly (well, within XML roundtrip accuracy anyway)

                Assert.AreEqual(nodeGroup.PrecursorMz, nodeGroup.IsotopeDist.GetMZI(0), SequenceMassCalc.MassTolerance);
                Assert.AreEqual(nodeGroup.PrecursorMz, nodeGroup.TransitionGroup.IsCustomIon ?
                                BioMassCalc.CalculateIonMz(nodeGroup.IsotopeDist.GetMassI(0),
                                                           nodeGroup.TransitionGroup.PrecursorAdduct.Unlabeled) :
                                SequenceMassCalc.GetMZ(nodeGroup.IsotopeDist.GetMassI(0),
                                                       nodeGroup.TransitionGroup.PrecursorAdduct), SequenceMassCalc.MassTolerance);

                // Check isotope distribution masses
                for (int i = 1; i < isotopePeaks.CountPeaks; i++)
                {
                    int massIndex = isotopePeaks.PeakIndexToMassIndex(i);
                    Assert.IsTrue(isotopePeaks.GetMZI(massIndex - 1) < isotopePeaks.GetMZI(massIndex));
                    double massDelta = GetMassDelta(isotopePeaks, massIndex);
                    if (nodeGroup.TransitionGroup.LabelType.IsLight)
                    {
                        // All positive should be close to 13C - C, and 0 should be the same as the next delta
                        double expectedDelta = (massIndex > 0 ? c13Delta : GetMassDelta(isotopePeaks, massIndex + 1));
                        Assert.AreEqual(expectedDelta, massDelta, 0.001);
                    }
                    else if (nodeGroup.TransitionGroup.LabelType.Name.Contains("15N"))
                    {
                        // All positive should be close to 13C, and all negative 15N
                        double expectedDelta = (massIndex > 0 ? c13Delta : n15Delta);
                        Assert.AreEqual(expectedDelta, massDelta, 0.0015);
                    }
                    else if (massIndex == 0)
                    {
                        double expectedDelta = (isotopePeaks.GetProportionI(massIndex - 1) == 0
                                                    ? GetMassDelta(isotopePeaks, massIndex + 1)
                                                    : 1.0017);
                        Assert.AreEqual(expectedDelta, massDelta, 0.001);
                    }
                    else
                    {
                        Assert.AreEqual(c13Delta, massDelta, 0.001);
                    }
                }
            }
            AssertEx.Serializable(docIsotopes, AssertEx.Cloned);

            // Narrow the resolution, and verify that predicted proportion of the isotope
            // distribution captured is reduced for all precursors
            var docIsotopesFt = docIsotopes.ChangeSettings(docIsotopes.Settings.ChangeTransitionFullScan(fs =>
                                                                                                         fs.ChangePrecursorResolution(FullScanMassAnalyzerType.ft_icr, 500 * 1000, 400)));

            docCheckPoints.Add(docIsotopesFt);
            var tranGroupsOld = docIsotopes.MoleculeTransitionGroups.ToArray();
            var tranGroupsNew = docIsotopesFt.MoleculeTransitionGroups.ToArray();

            Assume.AreEqual(tranGroupsOld.Length, tranGroupsNew.Length);
            for (int i = 0; i < tranGroupsOld.Length; i++)
            {
                Assert.AreNotSame(tranGroupsOld[i], tranGroupsNew[i]);
                Assert.AreNotSame(tranGroupsOld[i].IsotopeDist, tranGroupsNew[i].IsotopeDist);
                Assert.IsTrue(tranGroupsOld[i].IsotopeDist.ExpectedProportions.Sum() >
                              tranGroupsNew[i].IsotopeDist.ExpectedProportions.Sum());
            }

            // Use Min % of base peak and verify variation in transitions used
            const float minPercent1   = 10;
            var         docIsotopesP1 = docIsotopes.ChangeSettings(docIsotopes.Settings.ChangeTransitionFullScan(fs =>
                                                                                                                 fs.ChangePrecursorIsotopes(FullScanPrecursorIsotopes.Percent, minPercent1, enrichments)));

            docCheckPoints.Add(docIsotopesP1);
            tranGroupsNew = docIsotopesP1.MoleculeTransitionGroups.ToArray();
            int maxTran = 0;

            for (int i = 0; i < tranGroupsOld.Length; i++)
            {
                // Isotope distributions should not have changed
                var isotopePeaks = tranGroupsNew[i].IsotopeDist;
                Assert.AreSame(tranGroupsOld[i].IsotopeDist, isotopePeaks);
                // Expected transitions should be present
                maxTran = Math.Max(maxTran, tranGroupsNew[i].Children.Count);
                foreach (TransitionDocNode nodeTran in tranGroupsNew[i].Children)
                {
                    int massIndex = nodeTran.Transition.MassIndex;
                    Assume.IsTrue(minPercent1 <= isotopePeaks.GetProportionI(massIndex) * 100.0 / isotopePeaks.BaseMassPercent);
                }
            }
            Assume.AreEqual(5, maxTran);
            AssertEx.Serializable(docIsotopesP1, AssertEx.Cloned);  // Express any failure in terms of XML diffs

            // Use 10%, and check that 15N modifications all have M-1
            const float minPercent2   = 5;
            var         docIsotopesP2 = docIsotopesP1.ChangeSettings(docIsotopesP1.Settings.ChangeTransitionFullScan(fs =>
                                                                                                                     fs.ChangePrecursorIsotopes(FullScanPrecursorIsotopes.Percent, minPercent2, enrichments)));

            docCheckPoints.Add(docIsotopesP2);

            foreach (var nodeGroup in docIsotopesP2.MoleculeTransitionGroups)
            {
                var firstChild = (TransitionDocNode)nodeGroup.Children[0];
                if (nodeGroup.TransitionGroup.LabelType.Name.EndsWith("15N"))
                {
                    Assume.AreEqual(-1, firstChild.Transition.MassIndex);
                }
                else
                {
                    Assume.AreNotEqual(-1, firstChild.Transition.MassIndex);
                }
            }
            AssertEx.Serializable(docIsotopesP2, AssertEx.Cloned);

            // Use lower enrichment of 13C, and verify that this add M-1 for 13C labeled precursors
            var enrichmentsLow13C = enrichments.ChangeEnrichment(new IsotopeEnrichmentItem(BioMassCalc.C13, 0.9));
            var docIsotopesLow13C = docIsotopesP1.ChangeSettings(docIsotopesP1.Settings.ChangeTransitionFullScan(fs =>
                                                                                                                 fs.ChangePrecursorIsotopes(FullScanPrecursorIsotopes.Percent, minPercent2, enrichmentsLow13C)));

            tranGroupsNew = docIsotopesLow13C.MoleculeTransitionGroups.ToArray();
            for (int i = 0; i < tranGroupsOld.Length; i++)
            {
                var nodeGroup = tranGroupsNew[i];
                if (!Equals(nodeGroup.TransitionGroup.LabelType.Name, "heavy"))
                {
                    Assert.AreSame(tranGroupsOld[i].IsotopeDist, nodeGroup.IsotopeDist);
                }
                else
                {
                    var firstChild = (TransitionDocNode)nodeGroup.Children[0];
                    Assert.IsTrue(firstChild.Transition.MassIndex < 0);
                }
            }
            AssertEx.Serializable(docIsotopesLow13C, AssertEx.Cloned); // Express any failure as XML diffs

            // Use 0%, and check that everything has M-1 and lower
            var enrichmentsLow   = enrichmentsLow13C.ChangeEnrichment(new IsotopeEnrichmentItem(BioMassCalc.N15, 0.97));
            var docIsotopesLowP0 = docIsotopesP1.ChangeSettings(docIsotopesP1.Settings.ChangeTransitionFullScan(fs =>
                                                                                                                fs.ChangePrecursorIsotopes(FullScanPrecursorIsotopes.Percent, 0, enrichmentsLow)));

            docCheckPoints.Add(docIsotopesLowP0);
            foreach (var nodeGroup in docIsotopesLowP0.MoleculeTransitionGroups)
            {
                Assume.AreEqual(nodeGroup.IsotopeDist.CountPeaks, nodeGroup.Children.Count);
                var firstChild = (TransitionDocNode)nodeGroup.Children[0];
                if (nodeGroup.TransitionGroup.LabelType.IsLight)
                {
                    Assert.AreEqual(-1, firstChild.Transition.MassIndex);
                }
                else
                {
                    Assert.IsTrue(-1 > firstChild.Transition.MassIndex);
                }
            }
            AssertEx.Serializable(docIsotopesLowP0, AssertEx.Cloned);

            // Test a document with variable and heavy modifications, which caused problems for
            // the original implementation
            var docVariable = ResultsUtil.DeserializeDocument("HeavyVariable.sky", GetType());

            Assert.IsFalse(docVariable.MoleculeTransitionGroups.Any(nodeGroup => nodeGroup.IsotopeDist == null));

            foreach (var nodeGroup in docVariable.MoleculeTransitionGroups)
            {
                var isotopePeaks = nodeGroup.IsotopeDist;
                Assert.IsNotNull(isotopePeaks);
                // The peaks should always includ at least M-1
                Assert.IsTrue(isotopePeaks.MassIndexToPeakIndex(0) > 0);
                // Precursor mass and m/z values are expected to match exactly (well, within XML roundtrip tolerance anyway)
                var mzI = nodeGroup.IsotopeDist.GetMZI(0);
                Assert.AreEqual(nodeGroup.PrecursorMz, mzI, SequenceMassCalc.MassTolerance);

                // Check isotope distribution masses
                for (int i = 1; i < isotopePeaks.CountPeaks; i++)
                {
                    int massIndex = isotopePeaks.PeakIndexToMassIndex(i);
                    Assert.IsTrue(isotopePeaks.GetMZI(massIndex - 1) < isotopePeaks.GetMZI(massIndex));
                    double massDelta      = GetMassDelta(isotopePeaks, massIndex);
                    bool   containsSulfur = nodeGroup.TransitionGroup.Peptide.IsCustomMolecule
                        ? (nodeGroup.CustomMolecule.Formula.IndexOfAny("S".ToCharArray()) != -1)
                        : (nodeGroup.TransitionGroup.Peptide.Sequence.IndexOfAny("CM".ToCharArray()) != -1);
                    if (massIndex == 0)
                    {
                        double expectedDelta = (isotopePeaks.GetProportionI(massIndex - 1) == 0
                                                    ? GetMassDelta(isotopePeaks, massIndex + 1)
                                                    : 1.0017);
                        Assert.AreEqual(expectedDelta, massDelta, 0.001);
                    }
                    else if (!containsSulfur || massIndex == 1)
                    {
                        Assert.AreEqual(c13Delta, massDelta, 0.001);
                    }
                    else
                    {
                        Assert.AreEqual(1.00075, massDelta, 0.001);
                    }
                }
            }
            docCheckPoints.Add(docVariable);
        }
        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);
        }
Example #22
0
        public static Adduct CalcProductCharge(TypedMass productPrecursorMass,
                                               Adduct precursorCharge,
                                               IList <IonType> acceptedIonTypes,
                                               IonTable <TypedMass> productMasses,
                                               IList <IList <ExplicitLoss> > potentialLosses,
                                               double productMz,
                                               double tolerance,
                                               MassType massType,
                                               MassShiftType massShiftType,
                                               out IonType?ionType,
                                               out int?ordinal,
                                               out TransitionLosses losses,
                                               out int massShift)
        {
            // Get length of fragment ion mass array
            int len = productMasses.GetLength(1);

            // Check all possible ion types and offsets
            double?          minDelta      = null;
            var              bestCharge    = Adduct.EMPTY;
            IonType?         bestIonType   = null;
            int?             bestOrdinal   = null;
            TransitionLosses bestLosses    = null;
            int              bestMassShift = 0;

            // Check to see if it is the precursor
            foreach (var lossesTrial in TransitionGroup.CalcTransitionLosses(IonType.precursor, 0, massType, potentialLosses))
            {
                var productMass = productPrecursorMass - (lossesTrial != null ? lossesTrial.Mass : 0);
                int potentialMassShift;
                int nearestCharge;
                var charge = CalcProductCharge(productMass, productMz, tolerance, false, precursorCharge,
                                               massShiftType, out potentialMassShift, out nearestCharge);
                if (Equals(charge, precursorCharge))
                {
                    double potentialMz = SequenceMassCalc.GetMZ(productMass, charge) + potentialMassShift;
                    double delta       = Math.Abs(productMz - potentialMz);

                    if (CompareIonMatch(delta, lossesTrial, potentialMassShift, minDelta, bestLosses, bestMassShift) < 0)
                    {
                        bestCharge    = charge;
                        bestIonType   = IonType.precursor;
                        bestOrdinal   = len + 1;
                        bestLosses    = lossesTrial;
                        bestMassShift = potentialMassShift;

                        minDelta = delta;
                    }
                }
            }

            var categoryLast = -1;

            foreach (var typeAccepted in GetIonTypes(acceptedIonTypes))
            {
                var type     = typeAccepted.IonType;
                var category = typeAccepted.IonCategory;

                // Types have priorities.  If changing type category, and there is already a
                // suitable answer stop looking.
                if (category != categoryLast && minDelta.HasValue && MatchMz(minDelta.Value, tolerance))
                {
                    break;
                }
                categoryLast = category;

                for (int offset = 0; offset < len; offset++)
                {
                    foreach (var lossesTrial in TransitionGroup.CalcTransitionLosses(type, offset, massType, potentialLosses))
                    {
                        // Look for the closest match.
                        var productMass = productMasses[type, offset];
                        if (lossesTrial != null)
                        {
                            productMass -= lossesTrial.Mass;
                        }
                        int potentialMassShift;
                        int nearestCharge;
                        var chargeFound = CalcProductCharge(productMass, productMz, tolerance, false, precursorCharge,
                                                            massShiftType, out potentialMassShift, out nearestCharge);
                        if (!chargeFound.IsEmpty)
                        {
                            var    charge      = chargeFound;
                            double potentialMz = SequenceMassCalc.GetMZ(productMass, charge) + potentialMassShift;
                            double delta       = Math.Abs(productMz - potentialMz);
                            if (CompareIonMatch(delta, lossesTrial, potentialMassShift, minDelta, bestLosses, bestMassShift) < 0)
                            {
                                bestCharge  = charge;
                                bestIonType = type;
                                // The peptide length is 1 longer than the mass array
                                bestOrdinal   = Transition.OffsetToOrdinal(type, offset, len + 1);
                                bestLosses    = lossesTrial;
                                bestMassShift = potentialMassShift;

                                minDelta = delta;
                            }
                        }
                    }
                }
            }

            ionType   = bestIonType;
            ordinal   = bestOrdinal;
            losses    = bestLosses;
            massShift = bestMassShift;
            return(bestCharge);
        }
Example #23
0
            private bool MatchNext(RankParams rp, IonType type, int offset, TransitionLosses losses, Adduct adduct, string fragmentName, int len, bool filter, int end, int start, double startMz)
            {
                bool isFragment = !Transition.IsPrecursor(type);
                var  ionMass    = isFragment ? rp.massesMatch[type, offset] : rp.massPreMatch;

                if (losses != null)
                {
                    ionMass -= losses.Mass;
                }
                double ionMz = SequenceMassCalc.GetMZ(ionMass, adduct);

                // Unless trying to match everything, stop looking outside the instrument range
                if (!rp.matchAll && !rp.HasLosses && ionMz > rp.maxMz)
                {
                    return(false);
                }
                // Check filter properties, if apropriate
                if ((rp.matchAll || ionMz >= rp.minMz) && Math.Abs(ionMz - ObservedMz) < rp.tolerance)
                {
                    // Make sure each m/z value is only used for the most intense peak
                    // that is within the tolerance range.
                    if (rp.IsSeen(ionMz))
                    {
                        return(true); // Keep looking
                    }
                    rp.Seen(ionMz);

                    int ordinal = Transition.OffsetToOrdinal(type, offset, len + 1);
                    // If this m/z aready matched a different ion, just remember the second ion.
                    var predictedMass = isFragment ? rp.massesPredict[type, offset] : rp.massPrePredict;
                    if (losses != null)
                    {
                        predictedMass -= losses.Mass;
                    }
                    double predictedMz = SequenceMassCalc.GetMZ(predictedMass, adduct);
                    if (MatchedIons != null)
                    {
                        // If first type was excluded from causing a ranking, but second does, then make it the first
                        // Otherwise, this can cause very mysterious failures to rank transitions that appear in the
                        // document.
                        var match = new MatchedFragmentIon(type, ordinal, adduct, fragmentName, losses, predictedMz);
                        if (Rank == 0 && ApplyRanking(rp, type, offset, losses, adduct, filter, start, end, startMz, ionMz))
                        {
                            MatchedIons.Insert(0, match);
                        }
                        else
                        {
                            MatchedIons.Add(match);
                        }
                        if (MatchedIons.Count < RankParams.MAX_MATCH)
                        {
                            return(true);
                        }

                        rp.matched = true;
                        return(false);
                    }

                    // Avoid using the same predicted m/z on two different peaks
                    if (predictedMz == ionMz || !rp.IsSeen(predictedMz))
                    {
                        rp.Seen(predictedMz);

                        ApplyRanking(rp, type, offset, losses, adduct, filter, start, end, startMz, ionMz);

                        MatchedIons = new List <MatchedFragmentIon> {
                            new MatchedFragmentIon(type, ordinal, adduct, fragmentName, losses, predictedMz)
                        };
                        rp.matched = !rp.matchAll;
                        return(rp.matchAll);
                    }
                }
                // Stop looking once the mass has been passed, unless there are losses to consider
                if (rp.HasLosses)
                {
                    return(true);
                }
                return(ionMz <= ObservedMz);
            }
            private bool MatchNext(RankParams rp, IonType type, int offset, TransitionLosses losses, int charge, int len, bool filter, int end, int start, double startMz)
            {
                bool   isFragment = Transition.IsFragment(type);
                double ionMass    = isFragment ? rp.massesMatch[(int)type, offset] : rp.massPreMatch;

                if (losses != null)
                {
                    ionMass -= losses.Mass;
                }
                double ionMz = SequenceMassCalc.GetMZ(ionMass, charge);

                // Unless trying to match everything, stop looking outside the instrument range
                if (!rp.matchAll && !rp.HasLosses && ionMz > rp.maxMz)
                {
                    return(false);
                }
                // Check filter properties, if apropriate
                if ((rp.matchAll || ionMz >= rp.minMz) && Math.Abs(ionMz - ObservedMz) < rp.tolerance)
                {
                    // Make sure each m/z value is only used for the most intense peak
                    // that is within the tolerance range.
                    if (rp.IsSeen(ionMz))
                    {
                        return(true); // Keep looking
                    }
                    rp.Seen(ionMz);

                    int ordinal = Transition.OffsetToOrdinal(type, offset, len + 1);
                    // If this m/z aready matched a different ion, just remember the second ion.
                    double predictedMass = isFragment ? rp.massesPredict[(int)type, offset] : rp.massPrePredict;
                    if (losses != null)
                    {
                        predictedMass -= losses.Mass;
                    }
                    double predictedMz = SequenceMassCalc.GetMZ(predictedMass, charge);
                    if (Ordinal > 0)
                    {
                        // If first type was excluded from causing a ranking, but second does, then make it the first
                        // Otherwise, this can cause very mysterious failures to rank transitions that appear in the
                        // document.
                        if (Rank == 0 && ApplyRanking(rp, type, offset, losses, charge, filter, start, end, startMz, ionMz))
                        {
                            IonType2     = IonType;
                            Charge2      = Charge;
                            Ordinal2     = Ordinal;
                            Losses2      = Losses;
                            PredictedMz2 = PredictedMz;

                            IonType     = type;
                            Charge      = charge;
                            Ordinal     = ordinal;
                            Losses      = losses;
                            PredictedMz = predictedMz;
                        }
                        else
                        {
                            IonType2     = type;
                            Charge2      = charge;
                            Ordinal2     = ordinal;
                            Losses2      = losses;
                            PredictedMz2 = predictedMz;
                        }
                        rp.matched = true;
                        return(false);
                    }

                    // Avoid using the same predicted m/z on two different peaks
                    if (predictedMz == ionMz || !rp.IsSeen(predictedMz))
                    {
                        rp.Seen(predictedMz);

                        ApplyRanking(rp, type, offset, losses, charge, filter, start, end, startMz, ionMz);

                        IonType     = type;
                        Charge      = charge;
                        Ordinal     = ordinal;
                        Losses      = losses;
                        PredictedMz = predictedMz;
                        rp.matched  = (!rp.matchAll);
                        return(rp.matchAll);
                    }
                }
                // Stop looking once the mass has been passed, unless there are losses to consider
                if (rp.HasLosses)
                {
                    return(true);
                }
                return(ionMz <= ObservedMz);
            }
Example #25
0
        public IEnumerable <TransitionGroup> GetTransitionGroups(SrmSettings settings, PeptideDocNode nodePep, ExplicitMods mods, bool useFilter)
        {
            if (IsCustomIon)
            {
                // We can't generate nodes as we do with peptides, so just filter what we do have on instrument mz range
                foreach (var group in nodePep.TransitionGroups.Where(tranGroup => tranGroup.TransitionGroup.IsCustomIon))
                {
                    if (!useFilter || settings.TransitionSettings.IsMeasurablePrecursor(group.PrecursorMz))
                    {
                        yield return(group.TransitionGroup);
                    }
                }
            }
            else
            {
                IList <int> precursorCharges = settings.TransitionSettings.Filter.PrecursorCharges;
                if (!useFilter)
                {
                    precursorCharges = new List <int>();
                    for (int i = TransitionGroup.MIN_PRECURSOR_CHARGE; i < TransitionGroup.MAX_PRECURSOR_CHARGE; i++)
                    {
                        precursorCharges.Add(i);
                    }
                }

                var modSettings = settings.PeptideSettings.Modifications;

                double precursorMassLight  = settings.GetPrecursorMass(IsotopeLabelType.light, Sequence, mods);
                var    listPrecursorMasses = new List <KeyValuePair <IsotopeLabelType, double> >
                {
                    new KeyValuePair <IsotopeLabelType, double>(IsotopeLabelType.light, precursorMassLight)
                };

                foreach (var typeMods in modSettings.GetHeavyModifications())
                {
                    IsotopeLabelType labelType     = typeMods.LabelType;
                    double           precursorMass = precursorMassLight;
                    if (settings.HasPrecursorCalc(labelType, mods))
                    {
                        precursorMass = settings.GetPrecursorMass(labelType, Sequence, mods);
                    }

                    listPrecursorMasses.Add(new KeyValuePair <IsotopeLabelType, double>(labelType, precursorMass));
                }

                foreach (int charge in precursorCharges)
                {
                    if (useFilter && !settings.Accept(settings, this, mods, charge))
                    {
                        continue;
                    }

                    for (int i = 0; i < listPrecursorMasses.Count; i++)
                    {
                        var pair = listPrecursorMasses[i];
                        IsotopeLabelType labelType     = pair.Key;
                        double           precursorMass = pair.Value;
                        // Only return a heavy group, if the precursor masses differ
                        // between the light and heavy calculators
                        if (i == 0 || precursorMass != precursorMassLight)
                        {
                            if (settings.TransitionSettings.IsMeasurablePrecursor(SequenceMassCalc.GetMZ(precursorMass, charge)))
                            {
                                yield return(new TransitionGroup(this, null, charge, labelType));
                            }
                        }
                    }
                }
            }
        }
Example #26
0
        public SpectrumRanker(TargetInfo targetInfo, SrmSettings settings,
                              FragmentFilter fragmentFilter)
        {
            TargetInfoObj     = targetInfo;
            FragmentFilterObj = fragmentFilter;
            var             groupDocNode = TargetInfoObj.TransitionGroupDocNode;
            TransitionGroup group        = groupDocNode.TransitionGroup;
            bool            isProteomic  = group.IsProteomic;

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

            RankLimit = limitRanks ? settings.TransitionSettings.Libraries.IonCount : (int?)null;

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

            if (null != lookupMods && lookupMods.HasCrosslinks)
            {
                moleculeMasses = GetCrosslinkMasses(settings);
            }
            else
            {
                if (isProteomic && Sequence.IsProteomic)
                {
                    moleculeMasses = new MoleculeMasses(
                        SequenceMassCalc.GetMZ(calcMatchPre.GetPrecursorMass(Sequence), PrecursorAdduct),
                        new IonMasses(calcMatch.GetPrecursorFragmentMass(Sequence),
                                      calcMatch.GetFragmentIonMasses(Sequence)));
                }
                else if (!isProteomic && !Sequence.IsProteomic)
                {
                    string isotopicFormula;
                    var    knownFragments = new List <MatchedFragmentIon>();
                    foreach (var tran in groupDocNode.Transitions)
                    {
                        if (tran.Transition.IsNonPrecursorNonReporterCustomIon())
                        {
                            knownFragments.Add(new MatchedFragmentIon(IonType.custom, knownFragments.Count + 1,
                                                                      tran.Transition.Adduct,
                                                                      tran.GetFragmentIonName(CultureInfo.CurrentCulture,
                                                                                              settings.TransitionSettings.Libraries.IonMatchTolerance),
                                                                      null,
                                                                      tran.Mz));
                        }
                    }

                    var ionMasses =
                        new IonMasses(calcMatch.GetPrecursorFragmentMass(Sequence), IonTable <TypedMass> .EMPTY)
                        .ChangeKnownFragments(knownFragments);
                    moleculeMasses =
                        new MoleculeMasses(
                            SequenceMassCalc.GetMZ(
                                calcMatchPre.GetPrecursorMass(Sequence.Molecule, null, PrecursorAdduct,
                                                              out isotopicFormula), PrecursorAdduct), ionMasses);
                }
                else
                {
                    moleculeMasses = new MoleculeMasses(0.0,
                                                        new IonMasses(TypedMass.ZERO_MONO_MASSH, IonTable <TypedMass> .EMPTY));
                }

                if (!ReferenceEquals(calcPredict, calcMatch))
                {
                    var ionTable = moleculeMasses.MatchIonMasses.FragmentMasses;
                    if (Sequence.IsProteomic
                        ) // CONSIDER - eventually we may be able to predict fragments for small molecules?
                    {
                        ionTable = calcPredict.GetFragmentIonMasses(Sequence);
                    }
                    moleculeMasses =
                        moleculeMasses.ChangePredictIonMasses(new IonMasses(
                                                                  calcPredict.GetPrecursorFragmentMass(Sequence),
                                                                  ionTable));
                }
            }

            MoleculeMassesObj = moleculeMasses;

            // Get values of interest from the settings.
            TransitionSettings = settings.TransitionSettings;

            // Get potential losses to all fragments in this peptide
            PotentialLosses = TransitionGroup.CalcPotentialLosses(Sequence, settings.PeptideSettings.Modifications,
                                                                  lookupMods, MassType);
        }