// Unit test of annotations handler class private static void TestSpectrumPeakAnnotations() { var noNote = SpectrumPeakAnnotation.Create(new CustomIon(null, Adduct.FromChargeNoMass(-2), 100, 101, "noNote"), null); var noName = SpectrumPeakAnnotation.Create(new CustomIon(null, Adduct.FromChargeNoMass(-2), 100, 101, null), "noted"); var fullIon = new CustomIon("C12H5O3", Adduct.FromStringAssumeChargeOnly("M-H2O+"), null, null, "full"); var full = SpectrumPeakAnnotation.Create(fullIon, "noted"); var fullToo = SpectrumPeakAnnotation.Create(fullIon, "noted"); var nameOnlyTab = SpectrumPeakAnnotation.Create(new CustomIon(null, Adduct.FromChargeNoMass(-2), 100, 101, "test\ttabs"), "noted"); // Check tab escape var fullIonTabs = new CustomIon("C12H5", Adduct.FromChargeNoMass(-2), null, null, "full\ttabs"); var tabbedFull = SpectrumPeakAnnotation.Create(fullIonTabs, "noted\ttabs"); // Check tab escape Assume.AreEqual(full, fullToo); Assume.AreNotEqual(full, tabbedFull); var tests = new List <List <SpectrumPeakAnnotation> > { new List <SpectrumPeakAnnotation> { noNote, noName, full }, new List <SpectrumPeakAnnotation> { fullToo, nameOnlyTab, tabbedFull } }; var cached = SpectrumPeakAnnotation.ToCacheFormat(tests); var roundtrip = SpectrumPeakAnnotation.FromCacheFormat(cached); int i = 0; foreach (var annotsPerPeak in tests) { Assume.IsTrue(CollectionUtil.EqualsDeep(annotsPerPeak, roundtrip[i++])); } }
private static void TestAddingTransition() { RunUI(() => { SkylineWindow.ExpandPrecursors(); var node = SkylineWindow.SequenceTree.Nodes[0].FirstNode.FirstNode; SkylineWindow.SequenceTree.SelectedNode = node; }); var moleculeDlg = ShowDialog <EditCustomMoleculeDlg>(SkylineWindow.AddSmallMolecule); RunUI(() => { moleculeDlg.FormulaBox.Formula = C12H12; moleculeDlg.NameText = testNametextA; moleculeDlg.Adduct = Adduct.NonProteomicProtonatedFromCharge(1); }); OkDialog(moleculeDlg, moleculeDlg.OkDialog); var newDoc = SkylineWindow.Document; var compareIon = new CustomIon(C12H12, Adduct.NonProteomicProtonatedFromCharge(1), null, null, testNametextA); Assert.AreEqual(compareIon, newDoc.MoleculeTransitions.ElementAt(0).Transition.CustomIon); Assert.AreEqual(1, newDoc.MoleculeTransitions.ElementAt(0).Transition.Charge); // Verify that tree selection doesn't change just because we changed an ID object // (formerly the tree node would collapse and focus would jump up a level) RunUI(() => { Assert.AreEqual(SkylineWindow.SequenceTree.SelectedNode, SkylineWindow.SequenceTree.Nodes[0].FirstNode.FirstNode); }); }
public static SpectrumPeakAnnotation Create(SmallMoleculeLibraryAttributes mol, Adduct adduct, string comment, double?mzTheoretical) { double?massTheoretical = mzTheoretical.HasValue ? adduct.MassFromMz(mzTheoretical.Value, MassType.Monoisotopic).Value : (double?)null; var ion = new CustomIon(mol, adduct, massTheoretical); if ((mzTheoretical ?? 0.0) > 0) { if (Equals(ion.MonoisotopicMassMz, 0.0)) { // We didn't have enough info to calculate mz, use the provided theoretical value var massMono = adduct.MassFromMz(mzTheoretical.Value, MassType.Monoisotopic); var massAverage = adduct.MassFromMz(mzTheoretical.Value, MassType.Average); ion = new CustomIon(ion.GetSmallMoleculeLibraryAttributes(), ion.Adduct, massMono, massAverage); } else { // Check our calculated value against provided theoretical value, allowing quite a lot of wiggle (not everybody is using the same precision out there) var delta = .5; // Generous error for sanity check if (Math.Abs(ion.MonoisotopicMassMz - mzTheoretical.Value) > delta) { Assume.Fail(string.Format(@"SpectrumPeakAnnotation: mzTheoretical {0} and mzActual {1} disagree by more than {2} in {3} {4}", mzTheoretical, ion.MonoisotopicMassMz, delta, ion, comment ?? string.Empty)); } } } return(ion.IsEmpty && string.IsNullOrEmpty(comment) ? EMPTY : new SpectrumPeakAnnotation(ion, comment)); }
// Deserialize a collection of annotations (with multiple annotations per peak) public static List <List <SpectrumPeakAnnotation> > FromCacheFormat(string cached) { var result = new List <List <SpectrumPeakAnnotation> >(); if (string.IsNullOrEmpty(cached)) { return(result); } var annotationsPerPeak = cached.Split('\n'); foreach (var annotations in annotationsPerPeak) { if (string.IsNullOrEmpty(annotations)) { result.Add(null); } else { var list = new List <SpectrumPeakAnnotation>(); foreach (var annot in annotations.Split('\r')) { var lastTab = annot.LastIndexOf(TextUtil.SEPARATOR_TSV_STR, StringComparison.Ordinal); var ion = lastTab < 0 ? CustomIon.EMPTY : CustomIon.FromTSV(annot.Substring(0, lastTab)); var comment = lastTab < 0 ? string.Empty : annot.Substring(lastTab + 1).UnescapeTabAndCrLf(); list.Add(ion.IsEmpty && string.IsNullOrEmpty(comment) ? EMPTY : new SpectrumPeakAnnotation(ion, comment)); } result.Add(list); } } return(result); }
public override void ReadXml(XmlReader reader) { // Read tag attributes base.ReadXml(reader); Fragment = reader.GetAttribute(ATTR.cut); if (IsFragment) { Restrict = reader.GetAttribute(ATTR.no_cut); Terminus = reader.GetAttribute(ATTR.sense, ToSeqTerminus); MinFragmentLength = reader.GetNullableIntAttribute(ATTR.min_length) ?? DEFAULT_MIN_FRAGMENT_LENGTH; } else { var charges = TextUtil.ParseInts(reader.GetAttribute(ATTR.charges)); // Old version? if (charges.Length > 1) { throw new InvalidDataException(Resources.MeasuredIon_ReadXml_Multiple_charge_states_for_custom_ions_are_no_longer_supported_); } var parsedIon = CustomIon.Deserialize(reader); Adduct adduct; if (charges.Any()) // Old style - fix it up a little for our revised ideas about custom ion ionization { adduct = Adduct.FromChargeNoMass(charges[0]); if (string.IsNullOrEmpty(parsedIon.NeutralFormula)) // Adjust the user-supplied masses { SettingsCustomIon = new SettingsCustomIon(parsedIon.NeutralFormula, adduct, Math.Round(parsedIon.MonoisotopicMass + charges[0] * BioMassCalc.MONOISOTOPIC.GetMass(BioMassCalc.H), SequenceMassCalc.MassPrecision), // Assume user provided neutral mass. Round new value easiest XML roundtripping. Math.Round(parsedIon.AverageMass + charges[0] * BioMassCalc.AVERAGE.GetMass(BioMassCalc.H), SequenceMassCalc.MassPrecision), // Assume user provided neutral mass. Round new value easiest XML roundtripping. parsedIon.Name); } else // Adjust the formula to include ion atoms { if (charges[0] > 1) // XML deserializer will have added an H already { var adductProtonated = Adduct.FromChargeProtonated(charges[0] - 1); var formula = adductProtonated.ApplyToFormula(parsedIon.NeutralFormula); parsedIon = new CustomIon(formula, adduct, parsedIon.MonoisotopicMass, parsedIon.AverageMass, Name); } } } else { adduct = Adduct.FromStringAssumeChargeOnly(reader.GetAttribute(ATTR.charge)); // Ionization mass is already in formula } if (SettingsCustomIon == null) { SettingsCustomIon = new SettingsCustomIon(parsedIon.NeutralFormula, adduct, parsedIon.MonoisotopicMass, parsedIon.AverageMass, parsedIon.Name); } IsOptional = reader.GetBoolAttribute(ATTR.optional); } // Consume tag reader.Read(); Validate(); }
// Cannot find a way to use anything but a file with SQLite // public const string _bibliospecLiteLibPath = @"C:\Libraries\yeast.blib"; private List <SpectrumPeakAnnotation> MakeTestPeakAnnotation(Adduct adduct, float mz, string name, string note) { var ion = new CustomIon(null, adduct, adduct.MassFromMz(mz, MassType.Monoisotopic), adduct.MassFromMz(mz, MassType.Average), name); var annot = SpectrumPeakAnnotation.Create(ion, note); return(new List <SpectrumPeakAnnotation> { annot }); }
private static void TestAddingTransitionAsMasses() { RunUI(() => { SkylineWindow.ExpandPrecursors(); var node = SkylineWindow.SequenceTree.Nodes[0].FirstNode.FirstNode; SkylineWindow.SequenceTree.SelectedNode = node; }); var editMoleculeDlg = ShowDialog <EditCustomMoleculeDlg>(SkylineWindow.AddSmallMolecule); var formula = C12H12; var monoMass = BioMassCalc.MONOISOTOPIC.CalculateMassFromFormula(formula); var averageMass = BioMassCalc.AVERAGE.CalculateMassFromFormula(formula); var adduct = Adduct.M_PLUS; RunUI(() => { // Verify the interaction of explicitly set mz and charge without formula editMoleculeDlg.NameText = testNametextA; editMoleculeDlg.Adduct = adduct; var mzMono = editMoleculeDlg.Adduct.MzFromNeutralMass(monoMass); var mzAverage = editMoleculeDlg.Adduct.MzFromNeutralMass(averageMass); editMoleculeDlg.FormulaBox.MonoMass = monoMass; editMoleculeDlg.FormulaBox.AverageMass = averageMass; var massPrecisionTolerance = 0.00001; Assert.AreEqual(mzMono, double.Parse(editMoleculeDlg.FormulaBox.MonoText), massPrecisionTolerance); Assert.AreEqual(mzAverage, double.Parse(editMoleculeDlg.FormulaBox.AverageText), massPrecisionTolerance); }); OkDialog(editMoleculeDlg, editMoleculeDlg.OkDialog); var newDoc = SkylineWindow.Document; var compareIon = new CustomIon(null, adduct, monoMass, averageMass, testNametextA); Assert.AreEqual(compareIon, newDoc.MoleculeTransitions.ElementAt(0).Transition.CustomIon); Assert.AreEqual(1, newDoc.MoleculeTransitions.ElementAt(0).Transition.Charge); // Verify that tree selection doesn't change just because we changed an ID object // (formerly the tree node would collapse and focus would jump up a level) RunUI(() => { Assert.AreEqual(SkylineWindow.SequenceTree.SelectedNode, SkylineWindow.SequenceTree.Nodes[0].FirstNode.FirstNode); }); }
protected override void DoTest() { const int molCount = 1; var doc = SkylineWindow.Document; for (var loop = 0; loop < 3; loop++) { // Should be able to synchronize if both sibs have formula, or neither, but not one of each string testname; switch (loop) { case 0: testname = "ions_testing_masses.sky"; // Masses only break; case 1: testname = "ions_testing_mixed.sky"; // Starts out as two precursors, with no transitions, one is labeled 15N break; default: testname = "ions_testing.sky"; // Starts out as two precursors, first has a precursor transition, other has label 15N and serialized ion formula break; } string testPath = TestFilesDir.GetTestPath(testname); RunUI(() => SkylineWindow.OpenFile(testPath)); doc = WaitForDocumentChange(doc); SelectNode(SrmDocument.Level.Molecules, 0); Assert.AreEqual(molCount, SkylineWindow.Document.MoleculeCount); RunUI(SkylineWindow.ExpandPrecursors); Settings.Default.SynchronizeIsotopeTypes = true; // Select the first transition group SelectNode(SrmDocument.Level.TransitionGroups, 0); // Add precursor transition to it var pickList = ShowDialog <PopupPickList>(SkylineWindow.ShowPickChildrenInTest); var lloop = loop; RunUI(() => { pickList.ApplyFilter(false); switch (lloop) { default: pickList.SetItemChecked(0, true); // 0th item is M transition break; case 1: pickList.SetItemChecked(0, true); // 0th item is the M-1 transition pickList.SetItemChecked(1, true); // 1th item is the M transition break; } pickList.AutoManageChildren = false; pickList.IsSynchSiblings = true; // Cause a precursor transition to be added to heavy group }); OkDialog(pickList, pickList.OnOk); WaitForClosedForm(pickList); doc = WaitForDocumentChange(doc); switch (loop) { default: // There should now be a precursor transition for the second, heavy labeled transition group, and it // should match the mz of the transition group Assert.AreEqual(2, doc.MoleculeTransitions.Count()); Assert.AreNotEqual(doc.MoleculeTransitionGroups.ToArray()[0].PrecursorMz, doc.MoleculeTransitions.ToArray()[1].Mz); Assert.AreEqual(doc.MoleculeTransitionGroups.ToArray()[1].PrecursorMz, doc.MoleculeTransitions.ToArray()[1].Mz); break; case 1: // There should now be two precursor transitions M-1 and M for the second, heavy labeled transition group, and the // second precursor transition should match the mz of the transition group Assert.AreEqual(4, doc.MoleculeTransitions.Count()); Assert.AreNotEqual(doc.MoleculeTransitionGroups.ToArray()[0].PrecursorMz, doc.MoleculeTransitions.ToArray()[3].Mz); Assert.AreEqual(doc.MoleculeTransitionGroups.ToArray()[1].PrecursorMz, doc.MoleculeTransitions.ToArray()[3].Mz); break; } } // // Now with isotope distributions // var documentPath = TestFilesDir.GetTestPath("ions_testing_isotopes.sky"); RunUI(() => SkylineWindow.OpenFile(documentPath)); var newDoc = WaitForDocumentLoaded(); var transitionSettingsFilter = ShowDialog <TransitionSettingsUI>(SkylineWindow.ShowTransitionSettingsUI); RunUI(() => { transitionSettingsFilter.SelectedTab = TransitionSettingsUI.TABS.Filter; transitionSettingsFilter.SmallMoleculeFragmentTypes = "p"; // Allow precursors transitionSettingsFilter.SmallMoleculePrecursorAdducts = "[M+H]"; // Allow precursors }); OkDialog(transitionSettingsFilter, transitionSettingsFilter.OkDialog); newDoc = WaitForDocumentChange(newDoc); SelectNode(SrmDocument.Level.Molecules, 0); Assert.AreEqual(molCount, SkylineWindow.Document.MoleculeCount); RunUI(SkylineWindow.ExpandPrecursors); Settings.Default.SynchronizeIsotopeTypes = true; // Select the first transition group SelectNode(SrmDocument.Level.TransitionGroups, 0); // Add isotope labeled precursor transitions to it var pickList0 = ShowDialog <PopupPickList>(SkylineWindow.ShowPickChildrenInTest); RunUI(() => { pickList0.ApplyFilter(false); pickList0.SetItemChecked(0, true); pickList0.SetItemChecked(1, true); pickList0.SetItemChecked(2, true); pickList0.AutoManageChildren = false; Assert.IsTrue(pickList0.CanSynchSiblings); pickList0.IsSynchSiblings = true; // Cause precursor transitions to be added to heavy group }); OkDialog(pickList0, pickList0.OnOk); WaitForClosedForm(pickList0); Assert.AreEqual(SkylineWindow.Document.MoleculeTransitionGroups.ToArray()[1].PrecursorMz, SkylineWindow.Document.MoleculeTransitions.ToArray()[4].Mz); Assert.AreEqual(SkylineWindow.Document.MoleculeTransitionGroups.ToArray()[1].PrecursorMz + 1.003492, SkylineWindow.Document.MoleculeTransitions.ToArray()[5].Mz, 1e-6); // Verify that adding a custom transition prevents the synch siblings checkbox from appearing RunUI(() => { SkylineWindow.ExpandPrecursors(); var node = SkylineWindow.SequenceTree.Nodes[0].FirstNode.FirstNode; SkylineWindow.SequenceTree.SelectedNode = node; }); newDoc = WaitForDocumentChange(newDoc); var moleculeDlg = ShowDialog <EditCustomMoleculeDlg>(SkylineWindow.AddSmallMolecule); var C12H12 = "C12H12"; var testNametextA = "y1"; RunUI(() => { moleculeDlg.FormulaBox.Formula = C12H12; moleculeDlg.NameText = testNametextA; moleculeDlg.Adduct = Adduct.SINGLY_PROTONATED; }); OkDialog(moleculeDlg, moleculeDlg.OkDialog); newDoc = WaitForDocumentChange(newDoc); var compareIon = new CustomIon(C12H12, Adduct.SINGLY_PROTONATED, null, null, testNametextA); const int transY1Index = 3; Assert.AreEqual(compareIon, newDoc.MoleculeTransitions.ElementAt(transY1Index).Transition.CustomIon); Assert.AreEqual(1, newDoc.MoleculeTransitions.ElementAt(transY1Index).Transition.Charge); var pickList1 = ShowDialog <PopupPickList>(SkylineWindow.ShowPickChildrenInTest); RunUI(() => { pickList1.AutoManageChildren = true; Assert.IsFalse(pickList1.CanSynchSiblings); }); OkDialog(pickList1, pickList1.OnOk); Assert.AreEqual(7, newDoc.MoleculeTransitions.Count()); // Long as we're here, check mz filtering var transitionSettings = ShowDialog <TransitionSettingsUI>(SkylineWindow.ShowTransitionSettingsUI); RunUI(() => { transitionSettings.SelectedTab = TransitionSettingsUI.TABS.Instrument; transitionSettings.MinMz = 160; // Should drop that custom ion }); OkDialog(transitionSettings, transitionSettings.OkDialog); newDoc = WaitForDocumentChange(newDoc); Assert.AreEqual(6, newDoc.MoleculeTransitions.Count()); RunUI(() => { SkylineWindow.Undo(); }); newDoc = WaitForDocumentChange(newDoc); Assert.AreEqual(7, newDoc.MoleculeTransitions.Count()); RunUI(() => { SkylineWindow.ExpandPrecursors(); var node = SkylineWindow.SequenceTree.Nodes[0].FirstNode.Nodes[1]; SkylineWindow.SequenceTree.SelectedNode = node; }); var pickList2 = ShowDialog <PopupPickList>(SkylineWindow.ShowPickChildrenInTest); RunUI(() => { Assert.IsFalse(pickList2.CanSynchSiblings); // The light set has a non-precursor transition }); OkDialog(pickList2, pickList2.OnOk); var transitionSettings2 = ShowDialog <TransitionSettingsUI>(SkylineWindow.ShowTransitionSettingsUI); RunUI(() => { transitionSettings2.SelectedTab = TransitionSettingsUI.TABS.Instrument; transitionSettings2.MinMz = 300; // Should drop that custom ion }); OkDialog(transitionSettings2, transitionSettings2.OkDialog); newDoc = WaitForDocumentChange(newDoc); Assert.AreEqual(6, newDoc.MoleculeTransitions.Count()); var pickList3 = ShowDialog <PopupPickList>(SkylineWindow.ShowPickChildrenInTest); RunUI(() => { Assert.IsTrue(pickList3.CanSynchSiblings); }); OkDialog(pickList3, pickList3.OnOk); // Add another precursor, with explicit isotope declaration in the adduct [M4C13+H] // Open its picklist, clear filter so all isotopes appear // Select them all, and check synch siblings // Should be 3*3 transitions in doc SelectNode(SrmDocument.Level.Molecules, 0); var moleculeDlg2 = ShowDialog <EditCustomMoleculeDlg>(SkylineWindow.AddSmallMolecule); var adduct = moleculeDlg.FormulaBox.Adduct.ChangeIsotopeLabels(new Dictionary <string, int> { { "C'", 4 } }); // Not L10N RunUI(() => { moleculeDlg2.Adduct = adduct; }); OkDialog(moleculeDlg2, moleculeDlg2.OkDialog); newDoc = WaitForDocumentChange(newDoc); // Select the new transition group SelectNode(SrmDocument.Level.TransitionGroups, 2); // Add precursor transition to it var pickList4 = ShowDialog <PopupPickList>(SkylineWindow.ShowPickChildrenInTest); RunUI(() => { pickList4.ApplyFilter(false); pickList4.SetItemChecked(0, true); pickList4.SetItemChecked(1, true); pickList4.SetItemChecked(2, true); pickList4.AutoManageChildren = false; pickList4.IsSynchSiblings = true; // Cause a precursor transition to be added to heavy group }); OkDialog(pickList4, pickList4.OnOk); WaitForClosedForm(pickList4); newDoc = WaitForDocumentChange(newDoc); Assert.AreEqual(9, newDoc.MoleculeTransitionCount); }
public static SpectrumPeakAnnotation Create(CustomIon ion, string comment) { return(ion.IsEmpty && string.IsNullOrEmpty(comment) ? EMPTY : new SpectrumPeakAnnotation(ion, comment)); }
private SpectrumPeakAnnotation(CustomIon ion, string comment) { Ion = ion ?? CustomIon.EMPTY; Comment = comment ?? string.Empty; Assume.IsFalse(IsNullOrEmpty(this), @"empty peak annotation"); // You should be using Create() if there's any risk of creating empty objects }
private void ProcessTransitionGroup(IDictionary <LibKey, SpectrumMzInfo> spectra, PeptideGroupDocNode nodePepGroup, PeptideDocNode nodePep, TransitionGroupDocNode nodeTranGroup, int replicateIndex) { LibKey key; if (nodePep.IsProteomic) { var sequence = Document.Settings.GetPrecursorCalc(nodeTranGroup.TransitionGroup.LabelType, nodePep.ExplicitMods) .GetModifiedSequence(nodePep.Peptide.Target, SequenceModFormatType.lib_precision, false); key = new LibKey(sequence, nodeTranGroup.PrecursorAdduct.AdductCharge); } else { // For small molecules, the "modification" is expressed in the adduct key = new LibKey(nodeTranGroup.CustomMolecule.GetSmallMoleculeLibraryAttributes(), nodeTranGroup.PrecursorAdduct); } var mi = new List <SpectrumPeaksInfo.MI>(); var rt = 0.0; var im = IonMobilityAndCCS.EMPTY; var imGroup = TransitionGroupIonMobilityInfo.EMPTY; // CCS may be available only at group level var groupChromInfos = nodeTranGroup.GetSafeChromInfo(replicateIndex); if (!groupChromInfos.IsEmpty) { var chromInfo = groupChromInfos.First(info => info.OptimizationStep == 0); imGroup = chromInfo.IonMobilityInfo; } var maxApex = float.MinValue; var maxApexMs1 = float.MinValue; string chromFileName = null; double?mobilityMs1 = null; // Track MS1 ion mobility in order to derive high energy ion mobility offset value foreach (var nodeTran in nodeTranGroup.Transitions) { var chromInfos = nodeTran.GetSafeChromInfo(replicateIndex); if (chromInfos.IsEmpty) { continue; } var chromInfo = chromInfos.First(info => info.OptimizationStep == 0); if (chromInfo.Area == 0) { continue; } if (nodeTran.IsMs1) { // Track MS1 ion mobility in order to derive high energy ion mobility offset value if (chromInfo.Height > maxApexMs1) { maxApexMs1 = chromInfo.Height; mobilityMs1 = chromInfo.IonMobility.IonMobility.Mobility; } continue; } if (chromFileName == null) { var chromFileInfo = Document.Settings.MeasuredResults.Chromatograms[replicateIndex].MSDataFileInfos.FirstOrDefault(file => ReferenceEquals(file.Id, chromInfo.FileId)); if (chromFileInfo != null) { chromFileName = chromFileInfo.FilePath.GetFileName(); } } List <SpectrumPeakAnnotation> annotations = null; if (nodeTran.Transition.IsNonReporterCustomIon()) // CONSIDER(bspratt) include annotation for all non-peptide-fragment transitions? { var smallMoleculeLibraryAttributes = nodeTran.Transition.CustomIon.GetSmallMoleculeLibraryAttributes(); var ion = new CustomIon(smallMoleculeLibraryAttributes, nodeTran.Transition.Adduct, nodeTran.GetMoleculeMass()); annotations = new List <SpectrumPeakAnnotation> { SpectrumPeakAnnotation.Create(ion, nodeTran.Annotations.Note) }; } mi.Add(new SpectrumPeaksInfo.MI { Mz = nodeTran.Mz, Intensity = chromInfo.Area, Quantitative = nodeTran.ExplicitQuantitative, Annotations = annotations }); if (chromInfo.Height > maxApex) { maxApex = chromInfo.Height; rt = chromInfo.RetentionTime; var mobility = chromInfo.IonMobility.IonMobility; var mobilityHighEnergyOffset = 0.0; if (mobilityMs1.HasValue && mobility.HasValue && mobility.Mobility != mobilityMs1) { // Note any difference in MS1 and MS2 ion mobilities - the "high energy ion mobility offset" mobilityHighEnergyOffset = mobility.Mobility.Value - mobilityMs1.Value; mobility = mobility.ChangeIonMobility(mobilityMs1); } im = IonMobilityAndCCS.GetIonMobilityAndCCS(mobility, chromInfo.IonMobility.CollisionalCrossSectionSqA ?? imGroup.CollisionalCrossSection, mobilityHighEnergyOffset); } } if (chromFileName == null) { return; } SpectrumMzInfo spectrumMzInfo; if (!spectra.TryGetValue(key, out spectrumMzInfo)) { spectrumMzInfo = new SpectrumMzInfo { SourceFile = DocumentFilePath, Key = key, PrecursorMz = nodeTranGroup.PrecursorMz, SpectrumPeaks = new SpectrumPeaksInfo(mi.ToArray()), RetentionTimes = new List <SpectrumMzInfo.IonMobilityAndRT>(), IonMobility = im, Protein = nodePepGroup.Name, RetentionTime = rt }; spectra[key] = spectrumMzInfo; } var isBest = replicateIndex == nodePep.BestResult; if (isBest) { spectrumMzInfo.IonMobility = im; spectrumMzInfo.RetentionTime = rt; } spectrumMzInfo.RetentionTimes.Add(new SpectrumMzInfo.IonMobilityAndRT(chromFileName, im, rt, isBest)); }