Skip to content

Commit 5bac912

Browse files
committed
Implementation of neutral loss/gain
1 parent a7d8816 commit 5bac912

File tree

3 files changed

+143
-45
lines changed

3 files changed

+143
-45
lines changed

Writer/MgfSpectrumWriter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
179179
double? isolationWidth =
180180
trailerData.AsDouble("MS" + msLevel + " Isolation Width:");
181181

182-
if (reaction != null)
182+
if (reaction != null && !(msLevel == (int)MSOrderType.Nl || msLevel == (int)MSOrderType.Ng))
183+
// Precursor m/z and intensity is not applicable for neutral loss and neutral gain scans
183184
{
184185
var selectedIonMz =
185186
CalculateSelectedIonMz(reaction, monoisotopicMz, isolationWidth);

Writer/MzMlSpectrumWriter.cs

Lines changed: 120 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System;
1+
using ComponentAce.Compression.Libs.zlib;
2+
using log4net;
3+
using System;
24
using System.Collections.Generic;
35
using System.Collections.Specialized;
46
using System.IO;
@@ -10,14 +12,12 @@
1012
using System.Text.RegularExpressions;
1113
using System.Xml;
1214
using System.Xml.Serialization;
13-
using log4net;
1415
using ThermoFisher.CommonCore.Data;
1516
using ThermoFisher.CommonCore.Data.Business;
1617
using ThermoFisher.CommonCore.Data.FilterEnums;
1718
using ThermoFisher.CommonCore.Data.Interfaces;
1819
using ThermoRawFileParser.Util;
1920
using ThermoRawFileParser.Writer.MzML;
20-
using ComponentAce.Compression.Libs.zlib;
2121

2222
namespace ThermoRawFileParser.Writer
2323
{
@@ -1260,6 +1260,9 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
12601260
// Keep the CV params in a list and convert to array afterwards
12611261
var spectrumCvParams = new List<CVParamType>();
12621262

1263+
// Keep user params in a list and convert to array afterwards
1264+
var spectrumUserParams = new List<UserParamType>();
1265+
12631266
// Trailer extra data list
12641267
ScanTrailer trailerData;
12651268

@@ -1426,45 +1429,76 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
14261429
ParseInput.NewWarn();
14271430
}
14281431

1429-
try
1432+
1433+
if (msLevel == (int)MSOrderType.Ng)
14301434
{
1431-
try //since there is no direct way to get the number of reactions available, it is necessary to try and fail
1435+
// Neutral loss/gain spectra do not have precursor information
1436+
spectrum.productList = ConstructProductList(scanNumber, scanEvent, out double neutralgain);
1437+
spectrumUserParams.Add(new UserParamType
14321438
{
1433-
scanEvent.GetReaction(_precursorTree[_precursorScanNumber].ReactionCount);
1434-
}
1435-
catch (ArgumentOutOfRangeException ex)
1439+
name = "neutral gain",
1440+
type = "xsd:float",
1441+
value = neutralgain.ToString(),
1442+
unitAccession = "MS:1000040",
1443+
unitCvRef = "MS",
1444+
unitName = "m/z"
1445+
});
1446+
}
1447+
else if (msLevel == (int)MSOrderType.Nl)
1448+
{
1449+
spectrum.productList = ConstructProductList(scanNumber, scanEvent, out double neutralloss);
1450+
spectrumUserParams.Add(new UserParamType
1451+
{
1452+
name = "neutral loss",
1453+
type = "xsd:float",
1454+
value = neutralloss.ToString(),
1455+
unitAccession = "MS:1000040",
1456+
unitCvRef = "MS",
1457+
unitName = "m/z"
1458+
});
1459+
}
1460+
else
1461+
{
1462+
try
14361463
{
1437-
Log.Debug($"Using Tribrid decision tree fix for scan# {scanNumber}");
1438-
//Is it a decision tree scheduled scan on tribrid?
1439-
if (msLevel == _precursorTree[_precursorScanNumber].MSLevel)
1464+
try //since there is no direct way to get the number of reactions available, it is necessary to try and fail
14401465
{
1441-
_precursorScanNumber = GetParentFromScanString(result.Groups[1].Value);
1466+
scanEvent.GetReaction(_precursorTree[_precursorScanNumber].ReactionCount);
14421467
}
1443-
else
1468+
catch (ArgumentOutOfRangeException ex)
14441469
{
1445-
throw new RawFileParserException(
1446-
$"Tribrid decision tree fix failed - cannot get reaction# {_precursorTree[_precursorScanNumber].ReactionCount} from {scanEvent.ToString()}",
1447-
ex);
1470+
Log.Debug($"Using Tribrid decision tree fix for scan# {scanNumber}");
1471+
//Is it a decision tree scheduled scan on tribrid?
1472+
if (msLevel == _precursorTree[_precursorScanNumber].MSLevel)
1473+
{
1474+
_precursorScanNumber = GetParentFromScanString(result.Groups[1].Value);
1475+
}
1476+
else
1477+
{
1478+
throw new RawFileParserException(
1479+
$"Tribrid decision tree fix failed - cannot get reaction# {_precursorTree[_precursorScanNumber].ReactionCount} from {scanEvent.ToString()}",
1480+
ex);
1481+
}
14481482
}
1449-
}
14501483

1451-
// Construct and set the precursor list element of the spectrum
1452-
spectrum.precursorList =
1453-
ConstructPrecursorList(_precursorScanNumber, scanEvent, charge, monoisotopicMz, isolationWidth,
1454-
SPSMasses, out var reactionCount);
1484+
// Construct and set the precursor list element of the spectrum
1485+
spectrum.precursorList =
1486+
ConstructPrecursorList(_precursorScanNumber, scanEvent, charge, monoisotopicMz, isolationWidth,
1487+
SPSMasses, out var reactionCount);
14551488

1456-
//save precursor information for later reference
1457-
_precursorTree[scanNumber] = new PrecursorInfo(_precursorScanNumber, msLevel, reactionCount, spectrum.precursorList.precursor);
1458-
}
1459-
catch (Exception e)
1460-
{
1461-
var extra = (e.InnerException is null) ? "" : $"\n{e.InnerException.StackTrace}";
1489+
//save precursor information for later reference
1490+
_precursorTree[scanNumber] = new PrecursorInfo(_precursorScanNumber, msLevel, reactionCount, spectrum.precursorList.precursor);
1491+
}
1492+
catch (Exception e)
1493+
{
1494+
var extra = (e.InnerException is null) ? "" : $"\n{e.InnerException.StackTrace}";
14621495

1463-
Log.Warn($"Failed creating precursor list for scan# {scanNumber} - precursor information for this and dependent scans will be empty\nException details:{e.Message}\n{e.StackTrace}\n{extra}");
1464-
ParseInput.NewWarn();
1496+
Log.Warn($"Failed creating precursor list for scan# {scanNumber} - precursor information for this and dependent scans will be empty\nException details:{e.Message}\n{e.StackTrace}\n{extra}");
1497+
ParseInput.NewWarn();
14651498

1466-
_precursorTree[scanNumber] = new PrecursorInfo(_precursorScanNumber, 1, 0, new PrecursorType[0]);
1499+
_precursorTree[scanNumber] = new PrecursorInfo(_precursorScanNumber, 1, 0, new PrecursorType[0]);
14671500

1501+
}
14681502
}
14691503
}
14701504

