public SrmDocument ConvertToSmallMolecules(SrmDocument document, ConvertToSmallMoleculesMode mode = ConvertToSmallMoleculesMode.formulas, bool invertCharges = false, bool ignoreDecoys=false) { if (mode == ConvertToSmallMoleculesMode.none) return document; var newdoc = new SrmDocument(document.Settings); var note = new Annotations(TestingConvertedFromProteomic, null, 1); // Mark this as a testing node so we don't sort it newdoc = (SrmDocument)newdoc.ChangeIgnoreChangingChildren(true); // Retain copied results foreach (var peptideGroupDocNode in document.MoleculeGroups) { if (!peptideGroupDocNode.IsProteomic) { newdoc = (SrmDocument)newdoc.Add(peptideGroupDocNode); // Already a small molecule } else { var newPeptideGroup = new PeptideGroup(); var newPeptideGroupDocNode = new PeptideGroupDocNode(newPeptideGroup, peptideGroupDocNode.Annotations.Merge(note), peptideGroupDocNode.Name, peptideGroupDocNode.Description, new PeptideDocNode[0], peptideGroupDocNode.AutoManageChildren); foreach (var mol in peptideGroupDocNode.Molecules) { var peptideSequence = mol.Peptide.Sequence; // Create a PeptideDocNode with the presumably baseline charge and label var precursorCharge = (mol.TransitionGroups.Any() ? mol.TransitionGroups.First().TransitionGroup.PrecursorCharge : 0) * (invertCharges ? -1 : 1); var isotopeLabelType = mol.TransitionGroups.Any() ? mol.TransitionGroups.First().TransitionGroup.LabelType : IsotopeLabelType.light; var moleculeCustomIon = ConvertToSmallMolecule(mode, document, mol, precursorCharge, isotopeLabelType); var precursorCustomIon = moleculeCustomIon; var newPeptide = new Peptide(moleculeCustomIon); var newPeptideDocNode = new PeptideDocNode(newPeptide, newdoc.Settings, null, null, null, null, mol.ExplicitRetentionTime, note, mol.Results, new TransitionGroupDocNode[0], mol.AutoManageChildren); foreach (var transitionGroupDocNode in mol.TransitionGroups) { if (transitionGroupDocNode.IsDecoy) { if (ignoreDecoys) continue; throw new Exception("There is no translation from decoy to small molecules"); // Not L10N } if (transitionGroupDocNode.TransitionGroup.PrecursorCharge != Math.Abs(precursorCharge) || !Equals(isotopeLabelType, transitionGroupDocNode.TransitionGroup.LabelType)) { // Different charges or labels mean different ion formulas precursorCharge = transitionGroupDocNode.TransitionGroup.PrecursorCharge * (invertCharges ? -1 : 1); isotopeLabelType = transitionGroupDocNode.TransitionGroup.LabelType; precursorCustomIon = ConvertToSmallMolecule(mode, document, mol, precursorCharge, isotopeLabelType); } var newTransitionGroup = new TransitionGroup(newPeptide, precursorCustomIon, precursorCharge, isotopeLabelType); // Remove any library info, since for the moment at least small molecules don't support this and it won't roundtrip var resultsNew = RemoveTransitionGroupChromInfoLibraryInfo(transitionGroupDocNode); var newTransitionGroupDocNode = new TransitionGroupDocNode(newTransitionGroup, transitionGroupDocNode.Annotations.Merge(note), document.Settings, null, null, transitionGroupDocNode.ExplicitValues, resultsNew, null, transitionGroupDocNode.AutoManageChildren); var mzShift = invertCharges ? 2.0 * BioMassCalc.MassProton : 0; // We removed hydrogen rather than added Assume.IsTrue((Math.Abs(newTransitionGroupDocNode.PrecursorMz + mzShift - transitionGroupDocNode.PrecursorMz) - Math.Abs(transitionGroupDocNode.TransitionGroup.PrecursorCharge * BioMassCalc.MassElectron)) <= 1E-5); foreach (var transition in transitionGroupDocNode.Transitions) { double mass = 0; var transitionCharge = transition.Transition.Charge * (invertCharges ? -1 : 1); var ionType = IonType.custom; CustomIon transitionCustomIon; double mzShiftTransition = 0; if (transition.Transition.IonType == IonType.precursor) { ionType = IonType.precursor; transitionCustomIon = new DocNodeCustomIon(precursorCustomIon.Formula, string.IsNullOrEmpty(precursorCustomIon.Formula) ? precursorCustomIon.MonoisotopicMass : (double?) null, string.IsNullOrEmpty(precursorCustomIon.Formula) ? precursorCustomIon.AverageMass : (double?) null, SmallMoleculeNameFromPeptide(peptideSequence, transitionCharge)); mzShiftTransition = invertCharges ? 2.0 * BioMassCalc.MassProton : 0; // We removed hydrogen rather than added } else if (transition.Transition.IonType == IonType.custom) { transitionCustomIon = transition.Transition.CustomIon; mass = transitionCustomIon.MonoisotopicMass; } else { // TODO - try to get fragment formula? mass = BioMassCalc.CalculateIonMassFromMz(transition.Mz, transition.Transition.Charge); transitionCustomIon = new DocNodeCustomIon(mass, mass,// We can't really get at mono vs average mass from m/z, but for test purposes this is fine transition.Transition.FragmentIonName); } if (mode == ConvertToSmallMoleculesMode.masses_and_names) { // Discard the formula if we're testing the use of mass-with-names (for matching in ratio calcs) target specification transitionCustomIon = new DocNodeCustomIon(transitionCustomIon.MonoisotopicMass, transitionCustomIon.AverageMass, transition.Transition.FragmentIonName); } else if (mode == ConvertToSmallMoleculesMode.masses_only) { // Discard the formula and name if we're testing the use of mass-only target specification transitionCustomIon = new DocNodeCustomIon(transitionCustomIon.MonoisotopicMass, transitionCustomIon.AverageMass); } var newTransition = new Transition(newTransitionGroup, ionType, null, transition.Transition.MassIndex, transition.Transition.Charge * (invertCharges ? -1 : 1), null, transitionCustomIon); if (ionType == IonType.precursor) { mass = document.Settings.GetFragmentMass(transitionGroupDocNode.TransitionGroup.LabelType, null, newTransition, newTransitionGroupDocNode.IsotopeDist); } var newTransitionDocNode = new TransitionDocNode(newTransition, transition.Annotations.Merge(note), null, mass, transition.IsotopeDistInfo, null, transition.Results); Assume.IsTrue((Math.Abs(newTransitionDocNode.Mz + mzShiftTransition - transition.Mz) - Math.Abs(transitionGroupDocNode.TransitionGroup.PrecursorCharge * BioMassCalc.MassElectron)) <= 1E-5, String.Format("unexpected mz difference {0}-{1}={2}", newTransitionDocNode.Mz , transition.Mz, newTransitionDocNode.Mz - transition.Mz)); // Not L10N newTransitionGroupDocNode = (TransitionGroupDocNode)newTransitionGroupDocNode.Add(newTransitionDocNode); } if (newPeptideDocNode != null) newPeptideDocNode = (PeptideDocNode)newPeptideDocNode.Add(newTransitionGroupDocNode); } newPeptideGroupDocNode = (PeptideGroupDocNode)newPeptideGroupDocNode.Add(newPeptideDocNode); } newdoc = (SrmDocument)newdoc.Add(newPeptideGroupDocNode); } } // No retention time prediction for small molecules (yet?) newdoc = newdoc.ChangeSettings(newdoc.Settings.ChangePeptideSettings(newdoc.Settings.PeptideSettings.ChangePrediction( newdoc.Settings.PeptideSettings.Prediction.ChangeRetentionTime(null)))); return newdoc; }