public Pre372CustomIonTransitionGroupHandler(XmlReader readerIn, double settingsMzMatchToler)
        {
            ReadAhead = new XmlReadAhead(readerIn); // Read the current element and its children

            // Put the contents of the readahead buffer into parseable XML form, return a reader for that
            var reader = ReadAhead.CreateXmlReader();

            // Advance the reader to the start of the precursors list, if any
            Assume.IsTrue(reader.IsStartElement(DocumentSerializer.EL.molecule));
            while (!reader.IsStartElement(DocumentSerializer.EL.precursor) && !reader.EOF)
            {
                reader.Read();
            }

            // Gather info on all declared precursors
            _precursorRawDetails = new List <PrecursorRawDetails>();
            while (reader.IsStartElement(DocumentSerializer.EL.precursor))
            {
                var details = new PrecursorRawDetails
                {
                    _formulaUnlabeled = string.Empty,
                    _labels           = null,
                    _nominalAdduct    = Adduct.EMPTY,
                    _proposedAdduct   = Adduct.EMPTY,
                    _declaredCharge   = reader.GetIntAttribute(DocumentSerializer.ATTR.charge),
                    _declaredMz       = reader.GetDoubleAttribute(DocumentSerializer.ATTR.precursor_mz),
                    _declaredHeavy    = !IsotopeLabelType.LIGHT_NAME.Equals(reader.GetAttribute(DocumentSerializer.ATTR.isotope_label) ?? IsotopeLabelType.LIGHT_NAME)
                };
                var formula = reader.GetAttribute(DocumentSerializer.ATTR.ion_formula);
                if (formula != null)
                {
                    details._formulaUnlabeled = formula.Trim(); // We've seen tailing spaces in the wild
                    string   precursorFormula;
                    Adduct   precursorAdduct;
                    Molecule precursorMol;
                    if (IonInfo.IsFormulaWithAdduct(formula, out precursorMol, out precursorAdduct, out precursorFormula))
                    {
                        details._formulaUnlabeled = precursorFormula;
                        details._nominalAdduct    = precursorAdduct;
                    }
                    else
                    {
                        details._labels           = BioMassCalc.MONOISOTOPIC.FindIsotopeLabelsInFormula(details._formulaUnlabeled);
                        details._formulaUnlabeled = BioMassCalc.MONOISOTOPIC.StripLabelsFromFormula(details._formulaUnlabeled);
                    }
                }
                _precursorRawDetails.Add(details);
                reader.ReadToNextSibling(DocumentSerializer.EL.precursor);
            }

            // We want to use the recorded mz values as nearly as possible
            // Inspect the XML to find the expected precision, compare that with current settings
            MzToler = ReadAhead.ElementPrecision(DocumentSerializer.EL.precursor_mz);
            if (MzToler < 1.0)
            {
                MzToler *= 5;
            }
            MzToler = Math.Min(MzToler, settingsMzMatchToler);
        }
示例#2
0
 private void TestException(string formula, string adductText)
 {
     AssertEx.ThrowsException <InvalidOperationException>(() =>
     {
         var adduct = Adduct.FromStringAssumeProtonated(adductText);
         IonInfo.ApplyAdductToFormula(formula, adduct);
     });
 }
示例#3
0
        private void TestPentaneAdduct(string adductText, string expectedFormula, int expectedCharge, HashSet <string> coverage)
        {
            var adduct = Adduct.FromStringAssumeProtonated(adductText);
            var actual = IonInfo.ApplyAdductToFormula(PENTANE, adduct).ToString();

            Assert.AreEqual(expectedFormula, actual, "unexpected formula for adduct " + adduct);
            Assert.AreEqual(expectedCharge, adduct.AdductCharge, "unexpected charge for adduct " + adduct);
            coverage.Add(adduct.AsFormula());
        }
