diff --git a/mzLib/Development/Deconvolution/StandardDeconvolutionTest.cs b/mzLib/Development/DeconvolutionDevelopment/StandardDeconvolutionTest.cs similarity index 56% rename from mzLib/Development/Deconvolution/StandardDeconvolutionTest.cs rename to mzLib/Development/DeconvolutionDevelopment/StandardDeconvolutionTest.cs index ea02b6dd4..002e0f9b5 100644 --- a/mzLib/Development/Deconvolution/StandardDeconvolutionTest.cs +++ b/mzLib/Development/DeconvolutionDevelopment/StandardDeconvolutionTest.cs @@ -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/DeconvolutionAlgorithm.cs b/mzLib/MassSpectrometry/Deconvolution/Algorithms/DeconvolutionAlgorithm.cs index 1bb6bf523..efb8cd248 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Algorithms/DeconvolutionAlgorithm.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Algorithms/DeconvolutionAlgorithm.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Chemistry; using MzLibUtil; diff --git a/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecAlgorithm.cs b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecAlgorithm.cs new file mode 100644 index 000000000..6c0dab348 --- /dev/null +++ b/mzLib/MassSpectrometry/Deconvolution/Algorithms/IsoDecAlgorithm.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using MzLibUtil; + +namespace MassSpectrometry +{ + /// + /// Performs deconvolution on a single spectrum or region of spectrum using the Isodec algorithm + /// + /// Isodec only needs to region of interest and does not use surrounding charge states as references. + /// Isodec can report multiple monoisotopic masses for a single peak if enabled by ReportMultipleMonoisos parameter + /// In this case, the resulting isotopic envelopes will have the same precursor ID. + /// + /// + internal class IsoDecAlgorithm : DeconvolutionAlgorithm + { + internal IsoDecAlgorithm(DeconvolutionParameters deconParameters) : base(deconParameters) + { + + } + + /// + /// Struct passed by pointer in memory to the Isodec.dll + /// + [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; + } + + /// + /// Calls the Isodec.dll to perform deconvolution on the given spectrum + /// The Isodec.dll requires three other dll's as dependencies: isogenmass.dll, libmmd.dll, scml_dispmd.dll + /// + /// + /// + /// + /// + /// + /// + /// + + [DllImport("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(); + } + } + + /// + /// Converts the isodec output (MatchedPeak) to IsotopicEnvelope for return + /// + /// + /// + /// + /// + private List ConvertToIsotopicEnvelopes(IsoDecDeconvolutionParameters parameters, MatchedPeak[] matchedpeaks, MzSpectrum spectrum) + { + List result = new List(); + int currentId = 0; + var tolerance = new PpmTolerance(5); + 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], tolerance); + 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/Deconvoluter.cs b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs index 773422862..af32b78f9 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs @@ -4,12 +4,6 @@ namespace MassSpectrometry { - public enum DeconvolutionType - { - ClassicDeconvolution, - ExampleNewDeconvolutionTemplate, - } - /// /// Context class for all deconvolution /// @@ -26,9 +20,7 @@ public static IEnumerable Deconvolute(MsDataScan scan, DeconvolutionParameters deconvolutionParameters, MzRange rangeToGetPeaksFrom = null) { // set any specific deconvolution parameters found only in the MsDataScan - - foreach (var isotopicEnvelope in Deconvolute(scan.MassSpectrum, deconvolutionParameters, rangeToGetPeaksFrom)) - yield return isotopicEnvelope; + return Deconvolute(scan.MassSpectrum, deconvolutionParameters, rangeToGetPeaksFrom); } /// @@ -43,37 +35,53 @@ public static IEnumerable Deconvolute(MzSpectrum spectrum, { rangeToGetPeaksFrom ??= spectrum.Range; + // Short circuit deconvolution if it is called on a neutral mass spectrum + if (spectrum is NeutralMassSpectrum newt) + return DeconvoluteNeutralMassSpectrum(newt, rangeToGetPeaksFrom); + // set deconvolution algorithm - DeconvolutionAlgorithm deconAlgorithm; - switch (deconvolutionParameters.DeconvolutionType) - { - case DeconvolutionType.ClassicDeconvolution: - deconAlgorithm = new ClassicDeconvolutionAlgorithm(deconvolutionParameters); - break; + DeconvolutionAlgorithm deconAlgorithm = CreateAlgorithm(deconvolutionParameters); - case DeconvolutionType.ExampleNewDeconvolutionTemplate: - deconAlgorithm = new ExampleNewDeconvolutionAlgorithmTemplate(deconvolutionParameters); - break; + // Delegate deconvolution to the algorithm + return deconAlgorithm.Deconvolute(spectrum, rangeToGetPeaksFrom); + } - default: throw new MzLibException("DeconvolutionType not yet supported"); - } + /// + /// Factory method to create the correct deconvolution algorithm from the parameters + /// + /// + /// + /// + private static DeconvolutionAlgorithm CreateAlgorithm(DeconvolutionParameters parameters) + { + return parameters.DeconvolutionType switch + { + DeconvolutionType.ClassicDeconvolution => new ClassicDeconvolutionAlgorithm(parameters), + DeconvolutionType.ExampleNewDeconvolutionTemplate => new ExampleNewDeconvolutionAlgorithmTemplate(parameters), + DeconvolutionType.IsoDecDeconvolution => new IsoDecAlgorithm(parameters), + _ => throw new MzLibException("DeconvolutionType not yet supported") + }; + } - // Short circuit deconvolution if it is called on a neutral mass spectrum - if (spectrum is NeutralMassSpectrum newt) + /// + /// Returns all peaks in the neutral mass spectrum as an isotopic envelope with a single peak + /// + /// + /// + /// + private static IEnumerable DeconvoluteNeutralMassSpectrum(NeutralMassSpectrum neutralSpectrum, MzRange range) + { + for (int i = 0; i < neutralSpectrum.XArray.Length; i++) { - for (int i = 0; i < newt.XArray.Length; i++) + double neutralMass = neutralSpectrum.XArray[i]; + double intensity = neutralSpectrum.YArray[i]; + int chargeState = neutralSpectrum.Charges[i]; + + if (range.Contains(neutralMass.ToMz(chargeState))) { - // 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]); + yield return new IsotopicEnvelope(neutralMass, intensity, chargeState); } } - else - { - foreach (var isotopicEnvelope in deconAlgorithm.Deconvolute(spectrum, rangeToGetPeaksFrom)) - yield return isotopicEnvelope; - } } } } 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..649ee9a11 --- /dev/null +++ b/mzLib/MassSpectrometry/Deconvolution/Parameters/IsoDecDeconvolutionParameters.cs @@ -0,0 +1,214 @@ +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; protected 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; protected set; } = (float)1.00727276467; + + /// + /// Use set the -1 isotope as 0 to help force better alignments + /// + public int MinusOneAreasZero { get; protected set; } = 1; + + /// + /// Threshold for isotope distribution. Will remove relative intensities below this. + /// + public float IsotopeThreshold { get; protected 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 9d8e74edc..e662d170d 100644 --- a/mzLib/MassSpectrometry/MassSpectrometry.csproj +++ b/mzLib/MassSpectrometry/MassSpectrometry.csproj @@ -24,4 +24,19 @@ + + + + Always + + + Always + + + Always + + + Always + + diff --git a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs index 3b2ab3d1b..9fc5c05b1 100644 --- a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs +++ b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs @@ -17,6 +17,7 @@ public class IsotopicEnvelope : IHasMass internal double MostAbundantObservedIsotopicMass { get; private set; } public readonly int Charge; public readonly double TotalIntensity; + public readonly int PrecursorId; public double Score { get; private set; } @@ -46,6 +47,26 @@ public IsotopicEnvelope(double monoisotopicMass, double intensity, int charge) Peaks = [(monoisotopicMass.ToMz(charge), intensity)]; } + /// + /// Used for A deconvolution method that calculates its own score. + /// + /// All missed mono products of the same peak will share an ID if enabled in IsoDec + /// + /// + /// + /// + /// + public IsotopicEnvelope(int id, List<(double mz, double intensity)> peaks, double monoisotopicmass, int chargestate, double intensity, double score) + { + 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() { return Charge + "\t" + Peaks[0].mz.ToString("G8") + "\t" + Peaks.Count + "\t" + TotalIntensity; diff --git a/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs index 88a97d1ac..62422cc96 100644 --- a/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs +++ b/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs @@ -609,6 +609,26 @@ public int GetClosestPeakIndex(double x) return XArray.GetClosestIndex(x); } + public List GetPeakIndicesWithinTolerance(double x, Tolerance tolerance) + { + if (XArray.Length == 0) + return []; + + // find min and max allowed + var minX = tolerance.GetMinimumValue(x); + var maxX = tolerance.GetMaximumValue(x); + + // check if min and max are possible to find in this spectrum + if (XArray.First() > maxX || XArray.Last() < minX) + return []; + + // find index closest to extrema + int startingIndex = XArray.GetClosestIndex(minX, ArraySearchOption.Next); + int endIndex = XArray.GetClosestIndex(maxX, ArraySearchOption.Previous); + + return Enumerable.Range(startingIndex, endIndex - startingIndex + 1).ToList(); + } + public void ReplaceXbyApplyingFunction(Func convertor) { for (int i = 0; i < Size; i++) diff --git a/mzLib/MassSpectrometry/isodeclib.dll b/mzLib/MassSpectrometry/isodeclib.dll new file mode 100644 index 000000000..a89f4ad96 Binary files /dev/null and b/mzLib/MassSpectrometry/isodeclib.dll differ diff --git a/mzLib/MassSpectrometry/isogenmass.dll b/mzLib/MassSpectrometry/isogenmass.dll new file mode 100644 index 000000000..072d3f72d Binary files /dev/null and b/mzLib/MassSpectrometry/isogenmass.dll differ diff --git a/mzLib/MassSpectrometry/libmmd.dll b/mzLib/MassSpectrometry/libmmd.dll new file mode 100644 index 000000000..e8544358b Binary files /dev/null and b/mzLib/MassSpectrometry/libmmd.dll differ diff --git a/mzLib/MassSpectrometry/svml_dispmd.dll b/mzLib/MassSpectrometry/svml_dispmd.dll new file mode 100644 index 000000000..147636b82 Binary files /dev/null and b/mzLib/MassSpectrometry/svml_dispmd.dll differ diff --git a/mzLib/Test/TestDeconvolution.cs b/mzLib/Test/TestDeconvolution.cs index 7ee3f59a2..65d6d6c0c 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,6 +11,8 @@ using System.Linq; using Omics.Digestion; using Omics.Modifications; +using Proteomics; +using Proteomics.ProteolyticDigestion; using Test.FileReadingTests; namespace Test @@ -107,8 +106,8 @@ public static void CheckGetMostAbundantObservedIsotopicMass(string peptide, stri //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)); + Assert.That(lie2.Select(p => p.MostAbundantObservedIsotopicMass), + Is.EqualTo(lie3.Select(p => p.MostAbundantObservedIsotopicMass)).Within(.0005)); } #endregion @@ -208,12 +207,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 @@ -239,10 +235,130 @@ 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(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() { @@ -255,14 +371,14 @@ public static void TestExampleNewDeconvolutionInDeconvoluter() dataFile.CloseDynamicConnection(); // test switch statements in Deconvoluter - NUnit.Framework.Assert.Throws(() => _ = Deconvoluter.Deconvolute(spectrum, deconParams).ToList()); - NUnit.Framework.Assert.Throws(() => _ =Deconvoluter.Deconvolute(scan, deconParams).ToList()); + 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); - NUnit.Framework.Assert.Throws(() => _ = Deconvoluter.Deconvolute(spectrum, deconParams).ToList()); - NUnit.Framework.Assert.Throws(() => _ = Deconvoluter.Deconvolute(scan, deconParams).ToList()); + Assert.Throws(() => _ = Deconvoluter.Deconvolute(spectrum, deconParams).ToList()); + Assert.Throws(() => _ = Deconvoluter.Deconvolute(scan, deconParams).ToList()); } @@ -308,9 +424,9 @@ public void NeutralMassSpectrum_Deconvolute_AllInRange() var result = Deconvoluter.Deconvolute(spectrum, deconvolutionParameters, rangeToGetPeaksFrom).ToList(); // Assert - Assert.IsNotNull(result); - Assert.IsInstanceOf>(result); - Assert.AreEqual(2, result.Count()); + 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++) { @@ -339,9 +455,9 @@ public void NeutralMassSpectrum_Deconvolute_AllInRange_Charged() var result = Deconvoluter.Deconvolute(spectrum, deconvolutionParameters, rangeToGetPeaksFrom).ToList(); // Assert - Assert.IsNotNull(result); - Assert.IsInstanceOf>(result); - Assert.AreEqual(2, result.Count()); + 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++) { @@ -369,9 +485,9 @@ public void NeutralMassSpectrum_Deconvolute_SomeInRange() var result = Deconvoluter.Deconvolute(spectrum, deconvolutionParameters, rangeToGetPeaksFrom).ToList(); // Assert - Assert.IsNotNull(result); - Assert.IsInstanceOf>(result); - Assert.AreEqual(1, result.Count()); + 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++) { @@ -399,9 +515,9 @@ public void NeutralMassSpectrum_Deconvolute_SomeInRange_Charged() var result = Deconvoluter.Deconvolute(spectrum, deconvolutionParameters, rangeToGetPeaksFrom).ToList(); // Assert - Assert.IsNotNull(result); - Assert.IsInstanceOf>(result); - Assert.AreEqual(1, result.Count()); + 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++) { diff --git a/mzLib/Test/TestSpectra.cs b/mzLib/Test/TestSpectra.cs index fec83ba8b..22c4b3224 100644 --- a/mzLib/Test/TestSpectra.cs +++ b/mzLib/Test/TestSpectra.cs @@ -344,6 +344,7 @@ public void TestEqualsAndHashCode() Assert.That(!_mzSpectrumA.Equals((object)2)); } + #region Neutral Mass Spectrum [Test] public void NeutralMassSpectrum_Constructor_ValidArguments_InitializesProperties() @@ -449,5 +450,45 @@ public void NeutralMassSpectrum_Constructor2_InvalidArguments_ThrowsArgumentExce // Act & Assert Assert.Throws(() => new NeutralMassSpectrum(monoisotopicMassesIntensities, charges)); } + + #endregion + + [TestCase(50.0, new int[] { })] // Case: Nearest index is zero + [TestCase(101.0, new int[] { 1 })] // Case: Nearest index is within tolerance + [TestCase(102.0, new int[] { 1, 2 })] // Case: Upper and lower bounds within tolerance + [TestCase(104.0, new int[] { 3, 4, 2 })] // Case: Upper and lower bounds within tolerance + [TestCase(105.0, new int[] { 4, 5, 3 })] // Case: Upper and lower bounds within tolerance + [TestCase(106.0, new int[] { 5, 4 })] // Case: Upper and lower bounds with stopping conditions + [TestCase(107.0, new int[] { 5 })] // Case: Upper outside tolerance + [TestCase(200.0, new int[] { })] // Case: Nearest index is outside range + public void TestGetPeakIndicesWithinTolerance(double x, int[] expectedIndices) + { + // Arrange + var xArray = new [] { 99.0, 101.0, 103.0, 104.0, 105.0, 106.0 }; + var tolerance = new AbsoluteTolerance(1); + var testObject = new MzSpectrum(xArray, xArray, false); + + // Act + List result = testObject.GetPeakIndicesWithinTolerance(x, tolerance); + + // Assert + NUnit.Framework.Assert.That(result, Is.EquivalentTo(expectedIndices)); + } + + [Test] + public void TestGetPeakIndicesWithinTolerance_HandlesEmptyXArray_Gracefully() + { + // Arrange + var empty = new double[] { }; // Empty array + var tolerance = new PpmTolerance(10); + var testObject = new MzSpectrum(empty, empty, false); + double x = 103.0; + + // Act + List result = testObject.GetPeakIndicesWithinTolerance(x, tolerance); + + // Assert + NUnit.Framework.Assert.That(result, Is.Empty); + } } } \ No newline at end of file diff --git a/mzLib/mzLib.nuspec b/mzLib/mzLib.nuspec index 3aa393afe..6cc79db4b 100644 --- a/mzLib/mzLib.nuspec +++ b/mzLib/mzLib.nuspec @@ -46,6 +46,10 @@ + + + + @@ -68,6 +72,10 @@ + + + + @@ -85,4 +93,4 @@ - + \ No newline at end of file