@@ -1641,6 +1675,8 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
16411675

16421676
// Add the CV params to the spectrum
16431677
spectrum.cvParam = spectrumCvParams.ToArray();
1678+
// Add user params to the spectrum
1679+
spectrum.userParam = spectrumUserParams.ToArray();
16441680

16451681
// Binary data array list
16461682
var binaryData = new List<BinaryDataArrayType>();
@@ -2008,6 +2044,60 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
20082044

20092045
return spectrum;
20102046
}
2047+
2048+
private ProductListType ConstructProductList(int scanNumber, IScanEvent scanEvent, out double neutral)
2049+
{
2050+
IReaction reaction = null;
2051+
2052+
reaction = scanEvent.GetReaction(0);
2053+
2054+
double? isolationWidth = reaction.IsolationWidth;
2055+
if (isolationWidth < 0) isolationWidth = null;
2056+
2057+
neutral = reaction.PrecursorMass;
2058+
2059+
var product = new ProductType();
2060+
2061+
if (isolationWidth != null)
2062+
{
2063+
product.isolationWindow =
2064+
new ParamGroupType
2065+
{
2066+
cvParam = new CVParamType[2]
2067+
};
2068+
2069+
var offset = isolationWidth.Value / 2 + reaction.IsolationWidthOffset;
2070+
product.isolationWindow.cvParam[0] =
2071+
new CVParamType
2072+
{
2073+
accession = "MS:1000828",
2074+
name = "isolation window lower offset",
2075+
value = (isolationWidth.Value - offset).ToString(),
2076+
cvRef = "MS",
2077+
unitCvRef = "MS",
2078+
unitAccession = "MS:1000040",
2079+
unitName = "m/z"
2080+
};
2081+
product.isolationWindow.cvParam[1] =
2082+
new CVParamType
2083+
{
2084+
accession = "MS:1000829",
2085+
name = "isolation window upper offset",
2086+
value = offset.ToString(),
2087+
cvRef = "MS",
2088+
unitCvRef = "MS",
2089+
unitAccession = "MS:1000040",
2090+
unitName = "m/z"
2091+
};
2092+
}
2093+
2094+
return new ProductListType
2095+
{
2096+
count = "1",
2097+
product = new ProductType[] { product }
2098+
};
2099+
}
2100+
20112101
private SpectrumType ConstructPDASpectrum(int scanNumber, int instrumentNumber)
20122102
{
20132103
// Get each scan from the RAW file

Writer/ParquetSpectrumWriter.cs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ namespace ThermoRawFileParser.Writer
1313
struct MzParquet
1414
{
1515
public uint scan;
16-
public uint level;
16+
public byte level;
17+
public string scan_type;
1718
public float rt;
1819
public float mz;
1920
public float intensity;
@@ -141,6 +142,9 @@ private List<MzParquet> ReadScan(IRawDataPlus raw, int scanNumber)
141142
// Get Scan trailer
142143
ScanTrailer trailerData;
143144

145+
//Scan type
146+
string scan_type;
147+
144148
try
145149
{
146150
trailerData = new ScanTrailer(raw.GetTrailerExtraInformation(scanNumber));
@@ -173,7 +177,15 @@ private List<MzParquet> ReadScan(IRawDataPlus raw, int scanNumber)
173177
// Keep track of scan number for precursor reference
174178
_precursorScanNumbers[""] = scanNumber;
175179
_precursorTree[scanNumber] = new PrecursorInfo();
176-
180+
scan_type = "MS1 spectrum";
181+
}
182+
else if (msLevel == (int)MSOrderType.Nl)
183+
{
184+
scan_type = "constant neutral loss spectrum";
185+
}
186+
else if (msLevel == (int)MSOrderType.Ng)
187+
{
188+
scan_type = "constant neutral gain spectrum";
177189
}
178190
else
179191
{
@@ -183,20 +195,14 @@ private List<MzParquet> ReadScan(IRawDataPlus raw, int scanNumber)
183195
{
184196
// Keep track of scan number and isolation m/z for precursor reference
185197
result = _filterStringIsolationMzPattern.Match(scanEvent.ToString());
198+
scan_type = "MSn spectrum";
186199
}
187200
else if (msLevel == (int)MSOrderType.Par)
188201
{
189202
// Keep track of scan number and isolation m/z for precursor reference
190203
result = _filterStringParentMzPattern.Match(scanEvent.ToString());
191-
}
192-
else if (msLevel == (int)MSOrderType.Nl)
193-
{
194-
//fill later if necessary
195-
}
196-
else if (msLevel == (int)MSOrderType.Ng)
197-
{
198-
//fill later if necessary
199-
}
204+
scan_type = "precursor ion spectrum";
205+
}
200206
else
201207
{
202208
throw new ArgumentOutOfRangeException($"Unknown msLevel: {msLevel}");
@@ -253,7 +259,7 @@ private List<MzParquet> ReadScan(IRawDataPlus raw, int scanNumber)
253259
}
254260
}
255261

256-
// Get Precursor m/z and isolation window borders
262+
// Get Precursor m/z and isolation window borders, exccept for
257263
precursor_data = GetPrecursorData(precursor_scan, scanEvent, trailer_mz, trailer_isolationWidth, out var reactionCount);
258264

259265
//save precursor information for later reference
@@ -313,12 +319,13 @@ private List<MzParquet> ReadScan(IRawDataPlus raw, int scanNumber)
313319
MzParquet m;
314320
m.rt = (float)rt;
315321
m.scan = (uint)scanNumber;
316-
m.level = msLevel > 0 ? (uint)msLevel: 0;
322+
m.scan_type = scan_type;
323+
m.level = msLevel > 0 ? (byte)msLevel : (byte)2;
317324
m.intensity = (float)mzData.intensities[i];
318325
m.mz = (float)mzData.masses[i];
319326
m.isolation_lower = precursor_data.isolation_lower;
320327
m.isolation_upper = precursor_data.isolation_upper;
321-
m.precursor_scan = precursor_scan;
328+
m.precursor_scan = precursor_scan > 0? precursor_scan : 0;
322329
m.precursor_mz = precursor_data.mz;
323330
m.precursor_charge = (uint?)trailer_charge;
324331
m.ion_mobility = (float?)FAIMSCV;

0 commit comments

Comments
 (0)