示例#4
0
        protected virtual void ReadAttributes(XmlReader reader, out Adduct embeddedAdduct)
        {
            var formula = reader.GetAttribute(ATTR.formula);

            if (!string.IsNullOrEmpty(formula))
            {
                formula = BioMassCalc.AddH(formula);  // Update this old style formula to current by adding the hydrogen we formerly left out due to assuming protonation
            }
            else
            {
                var text = reader.GetAttribute(ATTR.ion_formula) ?? reader.GetAttribute(ATTR.neutral_formula);
                if (text != null)
                {
                    text = text.Trim(); // We've seen some trailing spaces in the wild
                }
                formula = text;
            }
            string   neutralFormula;
            Molecule mol;

            // We commonly see the adduct inline with the neutral formula ("C12H5[M+Na]"), so be ready to preserve that
            if (IonInfo.IsFormulaWithAdduct(formula, out mol, out embeddedAdduct, out neutralFormula))
            {
                formula = neutralFormula;
            }
            else
            {
                embeddedAdduct = Adduct.EMPTY;
            }
            if (string.IsNullOrEmpty(formula))
            {
                AverageMass      = ReadAverageMass(reader);
                MonoisotopicMass = ReadMonoisotopicMass(reader);
            }
            Formula = formula;

            Name = reader.GetAttribute(ATTR.custom_ion_name);

            if (string.IsNullOrEmpty(Name))
            {
                Name = reader.GetAttribute(ATTR.name) ?? string.Empty;
            }

            AccessionNumbers = MoleculeAccessionNumbers.FromSerializableString(reader.GetAttribute(ATTR.id));

            Validate();
        }
示例#5
0
 public CustomIon(string formula, Adduct adduct, TypedMass monoisotopicMass, TypedMass averageMass, string name)
     : base(formula, monoisotopicMass, averageMass, name)
 {
     if (adduct.IsEmpty)
     {
         var ionInfo = new IonInfo(NeutralFormula, adduct); // Analyzes the formula to see if it's something like "CH12[M+Na]"
         if (!Equals(NeutralFormula, ionInfo.NeutralFormula))
         {
             Formula = ionInfo.NeutralFormula;
         }
         Adduct = Adduct.FromStringAssumeProtonated(ionInfo.AdductText);
     }
     else
     {
         Adduct = adduct;
     }
 }
示例#6
0
        public static FragmentedMolecule GetFragmentedMolecule(SrmSettings settings, PeptideDocNode peptideDocNode,
                                                               TransitionGroupDocNode transitionGroupDocNode, TransitionDocNode transitionDocNode)
        {
            FragmentedMolecule fragmentedMolecule = EMPTY
                                                    .ChangePrecursorMassShift(0, settings.TransitionSettings.Prediction.PrecursorMassType)
                                                    .ChangeFragmentMassShift(0, settings.TransitionSettings.Prediction.FragmentMassType);

            if (peptideDocNode == null)
            {
                return(fragmentedMolecule);
            }
            var labelType = transitionGroupDocNode == null
                ? IsotopeLabelType.light
                : transitionGroupDocNode.TransitionGroup.LabelType;

            if (peptideDocNode.IsProteomic)
            {
                fragmentedMolecule = fragmentedMolecule.ChangeModifiedSequence(
                    ModifiedSequence.GetModifiedSequence(settings, peptideDocNode, labelType));
                if (transitionGroupDocNode != null)
                {
                    fragmentedMolecule = fragmentedMolecule
                                         .ChangePrecursorCharge(transitionGroupDocNode.PrecursorCharge);
                }
                if (transitionDocNode == null || transitionDocNode.IsMs1)
                {
                    return(fragmentedMolecule);
                }
                var transition = transitionDocNode.Transition;
                fragmentedMolecule = fragmentedMolecule
                                     .ChangeFragmentIon(transition.IonType, transition.Ordinal)
                                     .ChangeFragmentCharge(transition.Charge);
                var transitionLosses = transitionDocNode.Losses;
                if (transitionLosses != null)
                {
                    var fragmentLosses = transitionLosses.Losses.Select(transitionLoss => transitionLoss.Loss);
                    fragmentedMolecule = fragmentedMolecule.ChangeFragmentLosses(fragmentLosses);
                }
                return(fragmentedMolecule);
            }
            if (transitionGroupDocNode == null)
            {
                return(fragmentedMolecule
                       .ChangePrecursorFormula(
                           Molecule.Parse(peptideDocNode.CustomMolecule.Formula ?? string.Empty)));
            }
            var customMolecule = transitionGroupDocNode.CustomMolecule;

            fragmentedMolecule =
                fragmentedMolecule.ChangePrecursorCharge(transitionGroupDocNode.TransitionGroup
                                                         .PrecursorCharge);
            if (customMolecule.Formula != null)
            {
                var ionInfo = new IonInfo(customMolecule.Formula,
                                          transitionGroupDocNode.PrecursorAdduct);
                fragmentedMolecule = fragmentedMolecule
                                     .ChangePrecursorFormula(Molecule.Parse(ionInfo.FormulaWithAdductApplied));
            }
            else
            {
                fragmentedMolecule = fragmentedMolecule.ChangePrecursorMassShift(
                    transitionGroupDocNode.PrecursorAdduct.MassFromMz(
                        transitionGroupDocNode.PrecursorMz, transitionGroupDocNode.PrecursorMzMassType),
                    transitionGroupDocNode.PrecursorMzMassType);
            }
            if (transitionDocNode == null || transitionDocNode.IsMs1)
            {
                return(fragmentedMolecule);
            }
            var customIon = transitionDocNode.Transition.CustomIon;

            if (customIon.Formula != null)
            {
                fragmentedMolecule = fragmentedMolecule.ChangeFragmentFormula(
                    Molecule.Parse(customIon.FormulaWithAdductApplied));
            }
            else
            {
                fragmentedMolecule = fragmentedMolecule.ChangeFragmentMassShift(
                    transitionDocNode.Transition.Adduct.MassFromMz(
                        transitionDocNode.Mz, transitionDocNode.MzMassType),
                    transitionDocNode.MzMassType);
            }
            fragmentedMolecule = fragmentedMolecule
                                 .ChangeFragmentCharge(transitionDocNode.Transition.Charge);
            return(fragmentedMolecule);
        }
