diff --git a/mzLib/Development/Deconvolution/StandardDeconvolutionTest.cs b/mzLib/Development/DeconvolutionDevelopment/StandardDeconvolutionTest.cs similarity index 55% rename from mzLib/Development/Deconvolution/StandardDeconvolutionTest.cs rename to mzLib/Development/DeconvolutionDevelopment/StandardDeconvolutionTest.cs index ea02b6dd4..2b73876e1 100644 --- a/mzLib/Development/Deconvolution/StandardDeconvolutionTest.cs +++ b/mzLib/Development/DeconvolutionDevelopment/StandardDeconvolutionTest.cs @@ -14,7 +14,7 @@ namespace Development.Deconvolution /// /// [TestFixture] - [Ignore("Only needed when developing deconvolution methods")] + //[Ignore("Only needed when developing deconvolution methods")] [ExcludeFromCodeCoverage] public class StandardDeconvolutionTest { @@ -38,117 +38,152 @@ static StandardDeconvolutionTest() Loaders.LoadElements(); // define paths to spectra - var ubiquitinPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Deconvolution", "TestData", + var ubiquitinPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "DeconvolutionDevelopment", "TestData", "Averaged_221110_UbiqOnly.mzML"); - var hghPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Deconvolution", "TestData", + var hghPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "DeconvolutionDevelopment", "TestData", "Averaged_221110_HGHOnly.mzML"); - var cytoPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Deconvolution", "TestData", + var cytoPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "DeconvolutionDevelopment", "TestData", "Averaged_221110_CytoOnly.mzML"); // set up deconvoluters to be utilized by test cases - DeconvolutionParameters classicTopDownDeconvolutionParams = new ClassicDeconvolutionParameters(1, 60, 4, 3); - DeconvolutionParameters classicBottomUpDeconvolutionParams = new ClassicDeconvolutionParameters(1, 12, 4, 3); + List topDownDeconvolutionParametersToTest = + [ + new ClassicDeconvolutionParameters(1, 60, 4, 3), + new IsoDecDeconvolutionParameters() + ]; - // Add Individual peak test cases - List singlePeakDeconvolutionTestCases = new() + List bottomUpDeconvolutionParametersToTest = + [ + new ClassicDeconvolutionParameters(1, 12, 4, 3), + new IsoDecDeconvolutionParameters() + ]; + + + + // Add Individual peak test cases for top down + List singlePeakDeconvolutionTestCases = new(); + foreach (var deconParams in topDownDeconvolutionParametersToTest) { // uniquitin, direct injection - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", - ubiquitinPath, 1, 10038.4, 8, 1254.8, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", - ubiquitinPath, 1, 10039.41, 9, 1115.49, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", - ubiquitinPath, 1, 10041.4, 10, 1004.14, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", - ubiquitinPath, 1, 10041.46, 11, 912.86, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", - ubiquitinPath, 1, 10043.4, 12, 836.95, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", - ubiquitinPath, 1, 10043.41, 13, 772.57, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", - ubiquitinPath, 1, 10044.44, 14, 717.46, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", - ubiquitinPath, 1, 10045.5, 15, 669.70, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", - ubiquitinPath, 1, 10045.44, 16, 627.84, 20), + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection PolyUbiquitin, Averaged", + ubiquitinPath, 1, 10038.4, 8, 1254.8, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection PolyUbiquitin, Averaged", + ubiquitinPath, 1, 10039.41, 9, 1115.49, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection PolyUbiquitin, Averaged", + ubiquitinPath, 1, 10041.4, 10, 1004.14, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection PolyUbiquitin, Averaged", + ubiquitinPath, 1, 10041.46, 11, 912.86, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection PolyUbiquitin, Averaged", + ubiquitinPath, 1, 10043.4, 12, 836.95, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection PolyUbiquitin, Averaged", + ubiquitinPath, 1, 10043.41, 13, 772.57, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection PolyUbiquitin, Averaged", + ubiquitinPath, 1, 10044.44, 14, 717.46, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection PolyUbiquitin, Averaged", + ubiquitinPath, 1, 10045.5, 15, 669.70, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection PolyUbiquitin, Averaged", + ubiquitinPath, 1, 10045.44, 16, 627.84, 20)); // hgh, direct injection - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, "Direct Injection Human Growth Hormone, Averaged", - hghPath, 1, 22139.41, 11, 2012.29, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, + hghPath, 1, 22139.41, 11, 2012.29, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, "Direct Injection Human Growth Hormone, Averaged", - hghPath, 1, 22136.28, 12, 1844.69, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, + hghPath, 1, 22136.28, 12, 1844.69, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, "Direct Injection Human Growth Hormone, Averaged", - hghPath, 1, 22137.31, 13, 1702.87, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, + hghPath, 1, 22137.31, 13, 1702.87, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, "Direct Injection Human Growth Hormone, Averaged", - hghPath, 1, 22139.32, 14, 1581.38, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, + hghPath, 1, 22139.32, 14, 1581.38, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, "Direct Injection Human Growth Hormone, Averaged", - hghPath, 1, 22139.25, 15, 1475.95, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, + hghPath, 1, 22139.25, 15, 1475.95, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, "Direct Injectio Human Growth Hormone, Averaged", - hghPath, 1, 22140.32, 16, 1383.77, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, + hghPath, 1, 22140.32, 16, 1383.77, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, "Direct Injection Human Growth Hormone, Averaged", - hghPath, 1, 22141.31, 17, 1302.43, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, + hghPath, 1, 22141.31, 17, 1302.43, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, "Direct Injection Human Growth Hormone, Averaged", - hghPath, 1, 22142.34, 18, 1230.13, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, + hghPath, 1, 22142.34, 18, 1230.13, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, "Direct Injection Human Growth Hormone, Averaged", - hghPath, 1, 22143.36, 19, 1165.44, 20), + hghPath, 1, 22143.36, 19, 1165.44, 20)); // cytochrome c, direct injection - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12367.44, 9, 1374.16, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12367.4, 10, 1236.74, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12368.4, 11, 1124.40, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12370.44, 12, 1030.87, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12371.45, 13, 951.65, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12373.48, 14, 883.82, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12373.5, 15, 824.90, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12374.56, 16, 773.41, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12374.47, 17, 727.91, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12376.44, 18, 687.58, 20), - new SinglePeakDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", - cytoPath, 1, 12360.6, 20, 619.03, 20) - }; + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12367.44, 9, 1374.16, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12367.4, 10, 1236.74, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12368.4, 11, 1124.40, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12370.44, 12, 1030.87, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12371.45, 13, 951.65, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12373.48, 14, 883.82, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12373.5, 15, 824.90, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12374.56, 16, 773.41, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12374.47, 17, 727.91, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12376.44, 18, 687.58, 20)); + singlePeakDeconvolutionTestCases.Add(new SinglePeakDeconvolutionTestCase(deconParams, + "Direct Injection Cytochrome C, Averaged", + cytoPath, 1, 12360.6, 20, 619.03, 20)); + } _singlePeakTestCases = singlePeakDeconvolutionTestCases; - // Add whole spectrum test cases - List wholeSpectrumDeconvolutionTestCases = new() + // Add whole spectrum test cases for top down + List wholeSpectrumDeconvolutionTestCases = new(); + foreach (var deconParams in topDownDeconvolutionParametersToTest) { - new WholeSpectrumDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection PolyUbiquitin, Averaged", + wholeSpectrumDeconvolutionTestCases.Add(new WholeSpectrumDeconvolutionTestCase(deconParams, "Direct Injection PolyUbiquitin, Averaged", ubiquitinPath, 1, 20, new[] { 10038.4, 10039.41, 10041.4, 10041.46, 10043.4, 10043.41, 10044.44, 10045.5, 10045.44, }, new[] { 8, 9, 10, 11, 12, 13, 14, 15, 16 }, - new[] { 1254.8, 1115.49, 1004.14, 912.86, 836.95, 772.57, 717.46, 669.70, 627.84 }), + new[] { 1254.8, 1115.49, 1004.14, 912.86, 836.95, 772.57, 717.46, 669.70, 627.84 })); - new WholeSpectrumDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Human Growth Hormone, Averaged", + wholeSpectrumDeconvolutionTestCases.Add(new WholeSpectrumDeconvolutionTestCase(deconParams, "Direct Injection Human Growth Hormone, Averaged", hghPath, 1, 20, new []{22139.41, 22136.28, 22137.31, 22139.32, 22139.25, 22140.32, 22141.31, 22142.34, 22143.36}, new []{11, 12, 13, 14, 15, 16, 17, 18, 19}, - new []{2012.29, 1844.69, 1702.87, 1581.38, 1475.95, 1383.77, 1302.43, 1230.13, 1165.44}), + new []{2012.29, 1844.69, 1702.87, 1581.38, 1475.95, 1383.77, 1302.43, 1230.13, 1165.44})); - new WholeSpectrumDeconvolutionTestCase(classicTopDownDeconvolutionParams, "Direct Injection Cytochrome C, Averaged", + wholeSpectrumDeconvolutionTestCases.Add(new WholeSpectrumDeconvolutionTestCase(deconParams, "Direct Injection Cytochrome C, Averaged", cytoPath, 1, 20, new []{12367.44, 12367.4, 12368.4, 12370.44, 12371.45, 12373.48, 12373.5, 12374.56, 12374.47, 12376.44, 12360.6}, new []{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20}, - new []{1374.16, 1236.74, 1124.40, 1030.87, 951.65, 883.82, 824.90, 773.41, 727.91, 687.58, 619.03}), + new []{1374.16, 1236.74, 1124.40, 1030.87, 951.65, 883.82, 824.90, 773.41, 727.91, 687.58, 619.03})); }; _wholeSpectrumDeconvolutionTestCases = wholeSpectrumDeconvolutionTestCases; + + // TODO: Add cases for bottom up deconvolution } #endregion diff --git a/mzLib/Development/Deconvolution/TestCases/SinglePeakDeconvolutionTestCase.cs b/mzLib/Development/DeconvolutionDevelopment/TestCases/SinglePeakDeconvolutionTestCase.cs similarity index 98% rename from mzLib/Development/Deconvolution/TestCases/SinglePeakDeconvolutionTestCase.cs rename to mzLib/Development/DeconvolutionDevelopment/TestCases/SinglePeakDeconvolutionTestCase.cs index 2bcf25db8..4e63f2392 100644 --- a/mzLib/Development/Deconvolution/TestCases/SinglePeakDeconvolutionTestCase.cs +++ b/mzLib/Development/DeconvolutionDevelopment/TestCases/SinglePeakDeconvolutionTestCase.cs @@ -23,7 +23,7 @@ public class SinglePeakDeconvolutionTestCase public SinglePeakDeconvolutionTestCase(DeconvolutionParameters deconParameters, string sampleInformation, string spectrumPath, int scanNumber, double expectedMostAbundantObservedIsotopicMass, int expectedIonChargeState, double selectedIonMz, double precursorPpmMassTolerance) { - + DeconvolutionParameters = deconParameters; SampleInformation = sampleInformation; ExpectedMostAbundantObservedIsotopicMass = expectedMostAbundantObservedIsotopicMass; ExpectedIonChargeState = expectedIonChargeState; diff --git a/mzLib/Development/Deconvolution/TestCases/TestDevelopmentTestCases.cs b/mzLib/Development/DeconvolutionDevelopment/TestCases/TestDevelopmentTestCases.cs similarity index 100% rename from mzLib/Development/Deconvolution/TestCases/TestDevelopmentTestCases.cs rename to mzLib/Development/DeconvolutionDevelopment/TestCases/TestDevelopmentTestCases.cs diff --git a/mzLib/Development/Deconvolution/TestCases/WholeSpectrumDeconvolutionTestCase.cs b/mzLib/Development/DeconvolutionDevelopment/TestCases/WholeSpectrumDeconvolutionTestCase.cs similarity index 100% rename from mzLib/Development/Deconvolution/TestCases/WholeSpectrumDeconvolutionTestCase.cs rename to mzLib/Development/DeconvolutionDevelopment/TestCases/WholeSpectrumDeconvolutionTestCase.cs diff --git a/mzLib/Development/Deconvolution/TestData/Averaged_221110_CytoOnly.mzML b/mzLib/Development/DeconvolutionDevelopment/TestData/Averaged_221110_CytoOnly.mzML similarity index 100% rename from mzLib/Development/Deconvolution/TestData/Averaged_221110_CytoOnly.mzML rename to mzLib/Development/DeconvolutionDevelopment/TestData/Averaged_221110_CytoOnly.mzML diff --git a/mzLib/Development/Deconvolution/TestData/Averaged_221110_HGHOnly.mzML b/mzLib/Development/DeconvolutionDevelopment/TestData/Averaged_221110_HGHOnly.mzML similarity index 100% rename from mzLib/Development/Deconvolution/TestData/Averaged_221110_HGHOnly.mzML rename to mzLib/Development/DeconvolutionDevelopment/TestData/Averaged_221110_HGHOnly.mzML diff --git a/mzLib/Development/Deconvolution/TestData/Averaged_221110_UbiqOnly.mzML b/mzLib/Development/DeconvolutionDevelopment/TestData/Averaged_221110_UbiqOnly.mzML similarity index 100% rename from mzLib/Development/Deconvolution/TestData/Averaged_221110_UbiqOnly.mzML rename to mzLib/Development/DeconvolutionDevelopment/TestData/Averaged_221110_UbiqOnly.mzML diff --git a/mzLib/Development/Development.csproj b/mzLib/Development/Development.csproj index 0f0eaf199..cd65a3163 100644 --- a/mzLib/Development/Development.csproj +++ b/mzLib/Development/Development.csproj @@ -23,13 +23,13 @@ - + PreserveNewest - + PreserveNewest - + PreserveNewest diff --git a/mzLib/MassSpectrometry/Deconvolution/Algorithms/ClassicDeconvolutionAlgorithm.cs b/mzLib/MassSpectrometry/Deconvolution/Algorithms/ClassicDeconvolutionAlgorithm.cs index 4a919a7e5..8f7bb320b 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Algorithms/ClassicDeconvolutionAlgorithm.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Algorithms/ClassicDeconvolutionAlgorithm.cs @@ -1,20 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Chemistry; -using Easy.Common.Extensions; using MathNet.Numerics.Statistics; using MzLibUtil; namespace MassSpectrometry { - public class ClassicDeconvolutionAlgorithm : DeconvolutionAlgorithm + internal class ClassicDeconvolutionAlgorithm : DeconvolutionAlgorithm { private MzSpectrum spectrum; - public ClassicDeconvolutionAlgorithm(DeconvolutionParameters deconParameters) : base(deconParameters) + internal ClassicDeconvolutionAlgorithm(DeconvolutionParameters deconParameters) : base(deconParameters) { } @@ -25,7 +22,7 @@ public ClassicDeconvolutionAlgorithm(DeconvolutionParameters deconParameters) : /// spectrum to deconvolute /// Range of peaks to deconvolute /// - public override IEnumerable Deconvolute(MzSpectrum spectrumToDeconvolute, MzRange range) + internal override IEnumerable Deconvolute(MzSpectrum spectrumToDeconvolute, MzRange range) { var deconParams = DeconvolutionParameters as ClassicDeconvolutionParameters ?? throw new MzLibException("Deconvolution params and algorithm do not match"); spectrum = spectrumToDeconvolute; @@ -205,7 +202,7 @@ private IsotopicEnvelope FindIsotopicEnvelope(int massIndex, double candidateFor } } - return new IsotopicEnvelope(listOfObservedPeaks, monoisotopicMass, chargeState, totalIntensity, Statistics.StandardDeviation(listOfRatios), massIndex); + return new IsotopicEnvelope(listOfObservedPeaks, monoisotopicMass, chargeState, totalIntensity, listOfRatios.StandardDeviation()); } private int ObserveAdjacentChargeStates(IsotopicEnvelope originalEnvelope, double mostIntensePeakMz, int massIndex, double deconvolutionTolerancePpm, double intensityRatioLimit, double minChargeToLookFor, double maxChargeToLookFor, List monoisotopicMassPredictions) diff --git a/mzLib/MassSpectrometry/Deconvolution/Algorithms/DeconvolutionAlgorithm.cs b/mzLib/MassSpectrometry/Deconvolution/Algorithms/DeconvolutionAlgorithm.cs index e8a052e39..0487b40da 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Algorithms/DeconvolutionAlgorithm.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Algorithms/DeconvolutionAlgorithm.cs @@ -1,13 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Chemistry; using MzLibUtil; namespace MassSpectrometry { + /// + /// Parent class defining minimum requirement to be used + /// public abstract class DeconvolutionAlgorithm { // For ClassicDeconv. If not used elsewhere, move to that class @@ -79,6 +80,8 @@ protected DeconvolutionAlgorithm(DeconvolutionParameters deconParameters) /// spectrum to be deconvoluted /// Range of peaks to deconvolute /// - public abstract IEnumerable Deconvolute(MzSpectrum spectrum, MzRange range); - } + internal abstract IEnumerable Deconvolute(MzSpectrum spectrum, MzRange range); + + + } } diff --git a/mzLib/MassSpectrometry/Deconvolution/Algorithms/ExampleNewDeconvolutionAlgorithmTemplate.cs b/mzLib/MassSpectrometry/Deconvolution/Algorithms/ExampleNewDeconvolutionAlgorithmTemplate.cs index 18957d8d0..c70c10b63 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Algorithms/ExampleNewDeconvolutionAlgorithmTemplate.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Algorithms/ExampleNewDeconvolutionAlgorithmTemplate.cs @@ -1,22 +1,19 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using MzLibUtil; namespace MassSpectrometry { [ExcludeFromCodeCoverage] - public class ExampleNewDeconvolutionAlgorithmTemplate : DeconvolutionAlgorithm + internal class ExampleNewDeconvolutionAlgorithmTemplate : DeconvolutionAlgorithm { - public ExampleNewDeconvolutionAlgorithmTemplate(DeconvolutionParameters deconParameters) : base(deconParameters) + internal ExampleNewDeconvolutionAlgorithmTemplate(DeconvolutionParameters deconParameters) : base(deconParameters) { } - public override IEnumerable Deconvolute(MzSpectrum spectrum, MzRange range = null) + internal override IEnumerable Deconvolute(MzSpectrum spectrum, MzRange range = null) { var deconParams = DeconvolutionParameters as ExampleNewDeconvolutionParametersTemplate ?? throw new MzLibException("Deconvolution params and algorithm do not match"); range ??= spectrum.Range; diff --git a/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecAlgorithm.cs b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecAlgorithm.cs new file mode 100644 index 000000000..6892f6140 --- /dev/null +++ b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecAlgorithm.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using MzLibUtil; + +namespace MassSpectrometry +{ + internal class IsoDecAlgorithm : DeconvolutionAlgorithm + { + + internal IsoDecAlgorithm(DeconvolutionParameters deconParameters) : base(deconParameters) + { + + } + + [StructLayout(LayoutKind.Sequential, Pack =1)] + public struct MatchedPeak + { + public float mz; + public int z; + public float monoiso; + public float peakmass; + public float avgmass; + public float area; + public float peakint; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public float[] matchedindsiso; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public float[] matchedindsexp; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public float[] isomz; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public float[] isodist; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public float[] isomass; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public float[] monoisos; + int startindex; + int endindex; + public float score; + public int realisolength; + } + + [DllImport("Deconvolution/Algorithms/IsoDecResources/isodeclib.dll", EntryPoint = "process_spectrum", CallingConvention = CallingConvention.Cdecl)] + protected static extern int process_spectrum(double[] cmz, float[] cintensity, int c, string fname, IntPtr matchedpeaks, IsoDecDeconvolutionParameters.IsoSettings settings); + + internal override IEnumerable Deconvolute(MzSpectrum spectrum, MzRange range) + { + var deconParams = DeconvolutionParameters as IsoDecDeconvolutionParameters ?? throw new MzLibException("Deconvolution params and algorithm do not match"); + + var firstIndex = spectrum.GetClosestPeakIndex(range.Minimum); + var lastIndex = spectrum.GetClosestPeakIndex(range.Maximum); + + var mzs = spectrum.XArray[firstIndex..lastIndex] + .Select(p => p) + .ToArray(); + var intensities = spectrum.YArray[firstIndex..lastIndex] + .Select(p => (float)p) + .ToArray(); + + var mpArray = new byte[intensities.Length * Marshal.SizeOf(typeof(MatchedPeak))]; + GCHandle handle = GCHandle.Alloc(mpArray, GCHandleType.Pinned); + try + { + IntPtr matchedPeaksPtr = (IntPtr)handle.AddrOfPinnedObject(); + IsoDecDeconvolutionParameters.IsoSettings settings = deconParams.ToIsoSettings(); + int result = process_spectrum(mzs, intensities, intensities.Length, null, matchedPeaksPtr, settings); + if (result <= 0) + return Enumerable.Empty(); + + // Handle results + MatchedPeak[] matchedpeaks = new MatchedPeak[result]; + for (int i = 0; i < result; i++) + { + matchedpeaks[i] = Marshal.PtrToStructure(matchedPeaksPtr + i * Marshal.SizeOf(typeof(MatchedPeak))); + } + + return ConvertToIsotopicEnvelopes(deconParams, matchedpeaks, spectrum); + } + finally + { + handle.Free(); + } + } + + private List ConvertToIsotopicEnvelopes(IsoDecDeconvolutionParameters parameters, MatchedPeak[] matchedpeaks, MzSpectrum spectrum) + { + List result = new List(); + int currentId = 0; + foreach(MatchedPeak peak in matchedpeaks) + { + List<(double,double)> peaks = new List<(double,double)> (); + for (int i = 0; i < peak.realisolength; i++) + { + + List indicesWithinTolerance = spectrum.GetPeakIndicesWithinTolerance(peak.isomz[i], new PpmTolerance(5)); + double maxIntensity = 0; + int maxIndex = -1; + foreach (int index in indicesWithinTolerance) + { + if (spectrum.YArray[index] > maxIntensity) { maxIntensity = spectrum.YArray[index]; maxIndex = index; } + } + if (maxIndex >= 0) + { + peaks.Add((spectrum.XArray[maxIndex], spectrum.YArray[maxIndex])); + } + else + { + peaks.Add((peak.isomz[i], 0)); + } + + } + int charge = peak.z; + if(parameters.Polarity == Polarity.Negative) { charge = -peak.z; } + if(parameters.ReportMulitpleMonoisos) + { + foreach (float monoiso in peak.monoisos) + { + if (monoiso > 0) { result.Add(new IsotopicEnvelope(currentId, peaks, (double)monoiso, charge, peak.peakint, peak.score)); } + } + } + else { result.Add(new IsotopicEnvelope(currentId, peaks, (double)peak.monoiso, charge, peak.peakint, peak.score)); } + currentId++; + } + return result; + } + } +} diff --git a/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/isodeclib.dll b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/isodeclib.dll new file mode 100644 index 000000000..a89f4ad96 Binary files /dev/null and b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/isodeclib.dll differ diff --git a/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/isogenmass.dll b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/isogenmass.dll new file mode 100644 index 000000000..072d3f72d Binary files /dev/null and b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/isogenmass.dll differ diff --git a/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/libmmd.dll b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/libmmd.dll new file mode 100644 index 000000000..89e85631c Binary files /dev/null and b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/libmmd.dll differ diff --git a/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/svml_dispmd.dll b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/svml_dispmd.dll new file mode 100644 index 000000000..147636b82 Binary files /dev/null and b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecResources/svml_dispmd.dll differ diff --git a/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs index d419561f2..d084b1a30 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs @@ -1,19 +1,11 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Easy.Common.Extensions; -using Easy.Common.Interfaces; +using Chemistry; using MzLibUtil; namespace MassSpectrometry { - public enum DeconvolutionType - { - ClassicDeconvolution, - ExampleNewDeconvolutionTemplate, - } + /// /// Context class for all deconvolution @@ -30,27 +22,10 @@ public static class Deconvoluter public static IEnumerable Deconvolute(MsDataScan scan, DeconvolutionParameters deconvolutionParameters, MzRange rangeToGetPeaksFrom = null) { - rangeToGetPeaksFrom ??= scan.MassSpectrum.Range; - - // set deconvolution algorithm and any specific deconvolution parameters found in the MsDataScan - DeconvolutionAlgorithm deconAlgorithm; - switch (deconvolutionParameters.DeconvolutionType) - { - case DeconvolutionType.ClassicDeconvolution: - deconAlgorithm = new ClassicDeconvolutionAlgorithm(deconvolutionParameters); - break; - - case DeconvolutionType.ExampleNewDeconvolutionTemplate: - deconAlgorithm = new ExampleNewDeconvolutionAlgorithmTemplate(deconvolutionParameters); - break; - - default: throw new MzLibException("DeconvolutionType not yet supported"); - } - - return deconAlgorithm.Deconvolute(scan.MassSpectrum, rangeToGetPeaksFrom); + // set any specific deconvolution parameters found only in the MsDataScan + foreach (var isotopicEnvelope in Deconvolute(scan.MassSpectrum, deconvolutionParameters, rangeToGetPeaksFrom)) + yield return isotopicEnvelope; } - - /// /// Static deconvolution of an MzSpectrum that does not require Deconvoluter construction @@ -63,23 +38,31 @@ public static IEnumerable Deconvolute(MzSpectrum spectrum, DeconvolutionParameters deconvolutionParameters, MzRange rangeToGetPeaksFrom = null) { rangeToGetPeaksFrom ??= spectrum.Range; - // set deconvolution algorithm - DeconvolutionAlgorithm deconAlgorithm; - switch (deconvolutionParameters.DeconvolutionType) + DeconvolutionAlgorithm deconAlgorithm = deconvolutionParameters.DeconvolutionType switch { - case DeconvolutionType.ClassicDeconvolution: - deconAlgorithm = new ClassicDeconvolutionAlgorithm(deconvolutionParameters); - break; + DeconvolutionType.ClassicDeconvolution => new ClassicDeconvolutionAlgorithm(deconvolutionParameters), + DeconvolutionType.ExampleNewDeconvolutionTemplate => new ExampleNewDeconvolutionAlgorithmTemplate(deconvolutionParameters), + DeconvolutionType.IsoDecDeconvolution => new IsoDecAlgorithm(deconvolutionParameters), + _ => throw new MzLibException("DeconvolutionType not yet supported") + }; - case DeconvolutionType.ExampleNewDeconvolutionTemplate: - deconAlgorithm = new ExampleNewDeconvolutionAlgorithmTemplate(deconvolutionParameters); - break; - - default: throw new MzLibException("DeconvolutionType not yet supported"); + // Short circuit deconvolution if it is called on a neutral mass spectrum + if (spectrum is NeutralMassSpectrum newt) + { + for (int i = 0; i < newt.XArray.Length; i++) + { + // skip this peak if it's outside the range of interest (e.g. if we're only interested in deconvoluting a small m/z range) + if (!rangeToGetPeaksFrom.Contains(newt.XArray[i].ToMz(newt.Charges[i]))) + continue; + yield return new IsotopicEnvelope(newt.XArray[i], newt.YArray[i], newt.Charges[i]); + } + } + else + { + foreach (var isotopicEnvelope in deconAlgorithm.Deconvolute(spectrum, rangeToGetPeaksFrom).ToList()) + yield return isotopicEnvelope; } - - return deconAlgorithm.Deconvolute(spectrum, rangeToGetPeaksFrom); } } } diff --git a/mzLib/MassSpectrometry/Deconvolution/DeconvolutionType.cs b/mzLib/MassSpectrometry/Deconvolution/DeconvolutionType.cs new file mode 100644 index 000000000..f2fece52e --- /dev/null +++ b/mzLib/MassSpectrometry/Deconvolution/DeconvolutionType.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MassSpectrometry +{ + public enum DeconvolutionType + { + ClassicDeconvolution, + ExampleNewDeconvolutionTemplate, + IsoDecDeconvolution, + } +} diff --git a/mzLib/MassSpectrometry/Deconvolution/Parameters/IsoDecDeconvolutionParameters.cs b/mzLib/MassSpectrometry/Deconvolution/Parameters/IsoDecDeconvolutionParameters.cs new file mode 100644 index 000000000..2d049016f --- /dev/null +++ b/mzLib/MassSpectrometry/Deconvolution/Parameters/IsoDecDeconvolutionParameters.cs @@ -0,0 +1,215 @@ +using System.Runtime.InteropServices; + +namespace MassSpectrometry; + +public class IsoDecDeconvolutionParameters : DeconvolutionParameters +{ + public override DeconvolutionType DeconvolutionType { get; protected set; } = DeconvolutionType.IsoDecDeconvolution; + + #region Interop Parameters + + /// + /// The struct that is passed into the isodec.dll + /// + public struct IsoSettings + { + public int phaseres; // Precision of encoding matrix + public int verbose; // Verbose output + public int peakwindow; // Peak Detection Window + public float peakthresh; // Peak Detection Threshold + public int minpeaks; // Minimum Peaks for an allowed peak + public float css_thresh; // Minimum cosine similarity score for isotope distribution + public float matchtol; // Match Tolerance for peak detection in ppm + public int maxshift; // Maximum shift allowed for isotope distribution + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public float[] mzwindow; // MZ Window for isotope distribution + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public float[] plusoneintwindow; // Plus One Intensity range. Will be used for charge state 1 + public int knockdown_rounds; // Number of knockdown rounds + public float min_score_diff; // Minimum score difference for isotope distribution to allow missed monoisotopic peaks + public float minareacovered; // Minimum area covered by isotope distribution. Use in or with css_thresh + public int isolength; // Isotope Distribution Length + public double mass_diff_c; // Mass difference between isotopes + public float adductmass; // Adduct Mass + public int minusoneaszero; // Use set the -1 isotope as 0 to help force better alignments + public float isotopethreshold; // Threshold for isotope distribution. Will remove relative intensities below this. + public float datathreshold; // Threshold for data. Will remove relative intensities below this relative to max intensity in each cluster + public float zscore_threshold; //Ratio above which a secondary charge state prediction will be returned. + } + + private IsoSettings? _isoSettings; + + internal IsoSettings ToIsoSettings() + { + if (_isoSettings != null) + return _isoSettings.Value; + + IsoSettings result = new IsoSettings + { + phaseres = PhaseRes, + verbose = Verbose, + peakwindow = PeakWindow, + peakthresh = PeakThreshold, + minpeaks = MinPeaks, + css_thresh = CssThreshold, + matchtol = MatchTolerance, + maxshift = MaxShift, + mzwindow = MzWindow, + plusoneintwindow = PlusOneIntWindow, + knockdown_rounds = KnockdownRounds, + min_score_diff = MinScoreDiff, + minareacovered = MinAreaCovered, + isolength = IsoLength, + mass_diff_c = MassDiffC, + adductmass = AdductMass, + minusoneaszero = MinusOneAreasZero, + isotopethreshold = IsotopeThreshold, + datathreshold = DataThreshold, + zscore_threshold = ZScoreThreshold + }; + + _isoSettings = result; + return result; + } + + #endregion + + #region User-Accessible Parameters + + /// + /// Precision of encoding matrix + /// + public int PhaseRes { get; set; } + + /// + /// Minimum cosine similarity score for isotope distribution + /// + public float CssThreshold { get; set; } + + /// + /// Match Tolerance for peak detection in ppm + /// + public float MatchTolerance { get; set; } + + /// + /// Maximum shift allowed for isotope distribution + /// + public int MaxShift { get; set; } + + /// + /// MZ Window for isotope distribution + /// + public float[] MzWindow { get; set; } + + /// + /// Number of knockdown rounds + /// + public int KnockdownRounds { get; set; } + + /// + /// Minimum area covered by isotope distribution. Use in or with css_thresh + /// + public float MinAreaCovered { get; set; } + + /// + /// Threshold for data. Will remove relative intensities below this relative to max intensity in each cluster + /// + public float DataThreshold { get; set; } + + /// + /// Report multiple monoisotopic peaks + /// + public bool ReportMulitpleMonoisos { get; set; } + + #endregion User-Accessible Parameters + + #region Hard-Coded Parameters + + /// + /// Verbose output + /// + public int Verbose { get; protected set; } = 0; + + /// + /// Peak Detection Window + /// + public int PeakWindow { get; protected set; } = 80; + + /// + /// Peak Detection Threshold + /// + public float PeakThreshold { get; protected set; } = (float)0.0001; + + /// + /// Minimum Peaks for an allowed peak + /// + public int MinPeaks { get; protected set; } = 3; + + /// + /// Plus One Intensity range. Will be used for charge state 1 + /// + public float[] PlusOneIntWindow { get; protected set; } = new float[] { (float)0.1, (float)0.6 }; + + /// + /// Minimum score difference for isotope distribution to allow missed monoisotopic peaks + /// + public float MinScoreDiff { get; set; } = (float)0.1; + + /// + /// Isotope Distribution Length + /// + public int IsoLength { get; protected set; } = 64; + + /// + /// Mass difference between isotopes + /// + public double MassDiffC { get; protected set; } = 1.0033; + + /// + /// Adduct Mass + /// + public float AdductMass { get; set; } = (float)1.00727276467; + + /// + /// Use set the -1 isotope as 0 to help force better alignments + /// + public int MinusOneAreasZero { get; set; } = 1; + + /// + /// Threshold for isotope distribution. Will remove relative intensities below this. + /// + public float IsotopeThreshold { get; set; } = (float)0.01; + + /// + /// Ratio above which a secondary charge state prediction will be returned. + /// + public float ZScoreThreshold { get; protected set; } = (float)0.95; + + #endregion Hard-Coded Parameters + + public IsoDecDeconvolutionParameters( + Polarity polarity = Polarity.Positive, + int phaseRes = 8, + bool reportMultipleMonoisos = true, + float cssThreshold = (float)0.7, + float matchTolerance = (float)5, + int maxShift = 3, + float[] mzWindow = null, + int knockdownRounds = 5, + float minAreaCovered = (float)0.20, + float relativeDataThreshold = (float)0.05) + : base(1, 50, polarity) + { + PhaseRes = phaseRes; + ReportMulitpleMonoisos = reportMultipleMonoisos; + CssThreshold = cssThreshold; + MatchTolerance = matchTolerance; + MaxShift = maxShift; + MzWindow = mzWindow ?? new float[] { (float)-1.05, (float)2.05 }; + KnockdownRounds = knockdownRounds; + MinAreaCovered = minAreaCovered; + DataThreshold = relativeDataThreshold; + if (Polarity == Polarity.Negative) + AdductMass = (float)-1.00727276467; + } +} \ No newline at end of file diff --git a/mzLib/MassSpectrometry/MassSpectrometry.csproj b/mzLib/MassSpectrometry/MassSpectrometry.csproj index 6af63b9e4..1061dae97 100644 --- a/mzLib/MassSpectrometry/MassSpectrometry.csproj +++ b/mzLib/MassSpectrometry/MassSpectrometry.csproj @@ -20,4 +20,33 @@ + + + + + + + + Always + isodeclib.dll + lib\$(TargetFramework) + + + Always + isogenmass.dll + lib\$(TargetFramework) + + + Always + libmmd.dll + lib\$(TargetFramework) + + + Always + svml_dispmd.dll + lib\$(TargetFramework) + + + + diff --git a/mzLib/MassSpectrometry/MsDataFile.cs b/mzLib/MassSpectrometry/MsDataFile.cs index 7242f2cf7..3e88fcfef 100644 --- a/mzLib/MassSpectrometry/MsDataFile.cs +++ b/mzLib/MassSpectrometry/MsDataFile.cs @@ -23,9 +23,6 @@ namespace MassSpectrometry { - // TODO: Define scope of class - // Class scope is to provide to the data loaded from the DataFile. - /// /// A class for interacting with data collected from a Mass Spectrometer, and stored in a file /// diff --git a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs index 7e42426b1..c9fe6d602 100644 --- a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs +++ b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs @@ -14,29 +14,57 @@ public class IsotopicEnvelope : IHasMass /// /// Mass of most abundant observed isotopic peak, not accounting for addition or subtraction or protons due to ESI charge state induction /// - public double MostAbundantObservedIsotopicMass { get; private set; } + internal double MostAbundantObservedIsotopicMass { get; private set; } public readonly int Charge; public readonly double TotalIntensity; - public readonly double StDev; - public readonly int MassIndex; + public readonly int PrecursorId; public double Score { get; private set; } - public IsotopicEnvelope(List<(double mz, double intensity)> bestListOfPeaks, double bestMonoisotopicMass, int bestChargeState, double bestTotalIntensity, double bestStDev, int bestMassIndex) + /// + /// Used for an isotopic envelope that mzLib deconvoluted (e.g., from a mass spectrum) + /// + public IsotopicEnvelope(List<(double mz, double intensity)> bestListOfPeaks, double bestMonoisotopicMass, int bestChargeState, double bestTotalIntensity, double bestStDev) { Peaks = bestListOfPeaks; MonoisotopicMass = bestMonoisotopicMass; - MostAbundantObservedIsotopicMass = GetMostAbundantObservedIsotopicMass(bestListOfPeaks, bestChargeState); + MostAbundantObservedIsotopicMass = bestListOfPeaks.MaxBy(p => p.intensity).mz * Math.Abs(bestChargeState); Charge = bestChargeState; TotalIntensity = bestTotalIntensity; - StDev = bestStDev; - MassIndex = bestMassIndex; - Score = ScoreIsotopeEnvelope(); + Score = ScoreIsotopeEnvelope(bestStDev); + } + + /// + /// Used for a neutral mass read in from a deconvoluted file + /// Assumes the mass is correct: score is max value + /// + public IsotopicEnvelope(double monoisotopicMass, double intensity, int charge) + { + MonoisotopicMass = monoisotopicMass; + Charge = charge; + TotalIntensity = intensity; + Score = double.MaxValue; + Peaks = [(monoisotopicMass.ToMz(charge), intensity)]; } - public double GetMostAbundantObservedIsotopicMass(List<(double mz, double intensity)> peaks, int charge) + /// + /// Used for A deconvolution method that calculates its own score. + /// + /// All missed mono products of the same peak will share an ID + /// + /// + /// + /// + /// + public IsotopicEnvelope(int id, List<(double mz, double intensity)> peaks, double monoisotopicmass, int chargestate, double intensity, double score) { - return peaks.MaxBy(p => p.intensity).mz * Math.Abs(charge); + PrecursorId = id; + Peaks = peaks; + MonoisotopicMass = monoisotopicmass; + Charge = chargestate; + TotalIntensity = intensity; + Score = score; + MostAbundantObservedIsotopicMass = peaks.MaxBy(p => p.intensity).mz * Math.Abs(chargestate); } public override string ToString() @@ -44,10 +72,10 @@ public override string ToString() return Charge + "\t" + Peaks[0].mz.ToString("G8") + "\t" + Peaks.Count + "\t" + TotalIntensity; } - private double ScoreIsotopeEnvelope() //likely created by Stefan Solntsev using peptide data + private double ScoreIsotopeEnvelope(double stDev) //likely created by Stefan Solntsev using peptide data { return Peaks.Count >= 2 ? - TotalIntensity / Math.Pow(StDev, 0.13) * Math.Pow(Peaks.Count, 0.4) / Math.Pow(Math.Abs(Charge), 0.06) : + TotalIntensity / Math.Pow(stDev, 0.13) * Math.Pow(Peaks.Count, 0.4) / Math.Pow(Math.Abs(Charge), 0.06) : 0; } @@ -60,6 +88,5 @@ public void SetMedianMonoisotopicMass(List monoisotopicMassPredictions) { MonoisotopicMass = monoisotopicMassPredictions.Median(); } - } } \ No newline at end of file diff --git a/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs index 2e9fcc7a4..f081f69a5 100644 --- a/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs +++ b/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs @@ -22,10 +22,8 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.Text.Json; namespace MassSpectrometry { @@ -126,7 +124,7 @@ public MzRange Range } } - public double? FirstX + public virtual double? FirstX { get { @@ -138,7 +136,7 @@ public double? FirstX } } - public double? LastX + public virtual double? LastX { get { @@ -373,7 +371,7 @@ public IsotopicEnvelope FindIsotopicEnvelope(int massIndex, double candidateForM } } - return new IsotopicEnvelope(listOfObservedPeaks, monoisotopicMass, chargeState, totalIntensity, Statistics.StandardDeviation(listOfRatios), massIndex); + return new IsotopicEnvelope(listOfObservedPeaks, monoisotopicMass, chargeState, totalIntensity, listOfRatios.StandardDeviation()); } [Obsolete("Deconvolution Has been moved to the Deconvoluter Object")] @@ -611,6 +609,37 @@ public int GetClosestPeakIndex(double x) return XArray.GetClosestIndex(x); } + public List GetPeakIndicesWithinTolerance(double x, PpmTolerance tolerance) + { + List indices = new List(); + int nearestIdx = XArray.GetClosestIndex(x); + if(nearestIdx == 0) { return indices; } + if (tolerance.Within(XArray[nearestIdx], x)) { indices.Add(nearestIdx); } + int shift = 1; + + bool findingUpper = true; + bool findingLower = true; + + while(findingLower || findingLower) + { + int upperIdx = nearestIdx+ shift; + int lowerIdx = nearestIdx-shift; + if(findingUpper && upperIdx < XArray.Length) + { + if(tolerance.Within(XArray[upperIdx], x)) {indices.Add(upperIdx); } + else { findingUpper = false; } + } + + if(findingLower && lowerIdx >= 0) + { + if (tolerance.Within(XArray[lowerIdx], x)) { indices.Add(lowerIdx); } + else { findingLower = false; } + } + shift++; + } + return indices; + } + public void ReplaceXbyApplyingFunction(Func convertor) { for (int i = 0; i < Size; i++) @@ -796,7 +825,12 @@ private MzPeak GetPeak(int index) return peakList[index]; } - private MzPeak GeneratePeak(int index) + /// + /// The source of all peaks which populate the peakList + /// + /// + /// + protected virtual MzPeak GeneratePeak(int index) { return new MzPeak(XArray[index], YArray[index]); } diff --git a/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs new file mode 100644 index 000000000..dcb5d7d2b --- /dev/null +++ b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs @@ -0,0 +1,65 @@ +using System; +using Chemistry; + +namespace MassSpectrometry +{ + public class NeutralMassSpectrum : MzSpectrum + { + public int[] Charges { get; init; } + public NeutralMassSpectrum(double[,] monoisotopicMassesIntensities, int[] charges) : base(monoisotopicMassesIntensities) + { + if (monoisotopicMassesIntensities.GetLength(0) != charges.Length) + throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same."); + + Charges = charges; + + double minMz = double.MaxValue; + double maxMz = double.MinValue; + for (int i = 0; i < monoisotopicMassesIntensities.GetLength(0); i++) + { + var mz = monoisotopicMassesIntensities[i,0].ToMz(charges[i]); + if (mz < minMz) + minMz = mz; + if (mz > maxMz) + maxMz = mz; + } + + FirstX = minMz; + LastX = maxMz; + } + + public NeutralMassSpectrum(double[] monoisotopicMasses, double[] intensities, int[] charges, bool shouldCopy) + : base(monoisotopicMasses, intensities, shouldCopy) + { + if (monoisotopicMasses.GetLength(0) != intensities.Length || monoisotopicMasses.Length != charges.Length) + throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same."); + + Charges = charges; + + double minMz = double.MaxValue; + double maxMz = double.MinValue; + for (int i = 0; i < monoisotopicMasses.Length; i++) + { + var mz = monoisotopicMasses[i].ToMz(charges[i]); + if (mz < minMz) + minMz = mz; + if (mz > maxMz) + maxMz = mz; + } + + FirstX = minMz; + LastX = maxMz; + } + + public override double? FirstX { get; } // in m/z + public override double? LastX { get; } // in m/z + + /// + /// Converts to a charged spectrum + /// + protected override MzPeak GeneratePeak(int index) + { + return new MzPeak(XArray[index].ToMz(Charges[index]), YArray[index]); + } + } +} diff --git a/mzLib/Test/TestDeconvolution.cs b/mzLib/Test/TestDeconvolution.cs index 8f6cbb9ee..67c34c8e1 100644 --- a/mzLib/Test/TestDeconvolution.cs +++ b/mzLib/Test/TestDeconvolution.cs @@ -3,9 +3,6 @@ using MassSpectrometry; using MzLibUtil; using NUnit.Framework; -using Assert = NUnit.Framework.Legacy.ClassicAssert; -using Proteomics; -using Proteomics.ProteolyticDigestion; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -14,7 +11,10 @@ using System.Linq; using Omics.Digestion; using Omics.Modifications; +using Proteomics; +using Proteomics.ProteolyticDigestion; using Test.FileReadingTests; +using IsoDecDeconvolutionParameters = MassSpectrometry.IsoDecDeconvolutionParameters; namespace Test { @@ -26,15 +26,18 @@ public sealed class TestDeconvolution #region Old Deconvolution [Test] - [TestCase(586.2143122, 24, 41983672, 586.2)]//This is a lesser abundant charge state envelope at the low mz end - [TestCase(740.372202090153, 19, 108419280, 740.37)]//This is the most abundant charge state envelope - [TestCase(1081.385183, 13, 35454636, 1081.385)]//This is a lesser abundant charge state envelope at the high mz end - public void TestDeconvolutionProteoformMultiChargeState(double selectedIonMz, int selectedIonChargeStateGuess, double selectedIonIntensity, double isolationMz) + [TestCase(586.2143122, 24, 41983672, 586.2)] //This is a lesser abundant charge state envelope at the low mz end + [TestCase(740.372202090153, 19, 108419280, 740.37)] //This is the most abundant charge state envelope + [TestCase(1081.385183, 13, 35454636, + 1081.385)] //This is a lesser abundant charge state envelope at the high mz end + public void TestDeconvolutionProteoformMultiChargeState(double selectedIonMz, int selectedIonChargeStateGuess, + double selectedIonIntensity, double isolationMz) { MsDataScan[] Scans = new MsDataScan[1]; //txt file, not mgf, because it's an MS1. Most intense proteoform has mass of ~14037.9 Da - string Ms1SpectrumPath = Path.Combine(TestContext.CurrentContext.TestDirectory, @"DataFiles\14kDaProteoformMzIntensityMs1.txt"); + string Ms1SpectrumPath = Path.Combine(TestContext.CurrentContext.TestDirectory, + @"DataFiles\14kDaProteoformMzIntensityMs1.txt"); string[] spectrumLines = File.ReadAllLines(Ms1SpectrumPath); @@ -51,7 +54,9 @@ public void TestDeconvolutionProteoformMultiChargeState(double selectedIonMz, in MzSpectrum spectrum = new MzSpectrum(ms1mzs, ms1intensities, false); - Scans[0] = new MsDataScan(spectrum, 1, 1, false, Polarity.Positive, 1.0, new MzRange(495, 1617), "first spectrum", MZAnalyzerType.Unknown, spectrum.SumOfAllY, null, null, null, selectedIonMz, selectedIonChargeStateGuess, selectedIonIntensity, isolationMz, 4); + Scans[0] = new MsDataScan(spectrum, 1, 1, false, Polarity.Positive, 1.0, new MzRange(495, 1617), + "first spectrum", MZAnalyzerType.Unknown, spectrum.SumOfAllY, null, null, null, selectedIonMz, + selectedIonChargeStateGuess, selectedIonIntensity, isolationMz, 4); var myMsDataFile = new FakeMsDataFile(Scans); @@ -68,21 +73,24 @@ public void TestDeconvolutionProteoformMultiChargeState(double selectedIonMz, in [Test] [TestCase("APSGGKK", "12-18-17_frac7_calib_ms1_663_665.mzML", 2)] - [TestCase("PKRKAEGDAKGDKAKVKDEPQRRSARLSAKPAPPKPEPKPKKAPAKKGEKVPKGKKGKADAGKEGNNPAENGDAKTDQAQKAEGAGDAK", "FXN11_tr1_032017-calib_ms1_scans716_718.mzML", 8)] - [TestCase("PKRKVSSAEGAAKEEPKRRSARLSAKPPAKVEAKPKKAAAKDKSSDKKVQTKGKRGAKGKQAEVANQETKEDLPAENGETKTEESPASDEAGEKEAKSD", "FXN11_tr1_032017-calib_ms1_scans781_783.mzML", 16)] + [TestCase("PKRKAEGDAKGDKAKVKDEPQRRSARLSAKPAPPKPEPKPKKAPAKKGEKVPKGKKGKADAGKEGNNPAENGDAKTDQAQKAEGAGDAK", + "FXN11_tr1_032017-calib_ms1_scans716_718.mzML", 8)] + [TestCase("PKRKVSSAEGAAKEEPKRRSARLSAKPPAKVEAKPKKAAAKDKSSDKKVQTKGKRGAKGKQAEVANQETKEDLPAENGETKTEESPASDEAGEKEAKSD", + "FXN11_tr1_032017-calib_ms1_scans781_783.mzML", 16)] public static void CheckGetMostAbundantObservedIsotopicMass(string peptide, string file, int charge) { Protein test1 = new Protein(peptide, "Accession"); DigestionParams d = new DigestionParams(); - PeptideWithSetModifications pw = new PeptideWithSetModifications(test1, d, 1, test1.Length, CleavageSpecificity.None, "", 0, new Dictionary(), 0); + PeptideWithSetModifications pw = new PeptideWithSetModifications(test1, d, 1, test1.Length, + CleavageSpecificity.None, "", 0, new Dictionary(), 0); double m = pw.MostAbundantMonoisotopicMass.ToMz(charge); string singleScan = Path.Combine(TestContext.CurrentContext.TestDirectory, "DataFiles", file); - var reader = MsDataFileReader.GetDataFile(singleScan); + var reader = MsDataFileReader.GetDataFile(singleScan); reader.LoadAllStaticData(); List singlescan = reader.GetAllScansList(); - + MzSpectrum singlespec = singlescan[0].MassSpectrum; MzRange singleRange = new MzRange(singlespec.XArray.Min(), singlespec.XArray.Max()); int minAssumedChargeState = 1; @@ -91,13 +99,16 @@ public static void CheckGetMostAbundantObservedIsotopicMass(string peptide, stri double intensityRatioLimit = 3; //check assigned correctly - List lie2 = singlespec.Deconvolute(singleRange, minAssumedChargeState, maxAssumedChargeState, deconvolutionTolerancePpm, intensityRatioLimit).ToList(); + List lie2 = singlespec.Deconvolute(singleRange, minAssumedChargeState, + maxAssumedChargeState, deconvolutionTolerancePpm, intensityRatioLimit).ToList(); List lie2_charge = lie2.Where(p => p.Charge == charge).ToList(); Assert.That(lie2_charge[0].MostAbundantObservedIsotopicMass / charge, Is.EqualTo(m).Within(0.1)); //check that if already assigned, skips assignment and just recalls same value - List lie3 = singlespec.Deconvolute(singleRange, minAssumedChargeState, maxAssumedChargeState, deconvolutionTolerancePpm, intensityRatioLimit).ToList(); - Assert.AreEqual(lie2.Select(p => p.MostAbundantObservedIsotopicMass), lie3.Select(p => p.MostAbundantObservedIsotopicMass)); + List lie3 = singlespec.Deconvolute(singleRange, minAssumedChargeState, + maxAssumedChargeState, deconvolutionTolerancePpm, intensityRatioLimit).ToList(); + Assert.That(lie2.Select(p => p.MostAbundantObservedIsotopicMass), + Is.EqualTo(lie3.Select(p => p.MostAbundantObservedIsotopicMass)).Within(.0005)); } #endregion @@ -105,15 +116,18 @@ public static void CheckGetMostAbundantObservedIsotopicMass(string peptide, stri #region Classic Deconvolution [Test] - [TestCase(586.2143122, 24, 41983672, 586.2)]//This is a lesser abundant charge state envelope at the low mz end - [TestCase(740.372202090153, 19, 108419280, 740.37)]//This is the most abundant charge state envelope - [TestCase(1081.385183, 13, 35454636, 1081.385)]//This is a lesser abundant charge state envelope at the high mz end - public void TestClassicDeconvolutionProteoformMultiChargeState(double selectedIonMz, int selectedIonChargeStateGuess, double selectedIonIntensity, double isolationMz) + [TestCase(586.2143122, 24, 41983672, 586.2)] //This is a lesser abundant charge state envelope at the low mz end + [TestCase(740.372202090153, 19, 108419280, 740.37)] //This is the most abundant charge state envelope + [TestCase(1081.385183, 13, 35454636, + 1081.385)] //This is a lesser abundant charge state envelope at the high mz end + public void TestClassicDeconvolutionProteoformMultiChargeState(double selectedIonMz, + int selectedIonChargeStateGuess, double selectedIonIntensity, double isolationMz) { MsDataScan[] Scans = new MsDataScan[1]; //txt file, not mgf, because it's an MS1. Most intense proteoform has mass of ~14037.9 Da - string Ms1SpectrumPath = Path.Combine(TestContext.CurrentContext.TestDirectory, @"DataFiles\14kDaProteoformMzIntensityMs1.txt"); + string Ms1SpectrumPath = Path.Combine(TestContext.CurrentContext.TestDirectory, + @"DataFiles\14kDaProteoformMzIntensityMs1.txt"); string[] spectrumLines = File.ReadAllLines(Ms1SpectrumPath); @@ -130,7 +144,9 @@ public void TestClassicDeconvolutionProteoformMultiChargeState(double selectedIo MzSpectrum spectrum = new MzSpectrum(ms1mzs, ms1intensities, false); - Scans[0] = new MsDataScan(spectrum, 1, 1, false, Polarity.Positive, 1.0, new MzRange(495, 1617), "first spectrum", MZAnalyzerType.Unknown, spectrum.SumOfAllY, null, null, null, selectedIonMz, selectedIonChargeStateGuess, selectedIonIntensity, isolationMz, 4); + Scans[0] = new MsDataScan(spectrum, 1, 1, false, Polarity.Positive, 1.0, new MzRange(495, 1617), + "first spectrum", MZAnalyzerType.Unknown, spectrum.SumOfAllY, null, null, null, selectedIonMz, + selectedIonChargeStateGuess, selectedIonIntensity, isolationMz, 4); var myMsDataFile = new FakeMsDataFile(Scans); @@ -141,7 +157,8 @@ public void TestClassicDeconvolutionProteoformMultiChargeState(double selectedIo DeconvolutionParameters deconParameters = new ClassicDeconvolutionParameters(1, 60, 4, 3); List isolatedMasses = scan.GetIsolatedMassesAndCharges(scan, deconParameters).ToList(); - List isolatedMasses2 = scan.GetIsolatedMassesAndCharges(scan.MassSpectrum, deconParameters).ToList(); + List isolatedMasses2 = + scan.GetIsolatedMassesAndCharges(scan.MassSpectrum, deconParameters).ToList(); List monoIsotopicMasses = isolatedMasses.Select(m => m.MonoisotopicMass).ToList(); List monoIsotopicMasses2 = isolatedMasses2.Select(m => m.MonoisotopicMass).ToList(); @@ -154,13 +171,16 @@ public void TestClassicDeconvolutionProteoformMultiChargeState(double selectedIo [Test] [TestCase("APSGGKK", "12-18-17_frac7_calib_ms1_663_665.mzML", 2)] - [TestCase("PKRKAEGDAKGDKAKVKDEPQRRSARLSAKPAPPKPEPKPKKAPAKKGEKVPKGKKGKADAGKEGNNPAENGDAKTDQAQKAEGAGDAK", "FXN11_tr1_032017-calib_ms1_scans716_718.mzML", 8)] - [TestCase("PKRKVSSAEGAAKEEPKRRSARLSAKPPAKVEAKPKKAAAKDKSSDKKVQTKGKRGAKGKQAEVANQETKEDLPAENGETKTEESPASDEAGEKEAKSD", "FXN11_tr1_032017-calib_ms1_scans781_783.mzML", 16)] + [TestCase("PKRKAEGDAKGDKAKVKDEPQRRSARLSAKPAPPKPEPKPKKAPAKKGEKVPKGKKGKADAGKEGNNPAENGDAKTDQAQKAEGAGDAK", + "FXN11_tr1_032017-calib_ms1_scans716_718.mzML", 8)] + [TestCase("PKRKVSSAEGAAKEEPKRRSARLSAKPPAKVEAKPKKAAAKDKSSDKKVQTKGKRGAKGKQAEVANQETKEDLPAENGETKTEESPASDEAGEKEAKSD", + "FXN11_tr1_032017-calib_ms1_scans781_783.mzML", 16)] public static void CheckClassicGetMostAbundantObservedIsotopicMass(string peptide, string file, int charge) { Protein test1 = new Protein(peptide, "Accession"); DigestionParams d = new DigestionParams(); - PeptideWithSetModifications pw = new PeptideWithSetModifications(test1, d, 1, test1.Length, CleavageSpecificity.None, "", 0, new Dictionary(), 0); + PeptideWithSetModifications pw = new PeptideWithSetModifications(test1, d, 1, test1.Length, + CleavageSpecificity.None, "", 0, new Dictionary(), 0); double m = pw.MostAbundantMonoisotopicMass.ToMz(charge); string singleScan = Path.Combine(TestContext.CurrentContext.TestDirectory, "DataFiles", file); @@ -176,7 +196,8 @@ public static void CheckClassicGetMostAbundantObservedIsotopicMass(string peptid double intensityRatioLimit = 3; DeconvolutionParameters deconParameters = - new ClassicDeconvolutionParameters(minAssumedChargeState, maxAssumedChargeState, deconvolutionTolerancePpm, + new ClassicDeconvolutionParameters(minAssumedChargeState, maxAssumedChargeState, + deconvolutionTolerancePpm, intensityRatioLimit); //check assigned correctly @@ -187,11 +208,9 @@ public static void CheckClassicGetMostAbundantObservedIsotopicMass(string peptid //check that if already assigned, skips assignment and just recalls same value List lie3 = Deconvoluter.Deconvolute(singlespec, deconParameters, singleRange).ToList(); - Assert.AreEqual(lie2.Select(p => p.MostAbundantObservedIsotopicMass), lie3.Select(p => p.MostAbundantObservedIsotopicMass)); + Assert.That(lie2.Select(p => p.MostAbundantObservedIsotopicMass), Is.EqualTo(lie3.Select(p => p.MostAbundantObservedIsotopicMass))); } - #endregion - [Test] [TestCase(373.85, -5, 1874.28)] // GUAGUC -5 [TestCase(467.57, -4, 1874.28)] // GUAGUC -4 @@ -217,29 +236,152 @@ public void TestNegativeModeClassicDeconvolution(double expectedMz, int expected envelope.Peaks.Any(peak => tolerance.Within(peak.mz, expectedMz))); if (resultsWithPeakOfInterest is null) Assert.Fail(); - Assert.That(tolerance.Within(expectedMonoMass, resultsWithPeakOfInterest.MonoisotopicMass)); + Assert.That(expectedMonoMass, Is.EqualTo(resultsWithPeakOfInterest.MonoisotopicMass).Within(0.01)); Assert.That(expectedCharge, Is.EqualTo(resultsWithPeakOfInterest.Charge)); } + #endregion + + #region IsoDec Deconvolution + + [Test] + [TestCase(586.2143122, 24, 41983672, 586.2)]//This is a lesser abundant charge state envelope at the low mz end + [TestCase(740.372202090153, 19, 108419280, 740.37)]//This is the most abundant charge state envelope + [TestCase(1081.385183, 13, 35454636, 1081.385)]//This is a lesser abundant charge state envelope at the high mz end + public void TestIsoDecDeconvolutionProteoformMultiChargeState(double selectedIonMz, int selectedIonChargeStateGuess, double selectedIonIntensity, double isolationMz) + { + MsDataScan[] Scans = new MsDataScan[1]; + + //txt file, not mgf, because it's an MS1. Most intense proteoform has mass of ~14037.9 Da + string Ms1SpectrumPath = Path.Combine(TestContext.CurrentContext.TestDirectory, @"DataFiles\14kDaProteoformMzIntensityMs1.txt"); + + string[] spectrumLines = File.ReadAllLines(Ms1SpectrumPath); + + int mzIntensityPairsCount = spectrumLines.Length; + double[] ms1mzs = new double[mzIntensityPairsCount]; + double[] ms1intensities = new double[mzIntensityPairsCount]; + + for (int i = 0; i < mzIntensityPairsCount; i++) + { + string[] pair = spectrumLines[i].Split('\t'); + ms1mzs[i] = Convert.ToDouble(pair[0], CultureInfo.InvariantCulture); + ms1intensities[i] = Convert.ToDouble(pair[1], CultureInfo.InvariantCulture); + } + + MzSpectrum spectrum = new MzSpectrum(ms1mzs, ms1intensities, false); + + Scans[0] = new MsDataScan(spectrum, 1, 1, false, Polarity.Positive, 1.0, new MzRange(495, 1617), "first spectrum", MZAnalyzerType.Unknown, spectrum.SumOfAllY, null, null, null, selectedIonMz, selectedIonChargeStateGuess, selectedIonIntensity, isolationMz, 4); + + var myMsDataFile = new FakeMsDataFile(Scans); + + MsDataScan scan = myMsDataFile.GetAllScansList()[0]; + + // The ones marked 2 are for checking an overload method + + DeconvolutionParameters deconParameters = new IsoDecDeconvolutionParameters(); + + IsoDecAlgorithm alg = new IsoDecAlgorithm(deconParameters); + List allMasses = alg.Deconvolute(scan.MassSpectrum, new MzRange((double)scan.MassSpectrum.FirstX, (double)scan.MassSpectrum.LastX)).ToList(); + + List isolatedMasses = scan.GetIsolatedMassesAndCharges(scan, deconParameters).ToList(); + List isolatedMasses2 = scan.GetIsolatedMassesAndCharges(scan.MassSpectrum, deconParameters).ToList(); + + List monoIsotopicMasses = isolatedMasses.Select(m => m.MonoisotopicMass).ToList(); + List monoIsotopicMasses2 = isolatedMasses2.Select(m => m.MonoisotopicMass).ToList(); + Assert.That(monoIsotopicMasses2.Count, Is.EqualTo(monoIsotopicMasses.Count)); + + //The primary monoisotopic mass should be the same regardless of which peak in which charge state was selected for isolation. + //this case is interesting because other monoisotopic mass may have a sodium adduct. The unit test could be expanded to consider this. + //Updated the tolerance on this test to be 5 ppm (which felt reasonable to me? --JGP) + double ppmwidth = (14037.926829 / 1e6) * 5; + bool isAnyEqual1 = monoIsotopicMasses.Any(m => m >= 14037.926829 - ppmwidth && m <= 14037.926826 + ppmwidth); + bool isAnyEqual2 = monoIsotopicMasses2.Any(m => m >= 14037.926829 - ppmwidth && m <= 14037.926826 + ppmwidth); + Assert.That(isAnyEqual1); + Assert.That(isAnyEqual2); + } + + [Test] + [TestCase("APSGGKK", "12-18-17_frac7_calib_ms1_663_665.mzML", 2)] + [TestCase("PKRKAEGDAKGDKAKVKDEPQRRSARLSAKPAPPKPEPKPKKAPAKKGEKVPKGKKGKADAGKEGNNPAENGDAKTDQAQKAEGAGDAK", "FXN11_tr1_032017-calib_ms1_scans716_718.mzML", 8)] + [TestCase("PKRKVSSAEGAAKEEPKRRSARLSAKPPAKVEAKPKKAAAKDKSSDKKVQTKGKRGAKGKQAEVANQETKEDLPAENGETKTEESPASDEAGEKEAKSD", "FXN11_tr1_032017-calib_ms1_scans781_783.mzML", 16)] + public static void CheckIsoDecGetMostAbundantObservedIsotopicMass(string peptide, string file, int charge) + { + Protein test1 = new Protein(peptide, "Accession"); + DigestionParams d = new DigestionParams(); + PeptideWithSetModifications pw = new PeptideWithSetModifications(test1, d, 1, test1.Length, CleavageSpecificity.None, "", 0, new Dictionary(), 0); + double m = pw.MostAbundantMonoisotopicMass.ToMz(charge); + + string singleScan = Path.Combine(TestContext.CurrentContext.TestDirectory, "DataFiles", file); + Mzml singleMZML = (Mzml)MsDataFileReader.GetDataFile(singleScan).LoadAllStaticData(); + + List singlescan = singleMZML.GetAllScansList(); + + MzSpectrum singlespec = singlescan[0].MassSpectrum; + MzRange singleRange = new MzRange(singlespec.XArray.Min(), singlespec.XArray.Max()); + DeconvolutionParameters deconParameters = new IsoDecDeconvolutionParameters(); + + //check assigned correctly + List lie2 = Deconvoluter.Deconvolute(singlespec, deconParameters, singleRange).ToList(); + List lie2_charge = lie2.Where(p => p.Charge == charge).ToList(); + Assert.That(lie2_charge[0].MostAbundantObservedIsotopicMass / charge, Is.EqualTo(m).Within(0.1)); + + //check that if already assigned, skips assignment and just recalls same value + List lie3 = Deconvoluter.Deconvolute(singlespec, deconParameters, singleRange).ToList(); + Assert.That(lie2.Select(p => p.MostAbundantObservedIsotopicMass), Is.EqualTo(lie3.Select(p => p.MostAbundantObservedIsotopicMass))); + } + + [Test] + [TestCase(373.85, -5, 1874.28)] // GUAGUC -5 + [TestCase(467.57, -4, 1874.28)] // GUAGUC -4 + [TestCase(623.75, -3, 1874.28)] // GUAGUC -3 + [TestCase(936.13, -2, 1874.28)] // GUAGUC -2 + [TestCase(473.05, -4, 1896.26)] // GUAGUC +Na -H -4 + [TestCase(631.07, -3, 1896.26)] // GUAGUC +Na -H -3 + [TestCase(947.121, -2, 1896.26)] // GUAGUC +Na -H -2 + public void TestNegativeModeIsoDecDeconvolution(double expectedMz, int expectedCharge, double expectedMonoMass) + { + // get scan + string filePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "DataFiles", + "GUACUG_NegativeMode_Sliced.mzML"); + var scan = MsDataFileReader.GetDataFile(filePath).GetAllScansList().First(); + var tolerance = new PpmTolerance(20); + + // set up deconvolution + DeconvolutionParameters deconParams = new IsoDecDeconvolutionParameters(Polarity.Negative); + + List deconvolutionResults = Deconvoluter.Deconvolute(scan, deconParams).ToList(); + // ensure each expected result is found, with correct mz, charge, and monoisotopic mass + /*var resultsWithPeakOfInterest = deconvolutionResults.FirstOrDefault(envelope => tolerance.Within(envelope.MonoisotopicMass, expectedMonoMass));*/ + var resultsWithPeakOfInterest = deconvolutionResults.FirstOrDefault(envelope => + envelope.Peaks.Any(peak => tolerance.Within(peak.mz, expectedMz))); + if (resultsWithPeakOfInterest is null) Assert.Fail(); + + Assert.That(expectedMonoMass, Is.EqualTo(resultsWithPeakOfInterest.MonoisotopicMass).Within(0.01)); + Assert.That(expectedCharge, Is.EqualTo(resultsWithPeakOfInterest.Charge)); + } + + #endregion + [Test] public static void TestExampleNewDeconvolutionInDeconvoluter() { DeconvolutionParameters deconParams = new ExampleNewDeconvolutionParametersTemplate(1, 60); - var dataFile = MsDataFileReader.GetDataFile(Path.Combine(TestContext.CurrentContext.TestDirectory, "DataFiles", "GUACUG_NegativeMode_Sliced.mzML")); + var dataFile = MsDataFileReader.GetDataFile(Path.Combine(TestContext.CurrentContext.TestDirectory, + "DataFiles", "GUACUG_NegativeMode_Sliced.mzML")); dataFile.InitiateDynamicConnection(); var scan = dataFile.GetOneBasedScanFromDynamicConnection(726); var spectrum = scan.MassSpectrum; dataFile.CloseDynamicConnection(); // test switch statements in Deconvoluter - Assert.Throws(() => Deconvoluter.Deconvolute(spectrum, deconParams)); - Assert.Throws(() => Deconvoluter.Deconvolute(scan, deconParams)); + Assert.Throws(() => _ = Deconvoluter.Deconvolute(spectrum, deconParams).ToList()); + Assert.Throws(() => _ = Deconvoluter.Deconvolute(scan, deconParams).ToList()); // test default exceptions in deconvoluter var badEnumValue = (DeconvolutionType)Int32.MaxValue; deconParams.GetType().GetProperty("DeconvolutionType")!.SetValue(deconParams, badEnumValue); - Assert.Throws(() => Deconvoluter.Deconvolute(spectrum, deconParams)); - Assert.Throws(() => Deconvoluter.Deconvolute(scan, deconParams)); + Assert.Throws(() => _ = Deconvoluter.Deconvolute(spectrum, deconParams).ToList()); + Assert.Throws(() => _ = Deconvoluter.Deconvolute(scan, deconParams).ToList()); } @@ -247,7 +389,8 @@ public static void TestExampleNewDeconvolutionInDeconvoluter() public static void Test_MsDataScan_GetIsolatedMassesAndCharges() { // get scan - string filePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "DataFiles", "GUACUG_NegativeMode_Sliced.mzML"); + string filePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "DataFiles", + "GUACUG_NegativeMode_Sliced.mzML"); var dataFile = MsDataFileReader.GetDataFile(filePath); var precursorScan = dataFile.GetOneBasedScan(1); var fragmentationScan = dataFile.GetOneBasedScan(2); @@ -267,5 +410,126 @@ public static void Test_MsDataScan_GetIsolatedMassesAndCharges() ms2Result = fragmentationScan.GetIsolatedMassesAndCharges(precursorScan, deconParams).ToList(); Assert.That(ms2Result.Count, Is.EqualTo(1)); } + + [Test] + public void NeutralMassSpectrum_Deconvolute_AllInRange() + { + // Arrange + var xArray = new[] { 260.774188159546, 391.660998843979 }; + var yArray = new[] { 1000.0, 1.0 }; + var charges = new[] { 1, 1 }; + var spectrum = new NeutralMassSpectrum(xArray, yArray, charges, false); + var deconvolutionParameters = new ClassicDeconvolutionParameters(1, 60, 20, 2); + var rangeToGetPeaksFrom = new MzRange(260.0, 400.0); + + // Act + var result = Deconvoluter.Deconvolute(spectrum, deconvolutionParameters, rangeToGetPeaksFrom).ToList(); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.Count(), Is.EqualTo(2)); + + for (int i = 0; i < result.Count(); i++) + { + Assert.That(result[i].MonoisotopicMass, Is.EqualTo(xArray[i])); + Assert.That(result[i].TotalIntensity, Is.EqualTo(yArray[i])); + Assert.That(result[i].Peaks.Count, Is.EqualTo(1)); + Assert.That(result[i].Peaks.First().mz, Is.EqualTo(xArray[i].ToMz(charges[i]))); + Assert.That(result[i].Peaks.First().intensity, Is.EqualTo(yArray[i])); + Assert.That(result[i].Charge, Is.EqualTo(charges[i])); + } + } + + + [Test] + public void NeutralMassSpectrum_Deconvolute_AllInRange_Charged() + { + // Arrange + var xArray = new[] { 260.774188159546, 391.660998843979 }; + var yArray = new[] { 1000.0, 1.0 }; + var charges = new[] { 3, 3 }; + var spectrum = new NeutralMassSpectrum(xArray, yArray, charges, false); + var deconvolutionParameters = new ClassicDeconvolutionParameters(1, 60, 20, 2); + var rangeToGetPeaksFrom = new MzRange(00, 200.0); + + // Act + var result = Deconvoluter.Deconvolute(spectrum, deconvolutionParameters, rangeToGetPeaksFrom).ToList(); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.Count(), Is.EqualTo(2)); + + for (int i = 0; i < result.Count(); i++) + { + Assert.That(result[i].MonoisotopicMass, Is.EqualTo(xArray[i])); + Assert.That(result[i].TotalIntensity, Is.EqualTo(yArray[i])); + Assert.That(result[i].Peaks.Count, Is.EqualTo(1)); + Assert.That(result[i].Peaks.First().mz, Is.EqualTo(xArray[i].ToMz(charges[i]))); + Assert.That(result[i].Peaks.First().intensity, Is.EqualTo(yArray[i])); + Assert.That(result[i].Charge, Is.EqualTo(charges[i])); + } + } + + [Test] + public void NeutralMassSpectrum_Deconvolute_SomeInRange() + { + // Arrange + var xArray = new[] { 260.774188159546, 391.660998843979 }; + var yArray = new[] { 1000.0, 1.0 }; + var charges = new[] { 1, 1 }; + var spectrum = new NeutralMassSpectrum(xArray, yArray, charges, false); + var deconvolutionParameters = new ClassicDeconvolutionParameters(1, 60, 20, 2); + var rangeToGetPeaksFrom = new MzRange(260.0, 300.0); + + // Act + var result = Deconvoluter.Deconvolute(spectrum, deconvolutionParameters, rangeToGetPeaksFrom).ToList(); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.Count(), Is.EqualTo(1)); + + for (int i = 0; i < result.Count(); i++) + { + Assert.That(result[i].MonoisotopicMass, Is.EqualTo(xArray[i])); + Assert.That(result[i].TotalIntensity, Is.EqualTo(yArray[i])); + Assert.That(result[i].Peaks.Count, Is.EqualTo(1)); + Assert.That(result[i].Peaks.First().mz, Is.EqualTo(xArray[i].ToMz(charges[i]))); + Assert.That(result[i].Peaks.First().intensity, Is.EqualTo(yArray[i])); + Assert.That(result[i].Charge, Is.EqualTo(charges[i])); + } + } + + [Test] + public void NeutralMassSpectrum_Deconvolute_SomeInRange_Charged() + { + // Arrange + var xArray = new[] { 260.774188159546, 391.660998843979 }; + var yArray = new[] { 1000.0, 1.0 }; + var charges = new[] { 1, 20 }; + var spectrum = new NeutralMassSpectrum(xArray, yArray, charges, false); + var deconvolutionParameters = new ClassicDeconvolutionParameters(1, 60, 20, 2); + var rangeToGetPeaksFrom = new MzRange(260.0, 300.0); + + // Act + var result = Deconvoluter.Deconvolute(spectrum, deconvolutionParameters, rangeToGetPeaksFrom).ToList(); + + // Assert + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.InstanceOf>()); + Assert.That(result.Count(), Is.EqualTo(1)); + + for (int i = 0; i < result.Count(); i++) + { + Assert.That(result[i].MonoisotopicMass, Is.EqualTo(xArray[i])); + Assert.That(result[i].TotalIntensity, Is.EqualTo(yArray[i])); + Assert.That(result[i].Peaks.Count, Is.EqualTo(1)); + Assert.That(result[i].Peaks.First().mz, Is.EqualTo(xArray[i].ToMz(charges[i]))); + Assert.That(result[i].Peaks.First().intensity, Is.EqualTo(yArray[i])); + Assert.That(result[i].Charge, Is.EqualTo(charges[i])); + } + } } -} \ No newline at end of file +} diff --git a/mzLib/Test/TestSpectra.cs b/mzLib/Test/TestSpectra.cs index e33d9adb1..fec83ba8b 100644 --- a/mzLib/Test/TestSpectra.cs +++ b/mzLib/Test/TestSpectra.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Chemistry; using Assert = NUnit.Framework.Legacy.ClassicAssert; using Stopwatch = System.Diagnostics.Stopwatch; @@ -342,5 +343,111 @@ public void TestEqualsAndHashCode() Assert.That(!_mzSpectrumA.Equals(2)); Assert.That(!_mzSpectrumA.Equals((object)2)); } + + + [Test] + public void NeutralMassSpectrum_Constructor_ValidArguments_InitializesProperties() + { + double[] monoisotopicMasses = { 100.0, 200.0, 300.0 }; + double[] intensities = { 0.5, 0.8, 1.0 }; + int[] charges = { 1, 2, 3 }; + + var spectrum = new NeutralMassSpectrum(monoisotopicMasses, intensities, charges, true); + + Assert.That(monoisotopicMasses.Length, Is.EqualTo(spectrum.XArray.Length)); + Assert.That(intensities.Length, Is.EqualTo(spectrum.YArray.Length)); + Assert.That(charges.Length, Is.EqualTo(spectrum.Charges.Length)); + } + + [Test] + public void NeutralMassSpectrum_Constructor_InvalidArguments_ThrowsArgumentException() + { + double[] monoisotopicMasses = { 100.0, 200.0, 300.0 }; + double[] intensities = { 0.5, 0.8 }; + int[] charges = { 1, 2, 3 }; + bool shouldCopy = true; + + Assert.Throws(() => new NeutralMassSpectrum(monoisotopicMasses, intensities, charges, shouldCopy)); + } + + [Test] + public void NeutralMassSpectrum_MzPeak() + { + double[] monoisotopicMasses = { 100.0, 200.0, 300.0 }; + double[] intensities = { 0.5, 0.8, 1.0 }; + int[] charges = { 1, 2, 3 }; + var spectrum = new NeutralMassSpectrum(monoisotopicMasses, intensities, charges, true); + + + var peak = spectrum.Extract(50, 210).ToArray(); + Assert.That(peak.Length, Is.EqualTo(2)); + + for (int i = 0; i < peak.Length; i++) + { + double mono = monoisotopicMasses[i]; + int charge = charges[i]; + double intensity = intensities[i]; + double mz = mono.ToMz(charge); + + Assert.That(peak[i].Mz, Is.EqualTo(mz)); + Assert.That(peak[i].Intensity, Is.EqualTo(intensity)); + } + } + + [Test] + public void NeutralMassSpectrum_MzRange() + { + double[] monoisotopicMasses = { 100.0, 200.0, 300.0 }; + double[] intensities = { 0.5, 0.8, 1.0 }; + int[] charges = { 1, 2, 3 }; + var spectrum = new NeutralMassSpectrum(monoisotopicMasses, intensities, charges, true); + + + var peak = spectrum.Extract(50, 2100).ToArray(); + Assert.That(peak.Length, Is.EqualTo(3)); + var minPeak = peak.MinBy(p => p.Mz); + var maxPeak = peak.MaxBy(p => p.Mz); + + Assert.That(minPeak.Mz, Is.EqualTo(spectrum.Range.Minimum)); + Assert.That(minPeak.Mz, Is.EqualTo(spectrum.FirstX)); + Assert.That(maxPeak.Mz, Is.EqualTo(spectrum.Range.Maximum)); + Assert.That(maxPeak.Mz, Is.EqualTo(spectrum.LastX)); + + for (int i = 0; i < peak.Length; i++) + { + double mono = monoisotopicMasses[i]; + int charge = charges[i]; + double intensity = intensities[i]; + double mz = mono.ToMz(charge); + + Assert.That(peak[i].Mz, Is.EqualTo(mz)); + Assert.That(peak[i].Intensity, Is.EqualTo(intensity)); + } + } + + [Test] + public void NeutralMassSpectrum_Constructor_ValidArguments_InitializesCharges() + { + // Arrange + double[,] monoisotopicMassesIntensities = new double[,] { { 100.0, 200.0 }, { 300.0, 400.0 } }; + int[] charges = new int[] { 1, 2 }; + + // Act + var spectrum = new NeutralMassSpectrum(monoisotopicMassesIntensities, charges); + + // Assert + Assert.AreEqual(charges, spectrum.Charges); + } + + [Test] + public void NeutralMassSpectrum_Constructor2_InvalidArguments_ThrowsArgumentException() + { + // Arrange + double[,] monoisotopicMassesIntensities = new double[,] { { 100.0, 200.0 }, { 300.0, 400.0 } }; + int[] charges = new int[] { 1, 2, 3 }; + + // Act & Assert + Assert.Throws(() => new NeutralMassSpectrum(monoisotopicMassesIntensities, charges)); + } } } \ No newline at end of file diff --git a/mzLib/mzLib.nuspec b/mzLib/mzLib.nuspec index 626ecf12f..d97e3a5ce 100644 --- a/mzLib/mzLib.nuspec +++ b/mzLib/mzLib.nuspec @@ -2,7 +2,7 @@ mzLib - 1.0.547 + 5.2.35 mzLib Stef S. Stef S. @@ -42,6 +42,10 @@ + + + + @@ -64,6 +68,10 @@ + + + + @@ -81,4 +89,4 @@ - + \ No newline at end of file