11using System ;
22using System . Globalization ;
3+ using System . Linq ;
34using System . Reflection ;
5+ using System . Text . RegularExpressions ;
46using log4net ;
7+ using ThermoFisher . CommonCore . Data ;
58using ThermoFisher . CommonCore . Data . Business ;
69using ThermoFisher . CommonCore . Data . FilterEnums ;
710using ThermoFisher . CommonCore . Data . Interfaces ;
11+ using ThermoRawFileParser . Util ;
812
913namespace ThermoRawFileParser . Writer
1014{
@@ -16,6 +20,15 @@ public class MgfSpectrumWriter : SpectrumWriter
1620 private const string PositivePolarity = "+" ;
1721 private const string NegativePolarity = "-" ;
1822
23+ //filter string
24+ private const string FilterStringIsolationMzPattern = @"ms2 (.*?)@" ;
25+
26+ //precursor scan number for MS2 scans
27+ private int _precursorMs1ScanNumber ;
28+
29+ // Precursor scan number (value) and isolation m/z (key) for reference in the precursor element of an MS3 spectrum
30+ private readonly LimitedSizeDictionary < string , int > _precursorMs2ScanNumbers = new LimitedSizeDictionary < string , int > ( 40 ) ;
31+
1932 // Precursor scan number for reference in the precursor element of an MS2 spectrum
2033
2134 public MgfSpectrumWriter ( ParseInput parseInput ) : base ( parseInput )
@@ -36,7 +49,7 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
3649 {
3750 if ( ParseInput . LogFormat == LogFormat . DEFAULT )
3851 {
39- var scanProgress = ( int ) ( ( double ) scanNumber / ( lastScanNumber - firstScanNumber + 1 ) * 100 ) ;
52+ var scanProgress = ( int ) ( ( double ) scanNumber / ( lastScanNumber - firstScanNumber + 1 ) * 100 ) ;
4053 if ( scanProgress % ProgressPercentageStep == 0 )
4154 {
4255 if ( scanProgress != lastScanProgress )
@@ -60,6 +73,52 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
6073 // Get the scan event for this scan number
6174 var scanEvent = rawFile . GetScanEventForScanNumber ( scanNumber ) ;
6275
76+ // precursor reference
77+ var spectrumRef = "" ;
78+
79+ //keeping track of precursor scan
80+ switch ( scanFilter . MSOrder )
81+ {
82+ case MSOrderType . Ms :
83+
84+ // Keep track of scan number for precursor reference
85+ _precursorMs1ScanNumber = scanNumber ;
86+
87+ break ;
88+ case MSOrderType . Ms2 :
89+ // Keep track of scan number and isolation m/z for precursor reference
90+ var result = Regex . Match ( scanEvent . ToString ( ) , FilterStringIsolationMzPattern ) ;
91+ if ( result . Success )
92+ {
93+ if ( _precursorMs2ScanNumbers . ContainsKey ( result . Groups [ 1 ] . Value ) )
94+ {
95+ _precursorMs2ScanNumbers . Remove ( result . Groups [ 1 ] . Value ) ;
96+ }
97+
98+ _precursorMs2ScanNumbers . Add ( result . Groups [ 1 ] . Value , scanNumber ) ;
99+ }
100+
101+ spectrumRef = ConstructSpectrumTitle ( ( int ) Device . MS , 1 , _precursorMs1ScanNumber ) ;
102+ break ;
103+
104+ case MSOrderType . Ms3 :
105+ var precursorMs2ScanNumber = _precursorMs2ScanNumbers . Keys . FirstOrDefault (
106+ isolationMz => scanEvent . ToString ( ) . Contains ( isolationMz ) ) ;
107+ if ( ! precursorMs2ScanNumber . IsNullOrEmpty ( ) )
108+ {
109+ spectrumRef = ConstructSpectrumTitle ( ( int ) Device . MS , 1 , _precursorMs2ScanNumbers [ precursorMs2ScanNumber ] ) ;
110+ }
111+ else
112+ {
113+ throw new InvalidOperationException ( "Couldn't find a MS2 precursor scan for MS3 scan " + scanEvent ) ;
114+ }
115+ break ;
116+
117+ default :
118+ break ;
119+ }
120+
121+
63122 // don't include MS1 spectra
64123 if ( ParseInput . MsLevel . Contains ( ( int ) scanFilter . MSOrder ) )
65124 {
@@ -70,7 +129,6 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
70129 Writer . WriteLine ( $ "SCANS={ scanNumber } ") ;
71130 Writer . WriteLine (
72131 $ "RTINSECONDS={ ( time * 60 ) . ToString ( CultureInfo . InvariantCulture ) } ") ;
73-
74132 // trailer extra data list
75133 var trailerData = rawFile . GetTrailerExtraInformation ( scanNumber ) ;
76134 int ? charge = null ;
@@ -92,7 +150,7 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
92150 CultureInfo . CurrentCulture ) ;
93151 }
94152
95- if ( trailerData . Labels [ i ] == "MS" + ( int ) scanFilter . MSOrder + " Isolation Width:" )
153+ if ( trailerData . Labels [ i ] == "MS" + ( int ) scanFilter . MSOrder + " Isolation Width:" )
96154 {
97155 isolationWidth = double . Parse ( trailerData . Values [ i ] , NumberStyles . Any ,
98156 CultureInfo . CurrentCulture ) ;
@@ -121,6 +179,8 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
121179 Writer . WriteLine ( $ "CHARGE={ charge } { polarity } ") ;
122180 }
123181
182+ if ( ParseInput . MGFPrecursor ) Writer . WriteLine ( $ "PRECURSORTITLE={ spectrumRef } ") ;
183+
124184 // write the filter string
125185 //Writer.WriteLine($"SCANEVENT={scanEvent.ToString()}");
126186
0 commit comments