package com.compomics.util.experiment.identification.spectrum_annotation.spectrum_annotators; import com.compomics.util.experiment.biology.Ion; import com.compomics.util.experiment.biology.IonFactory; import com.compomics.util.experiment.biology.NeutralLoss; import com.compomics.util.experiment.biology.PTM; import com.compomics.util.experiment.biology.PTMFactory; import com.compomics.util.experiment.biology.Peptide; import com.compomics.util.experiment.biology.ions.PeptideFragmentIon; import com.compomics.util.experiment.identification.spectrum_annotation.NeutralLossesMap; import com.compomics.util.experiment.identification.spectrum_annotation.SpectrumAnnotator; import com.compomics.util.experiment.identification.matches.IonMatch; import com.compomics.util.experiment.identification.matches.ModificationMatch; import com.compomics.util.experiment.massspectrometry.MSnSpectrum; import com.compomics.util.experiment.massspectrometry.Peak; import com.compomics.util.experiment.identification.spectrum_annotation.AnnotationSettings; import com.compomics.util.preferences.SequenceMatchingPreferences; import com.compomics.util.experiment.identification.spectrum_annotation.SpecificAnnotationSettings; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; /** * Annotates a spectrum with peptide fragments. Warning: not multi-thread safe, * use different annotators for each thread. * * @author Marc Vaudel */ public class PeptideSpectrumAnnotator extends SpectrumAnnotator { /** * The theoretic peptide to match. */ private Peptide peptide; /** * Constructor. */ public PeptideSpectrumAnnotator() { } /** * Sets a new peptide to match. * * @param peptide the new peptide * @param precursorCharge the new precursor charge * @param specificAnnotationSettings if provided, only the ions detectable * using these settings will be selected */ public void setPeptide(Peptide peptide, int precursorCharge, SpecificAnnotationSettings specificAnnotationSettings) { setPeptide(peptide, null, precursorCharge, specificAnnotationSettings); } /** * Sets a new peptide to match. * * @param peptide the new peptide * @param possibleFragmentIons the possible fragment ions of the peptide * @param precursorCharge the new precursor charge * @param specificAnnotationSettings if provided, only the ions detectable * using these settings will be selected */ public void setPeptide(Peptide peptide, HashMap<Integer, HashMap<Integer, ArrayList<Ion>>> possibleFragmentIons, int precursorCharge, SpecificAnnotationSettings specificAnnotationSettings) { if (specificAnnotationSettings != null && super.specificAnnotationSettings == null || specificAnnotationSettings == null && super.specificAnnotationSettings != null || specificAnnotationSettings != null && super.specificAnnotationSettings != null && specificAnnotationSettings != super.specificAnnotationSettings || this.peptide == null || !this.peptide.getKey().equals(peptide.getKey()) || !this.peptide.sameModificationsAs(peptide) || this.precursorCharge != precursorCharge) { // Set new values this.peptide = peptide; this.precursorCharge = precursorCharge; if (possibleFragmentIons == null) { theoreticalFragmentIons = fragmentFactory.getFragmentIons(peptide, specificAnnotationSettings); } else { theoreticalFragmentIons = possibleFragmentIons; } if (massShift != 0 || massShiftNTerm != 0 || massShiftCTerm != 0) { updateMassShifts(); } } } /** * This method matches the potential fragment ions of a given peptide with a * given peak according to the annotation settings. * * @param peptide the peptide * @param specificAnnotationSettings the specific annotation settings * @param peak the peak to match * @return a list of potential ion matches */ public ArrayList<IonMatch> matchPeak(Peptide peptide, SpecificAnnotationSettings specificAnnotationSettings, Peak peak) { setPeptide(peptide, specificAnnotationSettings.getPrecursorCharge(), specificAnnotationSettings); return matchPeak(specificAnnotationSettings, peak); } /** * Returns the spectrum annotations of a spectrum in a list of IonMatches. * * Note that, except for +1 precursors, fragments ions will be expected to * have a charge strictly smaller than the precursor ion charge. * * @param annotationSettings the annotation settings * @param specificAnnotationSettings the specific annotation settings * @param spectrum the spectrum to match * @param peptide the peptide of interest * * @return an ArrayList of IonMatch containing the ion matches with the * given settings */ public synchronized ArrayList<IonMatch> getSpectrumAnnotation(AnnotationSettings annotationSettings, SpecificAnnotationSettings specificAnnotationSettings, MSnSpectrum spectrum, Peptide peptide) { return getSpectrumAnnotation(annotationSettings, specificAnnotationSettings, spectrum, peptide, null); } /** * Returns the spectrum annotations of a spectrum in a list of IonMatches. * * Note that, except for +1 precursors, fragments ions will be expected to * have a charge strictly smaller than the precursor ion charge. * * @param annotationSettings the annotation settings * @param specificAnnotationSettings the specific annotation settings * @param spectrum the spectrum to match * @param peptide the peptide of interest * @param possiblePeptideFragments the possible peptide fragments for this peptide * * @return an ArrayList of IonMatch containing the ion matches with the * given settings */ public synchronized ArrayList<IonMatch> getSpectrumAnnotation(AnnotationSettings annotationSettings, SpecificAnnotationSettings specificAnnotationSettings, MSnSpectrum spectrum, Peptide peptide, HashMap<Integer, HashMap<Integer, ArrayList<Ion>>> possiblePeptideFragments) { ArrayList<IonMatch> result = new ArrayList<IonMatch>(); setMassTolerance(specificAnnotationSettings.getFragmentIonAccuracy(), specificAnnotationSettings.isFragmentIonPpm(), annotationSettings.getTiesResolution()); if (spectrum != null) { setSpectrum(spectrum, spectrum.getIntensityLimit(annotationSettings.getAnnotationIntensityLimit())); } setPeptide(peptide, possiblePeptideFragments, specificAnnotationSettings.getPrecursorCharge(), specificAnnotationSettings); ArrayList<Integer> precursorCharges = new ArrayList<Integer>(); // possible charges for the precursor for (int i = 1; i <= precursorCharge; i++) { precursorCharges.add(i); } HashMap<Ion.IonType, HashSet<Integer>> ionTypes = specificAnnotationSettings.getIonTypes(); for (Ion.IonType ionType : ionTypes.keySet()) { HashMap<Integer, ArrayList<Ion>> ionMap = theoreticalFragmentIons.get(ionType.index); if (ionMap != null) { HashSet<Integer> subtypes = ionTypes.get(ionType); for (int subType : subtypes) { ArrayList<Ion> ions = ionMap.get(subType); if (ions != null) { for (Ion ion : ions) { if (lossesValidated(specificAnnotationSettings.getNeutralLossesMap(), ion)) { ArrayList<Integer> ionPossibleCharges = (ionType == Ion.IonType.PRECURSOR_ION) ? precursorCharges : specificAnnotationSettings.getSelectedCharges(); for (Integer charge : ionPossibleCharges) { if (chargeValidated(ion, charge, precursorCharge)) { IonMatch ionMatch = matchInSpectrum(ion, charge); if (ionMatch != null) { result.add(ionMatch); } } } } } } } } } return result; } /** * Returns the ion matches corresponding to fragment ions indexed by amino * acid number in the sequence. 1 is first amino acid. * * @param annotationSettings the annotation settings * @param specificAnnotationSettings the specific annotation settings * @param spectrum The spectrum to match * @param peptide The peptide of interest * * @return the ion matches corresponding to fragment ions indexed by amino * acid number in the sequence */ public HashMap<Integer, ArrayList<IonMatch>> getCoveredAminoAcids(AnnotationSettings annotationSettings, SpecificAnnotationSettings specificAnnotationSettings, MSnSpectrum spectrum, Peptide peptide) { HashMap<Integer, ArrayList<IonMatch>> matchesMap = new HashMap<Integer, ArrayList<IonMatch>>(); ArrayList<IonMatch> matches = getSpectrumAnnotation(annotationSettings, specificAnnotationSettings, spectrum, peptide); for (IonMatch ionMatch : matches) { Ion ion = ionMatch.ion; int number; if (ion.getType() == Ion.IonType.PEPTIDE_FRAGMENT_ION) { if (ion.getSubType() == PeptideFragmentIon.A_ION || ion.getSubType() == PeptideFragmentIon.B_ION || ion.getSubType() == PeptideFragmentIon.C_ION) { number = ((PeptideFragmentIon) ion).getNumber(); } else { number = peptide.getSequence().length() + 1 - ((PeptideFragmentIon) ion).getNumber(); } if (!matchesMap.containsKey(number)) { matchesMap.put(number, new ArrayList<IonMatch>()); } matchesMap.get(number).add(ionMatch); } } return matchesMap; } /** * Returns the expected ions in a map indexed by the possible charges. * * Note that, except for +1 precursors, fragments ions will be expected to * have a charge strictly smaller than the precursor ion charge. * * @param specificAnnotationSettings the specific annotation settings * @param peptide The peptide of interest * * @return an ArrayList of IonMatch containing the ion matches with the * given settings */ public HashMap<Integer, ArrayList<Ion>> getExpectedIons(SpecificAnnotationSettings specificAnnotationSettings, Peptide peptide) { return getExpectedIons(specificAnnotationSettings, peptide, null); } /** * Returns the expected ions in a map indexed by the possible charges. * * Note that, except for +1 precursors, fragments ions will be expected to * have a charge strictly smaller than the precursor ion charge. * * @param specificAnnotationSettings the specific annotation settings * @param peptide The peptide of interest * @param possibleFragmentIons the possible fragment ions for the given * peptide * * @return an ArrayList of IonMatch containing the ion matches with the * given settings */ public HashMap<Integer, ArrayList<Ion>> getExpectedIons(SpecificAnnotationSettings specificAnnotationSettings, Peptide peptide, HashMap<Integer, HashMap<Integer, ArrayList<Ion>>> possibleFragmentIons) { setPeptide(peptide, possibleFragmentIons, specificAnnotationSettings.getPrecursorCharge(), specificAnnotationSettings); return getExpectedIons(specificAnnotationSettings); } @Override public ArrayList<IonMatch> getCurrentAnnotation(MSnSpectrum spectrum, AnnotationSettings annotationSettings, SpecificAnnotationSettings specificAnnotationSettings) { return getSpectrumAnnotation(annotationSettings, specificAnnotationSettings, spectrum, peptide); } /** * Returns the possible neutral losses expected by default for a given * peptide. /!\ this method will work only if the PTM found in the peptide * are in the PTMFactory. * * @param peptide the peptide of interest * @param sequenceMatchingSettings the sequence matching settings for * peptide to protein mapping * @param ptmSequenceMatchingSettings the sequence matching settings for PTM * to peptide mapping * * @return the expected possible neutral losses * * @throws IOException exception thrown whenever an error occurred while * interacting with a file while mapping potential modification sites * @throws InterruptedException exception thrown whenever a threading issue * occurred while mapping potential modification sites * @throws ClassNotFoundException exception thrown whenever an error * occurred while deserializing an object from the ProteinTree * @throws SQLException exception thrown whenever an error occurred while * interacting with the ProteinTree */ public static NeutralLossesMap getDefaultLosses(Peptide peptide, SequenceMatchingPreferences sequenceMatchingSettings, SequenceMatchingPreferences ptmSequenceMatchingSettings) throws IOException, InterruptedException, ClassNotFoundException, SQLException { PTMFactory pTMFactory = PTMFactory.getInstance(); NeutralLossesMap neutralLossesMap = new NeutralLossesMap(); String sequence = peptide.getSequence(); int aaMin = sequence.length(); int aaMax = 0; if (IonFactory.getInstance().getDefaultNeutralLosses().contains(NeutralLoss.H2O)) { int firstIndex = sequence.indexOf("D"); if (firstIndex != -1) { aaMin = Math.min(firstIndex, aaMin); aaMax = Math.max(sequence.lastIndexOf("D"), aaMax); } firstIndex = sequence.indexOf("E"); if (firstIndex != -1) { aaMin = Math.min(firstIndex, aaMin); aaMax = Math.max(sequence.lastIndexOf("E"), aaMax); } firstIndex = sequence.indexOf("S"); if (firstIndex != -1) { aaMin = Math.min(firstIndex, aaMin); aaMax = Math.max(sequence.lastIndexOf("S"), aaMax); } firstIndex = sequence.indexOf("T"); if (firstIndex != -1) { aaMin = Math.min(firstIndex, aaMin); aaMax = Math.max(sequence.lastIndexOf("T"), aaMax); } if (aaMin < sequence.length()) { neutralLossesMap.addNeutralLoss(NeutralLoss.H2O, aaMin + 1, sequence.length() - aaMax); } } aaMin = sequence.length(); aaMax = 0; if (IonFactory.getInstance().getDefaultNeutralLosses().contains(NeutralLoss.NH3)) { int firstIndex = sequence.indexOf("K"); if (firstIndex != -1) { aaMin = Math.min(firstIndex, aaMin); aaMax = Math.max(sequence.lastIndexOf("K"), aaMax); } firstIndex = sequence.indexOf("N"); if (firstIndex != -1) { aaMin = Math.min(firstIndex, aaMin); aaMax = Math.max(sequence.lastIndexOf("N"), aaMax); } firstIndex = sequence.indexOf("Q"); if (firstIndex != -1) { aaMin = Math.min(firstIndex, aaMin); aaMax = Math.max(sequence.lastIndexOf("Q"), aaMax); } firstIndex = sequence.indexOf("R"); if (firstIndex != -1) { aaMin = Math.min(firstIndex, aaMin); aaMax = Math.max(sequence.lastIndexOf("R"), aaMax); } if (aaMin < sequence.length()) { neutralLossesMap.addNeutralLoss(NeutralLoss.NH3, aaMin + 1, sequence.length() - aaMax); } } int modMin = sequence.length(); int modMax = 0; if (peptide.isModified()) { for (ModificationMatch modMatch : peptide.getModificationMatches()) { PTM ptm = pTMFactory.getPTM(modMatch.getTheoreticPtm()); if (ptm == null) { throw new IllegalArgumentException("PTM " + modMatch.getTheoreticPtm() + " not loaded in PTM factory."); } for (NeutralLoss neutralLoss : ptm.getNeutralLosses()) { ArrayList<Integer> indexes = peptide.getPotentialModificationSites(ptm, sequenceMatchingSettings, ptmSequenceMatchingSettings); if (!indexes.isEmpty()) { Collections.sort(indexes); modMin = indexes.get(0); modMax = indexes.get(indexes.size() - 1); } neutralLossesMap.addNeutralLoss(neutralLoss, modMin, sequence.length() - modMax + 1); } } } return neutralLossesMap; } /** * Returns the currently inspected peptide. * * @return the currently inspected peptide */ public Peptide getCurrentlyLoadedPeptide() { return peptide; } }