Skip to content

Commit 7b7854d

Browse files
committed
Support supplemental activation
+ moved precursorScan from gloabal + guess precursor from scan filter with SA
1 parent 4e3b469 commit 7b7854d

File tree

3 files changed

+105
-65
lines changed

3 files changed

+105
-65
lines changed

Writer/MzMlSpectrumWriter.cs

Lines changed: 85 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,10 @@ public class MzMlSpectrumWriter : SpectrumWriter
4545
private readonly Dictionary<IonizationModeType, CVParamType> _ionizationTypes =
4646
new Dictionary<IonizationModeType, CVParamType>();
4747

48-
// Precursor scan number for reference in the precursor element of an MS2 spectrum
49-
private int _precursorScanNumber;
50-
5148
// Precursor scan number (value) and isolation m/z (key) for reference in the precursor element of an MSn spectrum
5249
private readonly Dictionary<string, int> _precursorScanNumbers = new Dictionary<string, int>();
5350

51+
//Precursor information for scans
5452
private Dictionary<int, PrecursorInfo> _precursorTree = new Dictionary<int, PrecursorInfo>();
5553

5654
private const string SourceFileId = "RAW1";
@@ -1156,6 +1154,9 @@ private ChromatogramType TraceToChromatogram(ChromatogramSignal trace, string ch
11561154
/// <returns>The SpectrumType object</returns>
11571155
private SpectrumType ConstructMSSpectrum(int scanNumber)
11581156
{
1157+
// Last precursor scan number for use in MSn spectrum
1158+
int _precursorScanNumber = 0;
1159+
11591160
// Get each scan from the RAW file
11601161
var scan = Scan.FromFile(_rawFile, scanNumber);
11611162

@@ -1272,19 +1273,44 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
12721273
else //try getting it from the scan filter
12731274
{
12741275
var parts = Regex.Split(result.Groups[1].Value, " ");
1275-
string parentFilter = String.Join(" ", parts.Take(parts.Length - 1));
1276+
1277+
//find the position of the first (from the end) precursor with a different mass
1278+
//to account for possible supplementary activations written in the filter
1279+
var lastIonMass = parts.Last().Split('@').First();
1280+
int last = parts.Length;
1281+
while (last > 0 &&
1282+
parts[last - 1].Split('@').First() == lastIonMass)
1283+
{
1284+
last--;
1285+
}
1286+
1287+
string parentFilter = String.Join(" ", parts.Take(last));
12761288
if (_precursorScanNumbers.ContainsKey(parentFilter))
12771289
{
12781290
_precursorScanNumber = _precursorScanNumbers[parentFilter];
12791291
}
12801292
}
12811293

1282-
// Construct and set the precursor list element of the spectrum
1283-
spectrum.precursorList =
1284-
ConstructPrecursorList(scanEvent, charge, scanFilter.MSOrder, monoisotopicMz, isolationWidth,
1285-
SPSMasses);
1294+
if (_precursorScanNumber > 0)
1295+
{
1296+
// Construct and set the precursor list element of the spectrum
1297+
spectrum.precursorList =
1298+
ConstructPrecursorList(_precursorScanNumber, scanEvent, charge, monoisotopicMz, isolationWidth,
1299+
SPSMasses, out var reactionCount);
12861300

1287-
_precursorTree[scanNumber] = new PrecursorInfo(_precursorScanNumber, spectrum.precursorList.precursor);
1301+
//save precursor information for later reference
1302+
_precursorTree[scanNumber] = new PrecursorInfo(_precursorScanNumber, reactionCount, spectrum.precursorList.precursor);
1303+
}
1304+
else
1305+
{
1306+
spectrum.precursorList = new PrecursorListType
1307+
{
1308+
count = "0",
1309+
precursor = new PrecursorType[0]
1310+
};
1311+
1312+
Log.Error($"Failed finding precursor for {scanNumber}");
1313+
}
12881314
}
12891315
else
12901316
{
@@ -1873,38 +1899,40 @@ private SpectrumType ConstructPDASpectrum(int scanNumber, int instrumentNumber)
18731899
/// <summary>
18741900
/// Populate the precursor list element
18751901
/// </summary>
1902+
/// <param name="precursorScanNumber">scan number for the last precursor</param>
18761903
/// <param name="scanEvent">the scan event</param>
1877-
/// <param name="charge">the charge</param>
1878-
/// <param name="msLevel">the MS level</param>
1879-
/// <param name="monoisotopicMz">the monoisotopic m/z value</param>
1880-
/// <param name="isolationWidth">the isolation width</param>
1904+
/// <param name="charge">the charge from trailer</param>
1905+
/// <param name="monoisotopicMz">the monoisotopic m/z value from trailer</param>
1906+
/// <param name="isolationWidth">the isolation width value from trailer</param>
18811907
/// <param name="SPSMasses">List of masses selected for SPS</param>
1908+
/// <param name="reactionCount">Number of activation reactions (see PrecursorInfo for details)</param>
18821909
/// <returns>the precursor list</returns>
1883-
private PrecursorListType ConstructPrecursorList(IScanEventBase scanEvent, int? charge, MSOrderType msLevel,
1884-
double? monoisotopicMz, double? isolationWidth, List<double> SPSMasses)
1910+
private PrecursorListType ConstructPrecursorList(int precursorScanNumber, IScanEventBase scanEvent, int? charge,
1911+
double? monoisotopicMz, double? isolationWidth, List<double> SPSMasses, out int reactionCount)
18851912
{
1886-
// Construct the precursors
1887-
18881913
List<PrecursorType> precursors = new List<PrecursorType>();
18891914

1915+
// Get precursors from earlier levels
1916+
var prevPrecursors = _precursorTree[precursorScanNumber];
1917+
18901918
var spectrumRef = "";
1891-
int precursorScanNumber = _precursorScanNumber;
1919+
int msLevel = (int)scanEvent.MSOrder;
18921920
IReaction reaction = null;
18931921
var precursorMz = 0.0;
1922+
reactionCount = prevPrecursors.ReactionCount;
18941923
try
18951924
{
1896-
spectrumRef = ConstructSpectrumTitle((int)Device.MS, 1, _precursorScanNumber);
1897-
reaction = scanEvent.GetReaction((int)msLevel - 2);
1898-
precursorScanNumber = _precursorScanNumber;
1899-
1925+
spectrumRef = ConstructSpectrumTitle((int)Device.MS, 1, precursorScanNumber);
1926+
reaction = scanEvent.GetReaction(reactionCount);
1927+
19001928
precursorMz = reaction.PrecursorMass;
19011929

19021930
//if isolation width was not found in the trailer, try to get one from the reaction
19031931
if (isolationWidth == null) isolationWidth = reaction.IsolationWidth;
19041932
}
19051933
catch (ArgumentOutOfRangeException)
19061934
{
1907-
// Do nothing
1935+
Log.Warn($"Failed to get reaction when parsing precursor {precursorScanNumber}");
19081936
}
19091937

19101938
var precursor = new PrecursorType
@@ -1946,7 +1974,7 @@ private PrecursorListType ConstructPrecursorList(IScanEventBase scanEvent, int?
19461974
if (selectedIonMz > ZeroDelta)
19471975
{
19481976
var selectedIonIntensity = CalculatePrecursorPeakIntensity(_rawFile, precursorScanNumber, reaction.PrecursorMass, isolationWidth,
1949-
ParseInput.NoPeakPicking.Contains((int)msLevel - 1));
1977+
ParseInput.NoPeakPicking.Contains(msLevel - 1));
19501978
if (selectedIonIntensity != null)
19511979
{
19521980
ionCvParams.Add(new CVParamType
@@ -2041,12 +2069,15 @@ private PrecursorListType ConstructPrecursorList(IScanEventBase scanEvent, int?
20412069
activationCvParams.Add(activation);
20422070
}
20432071

2044-
// TODO: implement supplemental activation
2072+
//increase reaction count after successful parsing
2073+
reactionCount++;
2074+
20452075
if (scanEvent.SupplementalActivation == TriState.On)
2076+
//the property is On if *at least* one of the levels had SA (i.e. not necissirily the last one), thus we need to try (and posibly fail)
20462077
{
20472078
try
20482079
{
2049-
reaction = scanEvent.GetReaction(1);
2080+
reaction = scanEvent.GetReaction(reactionCount);
20502081

20512082
if (reaction != null)
20522083
{
@@ -2065,36 +2096,40 @@ private PrecursorListType ConstructPrecursorList(IScanEventBase scanEvent, int?
20652096
});
20662097
}
20672098

2068-
// Add this supplemental CV term
2069-
// TODO: use a more generic approach
2070-
if (reaction.ActivationType == ActivationType.HigherEnergyCollisionalDissociation)
2099+
// Add the supplemental CV term
2100+
switch (reaction.ActivationType)
20712101
{
2072-
activationCvParams.Add(new CVParamType
2073-
{
2074-
accession = "MS:1002678",
2075-
name = "supplemental beam-type collision-induced dissociation",
2076-
cvRef = "MS",
2077-
value = ""
2078-
});
2079-
}
2102+
case ActivationType.HigherEnergyCollisionalDissociation:
2103+
activationCvParams.Add(new CVParamType
2104+
{
2105+
accession = "MS:1002678",
2106+
name = "supplemental beam-type collision-induced dissociation",
2107+
cvRef = "MS",
2108+
value = ""
2109+
}); break;
2110+
2111+
case ActivationType.CollisionInducedDissociation:
2112+
activationCvParams.Add(new CVParamType
2113+
{
2114+
accession = "MS:1002679",
2115+
name = "supplemental collision-induced dissociation",
2116+
cvRef = "MS",
2117+
value = ""
2118+
}); break;
2119+
2120+
default:
2121+
Log.Warn($"Unknown supplemental activation type: {reaction.ActivationType}");
2122+
break;
20802123

2081-
if (!OntologyMapping.DissociationTypes.TryGetValue(reaction.ActivationType, out var activation))
2082-
{
2083-
activation = new CVParamType
2084-
{
2085-
accession = "MS:1000044",
2086-
name = "Activation Method",
2087-
cvRef = "MS",
2088-
value = ""
2089-
};
20902124
}
20912125

2092-
activationCvParams.Add(activation);
2126+
//increase reaction count after successful parsing
2127+
reactionCount++;
20932128
}
20942129
}
20952130
catch (ArgumentOutOfRangeException)
20962131
{
2097-
// Do nothing
2132+
// If we failed do nothing
20982133
}
20992134
}
21002135

@@ -2145,11 +2180,8 @@ private PrecursorListType ConstructPrecursorList(IScanEventBase scanEvent, int?
21452180
precursors.Add(SPSPrecursor);
21462181
}
21472182

2148-
//Add precursors from previous levels
2149-
if (_precursorTree[precursorScanNumber].Scan != 0)
2150-
{
2151-
precursors.AddRange(_precursorTree[precursorScanNumber].Precursors);
2152-
}
2183+
//Add precursors from previous levels to the end of the list
2184+
precursors.AddRange(prevPrecursors.Precursors);
21532185

21542186
return new PrecursorListType
21552187
{

Writer/PrecursorInfo.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
1-
using System.Collections.Generic;
2-
3-
namespace ThermoRawFileParser.Writer
1+
namespace ThermoRawFileParser.Writer
42
{
3+
/// <summary>
4+
/// Class that stores info from precursors
5+
/// </summary>
56
public class PrecursorInfo
67
{
7-
public int MSLevel { get; set; }
8+
//for future use
9+
public int MSLevel { get; }
810

9-
public int Scan { get; set; }
11+
//precursor scan number, 0 - means not a precursor
12+
public int Scan { get; }
1013

11-
public MzML.PrecursorType[] Precursors { get { return _precursors.ToArray(); } }
14+
//technical field to store number of reactions the precursor has
15+
//every level of SA counts as additional reaction and thus we need to keep track of it
16+
public int ReactionCount { get; }
1217

13-
private List<MzML.PrecursorType> _precursors;
18+
//mzML-formatted precursor information for all levels
19+
public MzML.PrecursorType[] Precursors { get ; }
1420

1521
public PrecursorInfo()
1622
{
1723
Scan = 0;
18-
_precursors = new List<MzML.PrecursorType>();
24+
ReactionCount = 0;
25+
Precursors = new MzML.PrecursorType[0];
1926
}
2027

21-
public PrecursorInfo(int scan, MzML.PrecursorType[] precursors)
28+
public PrecursorInfo(int scan, int reactionCount, MzML.PrecursorType[] precursors)
2229
{
2330
Scan = scan;
24-
_precursors = new List<MzML.PrecursorType>(precursors);
31+
ReactionCount = reactionCount;
32+
Precursors = precursors;
2533
}
2634
}
2735
}

XIC/XicReader.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,11 @@ private static IChromatogramData GetChromatogramData(IRawDataPlus rawFile, IChro
169169
data = rawFile.GetChromatogramData(new IChromatogramSettings[] {settings}, firstScanNumber,
170170
lastScanNumber);
171171
}
172-
catch (InvalidFilterFormatException ex)
172+
catch (InvalidFilterFormatException)
173173
{
174174
Log.Warn($"Invalid filter string {settings.Filter}");
175175
}
176-
catch (InvalidFilterCriteriaException ex)
176+
catch (InvalidFilterCriteriaException)
177177
{
178178
Log.Warn($"Invalid filter string {settings.Filter}");
179179
}

0 commit comments

Comments
 (0)