Skip to content

Commit 34eda0c

Browse files
committed
First implementation of precursor ion spectrum
1 parent 8d7369c commit 34eda0c

File tree

4 files changed

+197
-19
lines changed

4 files changed

+197
-19
lines changed

ParseInput.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace ThermoRawFileParser
77
public class ParseInput
88
{
99
// All MS levels
10-
public static HashSet<int> AllLevels { get => new HashSet<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; }
10+
public static HashSet<int> AllLevels { get => new HashSet<int> { -3, -2, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; }
1111

1212
/// <summary>
1313
/// The RAW file path.

Writer/MzMlSpectrumWriter.cs

Lines changed: 157 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,11 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
137137

138138
if (_rawFile.HasMsData)
139139
{
140-
var nMS1 = rawFile.GetFilteredScanEnumerator("ms").Count();
141-
var nMS = rawFile.RunHeaderEx.SpectraCount;
140+
var scanCounts = WriterUtil.CountScanOrder(_rawFile);
141+
//Sum all scans with MSOrder > 1
142+
int nMSn = scanCounts.Sum(o => (int)o.Key > 1 ? o.Value : 0);
142143
// MS1
143-
if(ParseInput.MsLevel.Contains(1) && nMS1 > 0)
144+
if (ParseInput.MsLevel.Contains(1) && scanCounts[MSOrderType.Ms] > 0)
144145
content.Add(new CVParamType
145146
{
146147
accession = "MS:1000579",
@@ -149,14 +150,38 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
149150
value = ""
150151
});
151152
// MSn
152-
if(ParseInput.MsLevel.Any(n => n > 1) && nMS > nMS1)
153+
if(ParseInput.MsLevel.Any(n => n > 1) && nMSn > 0)
153154
content.Add(new CVParamType
154155
{
155156
accession = "MS:1000580",
156157
name = "MSn spectrum",
157158
cvRef = "MS",
158159
value = ""
159160
});
161+
if (scanCounts[MSOrderType.Par] > 0)
162+
content.Add(new CVParamType
163+
{
164+
accession = "MS:1000341",
165+
name = "precursor ion spectrum",
166+
cvRef = "MS",
167+
value = ""
168+
});
169+
if (scanCounts[MSOrderType.Nl] > 0)
170+
content.Add(new CVParamType
171+
{
172+
accession = "MS:1000326",
173+
name = "constant neutral loss spectrum",
174+
cvRef = "MS",
175+
value = ""
176+
});
177+
if (scanCounts[MSOrderType.Ng] > 0)
178+
content.Add(new CVParamType
179+
{
180+
accession = "MS:1000325",
181+
name = "constant neutral gain spectrum",
182+
cvRef = "MS",
183+
value = ""
184+
});
160185
// Ion current chromatogram
161186
content.Add(OntologyMapping.GetChromatogramType("current"));
162187
}
@@ -407,7 +432,7 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
407432
ParseInput.NewError();
408433
}
409434

410-
level = spectrum != null ? int.Parse(spectrum.cvParam.Where(p => p.accession == "MS:1000511").First().value) : 0;
435+
level = spectrum != null ? (int)_rawFile.GetScanEventForScanNumber(scanNumber).MSOrder : 0;
411436

412437
if (spectrum != null && ParseInput.MsLevel.Contains(level)) //applying final MS filter
413438
{
@@ -1233,16 +1258,7 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
12331258
};
12341259

12351260
// Keep the CV params in a list and convert to array afterwards
1236-
var spectrumCvParams = new List<CVParamType>
1237-
{
1238-
new CVParamType
1239-
{
1240-
name = "ms level",
1241-
accession = "MS:1000511",
1242-
value = msLevel.ToString(),
1243-
cvRef = "MS"
1244-
}
1245-
};
1261+
var spectrumCvParams = new List<CVParamType>();
12461262

12471263
// Trailer extra data list
12481264
ScanTrailer trailerData;
@@ -1295,7 +1311,6 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
12951311
ionInjectionTime);
12961312
spectrum.scanList = scanListType;
12971313