示例#7
0
        public void AdductParserTest()
        {
            TestAdductOperators();

            var coverage = new HashSet <string>();

            TestPentaneAdduct("[M+2NH4]", "C5H20N2", 2, coverage);           // multiple of a group
            TestPentaneAdduct("[M+2(NH4)]", "C5H20N2", 2, coverage);         // multiple of a group in parenthesis
            TestPentaneAdduct("[M+2H]", "C5H14", 2, coverage);
            TestPentaneAdduct("[M2C13+2H]", "C3C'2H14", 2, coverage);        // Labeled
            TestPentaneAdduct("[2M2C13+2H]", "C6C'4H26", 2, coverage);       // Labeled dimer
            TestPentaneAdduct("[2M2C14+2H]", "C6C\"4H26", 2, coverage);      // Labeled dimer
            TestPentaneAdduct("[M2C13]", "C3C'2H12", 0, coverage);           // Labeled no charge
            TestPentaneAdduct("[2M2C13]", "C6C'4H24", 0, coverage);          // Labeled, dimer, no charge
            TestPentaneAdduct("[2M]", "C10H24", 0, coverage);                // dimer no charge
            TestPentaneAdduct("[2M2C13+3]", "C6C'4H24", 3, coverage);        // Labeled, dimer, charge only
            TestPentaneAdduct("[2M2C13]+3", "C6C'4H24", 3, coverage);        // Labeled, dimer, charge only
            TestPentaneAdduct("[2M2C13+++]", "C6C'4H24", 3, coverage);       // Labeled, dimer, charge only
            TestPentaneAdduct("[2M2C13]+++", "C6C'4H24", 3, coverage);       // Labeled, dimer, charge only
            TestPentaneAdduct("[2M+3]", "C10H24", 3, coverage);              // dimer charge only
            TestPentaneAdduct("[2M]+3", "C10H24", 3, coverage);              // dimer charge only
            TestPentaneAdduct("[2M+++]", "C10H24", 3, coverage);             // dimer charge only
            TestPentaneAdduct("[2M]+++", "C10H24", 3, coverage);             // dimer charge only
            TestPentaneAdduct("[2M2C13-3]", "C6C'4H24", -3, coverage);       // Labeled, dimer, charge only
            TestPentaneAdduct("[2M2C13---]", "C6C'4H24", -3, coverage);      // Labeled, dimer, charge only
            TestPentaneAdduct("[2M-3]", "C10H24", -3, coverage);             // dimer charge only
            TestPentaneAdduct("[2M---]", "C10H24", -3, coverage);            // dimer charge only
            TestPentaneAdduct("[2M2C133H2+2H]", "C6C'4H20H'6", 2, coverage); // Labeled with some complexity, multiplied
            TestPentaneAdduct("M+H", "C5H13", 1, coverage);
            TestPentaneAdduct("M+", PENTANE, 1, coverage);
            TestPentaneAdduct("M+2", PENTANE, 2, coverage);
            TestPentaneAdduct("M+3", PENTANE, 3, coverage);
            TestPentaneAdduct("M-", PENTANE, -1, coverage);
            TestPentaneAdduct("M-2", PENTANE, -2, coverage);
            TestPentaneAdduct("M-3", PENTANE, -3, coverage);
            TestPentaneAdduct("M++", PENTANE, 2, coverage);
            TestPentaneAdduct("M--", PENTANE, -2, coverage);
            TestPentaneAdduct("M", PENTANE, 0, coverage);           // Trivial non-adduct
            TestPentaneAdduct("M+CH3COO", "C7H15O2", -1, coverage); // From XCMS
            TestPentaneAdduct("[M+H]1+", "C5H13", 1, coverage);
            TestPentaneAdduct("[M-H]1-", "C5H11", -1, coverage);
            TestPentaneAdduct("[M-2H]", "C5H10", -2, coverage);
            TestPentaneAdduct("[M-2H]2-", "C5H10", -2, coverage);
            TestPentaneAdduct("[M+2H]++", "C5H14", 2, coverage);
            TestPentaneAdduct("[MH2+2H]++", "C5H13H'", 2, coverage);        // Isotope
            TestPentaneAdduct("[MH3+2H]++", "C5H13H\"", 2, coverage);       // Isotope
            TestPentaneAdduct("[MD+2H]++", "C5H13H'", 2, coverage);         // Isotope
            TestPentaneAdduct("[MD+DMSO+2H]++", "C7H19H'OS", 2, coverage);  // Check handling of Deuterium and DMSO together
            TestPentaneAdduct("[MT+DMSO+2H]++", "C7H19H\"OS", 2, coverage); // Check handling of Tritium
            TestPentaneAdduct("[M+DMSO+2H]++", "C7H20OS", 2, coverage);
            TestPentaneAdduct("[M+DMSO+2H]2+", "C7H20OS", 2, coverage);
            TestPentaneAdduct("[M+MeOH-H]", "C6H15O", -1, coverage); // Methanol "CH3OH"
            TestPentaneAdduct("[M+MeOX-H]", "C6H14N", -1, coverage); // Methoxamine "CH3N"
            TestPentaneAdduct("[M+TMS-H]", "C8H19Si", -1, coverage); // MSTFA(N-methyl-N-trimethylsilytrifluoroacetamide) "C3H8Si"
            TestPentaneAdduct("[M+TMS+MeOX]-", "C9H23NSi", -1, coverage);
            TestPentaneAdduct("[M+HCOO]", "C6H13O2", -1, coverage);
            TestPentaneAdduct("[M+NOS]5+", "C5H12NOS", 5, coverage);  // Not a real adduct, but be ready for adducts we just don't know about
            TestPentaneAdduct("[M+NOS]5", "C5H12NOS", 5, coverage);   // Not a real adduct, but be ready for adducts we just don't know about
            TestPentaneAdduct("[M+NOS]5-", "C5H12NOS", -5, coverage); // Not a real adduct, but be ready for adducts we just don't know about

            // See http://fiehnlab.ucdavis.edu/staff/kind/Metabolomics/MS-Adduct-Calculator/
            // There you will find an excel spreadsheet from which I pulled these numbers, which as it turns out has several errors in it.
            // There is also a table in the web page itself that contains the same values and some unmarked corrections.
            // Sadly that faulty spreadsheet is copied all over the internet.  I've let the author know what we found. - bspratt
            TestTaxolAdduct("M+3H", 285.450928, 3, coverage);
            TestTaxolAdduct("M+2H+Na", 292.778220, 3, coverage);
            TestTaxolAdduct("M+H+2Na", 300.105557, 3, coverage); // Spreadsheet and table both say 300.209820, but also says adduct "mass" = 15.766190, I get 15.6618987 using their H and Na masses (and this is clearly m/z, not mass)
            TestTaxolAdduct("M+3Na", 307.432848, 3, coverage);
            TestTaxolAdduct("M+2H", 427.672721, 2, coverage);
            TestTaxolAdduct("M+H+NH4", 436.185995, 2, coverage);
            TestTaxolAdduct("M+H+Na", 438.663692, 2, coverage);
            TestTaxolAdduct("M+H+K", 446.650662, 2, coverage);
            TestTaxolAdduct("M+ACN+2H", 448.185995, 2, coverage);
            TestTaxolAdduct("M+2Na", 449.654663, 2, coverage);
            TestTaxolAdduct("M+2ACN+2H", 468.699268, 2, coverage);
            TestTaxolAdduct("M+3ACN+2H", 489.212542, 2, coverage);
            TestTaxolAdduct("M+H", 854.338166, 1, coverage);
            TestTaxolAdduct("M+NH4", 871.364713, 1, coverage);
            TestTaxolAdduct("M+Na", 876.320108, 1, coverage);
            TestTaxolAdduct("M+CH3OH+H", 886.364379, 1, coverage);
            TestTaxolAdduct("M+K", 892.294048, 1, coverage);
            TestTaxolAdduct("M+ACN+H", 895.364713, 1, coverage);
            TestTaxolAdduct("M+2Na-H", 898.302050, 1, coverage);
            TestTaxolAdduct("M+IsoProp+H", 914.396230, 1, coverage);
            TestTaxolAdduct("M+ACN+Na", 917.346655, 1, coverage);
            TestTaxolAdduct("M+2K-H", 930.249930, 1, coverage);  // Spreadsheet and table disagree - spreadsheet says "M+2K+H" but that's 3+, not 1+, and this fits the mz value
            TestTaxolAdduct("M+DMSO+H", 932.352110, 1, coverage);
            TestTaxolAdduct("M+2ACN+H", 936.391260, 1, coverage);
            TestTaxolAdduct("M+IsoProp+Na+H", 468.692724, 2, coverage); // Spreadsheet and table both say mz=937.386000 z=1 (does Isoprop interact somehow to eliminate half the ionization?)
            TestTaxolAdduct("2M+H", 1707.669056, 1, coverage);
            TestTaxolAdduct("2M+NH4", 1724.695603, 1, coverage);
            TestTaxolAdduct("2M+Na", 1729.650998, 1, coverage);
            TestTaxolAdduct("2M+3H2O+2H", 881.354, 2, coverage); // Does not appear in table.  Charge agrees but spreadsheet says mz= 1734.684900
            TestTaxolAdduct("2M+K", 1745.624938, 1, coverage);
            TestTaxolAdduct("2M+ACN+H", 1748.695603, 1, coverage);
            TestTaxolAdduct("2M+ACN+Na", 1770.677545, 1, coverage);
            TestTaxolAdduct("M-3H", 283.436354, -3, coverage);
            TestTaxolAdduct("M-2H", 425.658169, -2, coverage);
            TestTaxolAdduct("M-H2O-H", 834.312500, -1, coverage);
            TestTaxolAdduct("M+-H2O-H", 834.312500, -1, coverage); // Tolerate empty atom description ("+-")
            TestTaxolAdduct("M-H", 852.323614, -1, coverage);
            TestTaxolAdduct("M+Na-2H", 874.305556, -1, coverage);
            TestTaxolAdduct("M+Cl", 888.300292, -1, coverage);
            TestTaxolAdduct("M+K-2H", 890.279496, -1, coverage);
            TestTaxolAdduct("M+FA-H", 898.329091, -1, coverage);
            TestTaxolAdduct("M+Hac-H", 912.344741, -1, coverage);
            TestTaxolAdduct("M+Br", 932.249775, -1, coverage);
            TestTaxolAdduct("MT+TFA-H", 968.324767, -1, coverage); // Tritium label + TFA
            TestTaxolAdduct("M+TFA-H", 966.316476, -1, coverage);
            TestTaxolAdduct("2M-H", 1705.654504, -1, coverage);
            TestTaxolAdduct("2M+FA-H", 1751.659981, -1, coverage);
            TestTaxolAdduct("2M+Hac-H", 1765.675631, -1, coverage);
            TestTaxolAdduct("3M-H", 2558.985394, -1, coverage); // Spreadsheet and table give mz as 2560.999946 -but also gives adduct "mass" as 1.007276, should be -1.007276

            // A couple more simple ones we support with statics
            TestTaxolAdduct("M+4H", 214.3400149, 4, coverage);
            TestTaxolAdduct("M+5H", 171.6734671, 5, coverage);

            // And a few of our own to exercise the interaction of multiplier and isotope
            var dC13 = BioMassCalc.MONOISOTOPIC.GetMass(BioMassCalc.C13) - BioMassCalc.MONOISOTOPIC.GetMass(BioMassCalc.C);

            TestTaxolAdduct("M2C13+3H", 285.450928 + (2 * dC13) / 3.0, 3, coverage);
            TestTaxolAdduct("M2C13+2H+Na", 292.778220 + (2 * dC13) / 3.0, 3, coverage);
            TestTaxolAdduct("2M2C13+3H", 285.450906 + (massTaxol + 4 * dC13) / 3.0, 3, coverage);
            TestTaxolAdduct("2M2C13+2H+Na", 292.778220 + (massTaxol + 4 * dC13) / 3.0, 3, coverage);
            var dC14 = BioMassCalc.MONOISOTOPIC.GetMass(BioMassCalc.C14) - BioMassCalc.MONOISOTOPIC.GetMass(BioMassCalc.C);

            TestTaxolAdduct("M2C14+3H", 285.450928 + (2 * dC14) / 3.0, 3, coverage);
            TestTaxolAdduct("M2C14+2H+Na", 292.778220 + (2 * dC14) / 3.0, 3, coverage);
            TestTaxolAdduct("2M2C14+3H", 285.450906 + (massTaxol + 4 * dC14) / 3.0, 3, coverage);
            TestTaxolAdduct("2M2C14+2H+Na", 292.778220 + (massTaxol + 4 * dC14) / 3.0, 3, coverage);

            // Using example adducts from
            // https://gnps.ucsd.edu/ProteoSAFe/gnpslibrary.jsp?library=GNPS-LIBRARY#%7B%22Library_Class_input%22%3A%221%7C%7C2%7C%7C3%7C%7CEXACT%22%7D
            var Hectochlorin     = "C27H34Cl2N2O9S2";
            var massHectochlorin = 664.108276; // http://www.chemspider.com/Chemical-Structure.552449.html?rid=3a7c08af-0886-4e82-9e4f-5211b8efb373
            var adduct           = Adduct.FromStringAssumeProtonated("M+H");
            var mol  = IonInfo.ApplyAdductToFormula(Hectochlorin, adduct).ToString();
            var mass = BioMassCalc.MONOISOTOPIC.CalculateMassFromFormula(mol);

            Assert.AreEqual(massHectochlorin + BioMassCalc.MONOISOTOPIC.CalculateMassFromFormula("H"), mass, 0.00001);
            var mz = BioMassCalc.CalculateIonMz(BioMassCalc.MONOISOTOPIC.CalculateMassFromFormula(Hectochlorin), adduct);

            Assert.AreEqual(665.11555415, mz, .000001);  // GNPS says 665.0 for Hectochlorin M+H
            mol = IonInfo.ApplyAdductToFormula(Hectochlorin, Adduct.FromStringAssumeProtonated("MCl37+H")).ToString();
            Assert.AreEqual("C27ClCl'H35N2O9S2", mol);
            mass = BioMassCalc.MONOISOTOPIC.CalculateMassFromFormula(mol);
            Assert.AreEqual(667.11315, mass, .00001);
            mol = IonInfo.ApplyAdductToFormula(Hectochlorin, Adduct.FromStringAssumeProtonated("M2Cl37+H")).ToString();
            Assert.AreEqual("C27Cl'2H35N2O9S2", mol);

            // Test ability to describe isotope label by mass only
            var heavy = Adduct.FromStringAssumeProtonated("2M1.2345+H");

            mz    = BioMassCalc.CalculateIonMz(new TypedMass(massHectochlorin, MassType.Monoisotopic), heavy);
            heavy = Adduct.FromStringAssumeProtonated("2M1.2345");
            mz    = BioMassCalc.CalculateIonMass(new TypedMass(massHectochlorin, MassType.Monoisotopic), heavy);
            Assert.AreEqual(2 * (massHectochlorin + 1.23456), mz, .001);
            heavy = Adduct.FromStringAssumeProtonated("M1.2345");
            mz    = BioMassCalc.CalculateIonMass(new TypedMass(massHectochlorin, MassType.Monoisotopic), heavy);
            Assert.AreEqual(massHectochlorin + 1.23456, mz, .001);
            heavy = Adduct.FromStringAssumeProtonated("2M(-1.2345)+H");
            mz    = BioMassCalc.CalculateIonMz(new TypedMass(massHectochlorin, MassType.Monoisotopic), heavy);
            Assert.AreEqual((2 * (massHectochlorin - 1.23456) + BioMassCalc.MONOISOTOPIC.CalculateMassFromFormula("H")), mz, .001);
            heavy = Adduct.FromStringAssumeProtonated("2M(-1.2345)");
            mz    = BioMassCalc.CalculateIonMass(new TypedMass(massHectochlorin, MassType.Monoisotopic), heavy);
            Assert.AreEqual(2 * (massHectochlorin - 1.23456), mz, .001);
            heavy = Adduct.FromStringAssumeProtonated("2M(1.2345)+H");
            mz    = BioMassCalc.CalculateIonMz(new TypedMass(massHectochlorin, MassType.Monoisotopic), heavy);
            Assert.AreEqual((2 * (massHectochlorin + 1.23456) + BioMassCalc.MONOISOTOPIC.CalculateMassFromFormula("H")), mz, .001);
            heavy = Adduct.FromStringAssumeProtonated("2M(1.2345)");
            mz    = BioMassCalc.CalculateIonMass(new TypedMass(massHectochlorin, MassType.Monoisotopic), heavy);
            Assert.AreEqual(2 * (massHectochlorin + 1.23456), mz, .001);

            TestException(Hectochlorin, "M3Cl37+H"); // Trying to label more chlorines than exist in the molecule
            TestException(Hectochlorin, "M-3Cl+H");  // Trying to remove more chlorines than exist in the molecule
            TestException(PENTANE, "M+foo+H");       // Unknown adduct
            TestException(PENTANE, "M2Cl37H+H");     // nonsense label ("2Cl37H2" would make sense, but regular H doesn't belong)
            TestException(PENTANE, "M+2H+");         // Trailing sign - we now understand this as a charge state declaration, but this one doesn't match described charge
            TestException(PENTANE, "[M-2H]3-");      // Declared charge doesn't match described charge

            // Test label stripping
            Assert.AreEqual("C5H9NO2S", (new IonInfo("C5H9H'3NO2S[M-3H]")).UnlabeledFormula);

            // Peptide representations
            Assert.AreEqual("C40H65N11O16", (new SequenceMassCalc(MassType.Average)).GetNeutralFormula("PEPTIDER", null));

            // Figuring out adducts from old style skyline doc ion molecules and ion precursors
            var adductDiff = Adduct.FromFormulaDiff("C6H27NO2Si2C'5", "C'5H11NO2", 3);

            Assert.AreEqual("[M+C6H16Si2]3+", adductDiff.AdductFormula);
            Assert.AreEqual(3, adductDiff.AdductCharge);
            Assert.AreEqual(Adduct.FromString("[M+C6H16Si2]3+", Adduct.ADDUCT_TYPE.non_proteomic, null), adductDiff);
            adductDiff = Adduct.FromFormulaDiff("C6H27NO2", "C6H27NO2", 3);
            Assert.AreEqual("[M+3]", adductDiff.AdductFormula);
            Assert.AreEqual(3, adductDiff.AdductCharge);
            adductDiff = Adduct.ProtonatedFromFormulaDiff("C6H27NO2Si2C'5", "C'5H11NO2", 3);
            var expectedFromProtonatedDiff = "[M+C6H13Si2+3H]";

            Assert.AreEqual(expectedFromProtonatedDiff, adductDiff.AdductFormula);
            Assert.AreEqual(3, adductDiff.AdductCharge);
            Assert.AreEqual(Adduct.FromString(expectedFromProtonatedDiff, Adduct.ADDUCT_TYPE.non_proteomic, null), adductDiff);
            adductDiff = Adduct.ProtonatedFromFormulaDiff("C6H27NO2", "C6H27NO2", 3);
            Assert.AreEqual("[M+3H]", adductDiff.AdductFormula);
            Assert.AreEqual(3, adductDiff.AdductCharge);

            // Implied positive mode
            TestPentaneAdduct("MH", "C5H13", 1, coverage);       // implied pos mode seems to be fairly common in the wild
            TestPentaneAdduct("MH+", "C5H13", 1, coverage);      // implied pos mode seems to be fairly common in the wild
            TestPentaneAdduct("MNH4", "C5H16N", 1, coverage);    // implied pos mode seems to be fairly common in the wild
            TestPentaneAdduct("MNH4+", "C5H16N", 1, coverage);   // implied pos mode seems to be fairly common in the wild
            TestPentaneAdduct("2MNH4+", "C10H28N", 1, coverage); // implied pos mode seems to be fairly common in the wild

            // Explict charge states within the adduct
            TestPentaneAdduct("[M+S+]", "C5H12S", 1, coverage);   // We're trusting the user to declare charge
            TestPentaneAdduct("[3M+S+]", "C15H36S", 1, coverage); // We're trusting the user to declare charge
            TestPentaneAdduct("[M+S++]", "C5H12S", 2, coverage);  // We're trusting the user to declare charge
            TestPentaneAdduct("[MS+]", "C5H12S", 1, coverage);    // We're trusting the user to declare charge
            TestPentaneAdduct("[MS++]", "C5H12S", 2, coverage);   // We're trusting the user to declare charge
            TestPentaneAdduct("[M+S+2]", "C5H12S", 2, coverage);  // We're trusting the user to declare charge
            TestPentaneAdduct("[M+S]2+", "C5H12S", 2, coverage);  // We're trusting the user to declare charge
            TestPentaneAdduct("[M+S-]", "C5H12S", -1, coverage);  // We're trusting the user to declare charge
            TestPentaneAdduct("[M+S--]", "C5H12S", -2, coverage); // We're trusting the user to declare charge
            TestPentaneAdduct("[M-3H-3]", "C5H9", -3, coverage);  // We're trusting the user to declare charge
            TestPentaneAdduct("[M+S-2]", "C5H12S", -2, coverage); // We're trusting the user to declare charge
            TestPentaneAdduct("[M+S]2-", "C5H12S", -2, coverage); // We're trusting the user to declare charge

            // Did we test all the adducts we claim to support?
            foreach (var adducts in new[] {
                Adduct.DEFACTO_STANDARD_ADDUCTS,
                Adduct.COMMON_CHARGEONLY_ADDUCTS,
                Adduct.COMMON_SMALL_MOL_ADDUCTS.Select(a => a.AdductFormula),
                Adduct.COMMON_PROTONATED_ADDUCTS.Select(a => a.AdductFormula),
            })
            {
                foreach (var adductText in adducts)
                {
                    if (!coverage.Contains(adductText))
                    {
                        Assert.Fail("Need to add a test for adduct {0}", adductText);
                    }
                }
            }
        }
