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,10 +20,20 @@ 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 )
2235 {
36+ ParseInput . MsLevel . Remove ( 1 ) ; //MS1 spectra are not supposed to be in MGF
2337 }
2438
2539 /// <inheritdoc />
@@ -35,7 +49,7 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
3549 {
3650 if ( ParseInput . LogFormat == LogFormat . DEFAULT )
3751 {
38- var scanProgress = ( int ) ( ( double ) scanNumber / ( lastScanNumber - firstScanNumber + 1 ) * 100 ) ;
52+ var scanProgress = ( int ) ( ( double ) scanNumber / ( lastScanNumber - firstScanNumber + 1 ) * 100 ) ;
3953 if ( scanProgress % ProgressPercentageStep == 0 )
4054 {
4155 if ( scanProgress != lastScanProgress )
@@ -59,17 +73,65 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
5973 // Get the scan event for this scan number
6074 var scanEvent = rawFile . GetScanEventForScanNumber ( scanNumber ) ;
6175
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+
62122 // don't include MS1 spectra
63- if ( scanFilter . MSOrder != MSOrderType . Ms )
123+ if ( ParseInput . MsLevel . Contains ( ( int ) scanFilter . MSOrder ) )
64124 {
65125 IReaction reaction = GetReaction ( scanEvent , scanNumber ) ;
66126
67127 Writer . WriteLine ( "BEGIN IONS" ) ;
68- Writer . WriteLine ( $ "TITLE={ ConstructSpectrumTitle ( ( int ) Device . MS , 1 , scanNumber ) } ") ;
128+ if
129+ ( ParseInput . MGFPrecursor ) Writer . WriteLine ( $ "TITLE={ ConstructSpectrumTitle ( ( int ) Device . MS , 1 , scanNumber ) } [PRECURSOR={ spectrumRef } ]") ;
130+ else
131+ Writer . WriteLine ( $ "TITLE={ ConstructSpectrumTitle ( ( int ) Device . MS , 1 , scanNumber ) } ") ;
69132 Writer . WriteLine ( $ "SCANS={ scanNumber } ") ;
70133 Writer . WriteLine (
71134 $ "RTINSECONDS={ ( time * 60 ) . ToString ( CultureInfo . InvariantCulture ) } ") ;
72-
73135 // trailer extra data list
74136 var trailerData = rawFile . GetTrailerExtraInformation ( scanNumber ) ;
75137 int ? charge = null ;
@@ -91,7 +153,7 @@ public override void Write(IRawDataPlus rawFile, int firstScanNumber, int lastSc
91153 CultureInfo . CurrentCulture ) ;
92154 }
93155
94- if ( trailerData . Labels [ i ] == "MS" + ( int ) scanFilter . MSOrder + " Isolation Width:" )
156+ if ( trailerData . Labels [ i ] == "MS" + ( int ) scanFilter . MSOrder + " Isolation Width:" )
95157 {
96158 isolationWidth = double . Parse ( trailerData . Values [ i ] , NumberStyles . Any ,
97159 CultureInfo . CurrentCulture ) ;
0 commit comments