1298-
12991314
if (msLevel == 1)
13001315
{
13011316
spectrumCvParams.Add(new CVParamType
@@ -1306,6 +1321,14 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
13061321
value = ""
13071322
});
13081323

1324+
spectrumCvParams.Add(new CVParamType
1325+
{
1326+
name = "ms level",
1327+
accession = "MS:1000511",
1328+
value = msLevel.ToString(),
1329+
cvRef = "MS"
1330+
});
1331+
13091332
// Keep track of scan number for precursor reference
13101333
_precursorScanNumbers[""] = scanNumber;
13111334
_precursorTree[scanNumber] = new PrecursorInfo();
@@ -1314,12 +1337,20 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
13141337
else if (msLevel > 1)
13151338
{
13161339
spectrumCvParams.Add(new CVParamType
1317-
{
1340+
{
13181341
accession = "MS:1000580",
13191342
cvRef = "MS",
13201343
name = "MSn spectrum",
13211344
value = ""
1322-
});
1345+
});
1346+
1347+
spectrumCvParams.Add(new CVParamType
1348+
{
1349+
name = "ms level",
1350+
accession = "MS:1000511",
1351+
value = msLevel.ToString(),
1352+
cvRef = "MS"
1353+
});
13231354

13241355
// Keep track of scan number and isolation m/z for precursor reference
13251356
var result = _filterStringIsolationMzPattern.Match(scanEvent.ToString());
@@ -1393,6 +1424,114 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
13931424