示例#8
0
        private void UpdateAverageAndMonoTextsForFormula()
        {
            bool valid;

            try
            {
                var formula = Formula; // Get current formula and adduct

                var userinput = textFormula.Text.Trim();
                if (_editMode == EditMode.adduct_only)
                {
                    if (!string.IsNullOrEmpty(userinput) && !userinput.StartsWith(@"["))
                    {
                        // Assume they're trying to type an adduct
                        userinput = @"[" + userinput + @"]";
                    }
                    if (string.IsNullOrEmpty(NeutralFormula))
                    {
                        formula = null; // Parent molecule was described as mass only
                    }
                    else
                    {
                        formula = NeutralFormula + userinput; // Try to apply this new adduct to parent molecule
                    }
                }
                else
                {
                    formula = userinput;
                }
                string   neutralFormula;
                Molecule ion;
                Adduct   adduct;
                if (!IonInfo.IsFormulaWithAdduct(formula, out ion, out adduct, out neutralFormula))
                {
                    neutralFormula = formula;
                    if (!Adduct.TryParse(userinput, out adduct))
                    {
                        adduct = Adduct.EMPTY;
                    }
                }
                if (_editMode != EditMode.adduct_only)
                {
                    NeutralFormula = neutralFormula;
                }
                if (_editMode != EditMode.formula_only)
                {
                    Adduct = adduct;
                }
                // Update mass/mz displays
                if (string.IsNullOrEmpty(neutralFormula))
                {
                    if (!adduct.IsEmpty)
                    {
                        // No formula, but adduct changed
                        Adduct = adduct;
                        // ReSharper disable once PossibleNullReferenceException
                        GetTextFromMass(_neutralMonoMass, MassType.Monoisotopic); // Just to see if it throws or not
                        GetTextFromMass(_neutralAverageMass, MassType.Average);   // Just to see if it throws or not
                    }
                }
                else
                {
                    // Is there an isotopic label we should apply to get the mass?
                    if (IsotopeLabelsForMassCalc != null && (Adduct.IsEmpty || !Adduct.HasIsotopeLabels)) // If adduct declares an isotope, that takes precedence
                    {
                        neutralFormula = IsotopeLabelsForMassCalc.Aggregate(neutralFormula, (current, kvp) => current.Replace(kvp.Key, kvp.Value));
                    }
                    var monoMass    = SequenceMassCalc.FormulaMass(BioMassCalc.MONOISOTOPIC, neutralFormula, SequenceMassCalc.MassPrecision);
                    var averageMass = SequenceMassCalc.FormulaMass(BioMassCalc.AVERAGE, neutralFormula, SequenceMassCalc.MassPrecision);
                    GetTextFromMass(monoMass, MassType.Monoisotopic); // Just to see if it throws or not
                    GetTextFromMass(averageMass, MassType.Average);   // Just to see if it throws or not
                    MonoMass    = monoMass;
                    AverageMass = averageMass;
                }
                valid = true; // If we got here, formula parsed OK, or adduct did
                textFormula.ForeColor = Color.Black;
                if (_editMode == EditMode.adduct_only)
                {
                    textFormula.Text = userinput; // Enforce proper adduct formatting
                    if (adduct.IsEmpty)
                    {
                        valid = false; // Adduct did not parse
                    }
                }
                else if (_editMode == EditMode.formula_only)
                {
                    valid &= adduct.IsEmpty; // Should not have anything going on with adduct here
                }
            }
            catch (InvalidOperationException)
            {
                valid = false;
            }
            catch (ArgumentException)
            {
                valid = false;
            }
            if (valid)
            {
                textFormula.ForeColor = Color.Black;
            }
            else
            {
                textFormula.ForeColor = Color.Red;
                textMono.Text         = textAverage.Text = string.Empty;
            }

            // Allow direct editing of masses if direct editing of formula is allowed, but formula is empty
            MassEnabled = _editMode != EditMode.adduct_only && string.IsNullOrEmpty(_neutralFormula);
        }