Skip to content

Commit

Permalink
Neutral Mass Spectrum (#806)
Browse files Browse the repository at this point in the history
* began neutral mz spectrum

* 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.

* 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`.

* 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.

* added neutral mass file bool

* Adjsuted and tested neutral mass spectra

* 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.

* 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.

* fixed nuspec

* Update mzLib.nuspec

---------

Co-authored-by: Nic Bollis <nbollis@wisc.edu>
  • Loading branch information
nbollis and Nic Bollis authored Oct 29, 2024
1 parent 6c18e9f commit dc20e44
Show file tree
Hide file tree
Showing 11 changed files with 417 additions and 98 deletions.
Original file line number Diff line number Diff line change
@@ -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)
{

}
Expand All @@ -25,7 +22,7 @@ public ClassicDeconvolutionAlgorithm(DeconvolutionParameters deconParameters) :
/// <param name="spectrumToDeconvolute">spectrum to deconvolute</param>
/// <param name="range">Range of peaks to deconvolute</param>
/// <returns></returns>
public override IEnumerable<IsotopicEnvelope> Deconvolute(MzSpectrum spectrumToDeconvolute, MzRange range)
internal override IEnumerable<IsotopicEnvelope> Deconvolute(MzSpectrum spectrumToDeconvolute, MzRange range)
{
var deconParams = DeconvolutionParameters as ClassicDeconvolutionParameters ?? throw new MzLibException("Deconvolution params and algorithm do not match");
spectrum = spectrumToDeconvolute;
Expand Down Expand Up @@ -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<double> monoisotopicMassPredictions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

namespace MassSpectrometry
{
/// <summary>
/// Parent class defining minimum requirement to be used <see cref="Deconvoluter"/>
/// </summary>
public abstract class DeconvolutionAlgorithm
{
// For ClassicDeconv. If not used elsewhere, move to that class
Expand Down Expand Up @@ -79,6 +82,6 @@ protected DeconvolutionAlgorithm(DeconvolutionParameters deconParameters)
/// <param name="spectrum">spectrum to be deconvoluted</param>
/// <param name="range">Range of peaks to deconvolute</param>
/// <returns></returns>
public abstract IEnumerable<IsotopicEnvelope> Deconvolute(MzSpectrum spectrum, MzRange range);
internal abstract IEnumerable<IsotopicEnvelope> Deconvolute(MzSpectrum spectrum, MzRange range);
}
}
Original file line number Diff line number Diff line change
@@ -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<IsotopicEnvelope> Deconvolute(MzSpectrum spectrum, MzRange range = null)
internal override IEnumerable<IsotopicEnvelope> Deconvolute(MzSpectrum spectrum, MzRange range = null)
{
var deconParams = DeconvolutionParameters as ExampleNewDeconvolutionParametersTemplate ?? throw new MzLibException("Deconvolution params and algorithm do not match");
range ??= spectrum.Range;
Expand Down
48 changes: 21 additions & 27 deletions mzLib/MassSpectrometry/Deconvolution/Deconvoluter.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
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 Chemistry;
using MzLibUtil;

namespace MassSpectrometry
Expand All @@ -30,27 +25,11 @@ public static class Deconvoluter
public static IEnumerable<IsotopicEnvelope> Deconvolute(MsDataScan scan,
DeconvolutionParameters deconvolutionParameters, MzRange rangeToGetPeaksFrom = null)
{
rangeToGetPeaksFrom ??= scan.MassSpectrum.Range;
// set any specific deconvolution parameters found only in the MsDataScan

// 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);
foreach (var isotopicEnvelope in Deconvolute(scan.MassSpectrum, deconvolutionParameters, rangeToGetPeaksFrom))
yield return isotopicEnvelope;
}



/// <summary>
/// Static deconvolution of an MzSpectrum that does not require Deconvoluter construction
Expand Down Expand Up @@ -79,7 +58,22 @@ public static IEnumerable<IsotopicEnvelope> 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;
}
}
}
}
4 changes: 4 additions & 0 deletions mzLib/MassSpectrometry/MassSpectrometry.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@
<ProjectReference Include="..\MzLibUtil\MzLibUtil.csproj" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Development" />
<InternalsVisibleTo Include="Test" />
</ItemGroup>
</Project>
3 changes: 0 additions & 3 deletions mzLib/MassSpectrometry/MsDataFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@

namespace MassSpectrometry
{
// TODO: Define scope of class
// Class scope is to provide to the data loaded from the DataFile.

/// <summary>
/// A class for interacting with data collected from a Mass Spectrometer, and stored in a file
/// </summary>
Expand Down
32 changes: 19 additions & 13 deletions mzLib/MassSpectrometry/MzSpectra/IsotopicEnvelope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,47 @@ public class IsotopicEnvelope : IHasMass
/// <summary>
/// Mass of most abundant observed isotopic peak, not accounting for addition or subtraction or protons due to ESI charge state induction
/// </summary>
public double MostAbundantObservedIsotopicMass { get; private set; }
internal double MostAbundantObservedIsotopicMass { get; private set; }
public readonly int Charge;
public readonly double TotalIntensity;
public readonly double StDev;
public readonly int MassIndex;

public double Score { get; private set; }

public IsotopicEnvelope(List<(double mz, double intensity)> bestListOfPeaks, double bestMonoisotopicMass, int bestChargeState, double bestTotalIntensity, double bestStDev, int bestMassIndex)
/// <summary>
/// Used for an isotopic envelope that mzLib deconvoluted (e.g., from a mass spectrum)
/// </summary>
public IsotopicEnvelope(List<(double mz, double intensity)> bestListOfPeaks, double bestMonoisotopicMass, int bestChargeState, double bestTotalIntensity, double bestStDev)
{
Peaks = bestListOfPeaks;
MonoisotopicMass = bestMonoisotopicMass;
MostAbundantObservedIsotopicMass = GetMostAbundantObservedIsotopicMass(bestListOfPeaks, bestChargeState);
MostAbundantObservedIsotopicMass = bestListOfPeaks.MaxBy(p => p.intensity).mz * Math.Abs(bestChargeState);
Charge = bestChargeState;
TotalIntensity = bestTotalIntensity;
StDev = bestStDev;
MassIndex = bestMassIndex;
Score = ScoreIsotopeEnvelope();
Score = ScoreIsotopeEnvelope(bestStDev);
}

public double GetMostAbundantObservedIsotopicMass(List<(double mz, double intensity)> peaks, int charge)
/// <summary>
/// Used for a neutral mass read in from a deconvoluted file
/// Assumes the mass is correct: score is max value
/// </summary>
public IsotopicEnvelope(double monoisotopicMass, double intensity, int charge)
{
return peaks.MaxBy(p => p.intensity).mz * Math.Abs(charge);
MonoisotopicMass = monoisotopicMass;
Charge = charge;
TotalIntensity = intensity;
Score = double.MaxValue;
Peaks = [(monoisotopicMass.ToMz(charge), intensity)];
}

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;
}

Expand All @@ -60,6 +67,5 @@ public void SetMedianMonoisotopicMass(List<double> monoisotopicMassPredictions)
{
MonoisotopicMass = monoisotopicMassPredictions.Median();
}

}
}
15 changes: 9 additions & 6 deletions mzLib/MassSpectrometry/MzSpectra/MzSpectrum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -126,7 +124,7 @@ public MzRange Range
}
}

public double? FirstX
public virtual double? FirstX
{
get
{
Expand All @@ -138,7 +136,7 @@ public double? FirstX
}
}

public double? LastX
public virtual double? LastX
{
get
{
Expand Down Expand Up @@ -373,7 +371,7 @@ public IsotopicEnvelope FindIsotopicEnvelope(int massIndex, double candidateForM
}
}

return new IsotopicEnvelope(listOfObservedPeaks, monoisotopicMass, chargeState, totalIntensity, Statistics.StandardDeviation(listOfRatios), massIndex);
return new IsotopicEnvelope(listOfObservedPeaks, monoisotopicMass, chargeState, totalIntensity, listOfRatios.StandardDeviation());
}

[Obsolete("Deconvolution Has been moved to the Deconvoluter Object")]
Expand Down Expand Up @@ -796,7 +794,12 @@ private MzPeak GetPeak(int index)
return peakList[index];
}

private MzPeak GeneratePeak(int index)
/// <summary>
/// The source of all peaks which populate the peakList
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
protected virtual MzPeak GeneratePeak(int index)
{
return new MzPeak(XArray[index], YArray[index]);
}
Expand Down
65 changes: 65 additions & 0 deletions mzLib/MassSpectrometry/MzSpectra/NeutralMassSpectrum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using Chemistry;

namespace MassSpectrometry
{
public class NeutralMassSpectrum : MzSpectrum
{
public int[] Charges { get; init; }
public NeutralMassSpectrum(double[,] monoisotopicMassesIntensities, int[] charges) : base(monoisotopicMassesIntensities)
{
if (monoisotopicMassesIntensities.GetLength(0) != charges.Length)
throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same.");

Charges = charges;

double minMz = double.MaxValue;
double maxMz = double.MinValue;
for (int i = 0; i < monoisotopicMassesIntensities.GetLength(0); i++)
{
var mz = monoisotopicMassesIntensities[i,0].ToMz(charges[i]);
if (mz < minMz)
minMz = mz;
if (mz > maxMz)
maxMz = mz;
}

FirstX = minMz;
LastX = maxMz;
}

public NeutralMassSpectrum(double[] monoisotopicMasses, double[] intensities, int[] charges, bool shouldCopy)
: base(monoisotopicMasses, intensities, shouldCopy)
{
if (monoisotopicMasses.GetLength(0) != intensities.Length || monoisotopicMasses.Length != charges.Length)
throw new ArgumentException("The lengths of monoisotopicMasses, intensities, and charges must be the same.");

Charges = charges;

double minMz = double.MaxValue;
double maxMz = double.MinValue;
for (int i = 0; i < monoisotopicMasses.Length; i++)
{
var mz = monoisotopicMasses[i].ToMz(charges[i]);
if (mz < minMz)
minMz = mz;
if (mz > maxMz)
maxMz = mz;
}

FirstX = minMz;
LastX = maxMz;
}

public override double? FirstX { get; } // in m/z
public override double? LastX { get; } // in m/z

/// <summary>
/// Converts to a charged spectrum
/// </summary>
protected override MzPeak GeneratePeak(int index)
{
return new MzPeak(XArray[index].ToMz(Charges[index]), YArray[index]);
}
}
}
Loading

0 comments on commit dc20e44

Please sign in to comment.