13941425
}
13951426
}
1427+
else if (msLevel == (int)MSOrderType.Par)
1428+
{
1429+
spectrumCvParams.Add(new CVParamType
1430+
{
1431+
accession = "MS:1000341",
1432+
cvRef = "MS",
1433+
name = "precursor ion spectrum",
1434+
value = ""
1435+
});
1436+
1437+
// Keep track of scan number and isolation m/z for precursor reference
1438+
var result = _filterStringParentMzPattern.Match(scanEvent.ToString());
1439+
if (result.Success)
1440+
{
1441+
if (_precursorScanNumbers.ContainsKey(result.Groups[1].Value))
1442+
{
1443+
_precursorScanNumbers.Remove(result.Groups[1].Value);
1444+
}
1445+
1446+
_precursorScanNumbers.Add(result.Groups[1].Value, scanNumber);
1447+
}
1448+
1449+
//update precursor scan if it is provided in trailer data
1450+
var trailerMasterScan = trailerData.AsPositiveInt("Master Scan Number:");
1451+
if (trailerMasterScan.HasValue)
1452+
{
1453+
_precursorScanNumber = trailerMasterScan.Value;
1454+
}
1455+
else //try getting it from the scan filter
1456+
{
1457+
_precursorScanNumber = GetParentFromScanString(result.Groups[1].Value);
1458+
}
1459+
1460+
//finding precursor scan failed
1461+
if (_precursorScanNumber == -2 || !_precursorTree.ContainsKey(_precursorScanNumber))
1462+
{
1463+
Log.Warn($"Cannot find precursor scan for scan# {scanNumber}");
1464+
_precursorTree[_precursorScanNumber] = new PrecursorInfo(0, msLevel, FindLastReaction(scanEvent, msLevel), new PrecursorType[0]);
1465+
ParseInput.NewWarn();
1466+
}
1467+
1468+
try
1469+
{
1470+
try //since there is no direct way to get the number of reactions available, it is necessary to try and fail
1471+
{
1472+
scanEvent.GetReaction(_precursorTree[_precursorScanNumber].ReactionCount);
1473+
}
1474+
catch (ArgumentOutOfRangeException ex)
1475+
{
1476+
Log.Debug($"Using Tribrid decision tree fix for scan# {scanNumber}");
1477+
//Is it a decision tree scheduled scan on tribrid?
1478+
if (msLevel == _precursorTree[_precursorScanNumber].MSLevel)
1479+
{
1480+
_precursorScanNumber = GetParentFromScanString(result.Groups[1].Value);
1481+
}
1482+
else
1483+
{
1484+
throw new RawFileParserException(
1485+
$"Tribrid decision tree fix failed - cannot get reaction# {_precursorTree[_precursorScanNumber].ReactionCount} from {scanEvent.ToString()}",
1486+
ex);
1487+
}
1488+
}
1489+
1490+
// Construct and set the precursor list element of the spectrum
1491+
spectrum.precursorList =
1492+
ConstructPrecursorList(_precursorScanNumber, scanEvent, charge, monoisotopicMz, isolationWidth,
1493+
SPSMasses, out var reactionCount);
1494+
1495+
//save precursor information for later reference
1496+
_precursorTree[scanNumber] = new PrecursorInfo(_precursorScanNumber, msLevel, reactionCount, spectrum.precursorList.precursor);
1497+
}
1498+
catch (Exception e)
1499+
{
1500+
var extra = (e.InnerException is null) ? "" : $"\n{e.InnerException.StackTrace}";
1501+
1502+
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}");
1503+
ParseInput.NewWarn();
1504+
1505+
_precursorTree[scanNumber] = new PrecursorInfo(_precursorScanNumber, 1, 0, new PrecursorType[0]);
1506+
1507+
}
1508+
}
1509+
else if (msLevel == (int)MSOrderType.Nl)
1510+
{
1511+
spectrumCvParams.Add(new CVParamType
1512+
{
1513+
accession = "MS:1000326",
1514+
cvRef = "MS",
1515+
name = "constant neutral loss spectrum",
1516+
value = ""
1517+
});
1518+
// Keep track of scan number for precursor reference
1519+
_precursorScanNumbers[""] = scanNumber;
1520+
_precursorTree[scanNumber] = new PrecursorInfo();
1521+
}
1522+
else if (msLevel == (int)MSOrderType.Ng)
1523+
{
1524+
spectrumCvParams.Add(new CVParamType
1525+
{
1526+
accession = "MS:1000325",
1527+
cvRef = "MS",
1528+
name = "constant neutral gain spectrum",
1529+
value = ""
1530+
});
1531+
// Keep track of scan number for precursor reference
1532+
_precursorScanNumbers[""] = scanNumber;
1533+
_precursorTree[scanNumber] = new PrecursorInfo();
1534+
}
13961535
else
13971536
{
13981537
throw new ArgumentOutOfRangeException($"Unknown msLevel: {msLevel}");

Writer/SpectrumWriter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ public abstract class SpectrumWriter : ISpectrumWriter
5353

5454
// Filter string regex to extract an isoaltion entry
5555
private protected readonly Regex _filterStringIsolationMzPattern = new Regex(@"ms\d+ (.+?) \[");
56+
// Filter string regex to extract an parent entry
57+
private protected readonly Regex _filterStringParentMzPattern = new Regex(@"pr (.+?) \[");
5658

5759
/// <summary>
5860
/// Constructor.

Writer/WriterUtil.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using ThermoFisher.CommonCore.Data.FilterEnums;
7+
using ThermoFisher.CommonCore.Data.Interfaces;
8+
9+
namespace ThermoRawFileParser.Writer
10+
{
11+
static public class WriterUtil
12+
{
13+
public static Dictionary<MSOrderType, int> CountScanOrder(IRawDataPlus rawFile, int firstScan, int lastScan)
14+
{
15+
var scanOrderCounts = new Dictionary<MSOrderType, int>();
16+
foreach (MSOrderType item in Enum.GetValuesAsUnderlyingType(typeof(MSOrderType)))
17+
{
18+
scanOrderCounts[item] = 0;
19+
}
20+
21+
for (int scan=firstScan; scan <=lastScan; scan++)
22+
{
23+
var filter = rawFile.GetFilterForScanNumber(scan);
24+
scanOrderCounts[filter.MSOrder] += 1;
25+
}
26+
27+
scanOrderCounts[MSOrderType.Any] = scanOrderCounts.Values.Sum();
28+
29+
return scanOrderCounts;
30+
}
31+
32+
public static Dictionary<MSOrderType, int> CountScanOrder(IRawDataPlus rawFile)
33+
{
34+
return CountScanOrder(rawFile, rawFile.RunHeaderEx.FirstSpectrum, rawFile.RunHeaderEx.LastSpectrum);
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)