From bc1b6a9a7ad32c5355fb8f3e9bd1a0a57aacfbab Mon Sep 17 00:00:00 2001 From: nbollis Date: Thu, 10 Oct 2024 23:11:24 -0500 Subject: [PATCH 01/10] began neutral mz spectrum --- .../MzSpectra/IsotopicEnvelope.cs | 8 ++----- .../MzSpectra/NeutralMzSpectrum.cs | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 mzLib/MassSpectrometry/MzSpectra/NeutralMzSpectrum.cs diff --git a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs index 7e42426b1..e0933d901 100644 --- a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs +++ b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Chemistry; +using System.Runtime.InteropServices; namespace MassSpectrometry { @@ -26,7 +27,7 @@ public IsotopicEnvelope(List<(double mz, double intensity)> bestListOfPeaks, dou { Peaks = bestListOfPeaks; MonoisotopicMass = bestMonoisotopicMass; - MostAbundantObservedIsotopicMass = GetMostAbundantObservedIsotopicMass(bestListOfPeaks, bestChargeState); + MostAbundantObservedIsotopicMass = bestListOfPeaks.MaxBy(p => p.intensity).mz * Math.Abs(bestChargeState); Charge = bestChargeState; TotalIntensity = bestTotalIntensity; StDev = bestStDev; @@ -34,11 +35,6 @@ public IsotopicEnvelope(List<(double mz, double intensity)> bestListOfPeaks, dou Score = ScoreIsotopeEnvelope(); } - public double GetMostAbundantObservedIsotopicMass(List<(double mz, double intensity)> peaks, int charge) - { - return peaks.MaxBy(p => p.intensity).mz * Math.Abs(charge); - } - public override string ToString() { return Charge + "\t" + Peaks[0].mz.ToString("G8") + "\t" + Peaks.Count + "\t" + TotalIntensity; diff --git a/mzLib/MassSpectrometry/MzSpectra/NeutralMzSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/NeutralMzSpectrum.cs new file mode 100644 index 000000000..5ad66fb53 --- /dev/null +++ b/mzLib/MassSpectrometry/MzSpectra/NeutralMzSpectrum.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MassSpectrometry.MzSpectra +{ + public class NeutralMzSpectrum : MzSpectrum + { + public NeutralMzSpectrum(double[,] mzintensities) : base(mzintensities) + { + } + + public NeutralMzSpectrum(double[] monoisotopicMasses, double[] intensities, int[] charges, bool shouldCopy) + : base(monoisotopicMasses, intensities, shouldCopy) + { + if (monoisotopicMasses.Length != intensities.Length || monoisotopicMasses.Length != charges.Length) + throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same."); + + } + } +} From a8bba374cb48b32f7edc640a238e5b7922784fe9 Mon Sep 17 00:00:00 2001 From: Nic Bollis Date: Fri, 11 Oct 2024 00:28:08 -0500 Subject: [PATCH 02/10] Refactor visibility and clean up deconvolution code Changed `ClassicDeconvolutionAlgorithm`, `DeconvolutionAlgorithm`, and `ExampleNewDeconvolutionAlgorithmTemplate` classes and their members from `public` to `internal` to restrict visibility within the assembly. Added summary comment to `DeconvolutionAlgorithm` class. Refactored `Deconvoluter` class to remove unnecessary `using` directives and simplify the `Deconvolute` method by removing switch-case logic. Updated `IsotopicEnvelope` class by removing `MassIndex` and `StDev` properties, and modified constructor and `ScoreIsotopeEnvelope` method accordingly. Updated `MzSpectrum` class to use `StandardDeviation` extension method from `Easy.Common.Extensions`. Removed various unnecessary `using` directives from multiple files. --- .../ClassicDeconvolutionAlgorithm.cs | 11 +++---- .../Algorithms/DeconvolutionAlgorithm.cs | 5 +++- ...xampleNewDeconvolutionAlgorithmTemplate.cs | 9 ++---- .../Deconvolution/Deconvoluter.cs | 29 +++---------------- .../MzSpectra/IsotopicEnvelope.cs | 13 +++------ .../MassSpectrometry/MzSpectra/MzSpectrum.cs | 2 +- 6 files changed, 20 insertions(+), 49 deletions(-) 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..1bb6bf523 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Algorithms/DeconvolutionAlgorithm.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Algorithms/DeconvolutionAlgorithm.cs @@ -8,6 +8,9 @@ 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 +82,6 @@ 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/Deconvoluter.cs b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs index d419561f2..3656ac669 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Easy.Common.Extensions; -using Easy.Common.Interfaces; +using System.Collections.Generic; using MzLibUtil; namespace MassSpectrometry @@ -30,24 +24,9 @@ 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 + + return Deconvolute(scan.MassSpectrum, deconvolutionParameters, rangeToGetPeaksFrom); } diff --git a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs index e0933d901..1ba34539d 100644 --- a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs +++ b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using Chemistry; -using System.Runtime.InteropServices; namespace MassSpectrometry { @@ -18,21 +17,17 @@ public class IsotopicEnvelope : IHasMass public double MostAbundantObservedIsotopicMass { get; private set; } public readonly int Charge; public readonly double TotalIntensity; - public readonly double StDev; - public readonly int MassIndex; public double Score { get; private set; } - public IsotopicEnvelope(List<(double mz, double intensity)> bestListOfPeaks, double bestMonoisotopicMass, int bestChargeState, double bestTotalIntensity, double bestStDev, int bestMassIndex) + public IsotopicEnvelope(List<(double mz, double intensity)> bestListOfPeaks, double bestMonoisotopicMass, int bestChargeState, double bestTotalIntensity, double bestStDev) { Peaks = bestListOfPeaks; MonoisotopicMass = bestMonoisotopicMass; MostAbundantObservedIsotopicMass = bestListOfPeaks.MaxBy(p => p.intensity).mz * Math.Abs(bestChargeState); Charge = bestChargeState; TotalIntensity = bestTotalIntensity; - StDev = bestStDev; - MassIndex = bestMassIndex; - Score = ScoreIsotopeEnvelope(); + Score = ScoreIsotopeEnvelope(bestStDev); } public override string ToString() @@ -40,10 +35,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; } diff --git a/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs index 2e9fcc7a4..a8c9181ba 100644 --- a/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs +++ b/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs @@ -373,7 +373,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")] From 455f3c08fba208654d4c7e0d4849e985f0f2b061 Mon Sep 17 00:00:00 2001 From: Nic Bollis Date: Fri, 11 Oct 2024 00:47:44 -0500 Subject: [PATCH 03/10] Finish NeutralMassSpectrum - Added `InternalsVisibleTo` entries for "Development" and "Test" in `MassSpectrometry.csproj`. - Changed `MostAbundantObservedIsotopicMass` to `internal` in `IsotopicEnvelope.cs`. - Added a new constructor to `IsotopicEnvelope` with monoisotopic mass, intensity, and charge. - Added XML documentation and changed `GeneratePeak` to `protected virtual` in `MzSpectrum.cs`. - Removed unused `using` directives in `MzSpectrum.cs` and `NeutralMzSpectrum.cs`. - Modified `NeutralMzSpectrum` constructor to validate array lengths. - Added `Charges` property to `NeutralMzSpectrum` and initialized it in the constructor. - Overrode `GeneratePeak` in `NeutralMzSpectrum` to convert to a charged spectrum using `Charges`. --- .../MassSpectrometry/MassSpectrometry.csproj | 4 +++ .../MzSpectra/IsotopicEnvelope.cs | 18 ++++++++-- .../MassSpectrometry/MzSpectra/MzSpectrum.cs | 9 +++-- .../MzSpectra/NeutralMassSpectrum.cs | 34 +++++++++++++++++++ .../MzSpectra/NeutralMzSpectrum.cs | 23 ------------- 5 files changed, 60 insertions(+), 28 deletions(-) create mode 100644 mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs delete mode 100644 mzLib/MassSpectrometry/MzSpectra/NeutralMzSpectrum.cs diff --git a/mzLib/MassSpectrometry/MassSpectrometry.csproj b/mzLib/MassSpectrometry/MassSpectrometry.csproj index 6af63b9e4..9d8e74edc 100644 --- a/mzLib/MassSpectrometry/MassSpectrometry.csproj +++ b/mzLib/MassSpectrometry/MassSpectrometry.csproj @@ -20,4 +20,8 @@ + + + + diff --git a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs index 1ba34539d..a76b108b2 100644 --- a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs +++ b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs @@ -14,12 +14,15 @@ 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 double Score { get; private set; } + /// + /// 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; @@ -30,6 +33,18 @@ public IsotopicEnvelope(List<(double mz, double intensity)> bestListOfPeaks, dou 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; + } + public override string ToString() { return Charge + "\t" + Peaks[0].mz.ToString("G8") + "\t" + Peaks.Count + "\t" + TotalIntensity; @@ -51,6 +66,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 a8c9181ba..c768afb3c 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 { @@ -796,7 +794,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..bfa89d7d5 --- /dev/null +++ b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs @@ -0,0 +1,34 @@ +using System; +using Chemistry; + +namespace MassSpectrometry.MzSpectra +{ + public class NeutralMzSpectrum : MzSpectrum + { + public int[] Charges { get; init; } + public NeutralMzSpectrum(double[,] monoisotopicMassesIntensities, int[] charges) : base(monoisotopicMassesIntensities) + { + if (monoisotopicMassesIntensities.Length != charges.Length || monoisotopicMassesIntensities.Length != monoisotopicMassesIntensities.Rank) + throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same."); + + Charges = charges; + } + + public NeutralMzSpectrum(double[] monoisotopicMasses, double[] intensities, int[] charges, bool shouldCopy) + : base(monoisotopicMasses, intensities, shouldCopy) + { + if (monoisotopicMasses.Length != intensities.Length || monoisotopicMasses.Length != charges.Length) + throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same."); + + Charges = charges; + } + + /// + /// 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/MassSpectrometry/MzSpectra/NeutralMzSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/NeutralMzSpectrum.cs deleted file mode 100644 index 5ad66fb53..000000000 --- a/mzLib/MassSpectrometry/MzSpectra/NeutralMzSpectrum.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MassSpectrometry.MzSpectra -{ - public class NeutralMzSpectrum : MzSpectrum - { - public NeutralMzSpectrum(double[,] mzintensities) : base(mzintensities) - { - } - - public NeutralMzSpectrum(double[] monoisotopicMasses, double[] intensities, int[] charges, bool shouldCopy) - : base(monoisotopicMasses, intensities, shouldCopy) - { - if (monoisotopicMasses.Length != intensities.Length || monoisotopicMasses.Length != charges.Length) - throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same."); - - } - } -} From 0dd9e5285627669c4c3cdbab09bf044af0a9d613 Mon Sep 17 00:00:00 2001 From: Nic Bollis Date: Fri, 11 Oct 2024 01:05:14 -0500 Subject: [PATCH 04/10] Refactor Deconvoluter and rename NeutralMzSpectrum Added necessary using directives in Deconvoluter.cs. Modified Deconvoluter class for short-circuit deconvolution. Removed redundant lines in Deconvoluter.cs. Renamed NeutralMzSpectrum to NeutralMassSpectrum. Updated constructor and references accordingly. --- .../Deconvolution/Deconvoluter.cs | 20 ++++++++++++++++--- .../MzSpectra/NeutralMassSpectrum.cs | 8 ++++---- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs index 3656ac669..7360cff25 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Chemistry; using MzLibUtil; namespace MassSpectrometry @@ -28,8 +29,6 @@ public static IEnumerable Deconvolute(MsDataScan scan, return Deconvolute(scan.MassSpectrum, deconvolutionParameters, rangeToGetPeaksFrom); } - - /// /// Static deconvolution of an MzSpectrum that does not require Deconvoluter construction @@ -58,7 +57,22 @@ public static IEnumerable Deconvolute(MzSpectrum spectrum, default: throw new MzLibException("DeconvolutionType not yet supported"); } - return deconAlgorithm.Deconvolute(spectrum, rangeToGetPeaksFrom); + // 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)) + yield return isotopicEnvelope; + } } } } diff --git a/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs index bfa89d7d5..c3c902846 100644 --- a/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs +++ b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs @@ -1,12 +1,12 @@ using System; using Chemistry; -namespace MassSpectrometry.MzSpectra +namespace MassSpectrometry { - public class NeutralMzSpectrum : MzSpectrum + public class NeutralMassSpectrum : MzSpectrum { public int[] Charges { get; init; } - public NeutralMzSpectrum(double[,] monoisotopicMassesIntensities, int[] charges) : base(monoisotopicMassesIntensities) + public NeutralMassSpectrum(double[,] monoisotopicMassesIntensities, int[] charges) : base(monoisotopicMassesIntensities) { if (monoisotopicMassesIntensities.Length != charges.Length || monoisotopicMassesIntensities.Length != monoisotopicMassesIntensities.Rank) throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same."); @@ -14,7 +14,7 @@ public NeutralMzSpectrum(double[,] monoisotopicMassesIntensities, int[] charges) Charges = charges; } - public NeutralMzSpectrum(double[] monoisotopicMasses, double[] intensities, int[] charges, bool shouldCopy) + public NeutralMassSpectrum(double[] monoisotopicMasses, double[] intensities, int[] charges, bool shouldCopy) : base(monoisotopicMasses, intensities, shouldCopy) { if (monoisotopicMasses.Length != intensities.Length || monoisotopicMasses.Length != charges.Length) From 09cefc7d40e9685ba03f16618f24c3d1c8e00e08 Mon Sep 17 00:00:00 2001 From: Nic Bollis Date: Fri, 11 Oct 2024 02:07:25 -0500 Subject: [PATCH 05/10] added neutral mass file bool --- mzLib/MassSpectrometry/MsDataFile.cs | 4 +--- mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mzLib/MassSpectrometry/MsDataFile.cs b/mzLib/MassSpectrometry/MsDataFile.cs index 7242f2cf7..febc1aa32 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 /// @@ -35,6 +32,7 @@ public abstract class MsDataFile : IEnumerable public SourceFile SourceFile { get; set; } public int NumSpectra => Scans.Length; public string FilePath { get; } + public virtual bool IsNeutralMassFile { get; protected set; } = false; protected MsDataFile(int numSpectra, SourceFile sourceFile) { diff --git a/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs index c3c902846..1ee30981b 100644 --- a/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs +++ b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Chemistry; namespace MassSpectrometry From 72d8202cf7c057748b05895a4d6fd0ed1b1cfbff Mon Sep 17 00:00:00 2001 From: nbollis Date: Fri, 11 Oct 2024 18:52:55 -0500 Subject: [PATCH 06/10] Adjsuted and tested neutral mass spectra --- mzLib/MassSpectrometry/MsDataFile.cs | 1 - .../MzSpectra/IsotopicEnvelope.cs | 1 + .../MzSpectra/NeutralMassSpectrum.cs | 4 +- mzLib/Test/TestSpectra.cs | 76 +++++++++++++++++++ mzLib/mzLib.nuspec | 4 +- 5 files changed, 81 insertions(+), 5 deletions(-) diff --git a/mzLib/MassSpectrometry/MsDataFile.cs b/mzLib/MassSpectrometry/MsDataFile.cs index febc1aa32..3e88fcfef 100644 --- a/mzLib/MassSpectrometry/MsDataFile.cs +++ b/mzLib/MassSpectrometry/MsDataFile.cs @@ -32,7 +32,6 @@ public abstract class MsDataFile : IEnumerable public SourceFile SourceFile { get; set; } public int NumSpectra => Scans.Length; public string FilePath { get; } - public virtual bool IsNeutralMassFile { get; protected set; } = false; protected MsDataFile(int numSpectra, SourceFile sourceFile) { diff --git a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs index a76b108b2..3b2ab3d1b 100644 --- a/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs +++ b/mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs @@ -43,6 +43,7 @@ public IsotopicEnvelope(double monoisotopicMass, double intensity, int charge) Charge = charge; TotalIntensity = intensity; Score = double.MaxValue; + Peaks = [(monoisotopicMass.ToMz(charge), intensity)]; } public override string ToString() diff --git a/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs index 1ee30981b..e2fdbb34b 100644 --- a/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs +++ b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs @@ -10,7 +10,7 @@ public class NeutralMassSpectrum : MzSpectrum public int[] Charges { get; init; } public NeutralMassSpectrum(double[,] monoisotopicMassesIntensities, int[] charges) : base(monoisotopicMassesIntensities) { - if (monoisotopicMassesIntensities.Length != charges.Length || monoisotopicMassesIntensities.Length != monoisotopicMassesIntensities.Rank) + if (monoisotopicMassesIntensities.GetLength(0) != charges.Length) throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same."); Charges = charges; @@ -19,7 +19,7 @@ public NeutralMassSpectrum(double[,] monoisotopicMassesIntensities, int[] charge public NeutralMassSpectrum(double[] monoisotopicMasses, double[] intensities, int[] charges, bool shouldCopy) : base(monoisotopicMasses, intensities, shouldCopy) { - if (monoisotopicMasses.Length != intensities.Length || monoisotopicMasses.Length != charges.Length) + 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; diff --git a/mzLib/Test/TestSpectra.cs b/mzLib/Test/TestSpectra.cs index e33d9adb1..d65e58db8 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,80 @@ 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_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..6fab09939 100644 --- a/mzLib/mzLib.nuspec +++ b/mzLib/mzLib.nuspec @@ -2,7 +2,7 @@ mzLib - 1.0.547 + 5.2.3 mzLib Stef S. Stef S. @@ -81,4 +81,4 @@ - + \ No newline at end of file From 42778141b0d7b1c2a3f44c3ff97755ff970ab6d9 Mon Sep 17 00:00:00 2001 From: nbollis Date: Fri, 11 Oct 2024 19:14:10 -0500 Subject: [PATCH 07/10] Refactor Deconvoluter and add new tests Refactored Deconvoluter.cs to use a foreach loop for yielding IsotopicEnvelopes. Reformatted multiple test methods in TestDeconvolution.cs for better readability. Added new test methods to validate Deconvolute with NeutralMassSpectrum, ensuring correct processing of spectra with various charge states and ranges. --- .../Deconvolution/Deconvoluter.cs | 5 +- mzLib/Test/TestDeconvolution.cs | 216 +++++++++++++++--- 2 files changed, 184 insertions(+), 37 deletions(-) diff --git a/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs index 7360cff25..773422862 100644 --- a/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs +++ b/mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs @@ -26,8 +26,9 @@ public static IEnumerable Deconvolute(MsDataScan scan, DeconvolutionParameters deconvolutionParameters, MzRange rangeToGetPeaksFrom = null) { // set any specific deconvolution parameters found only in the MsDataScan - - return Deconvolute(scan.MassSpectrum, deconvolutionParameters, rangeToGetPeaksFrom); + + foreach (var isotopicEnvelope in Deconvolute(scan.MassSpectrum, deconvolutionParameters, rangeToGetPeaksFrom)) + yield return isotopicEnvelope; } /// diff --git a/mzLib/Test/TestDeconvolution.cs b/mzLib/Test/TestDeconvolution.cs index 8f6cbb9ee..7ee3f59a2 100644 --- a/mzLib/Test/TestDeconvolution.cs +++ b/mzLib/Test/TestDeconvolution.cs @@ -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.AreEqual(lie2.Select(p => p.MostAbundantObservedIsotopicMass), + lie3.Select(p => p.MostAbundantObservedIsotopicMass)); } #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,7 +208,8 @@ 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.AreEqual(lie2.Select(p => p.MostAbundantObservedIsotopicMass), + lie3.Select(p => p.MostAbundantObservedIsotopicMass)); } #endregion @@ -225,21 +247,22 @@ public void TestNegativeModeClassicDeconvolution(double expectedMz, int expected 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)); + NUnit.Framework.Assert.Throws(() => _ = Deconvoluter.Deconvolute(spectrum, deconParams).ToList()); + NUnit.Framework.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)); + NUnit.Framework.Assert.Throws(() => _ = Deconvoluter.Deconvolute(spectrum, deconParams).ToList()); + NUnit.Framework.Assert.Throws(() => _ = Deconvoluter.Deconvolute(scan, deconParams).ToList()); } @@ -247,14 +270,15 @@ 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); // set up deconvolution DeconvolutionParameters deconParams = new ClassicDeconvolutionParameters(-10, -1, 20, 3, Polarity.Negative); - + // get isolated masses and charges on an MS1 scan. This means the isolation window is null. var ms1Result = precursorScan.GetIsolatedMassesAndCharges(precursorScan.MassSpectrum, deconParams).ToList(); Assert.That(ms1Result.Count, Is.EqualTo(0)); @@ -262,10 +286,132 @@ public static void Test_MsDataScan_GetIsolatedMassesAndCharges() Assert.That(ms1Result.Count, Is.EqualTo(0)); // get isolated masses and charges on an MS2 scan. This should work correctly - var ms2Result = fragmentationScan.GetIsolatedMassesAndCharges(precursorScan.MassSpectrum, deconParams).ToList(); + var ms2Result = fragmentationScan.GetIsolatedMassesAndCharges(precursorScan.MassSpectrum, deconParams) + .ToList(); Assert.That(ms2Result.Count, Is.EqualTo(1)); 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.IsNotNull(result); + Assert.IsInstanceOf>(result); + Assert.AreEqual(2, result.Count()); + + 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.IsNotNull(result); + Assert.IsInstanceOf>(result); + Assert.AreEqual(2, result.Count()); + + 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.IsNotNull(result); + Assert.IsInstanceOf>(result); + Assert.AreEqual(1, result.Count()); + + 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.IsNotNull(result); + Assert.IsInstanceOf>(result); + Assert.AreEqual(1, result.Count()); + + 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 +} From 6c124c55ee2204c88e17730361d4d40722d942d5 Mon Sep 17 00:00:00 2001 From: nbollis Date: Fri, 11 Oct 2024 19:58:54 -0500 Subject: [PATCH 08/10] Make FirstX and LastX properties virtual; update tests - Changed FirstX and LastX properties in MzSpectrum to virtual. - Included MzLibUtil namespace in NeutralMassSpectrum class. - Updated NeutralMassSpectrum constructor to set FirstX and LastX. - Overrode FirstX and LastX in NeutralMassSpectrum class. - Added test NeutralMassSpectrum_MzRange to validate m/z range. --- .../MassSpectrometry/MzSpectra/MzSpectrum.cs | 4 +-- .../MzSpectra/NeutralMassSpectrum.cs | 33 +++++++++++++++++-- mzLib/Test/TestSpectra.cs | 31 +++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs index c768afb3c..88a97d1ac 100644 --- a/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs +++ b/mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs @@ -124,7 +124,7 @@ public MzRange Range } } - public double? FirstX + public virtual double? FirstX { get { @@ -136,7 +136,7 @@ public double? FirstX } } - public double? LastX + public virtual double? LastX { get { diff --git a/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs index e2fdbb34b..dcb5d7d2b 100644 --- a/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs +++ b/mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using Chemistry; namespace MassSpectrometry @@ -14,6 +12,20 @@ public NeutralMassSpectrum(double[,] monoisotopicMassesIntensities, int[] charge 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) @@ -23,8 +35,25 @@ public NeutralMassSpectrum(double[] monoisotopicMasses, double[] intensities, in 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 /// diff --git a/mzLib/Test/TestSpectra.cs b/mzLib/Test/TestSpectra.cs index d65e58db8..fec83ba8b 100644 --- a/mzLib/Test/TestSpectra.cs +++ b/mzLib/Test/TestSpectra.cs @@ -394,6 +394,37 @@ public void NeutralMassSpectrum_MzPeak() } } + [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() { From f049ee45a0ae83980a8f85607640a49d8847b076 Mon Sep 17 00:00:00 2001 From: nbollis Date: Fri, 11 Oct 2024 20:34:56 -0500 Subject: [PATCH 09/10] fixed nuspec --- mzLib/mzLib.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mzLib/mzLib.nuspec b/mzLib/mzLib.nuspec index 6fab09939..13da774e5 100644 --- a/mzLib/mzLib.nuspec +++ b/mzLib/mzLib.nuspec @@ -2,7 +2,7 @@ mzLib - 5.2.3 + 1.0.547 mzLib Stef S. Stef S. From 9fb3da3d6d91b51fff4a5289aa6f22de83043414 Mon Sep 17 00:00:00 2001 From: Nic Bollis Date: Fri, 11 Oct 2024 20:58:39 -0500 Subject: [PATCH 10/10] Update mzLib.nuspec --- mzLib/mzLib.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mzLib/mzLib.nuspec b/mzLib/mzLib.nuspec index 13da774e5..626ecf12f 100644 --- a/mzLib/mzLib.nuspec +++ b/mzLib/mzLib.nuspec @@ -81,4 +81,4 @@ - \ No newline at end of file +