public NeutralTerminusFragment(FragmentationTerminus terminus, double neutralMass, int fragmentNumber, int aminoAcidPosition) { this.Terminus = terminus; this.NeutralMass = (double)ClassExtensions.RoundedDouble(neutralMass); this.FragmentNumber = fragmentNumber; this.AminoAcidPosition = aminoAcidPosition; }
public static List <Modification> GetVariableTerminalMods(FragmentationTerminus fragmentationTerminus, List <Modification> variableModifications) { string terminalStringToFind = fragmentationTerminus == FragmentationTerminus.N ? "C-terminal" : "N-terminal"; //if singleN, want to find c-terminal mods and vice-versa return(variableModifications == null ? new List <Modification>() : variableModifications.Where(x => x.LocationRestriction.Contains(terminalStringToFind)).ToList()); }
private static List <MatchedFragmentIon> ReadFragmentIonsFromString(string matchedMzString, string peptideBaseSequence) { var peaks = matchedMzString.Split(MzSplit, StringSplitOptions.RemoveEmptyEntries).Select(v => v.Trim()) .ToList(); peaks.RemoveAll(p => p.Contains("\"")); List <MatchedFragmentIon> matchedIons = new List <MatchedFragmentIon>(); foreach (var peak in peaks) { var split = peak.Split(new char[] { '+', ':' }); string ionTypeAndNumber = split[0]; Match result = IonParser.Match(ionTypeAndNumber); ProductType productType = (ProductType)Enum.Parse(typeof(ProductType), result.Groups[1].Value); int fragmentNumber = int.Parse(result.Groups[2].Value); int z = int.Parse(split[1]); double mz = double.Parse(split[2], CultureInfo.InvariantCulture); double neutralLoss = 0; // check for neutral loss if (ionTypeAndNumber.Contains("-")) { string temp = ionTypeAndNumber.Replace("(", ""); temp = temp.Replace(")", ""); var split2 = temp.Split('-'); neutralLoss = double.Parse(split2[1], CultureInfo.InvariantCulture); } FragmentationTerminus terminus = FragmentationTerminus.None; if (TerminusSpecificProductTypes.ProductTypeToFragmentationTerminus.ContainsKey(productType)) { terminus = TerminusSpecificProductTypes.ProductTypeToFragmentationTerminus[productType]; } int aminoAcidPosition = fragmentNumber; if (terminus == FragmentationTerminus.C) { aminoAcidPosition = peptideBaseSequence.Length - fragmentNumber; } Product p = new Product(productType, terminus, mz.ToMass(z) - DissociationTypeCollection.GetMassShiftFromProductType(productType), fragmentNumber, aminoAcidPosition, neutralLoss); matchedIons.Add(new MatchedFragmentIon(ref p, mz, 1.0, z)); } return(matchedIons); }
/// <summary> /// A product is the individual neutral fragment from an MS dissociation. A fragmentation product here contains one of the two termini (N- or C-). /// The ProductType describes where along the backbone the fragmentaiton occurred (e.g. b-, y-, c-, zdot-). The neutral loss mass (if any) that /// occurred from a mod on the fragment is listed as a mass. Finally the neutral mass of the whole fragment is provided. /// </summary> public Product(ProductType productType, FragmentationTerminus terminus, double neutralMass, int fragmentNumber, int aminoAcidPosition, double neutralLoss) { NeutralMass = neutralMass; ProductType = productType; NeutralLoss = neutralLoss; Terminus = terminus; FragmentNumber = fragmentNumber; AminoAcidPosition = aminoAcidPosition; }
public readonly int SecondaryFragmentNumber; //used for internal fragment ions /// <summary> /// A product is the individual neutral fragment from an MS dissociation. A fragmentation product here contains one of the two termini (N- or C-). /// The ProductType describes where along the backbone the fragmentaiton occurred (e.g. b-, y-, c-, zdot-). The neutral loss mass (if any) that /// occurred from a mod on the fragment is listed as a mass. Finally the neutral mass of the whole fragment is provided. /// </summary> public Product(ProductType productType, FragmentationTerminus terminus, double neutralMass, int fragmentNumber, int aminoAcidPosition, double neutralLoss, ProductType?secondaryProductType = null, int secondaryFragmentNumber = 0) { NeutralMass = neutralMass; ProductType = productType; NeutralLoss = neutralLoss; Terminus = terminus; FragmentNumber = fragmentNumber; AminoAcidPosition = aminoAcidPosition; SecondaryProductType = secondaryProductType; SecondaryFragmentNumber = secondaryFragmentNumber; }
public DigestionParams(string protease = "trypsin", int maxMissedCleavages = 2, int minPeptideLength = 7, int maxPeptideLength = int.MaxValue, int maxModificationIsoforms = 1024, InitiatorMethionineBehavior initiatorMethionineBehavior = InitiatorMethionineBehavior.Variable, int maxModsForPeptides = 2, bool semiProteaseDigestion = false, FragmentationTerminus terminusTypeSemiProtease = FragmentationTerminus.N) { Protease = ProteaseDictionary.Dictionary[protease]; MaxMissedCleavages = maxMissedCleavages; MinPeptideLength = minPeptideLength; MaxPeptideLength = maxPeptideLength; MaxModificationIsoforms = maxModificationIsoforms; InitiatorMethionineBehavior = initiatorMethionineBehavior; MaxModsForPeptide = maxModsForPeptides; SemiProteaseDigestion = semiProteaseDigestion; TerminusTypeSemiProtease = terminusTypeSemiProtease; }
/// <summary> /// Generates theoretical fragments for given dissociation type for this peptide /// </summary> public IEnumerable <Product> Fragment(DissociationType dissociationType, FragmentationTerminus fragmentationTerminus) { // molecular ion //yield return new Product(ProductType.M, new NeutralTerminusFragment(FragmentationTerminus.None, this.MonoisotopicMass, Length, Length), 0); var productCollection = TerminusSpecificProductTypes.ProductIonTypesFromSpecifiedTerminus[fragmentationTerminus].Intersect(DissociationTypeCollection.ProductsFromDissociationType[dissociationType]); List <(ProductType, int)> skippers = new List <(ProductType, int)>(); foreach (var product in productCollection.Where(f => f != ProductType.zPlusOne)) { skippers.Add((product, BaseSequence.Length)); } switch (dissociationType) { case DissociationType.CID: skippers.Add((ProductType.b, 1)); break; case DissociationType.ETD: case DissociationType.ECD: case DissociationType.EThcD: skippers.AddRange(GetProlineZIonIndicies()); break; } foreach (var productType in productCollection) { // we're separating the N and C terminal masses and computing a separate compact peptide for each one // this speeds calculations up without producing unnecessary terminus fragment info FragmentationTerminus temporaryFragmentationTerminus = TerminusSpecificProductTypes.ProductTypeToFragmentationTerminus[productType]; NeutralTerminusFragment[] terminalMasses = CompactPeptide(temporaryFragmentationTerminus).TerminalMasses; for (int f = 0; f < terminalMasses.Length; f++) { // fragments with neutral loss if (AllModsOneIsNterminus.TryGetValue(terminalMasses[f].AminoAcidPosition + 1, out Modification mod) && mod.NeutralLosses != null && mod.NeutralLosses.TryGetValue(dissociationType, out List <double> neutralLosses)) { foreach (double neutralLoss in neutralLosses) { if (neutralLoss == 0) { continue; } for (int n = f; n < terminalMasses.Length; n++) { if (!skippers.Contains((productType, terminalMasses[n].FragmentNumber)))
} //used to look for unlabeled proteins (in addition to labeled proteins) for SILAC experiments public override bool Equals(object obj) { return(obj is DigestionParams a && MaxMissedCleavages.Equals(a.MaxMissedCleavages) && MinPeptideLength.Equals(a.MinPeptideLength) && MaxPeptideLength.Equals(a.MaxPeptideLength) && InitiatorMethionineBehavior.Equals(a.InitiatorMethionineBehavior) && MaxModificationIsoforms.Equals(a.MaxModificationIsoforms) && MaxModsForPeptide.Equals(a.MaxModsForPeptide) && Protease.Equals(a.Protease) && SearchModeType.Equals(a.SearchModeType) && FragmentationTerminus.Equals(a.FragmentationTerminus) && GeneratehUnlabeledProteinsForSilac.Equals(a.GeneratehUnlabeledProteinsForSilac)); }
public DigestionParams(string protease = "trypsin", int maxMissedCleavages = 2, int minPeptideLength = 7, int maxPeptideLength = int.MaxValue, int maxModificationIsoforms = 1024, InitiatorMethionineBehavior initiatorMethionineBehavior = InitiatorMethionineBehavior.Variable, int maxModsForPeptides = 2, CleavageSpecificity searchModeType = CleavageSpecificity.Full, FragmentationTerminus fragmentationTerminus = FragmentationTerminus.Both, bool generateUnlabeledProteinsForSilac = true) { Protease = ProteaseDictionary.Dictionary[protease]; MaxMissedCleavages = maxMissedCleavages; MinPeptideLength = minPeptideLength; MaxPeptideLength = maxPeptideLength; MaxModificationIsoforms = maxModificationIsoforms; InitiatorMethionineBehavior = initiatorMethionineBehavior; MaxModsForPeptide = maxModsForPeptides; SearchModeType = searchModeType; FragmentationTerminus = fragmentationTerminus; RecordSpecificProtease(); GeneratehUnlabeledProteinsForSilac = generateUnlabeledProteinsForSilac; }
private static List <MatchedFragmentIon> ReadFragmentIonsFromString(string matchedMzString, string matchedIntensityString, string peptideBaseSequence) { List <MatchedFragmentIon> matchedIons = new List <MatchedFragmentIon>(); if (matchedMzString.Length > 2) //check if there's an ion { List <string> peakMzs = CleanMatchedIonString(matchedMzString); List <string> peakIntensities = CleanMatchedIonString(matchedIntensityString); for (int index = 0; index < peakMzs.Count; index++) { string peak = peakMzs[index]; string[] split = peak.Split(new char[] { '+', ':' }); //TODO: needs update for negative charges that doesn't break internal fragment ions or neutral losses double intensity = peakMzs.Count == peakIntensities.Count ? //TODO: needs update for negative charges that doesn't break internal fragment ions or neutral losses double.Parse(peakIntensities[index].Split(new char[] { '+', ':', ']' })[2], CultureInfo.InvariantCulture) : 1.0; int fragmentNumber = 0; int secondaryFragmentNumber = 0; ProductType productType; ProductType? secondaryProductType = null; FragmentationTerminus terminus = FragmentationTerminus.None; //default for internal fragments int aminoAcidPosition; double neutralLoss = 0; //get theoretical fragment string ionTypeAndNumber = split[0]; //if an internal fragment if (ionTypeAndNumber.Contains("[")) { string[] internalSplit = split[0].Split('['); string[] productSplit = internalSplit[0].Split("I"); string[] positionSplit = internalSplit[1].Replace("]", "").Split('-'); productType = (ProductType)Enum.Parse(typeof(ProductType), productSplit[0]); secondaryProductType = (ProductType)Enum.Parse(typeof(ProductType), productSplit[1]); fragmentNumber = int.Parse(positionSplit[0]); secondaryFragmentNumber = int.Parse(positionSplit[1]); aminoAcidPosition = secondaryFragmentNumber - fragmentNumber; } else //terminal fragment { Match result = IonParser.Match(ionTypeAndNumber); productType = (ProductType)Enum.Parse(typeof(ProductType), result.Groups[1].Value); fragmentNumber = int.Parse(result.Groups[2].Value); // check for neutral loss if (ionTypeAndNumber.Contains("(")) { string temp = ionTypeAndNumber.Replace("(", ""); temp = temp.Replace(")", ""); var split2 = temp.Split('-'); neutralLoss = double.Parse(split2[1], CultureInfo.InvariantCulture); } //get terminus if (TerminusSpecificProductTypes.ProductTypeToFragmentationTerminus.ContainsKey(productType)) { terminus = TerminusSpecificProductTypes.ProductTypeToFragmentationTerminus[productType]; } //get amino acid position aminoAcidPosition = terminus == FragmentationTerminus.C ? peptideBaseSequence.Length - fragmentNumber : fragmentNumber; } //get charge and mz int z = int.Parse(split[1]); double mz = double.Parse(split[2], CultureInfo.InvariantCulture); Product p = new Product(productType, terminus, mz.ToMass(z), fragmentNumber, aminoAcidPosition, neutralLoss, secondaryProductType, secondaryFragmentNumber); matchedIons.Add(new MatchedFragmentIon(ref p, mz, intensity, z)); } } return(matchedIons); }
private Tuple <int, PeptideWithSetModifications> Accepts(List <Product> fragments, double scanPrecursorMass, PeptideWithSetModifications peptide, FragmentationTerminus fragmentationTerminus, MassDiffAcceptor searchMode, bool semiSpecificSearch) { int localminPeptideLength = CommonParameters.DigestionParams.MinPeptideLength; //Get terminal modifications, if any Dictionary <int, List <Modification> > databaseAnnotatedMods = semiSpecificSearch ? null : GetTerminalModPositions(peptide, CommonParameters.DigestionParams, VariableTerminalModifications); for (int i = localminPeptideLength - 1; i < fragments.Count; i++) //minus one start, because fragment 1 is at index 0 { Product fragment = fragments[i]; double theoMass = fragment.NeutralMass - DissociationTypeCollection.GetMassShiftFromProductType(fragment.ProductType) + WaterMonoisotopicMass; int notch = searchMode.Accepts(scanPrecursorMass, theoMass); //check for terminal mods that might reach the observed mass Modification terminalMod = null; if (!semiSpecificSearch && notch < 0 && databaseAnnotatedMods.TryGetValue(i + 1, out List <Modification> terminalModsAtThisIndex)) //look for i+1, because the mod might exist at the terminus { foreach (Modification mod in terminalModsAtThisIndex) { notch = searchMode.Accepts(scanPrecursorMass, theoMass + mod.MonoisotopicMass.Value); //overwrite the notch, since the other notch wasn't accepted if (notch >= 0) { terminalMod = mod; break; } } } if (notch >= 0) { PeptideWithSetModifications updatedPwsm = null; if (fragmentationTerminus == FragmentationTerminus.N) { int endResidue = peptide.OneBasedStartResidueInProtein + fragment.TerminusFragment.FragmentNumber - 1; //-1 for one based index Dictionary <int, Modification> updatedMods = new Dictionary <int, Modification>(); foreach (KeyValuePair <int, Modification> mod in peptide.AllModsOneIsNterminus) { if (mod.Key < endResidue - peptide.OneBasedStartResidueInProtein + 3) //check if we cleaved it off, +1 for N-terminus being mod 1 and first residue being mod 2, +1 again for the -1 on end residue for one based index, +1 (again) for the one-based start residue { updatedMods.Add(mod.Key, mod.Value); } } if (terminalMod != null) { updatedMods.Add(endResidue, terminalMod); } updatedPwsm = new PeptideWithSetModifications(peptide.Protein, peptide.DigestionParams, peptide.OneBasedStartResidueInProtein, endResidue, CleavageSpecificity.Unknown, "", 0, updatedMods, 0); } else //if C terminal ions, shave off the n-terminus { int startResidue = peptide.OneBasedEndResidueInProtein - fragment.TerminusFragment.FragmentNumber + 1; //plus one for one based index Dictionary <int, Modification> updatedMods = new Dictionary <int, Modification>(); //updateMods int indexShift = startResidue - peptide.OneBasedStartResidueInProtein; foreach (KeyValuePair <int, Modification> mod in peptide.AllModsOneIsNterminus) { if (mod.Key > indexShift + 1) //check if we cleaved it off, +1 for N-terminus being mod 1 and first residue being 2 { int key = mod.Key - indexShift; updatedMods.Add(key, mod.Value); } } if (terminalMod != null) { updatedMods.Add(startResidue - 1, terminalMod); } updatedPwsm = new PeptideWithSetModifications(peptide.Protein, peptide.DigestionParams, startResidue, peptide.OneBasedEndResidueInProtein, CleavageSpecificity.Unknown, "", 0, updatedMods, 0); } return(new Tuple <int, PeptideWithSetModifications>(notch, updatedPwsm)); } else if (theoMass > scanPrecursorMass) { break; } } //if the theoretical and experimental have the same mass or a terminal mod exists if (peptide.BaseSequence.Length >= localminPeptideLength) { double totalMass = peptide.MonoisotopicMass;// + Constants.ProtonMass; int notch = searchMode.Accepts(scanPrecursorMass, totalMass); if (notch >= 0) { //need to update so that the cleavage specificity is recorded PeptideWithSetModifications updatedPwsm = new PeptideWithSetModifications(peptide.Protein, peptide.DigestionParams, peptide.OneBasedStartResidueInProtein, peptide.OneBasedEndResidueInProtein, CleavageSpecificity.Unknown, "", 0, peptide.AllModsOneIsNterminus, peptide.NumFixedMods); return(new Tuple <int, PeptideWithSetModifications>(notch, updatedPwsm)); } else //try a terminal mod (if it exists) { if (!semiSpecificSearch && databaseAnnotatedMods.TryGetValue(peptide.Length, out List <Modification> terminalModsAtThisIndex)) { foreach (Modification terminalMod in terminalModsAtThisIndex) { notch = searchMode.Accepts(scanPrecursorMass, totalMass + terminalMod.MonoisotopicMass.Value); //overwrite the notch, since the other notch wasn't accepted if (notch >= 0) { //need to update the mod dictionary and don't want to overwrite the peptide incase it's in other scans Dictionary <int, Modification> updatedMods = new Dictionary <int, Modification>(); //updateMods foreach (KeyValuePair <int, Modification> mod in peptide.AllModsOneIsNterminus) { updatedMods.Add(mod.Key, mod.Value); } //add the terminal mod if (fragmentationTerminus == FragmentationTerminus.N) { updatedMods[peptide.OneBasedEndResidueInProtein] = terminalMod; } else { updatedMods[peptide.OneBasedStartResidueInProtein - 1] = terminalMod; } PeptideWithSetModifications updatedPwsm = new PeptideWithSetModifications(peptide.Protein, peptide.DigestionParams, peptide.OneBasedStartResidueInProtein, peptide.OneBasedEndResidueInProtein, CleavageSpecificity.Unknown, "", 0, updatedMods, peptide.NumFixedMods); return(new Tuple <int, PeptideWithSetModifications>(notch, updatedPwsm)); } } } } } return(new Tuple <int, PeptideWithSetModifications>(-1, null)); }
/// <summary> /// Generates theoretical fragments for given dissociation type for this peptide. /// The "products" parameter is filled with these fragments. /// </summary> public void Fragment(DissociationType dissociationType, FragmentationTerminus fragmentationTerminus, List <Product> products) { // This code is specifically written to be memory- and CPU -efficient because it is // called millions of times for a typical search (i.e., at least once per peptide). // If you modify this code, BE VERY CAREFUL about allocating new memory, especially // for new collections. This code also deliberately avoids using "yield return", again // for performance reasons. Be sure to benchmark any changes with a parallelized // fragmentation of every peptide in a database (i.e., test for speed decreases and // memory issues). products.Clear(); var massCaps = DissociationTypeCollection.GetNAndCTerminalMassShiftsForDissociationType(dissociationType); double cTermMass = 0; double nTermMass = 0; List <ProductType> nTermProductTypes = DissociationTypeCollection.GetTerminusSpecificProductTypesFromDissociation(dissociationType, FragmentationTerminus.N); List <ProductType> cTermProductTypes = DissociationTypeCollection.GetTerminusSpecificProductTypesFromDissociation(dissociationType, FragmentationTerminus.C); bool calculateNTermFragments = fragmentationTerminus == FragmentationTerminus.N || fragmentationTerminus == FragmentationTerminus.Both; bool calculateCTermFragments = fragmentationTerminus == FragmentationTerminus.C || fragmentationTerminus == FragmentationTerminus.Both; //From http://www.matrixscience.com/help/fragmentation_help.html //Low Energy CID -- In low energy CID(i.e.collision induced dissociation in a triple quadrupole or an ion trap) a peptide carrying a positive charge fragments mainly along its backbone, //generating predominantly b and y ions. In addition, for fragments containing RKNQ, peaks are seen for ions that have lost ammonia (-17 Da) denoted a*, b* and y*. For fragments containing //STED, loss of water(-18 Da) is denoted a°, b° and y°. Satellite ions from side chain cleavage are not observed. bool haveSeenNTermDegreeIon = false; bool haveSeenNTermStarIon = false; bool haveSeenCTermDegreeIon = false; bool haveSeenCTermStarIon = false; // these two collections keep track of the neutral losses observed so far on the n-term or c-term. // they are apparently necessary, but allocating memory for collections in this function results in // inefficient memory usage and thus frequent garbage collection. // TODO: If you can think of a way to remove these collections and still maintain correct // fragmentation, please do so. HashSet <double> nTermNeutralLosses = null; HashSet <double> cTermNeutralLosses = null; // n-terminus mod if (calculateNTermFragments) { if (AllModsOneIsNterminus.TryGetValue(1, out Modification mod)) { nTermMass += mod.MonoisotopicMass.Value; } } // c-terminus mod if (calculateCTermFragments) { if (AllModsOneIsNterminus.TryGetValue(BaseSequence.Length + 2, out Modification mod)) { cTermMass += mod.MonoisotopicMass.Value; } } for (int r = 0; r < BaseSequence.Length - 1; r++) { // n-term fragments if (calculateNTermFragments) { char nTermResidue = BaseSequence[r]; // get n-term residue mass if (Residue.TryGetResidue(nTermResidue, out Residue residue)) { nTermMass += residue.MonoisotopicMass; } else { nTermMass = double.NaN; } // add side-chain mod if (AllModsOneIsNterminus.TryGetValue(r + 2, out Modification mod)) { nTermMass += mod.MonoisotopicMass.Value; } // handle star and degree ions for low-res CID if (dissociationType == DissociationType.LowCID) { if (nTermResidue == 'R' || nTermResidue == 'K' || nTermResidue == 'N' || nTermResidue == 'Q') { haveSeenNTermStarIon = true; } if (nTermResidue == 'S' || nTermResidue == 'T' || nTermResidue == 'E' || nTermResidue == 'D') { haveSeenNTermDegreeIon = true; } } // skip first N-terminal fragment (b1, aDegree1, ...) for CID if (r == 0 && (dissociationType == DissociationType.CID || dissociationType == DissociationType.LowCID)) { goto CTerminusFragments; } // generate products for (int i = 0; i < nTermProductTypes.Count; i++) { if (dissociationType == DissociationType.LowCID) { if (!haveSeenNTermStarIon && (nTermProductTypes[i] == ProductType.aStar || nTermProductTypes[i] == ProductType.bStar)) { continue; } if (!haveSeenNTermDegreeIon && (nTermProductTypes[i] == ProductType.aDegree || nTermProductTypes[i] == ProductType.bDegree)) { continue; } } products.Add(new Product( nTermProductTypes[i], FragmentationTerminus.N, nTermMass + massCaps.Item1[i], r + 1, r + 1, 0)); if (mod != null && mod.NeutralLosses != null && mod.NeutralLosses.TryGetValue(dissociationType, out List <double> neutralLosses)) { foreach (double neutralLoss in neutralLosses.Where(p => p != 0)) { if (nTermNeutralLosses == null) { nTermNeutralLosses = new HashSet <double>(); } nTermNeutralLosses.Add(neutralLoss); } } if (nTermNeutralLosses != null) { foreach (double neutralLoss in nTermNeutralLosses) { products.Add(new Product( nTermProductTypes[i], FragmentationTerminus.N, nTermMass + massCaps.Item1[i] - neutralLoss, r + 1, r + 1, neutralLoss)); } } } } // c-term fragments CTerminusFragments: if (calculateCTermFragments) { char cTermResidue = BaseSequence[BaseSequence.Length - r - 1]; // get c-term residue mass if (Residue.TryGetResidue(cTermResidue, out Residue residue)) { cTermMass += residue.MonoisotopicMass; } else { cTermMass = double.NaN; } // add side-chain mod if (AllModsOneIsNterminus.TryGetValue(BaseSequence.Length - r + 1, out Modification mod)) { cTermMass += mod.MonoisotopicMass.Value; } // handle star and degree ions for low-res CID if (dissociationType == DissociationType.LowCID) { if (cTermResidue == 'R' || cTermResidue == 'K' || cTermResidue == 'N' || cTermResidue == 'Q') { haveSeenCTermStarIon = true; } if (cTermResidue == 'S' || cTermResidue == 'T' || cTermResidue == 'E' || cTermResidue == 'D') { haveSeenCTermDegreeIon = true; } } // generate products for (int i = 0; i < cTermProductTypes.Count; i++) { // skip zDot ions for proline residues for ETD/ECD/EThcD if (cTermResidue == 'P' && (dissociationType == DissociationType.ECD || dissociationType == DissociationType.ETD || dissociationType == DissociationType.EThcD) && cTermProductTypes[i] == ProductType.zDot) { continue; } if (dissociationType == DissociationType.LowCID) { if (!haveSeenCTermStarIon && cTermProductTypes[i] == ProductType.yStar) { continue; } if (!haveSeenCTermDegreeIon && cTermProductTypes[i] == ProductType.yDegree) { continue; } } products.Add(new Product( cTermProductTypes[i], FragmentationTerminus.C, cTermMass + massCaps.Item2[i], r + 1, BaseSequence.Length - r, 0)); if (mod != null && mod.NeutralLosses != null && mod.NeutralLosses.TryGetValue(dissociationType, out List <double> neutralLosses)) { foreach (double neutralLoss in neutralLosses.Where(p => p != 0)) { if (cTermNeutralLosses == null) { cTermNeutralLosses = new HashSet <double>(); } cTermNeutralLosses.Add(neutralLoss); } } if (cTermNeutralLosses != null) { foreach (double neutralLoss in cTermNeutralLosses) { products.Add(new Product( cTermProductTypes[i], FragmentationTerminus.C, cTermMass + massCaps.Item2[i] - neutralLoss, r + 1, BaseSequence.Length - r, neutralLoss)); } } } } } // zDot generates one more ion... if (cTermProductTypes.Contains(ProductType.zDot) && BaseSequence[0] != 'P') { // get c-term residue mass if (Residue.TryGetResidue(BaseSequence[0], out Residue residue)) { cTermMass += residue.MonoisotopicMass; } else { cTermMass = double.NaN; } // add side-chain mod if (AllModsOneIsNterminus.TryGetValue(1, out Modification mod)) { cTermMass += mod.MonoisotopicMass.Value; } // generate zDot product products.Add(new Product( ProductType.zDot, FragmentationTerminus.C, cTermMass + DissociationTypeCollection.GetMassShiftFromProductType(ProductType.zDot), BaseSequence.Length, 1, 0)); if (mod != null && mod.NeutralLosses != null && mod.NeutralLosses.TryGetValue(dissociationType, out List <double> neutralLosses)) { foreach (double neutralLoss in neutralLosses.Where(p => p != 0)) { products.Add(new Product( ProductType.zDot, FragmentationTerminus.C, cTermMass + DissociationTypeCollection.GetMassShiftFromProductType(ProductType.zDot) - neutralLoss, BaseSequence.Length, 1, neutralLoss)); } } } foreach (var mod in AllModsOneIsNterminus.Where(p => p.Value.NeutralLosses != null)) { // molecular ion minus neutral losses if (mod.Value.NeutralLosses.TryGetValue(dissociationType, out List <double> losses)) { foreach (double neutralLoss in losses.Where(p => p != 0)) { if (neutralLoss != 0) { products.Add(new Product(ProductType.M, FragmentationTerminus.Both, MonoisotopicMass - neutralLoss, 0, 0, neutralLoss)); } } } } // generate diagnostic ions // TODO: this code is memory-efficient but sort of CPU inefficient; it can be further optimized. // however, diagnostic ions are fairly rare so it's probably OK for now foreach (double diagnosticIon in AllModsOneIsNterminus.Where(p => p.Value.DiagnosticIons != null && p.Value.DiagnosticIons.ContainsKey(dissociationType)).SelectMany(p => p.Value.DiagnosticIons[dissociationType]).Distinct()) { int diagnosticIonLabel = (int)Math.Round(diagnosticIon.ToMz(1), 0); // the diagnostic ion is assumed to be annotated in the mod info as the *neutral mass* of the diagnostic ion, not the ionized species products.Add(new Product(ProductType.D, FragmentationTerminus.Both, diagnosticIon, diagnosticIonLabel, 0, 0)); } }
protected static IEnumerable <NeutralTerminusFragment> ComputeNeutralTerminusFragments(PeptideWithSetModifications peptide, FragmentationTerminus fragmentationTerminus) { double mass = 0; if (fragmentationTerminus == FragmentationTerminus.N || fragmentationTerminus == FragmentationTerminus.Both) { for (int r = 0; r <= peptide.Length - 1; r++) //This is a zero based indexed for residues. The index of the first amino acid in the peptide is 0. { mass += Residue.ResidueMonoisotopicMass[peptide[r]]; //This is a zero based indexed for residues. The index of the first amino acid in the peptide is 0. // side-chain mod if (peptide.AllModsOneIsNterminus.TryGetValue(r + 2, out Modification currentModification))//This is a one based index. The index of the fragment from the first amino acid is 1. { mass += (double)currentModification.MonoisotopicMass; } // N-terminal mod if (r == 0 && peptide.AllModsOneIsNterminus.TryGetValue(1, out currentModification)) { mass += (double)currentModification.MonoisotopicMass; } if (r != peptide.Length - 1) { yield return(new NeutralTerminusFragment(FragmentationTerminus.N, mass, r + 1, r + 1));//This is a one based index. The index of the fragment from the first amino acid is 1. } } } if (fragmentationTerminus == FragmentationTerminus.C || fragmentationTerminus == FragmentationTerminus.Both) { mass = 0; for (int r = peptide.Length - 1; r >= 0; r--) { mass += Residue.ResidueMonoisotopicMass[peptide[r]]; // side-chain mod if (peptide.AllModsOneIsNterminus.TryGetValue(r + 2, out Modification currentModification)) { mass += (double)currentModification.MonoisotopicMass; } // C-terminal mod if (r == peptide.Length - 1 && peptide.AllModsOneIsNterminus.TryGetValue(peptide.Length + 2, out currentModification)) { mass += (double)currentModification.MonoisotopicMass; } if (r != -1) { yield return(new NeutralTerminusFragment(FragmentationTerminus.C, mass, peptide.Length - r, r + 1)); } } } }
private void SaveButton_Click(object sender, RoutedEventArgs e) { CleavageSpecificity searchModeType = CleavageSpecificity.Full; //classic and modern by default if (semiSpecificSearchRadioButton.IsChecked.Value) //semi { searchModeType = CleavageSpecificity.Semi; } else if (nonSpecificSearchRadioButton.IsChecked.Value) //non { searchModeType = CleavageSpecificity.None; } //else it's the default of full if (searchModeType != CleavageSpecificity.Full) { if (((Protease)proteaseComboBox.SelectedItem).Name.Contains("non-specific")) { searchModeType = CleavageSpecificity.None; //prevents an accidental semi attempt of a non-specific protease if (cTerminalIons.IsChecked.Value) { Protease singleC = ProteaseDictionary.Dictionary["singleC"]; proteaseComboBox.SelectedItem = singleC; } else //we're not allowing no ion types. It must have N if it doesn't have C. { Protease singleN = ProteaseDictionary.Dictionary["singleN"]; proteaseComboBox.SelectedItem = singleN; } } if (!addCompIonCheckBox.IsChecked.Value) { MessageBox.Show("Warning: Complementary ions are strongly recommended when using this algorithm."); } //only use N or C termini, not both if (cTerminalIons.IsChecked.Value) { nTerminalIons.IsChecked = false; } else { nTerminalIons.IsChecked = true; } } if (!GlobalGuiSettings.CheckTaskSettingsValidity(precursorMassToleranceTextBox.Text, productMassToleranceTextBox.Text, missedCleavagesTextBox.Text, maxModificationIsoformsTextBox.Text, MinPeptideLengthTextBox.Text, MaxPeptideLengthTextBox.Text, maxThreadsTextBox.Text, minScoreAllowed.Text, peakFindingToleranceTextBox.Text, histogramBinWidthTextBox.Text, DeconvolutionMaxAssumedChargeStateTextBox.Text, NumberOfPeaksToKeepPerWindowTextBox.Text, MinimumAllowedIntensityRatioToBasePeakTexBox.Text, WindowWidthThomsonsTextBox.Text, NumberOfWindowsTextBox.Text, numberOfDatabaseSearchesTextBox.Text, MaxModNumTextBox.Text, MaxFragmentMassTextBox.Text, QValueTextBox.Text)) { return; } Protease protease = (Protease)proteaseComboBox.SelectedItem; DissociationType dissociationType = GlobalVariables.AllSupportedDissociationTypes[dissociationTypeComboBox.SelectedItem.ToString()]; CustomFragmentationWindow.Close(); FragmentationTerminus fragmentationTerminus = FragmentationTerminus.Both; if (nTerminalIons.IsChecked.Value && !cTerminalIons.IsChecked.Value) { fragmentationTerminus = FragmentationTerminus.N; } else if (!nTerminalIons.IsChecked.Value && cTerminalIons.IsChecked.Value) { fragmentationTerminus = FragmentationTerminus.C; } else if (!nTerminalIons.IsChecked.Value && !cTerminalIons.IsChecked.Value) //why would you want this { fragmentationTerminus = FragmentationTerminus.None; MessageBox.Show("Warning: No ion types were selected. MetaMorpheus will be unable to search MS/MS spectra."); } //else both int maxMissedCleavages = string.IsNullOrEmpty(missedCleavagesTextBox.Text) ? int.MaxValue : (int.Parse(missedCleavagesTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture)); int minPeptideLengthValue = (int.Parse(MinPeptideLengthTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture)); int maxPeptideLengthValue = string.IsNullOrEmpty(MaxPeptideLengthTextBox.Text) ? int.MaxValue : (int.Parse(MaxPeptideLengthTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture)); int MinVariantDepth = int.Parse(MinVariantDepthTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture); int MaxHeterozygousVariants = int.Parse(MaxHeterozygousVariantsTextBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture); int maxModificationIsoformsValue = (int.Parse(maxModificationIsoformsTextBox.Text, CultureInfo.InvariantCulture)); int maxModsForPeptideValue = (int.Parse(MaxModNumTextBox.Text, CultureInfo.InvariantCulture)); InitiatorMethionineBehavior initiatorMethionineBehavior = ((InitiatorMethionineBehavior)initiatorMethionineBehaviorComboBox.SelectedIndex); DigestionParams digestionParamsToSave = new DigestionParams( protease: protease.Name, maxMissedCleavages: maxMissedCleavages, minPeptideLength: minPeptideLengthValue, maxPeptideLength: maxPeptideLengthValue, maxModificationIsoforms: maxModificationIsoformsValue, initiatorMethionineBehavior: initiatorMethionineBehavior, maxModsForPeptides: maxModsForPeptideValue, searchModeType: searchModeType, fragmentationTerminus: fragmentationTerminus, generateUnlabeledProteinsForSilac: CheckBoxQuantifyUnlabeledForSilac.IsChecked.Value); Tolerance ProductMassTolerance; if (productMassToleranceComboBox.SelectedIndex == 0) { ProductMassTolerance = new AbsoluteTolerance(double.Parse(productMassToleranceTextBox.Text, CultureInfo.InvariantCulture)); } else { ProductMassTolerance = new PpmTolerance(double.Parse(productMassToleranceTextBox.Text, CultureInfo.InvariantCulture)); } Tolerance PrecursorMassTolerance; if (precursorMassToleranceComboBox.SelectedIndex == 0) { PrecursorMassTolerance = new AbsoluteTolerance(double.Parse(precursorMassToleranceTextBox.Text, CultureInfo.InvariantCulture)); } else { PrecursorMassTolerance = new PpmTolerance(double.Parse(precursorMassToleranceTextBox.Text, CultureInfo.InvariantCulture)); } TheTask.SearchParameters.MaxFragmentSize = Double.Parse(MaxFragmentMassTextBox.Text, CultureInfo.InvariantCulture); var listOfModsVariable = new List <(string, string)>(); foreach (var heh in VariableModTypeForTreeViewObservableCollection) { listOfModsVariable.AddRange(heh.Children.Where(b => b.Use).Select(b => (b.Parent.DisplayName, b.ModName))); } var listOfModsFixed = new List <(string, string)>(); foreach (var heh in FixedModTypeForTreeViewObservableCollection) { listOfModsFixed.AddRange(heh.Children.Where(b => b.Use).Select(b => (b.Parent.DisplayName, b.ModName))); } if (!GlobalGuiSettings.VariableModCheck(listOfModsVariable)) { return; } bool TrimMs1Peaks = trimMs1.IsChecked.Value; bool TrimMsMsPeaks = trimMsMs.IsChecked.Value; int?numPeaksToKeep = null; if (int.TryParse(NumberOfPeaksToKeepPerWindowTextBox.Text, out int numberOfPeaksToKeeep)) { numPeaksToKeep = numberOfPeaksToKeeep; } double?minimumAllowedIntensityRatioToBasePeak = null; if (double.TryParse(MinimumAllowedIntensityRatioToBasePeakTexBox.Text, out double minimumAllowedIntensityRatio)) { minimumAllowedIntensityRatioToBasePeak = minimumAllowedIntensityRatio; } double?windowWidthThompsons = null; if (double.TryParse(WindowWidthThomsonsTextBox.Text, out double windowWidth)) { windowWidthThompsons = windowWidth; } int?numberOfWindows = null; if (int.TryParse(NumberOfWindowsTextBox.Text, out int numWindows)) { numberOfWindows = numWindows; } bool normalizePeaksAccrossAllWindows = normalizePeaksInWindowCheckBox.IsChecked.Value; bool parseMaxThreadsPerFile = !maxThreadsTextBox.Text.Equals("") && (int.Parse(maxThreadsTextBox.Text) <= Environment.ProcessorCount && int.Parse(maxThreadsTextBox.Text) > 0); CommonParameters commonParamsToSave = new CommonParameters( taskDescriptor: OutputFileNameTextBox.Text != "" ? OutputFileNameTextBox.Text : "SearchTask", maxThreadsToUsePerFile: parseMaxThreadsPerFile ? int.Parse(maxThreadsTextBox.Text, CultureInfo.InvariantCulture) : new CommonParameters().MaxThreadsToUsePerFile, useDeltaScore: deltaScoreCheckBox.IsChecked.Value, reportAllAmbiguity: allAmbiguity.IsChecked.Value, deconvolutionMaxAssumedChargeState: int.Parse(DeconvolutionMaxAssumedChargeStateTextBox.Text, CultureInfo.InvariantCulture), totalPartitions: int.Parse(numberOfDatabaseSearchesTextBox.Text, CultureInfo.InvariantCulture), doPrecursorDeconvolution: deconvolutePrecursors.IsChecked.Value, useProvidedPrecursorInfo: useProvidedPrecursor.IsChecked.Value, scoreCutoff: double.Parse(minScoreAllowed.Text, CultureInfo.InvariantCulture), listOfModsFixed: listOfModsFixed, listOfModsVariable: listOfModsVariable, dissociationType: dissociationType, precursorMassTolerance: PrecursorMassTolerance, productMassTolerance: ProductMassTolerance, digestionParams: digestionParamsToSave, trimMs1Peaks: TrimMs1Peaks, trimMsMsPeaks: TrimMsMsPeaks, numberOfPeaksToKeepPerWindow: numPeaksToKeep, minimumAllowedIntensityRatioToBasePeak: minimumAllowedIntensityRatioToBasePeak, windowWidthThomsons: windowWidthThompsons, numberOfWindows: numberOfWindows, //maybe change this some day normalizePeaksAccrossAllWindows: normalizePeaksAccrossAllWindows, //maybe change this some day addCompIons: addCompIonCheckBox.IsChecked.Value, qValueOutputFilter: QValueCheckBox.IsChecked.Value ? double.Parse(QValueTextBox.Text, CultureInfo.InvariantCulture) : 1.0, assumeOrphanPeaksAreZ1Fragments: protease.Name != "top-down", minVariantDepth: MinVariantDepth, maxHeterozygousVariants: MaxHeterozygousVariants); if (classicSearchRadioButton.IsChecked.Value) { TheTask.SearchParameters.SearchType = SearchType.Classic; } else if (modernSearchRadioButton.IsChecked.Value) { TheTask.SearchParameters.SearchType = SearchType.Modern; } else //both semi and nonspecific are termed "nonspecific", because they both contain at least one nonspecific cleavage and they share the same algorithm { TheTask.SearchParameters.SearchType = SearchType.NonSpecific; } TheTask.SearchParameters.DoParsimony = checkBoxParsimony.IsChecked.Value; TheTask.SearchParameters.NoOneHitWonders = checkBoxNoOneHitWonders.IsChecked.Value; TheTask.SearchParameters.DoQuantification = !checkBoxNoQuant.IsChecked.Value; //SilacLabel deconvolution { if (StaticSilacLabelsObservableCollection.Count == 0) { TheTask.SearchParameters.SilacLabels = null; } else { List <Proteomics.SilacLabel> labelsToSave = new List <Proteomics.SilacLabel>(); foreach (SilacInfoForDataGrid info in StaticSilacLabelsObservableCollection) { Proteomics.SilacLabel labelToAdd = info.SilacLabel[0]; //This is needed to prevent double adding of additional labels. //A quick test is to create a silac condition with two labels, save, reopen the task, save, and reopen again. //Without this line, the second label will be doubled (K+8)&(R+10)&(R+10) if (labelToAdd.AdditionalLabels != null) { labelToAdd.AdditionalLabels.Clear(); } for (int infoIndex = 1; infoIndex < info.SilacLabel.Count; infoIndex++) { labelToAdd.AddAdditionalSilacLabel(info.SilacLabel[infoIndex]); } labelsToSave.Add(labelToAdd); } TheTask.SearchParameters.SilacLabels = labelsToSave; } } TheTask.SearchParameters.Normalize = checkBoxNormalize.IsChecked.Value; TheTask.SearchParameters.MatchBetweenRuns = checkBoxMatchBetweenRuns.IsChecked.Value; TheTask.SearchParameters.ModPeptidesAreDifferent = modPepsAreUnique.IsChecked.Value; TheTask.SearchParameters.QuantifyPpmTol = double.Parse(peakFindingToleranceTextBox.Text, CultureInfo.InvariantCulture); TheTask.SearchParameters.SearchTarget = checkBoxTarget.IsChecked.Value; TheTask.SearchParameters.WriteMzId = ckbMzId.IsChecked.Value; TheTask.SearchParameters.WriteDecoys = writeDecoyCheckBox.IsChecked.Value; TheTask.SearchParameters.WriteContaminants = writeContaminantCheckBox.IsChecked.Value; //TheTask.SearchParameters.OutPepXML = ckbPepXML.IsChecked.Value; if (checkBoxDecoy.IsChecked.Value) { if (radioButtonReverseDecoy.IsChecked.Value) { TheTask.SearchParameters.DecoyType = DecoyType.Reverse; } else //if (radioButtonSlideDecoy.IsChecked.Value) { TheTask.SearchParameters.DecoyType = DecoyType.Slide; } } else { TheTask.SearchParameters.DecoyType = DecoyType.None; } if (massDiffAcceptExact.IsChecked.HasValue && massDiffAcceptExact.IsChecked.Value) { TheTask.SearchParameters.MassDiffAcceptorType = MassDiffAcceptorType.Exact; } if (massDiffAccept1mm.IsChecked.HasValue && massDiffAccept1mm.IsChecked.Value) { TheTask.SearchParameters.MassDiffAcceptorType = MassDiffAcceptorType.OneMM; } if (massDiffAccept2mm.IsChecked.HasValue && massDiffAccept2mm.IsChecked.Value) { TheTask.SearchParameters.MassDiffAcceptorType = MassDiffAcceptorType.TwoMM; } if (massDiffAccept3mm.IsChecked.HasValue && massDiffAccept3mm.IsChecked.Value) { TheTask.SearchParameters.MassDiffAcceptorType = MassDiffAcceptorType.ThreeMM; } if (massDiffAccept187.IsChecked.HasValue && massDiffAccept187.IsChecked.Value) { TheTask.SearchParameters.MassDiffAcceptorType = MassDiffAcceptorType.ModOpen; } if (massDiffAcceptOpen.IsChecked.HasValue && massDiffAcceptOpen.IsChecked.Value) { TheTask.SearchParameters.MassDiffAcceptorType = MassDiffAcceptorType.Open; } if (massDiffAcceptCustom.IsChecked.HasValue && massDiffAcceptCustom.IsChecked.Value) { try { MassDiffAcceptor customMassDiffAcceptor = SearchTask.GetMassDiffAcceptor(null, MassDiffAcceptorType.Custom, customkMdacTextBox.Text); } catch (Exception ex) { MessageBox.Show("Could not parse custom mass difference acceptor: " + ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); return; } TheTask.SearchParameters.MassDiffAcceptorType = MassDiffAcceptorType.Custom; TheTask.SearchParameters.CustomMdac = customkMdacTextBox.Text; } //determine if semi or nonspecific with a specific protease. if (searchModeType == CleavageSpecificity.Semi || protease.CleavageSpecificity == CleavageSpecificity.Semi) { TheTask.SearchParameters.LocalFdrCategories = new List <FdrCategory> { FdrCategory.FullySpecific, FdrCategory.SemiSpecific }; } else if (searchModeType == CleavageSpecificity.None && protease.CleavageSpecificity != CleavageSpecificity.None) { TheTask.SearchParameters.LocalFdrCategories = new List <FdrCategory> { FdrCategory.FullySpecific, FdrCategory.SemiSpecific, FdrCategory.NonSpecific }; } else { TheTask.SearchParameters.LocalFdrCategories = new List <FdrCategory> { FdrCategory.FullySpecific }; } // displays warning if classic search is enabled with an open search mode if (TheTask.SearchParameters.SearchType == SearchType.Classic && (TheTask.SearchParameters.MassDiffAcceptorType == MassDiffAcceptorType.ModOpen || TheTask.SearchParameters.MassDiffAcceptorType == MassDiffAcceptorType.Open)) { MessageBoxResult result = MessageBox.Show("Modern Search mode is recommended when conducting open precursor mass searches to reduce search time.\n\n" + "Continue anyway?", "Modern search recommended", MessageBoxButton.OKCancel); if (result == MessageBoxResult.Cancel) { return; } } TheTask.SearchParameters.DoHistogramAnalysis = checkBoxHistogramAnalysis.IsChecked.Value; TheTask.SearchParameters.HistogramBinTolInDaltons = double.Parse(histogramBinWidthTextBox.Text, CultureInfo.InvariantCulture); TheTask.SearchParameters.WritePrunedDatabase = writePrunedDBCheckBox.IsChecked.Value; SetModSelectionForPrunedDB(); TheTask.CommonParameters = commonParamsToSave; DialogResult = true; }
public CompactPeptide(PeptideWithSetModifications peptideWithSetModifications, FragmentationTerminus fragmentationTerminus) { TerminalMasses = ComputeNeutralTerminusFragments(peptideWithSetModifications, fragmentationTerminus).ToArray(); MonoisotopicMassIncludingFixedMods = peptideWithSetModifications.MonoisotopicMass; }
public static List <ProductType> GetTerminusSpecificProductTypesFromDissociation(DissociationType dissociationType, FragmentationTerminus fragmentationTerminus) { if (!TerminusSpecificProductTypesFromDissociation.TryGetValue((dissociationType, fragmentationTerminus), out List <ProductType> productTypes)) { lock (TerminusSpecificProductTypesFromDissociation) { var productCollection = TerminusSpecificProductTypes.ProductIonTypesFromSpecifiedTerminus[fragmentationTerminus] .Intersect(DissociationTypeCollection.ProductsFromDissociationType[dissociationType]); if (!TerminusSpecificProductTypesFromDissociation.TryGetValue((dissociationType, fragmentationTerminus), out productTypes)) { productTypes = productCollection.ToList(); TerminusSpecificProductTypesFromDissociation.Add((dissociationType, fragmentationTerminus), productTypes); } } } return(productTypes); }
private Tuple <int, PeptideWithSetModifications> Accepts(List <Product> fragments, double scanPrecursorMass, PeptideWithSetModifications peptide, FragmentationTerminus fragmentationTerminus, MassDiffAcceptor searchMode) { //all masses in N and CTerminalMasses are b-ion masses, which are one water away from a full peptide int localminPeptideLength = commonParameters.DigestionParams.MinPeptideLength; for (int i = localminPeptideLength - 1; i < fragments.Count; i++) //minus one start, because fragment 1 is at index 0 { Product fragment = fragments[i]; double theoMass = fragment.NeutralMass - DissociationTypeCollection.GetMassShiftFromProductType(fragment.ProductType) + WaterMonoisotopicMass; int notch = searchMode.Accepts(scanPrecursorMass, theoMass); if (notch >= 0) { PeptideWithSetModifications updatedPwsm = null; if (fragmentationTerminus == FragmentationTerminus.N) { int endResidue = peptide.OneBasedStartResidueInProtein + fragment.TerminusFragment.FragmentNumber - 1; //-1 for one based index Dictionary <int, Modification> updatedMods = new Dictionary <int, Modification>(); foreach (KeyValuePair <int, Modification> mod in peptide.AllModsOneIsNterminus) { if (mod.Key < endResidue - peptide.OneBasedStartResidueInProtein + 3) //check if we cleaved it off, +1 for N-terminus being mod 1 and first residue being mod 2, +1 again for the -1 on end residue for one based index, +1 (again) for the one-based start residue { updatedMods.Add(mod.Key, mod.Value); } } updatedPwsm = new PeptideWithSetModifications(peptide.Protein, peptide.DigestionParams, peptide.OneBasedStartResidueInProtein, endResidue, CleavageSpecificity.Unknown, "", 0, updatedMods, 0); } else { int startResidue = peptide.OneBasedEndResidueInProtein - fragment.TerminusFragment.FragmentNumber + 1; //plus one for one based index Dictionary <int, Modification> updatedMods = new Dictionary <int, Modification>(); //updateMods int indexShift = startResidue - peptide.OneBasedStartResidueInProtein; foreach (KeyValuePair <int, Modification> mod in peptide.AllModsOneIsNterminus) { if (mod.Key > indexShift + 1) //check if we cleaved it off, +1 for N-terminus being mod 1 and first residue being 2 { int key = mod.Key - indexShift; updatedMods.Add(key, mod.Value); } } updatedPwsm = new PeptideWithSetModifications(peptide.Protein, peptide.DigestionParams, startResidue, peptide.OneBasedEndResidueInProtein, CleavageSpecificity.Unknown, "", 0, updatedMods, 0); } return(new Tuple <int, PeptideWithSetModifications>(notch, updatedPwsm)); } else if (theoMass > scanPrecursorMass) { break; } } //if the theoretical and experimental have the same mass if (fragments.Count > localminPeptideLength) { double totalMass = peptide.MonoisotopicMass;// + Constants.ProtonMass; int notch = searchMode.Accepts(scanPrecursorMass, totalMass); if (notch >= 0) { //need to update so that the cleavage specificity is recorded PeptideWithSetModifications updatedPwsm = new PeptideWithSetModifications(peptide.Protein, peptide.DigestionParams, peptide.OneBasedStartResidueInProtein, peptide.OneBasedEndResidueInProtein, CleavageSpecificity.Unknown, "", 0, peptide.AllModsOneIsNterminus, peptide.NumFixedMods); return(new Tuple <int, PeptideWithSetModifications>(notch, updatedPwsm)); } } return(new Tuple <int, PeptideWithSetModifications>(-1, null)); }