@@ -68,6 +68,8 @@ public MzMlSpectrumWriter(ParseInput parseInput) : base(parseInput)
6868 _mzMlNamespace . Add ( string . Empty , "http://psi.hupo.org/ms/mzml" ) ;
6969 _doIndexing = ParseInput . OutputFormat == OutputFormat . IndexMzML ;
7070 _osOffset = Environment . NewLine == "\n " ? 0 : 1 ;
71+ _precursorScanNumbers [ "" ] = - 1 ;
72+ _precursorTree [ - 1 ] = new PrecursorInfo ( ) ;
7173 }
7274
7375 /// <inheritdoc />
@@ -1275,7 +1277,7 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
12751277 } ) ;
12761278
12771279 // Keep track of scan number for precursor reference
1278- _precursorScanNumbers [ "" ] = scanNumber ;
1280+ _precursorScanNumbers [ "" ] = - 1 ;
12791281 _precursorTree [ scanNumber ] = new PrecursorInfo ( ) ;
12801282
12811283 }
@@ -1312,54 +1314,52 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
13121314 _precursorScanNumber = GetParentFromScanString ( result . Groups [ 1 ] . Value ) ;
13131315 }
13141316
1315- if ( _precursorScanNumber > 0 )
1317+ //finding precursor scan failed
1318+ if ( _precursorScanNumber == - 2 )
13161319 {
1320+ Log . Warn ( $ "Cannot find precursor scan for scan# { scanNumber } ") ;
1321+ _precursorTree [ - 2 ] = new PrecursorInfo ( 0 , msLevel , FindLastReaction ( scanEvent , msLevel ) , new PrecursorType [ 0 ] ) ;
1322+ }
13171323
1318- try
1324+ try
1325+ {
1326+ try //since there is no direct way to get the number of reactions available, it is necessary to try and fail
13191327 {
1320- try //since there is no direct way to get the number of reactions available, it is necessary to try and fail
1328+ scanEvent . GetReaction ( _precursorTree [ _precursorScanNumber ] . ReactionCount ) ;
1329+ }
1330+ catch ( ArgumentOutOfRangeException ex )
1331+ {
1332+ Log . Debug ( $ "Using Tribrid decision tree fix for scan# { scanNumber } ") ;
1333+ //Is it a decision tree scheduled scan on tribrid?
1334+ if ( msLevel == _precursorTree [ _precursorScanNumber ] . MSLevel )
13211335 {
1322- scanEvent . GetReaction ( _precursorTree [ _precursorScanNumber ] . ReactionCount ) ;
1336+ _precursorScanNumber = GetParentFromScanString ( result . Groups [ 1 ] . Value ) ;
13231337 }
1324- catch ( ArgumentOutOfRangeException ex )
1338+ else
13251339 {
1326- Log . Debug ( $ "Using Tribrid decision tree fix for scan# { scanNumber } ") ;
1327- //Is it a decision tree scheduled scan on tribrid?
1328- if ( msLevel == _precursorTree [ _precursorScanNumber ] . MSLevel )
1329- {
1330- _precursorScanNumber = GetParentFromScanString ( result . Groups [ 1 ] . Value ) ;
1331- }
1332- else
1333- {
1334- throw new RawFileParserException (
1335- $ "Tribrid decision tree fix failed - cannot get reaction# { _precursorTree [ _precursorScanNumber ] . ReactionCount } from { scanEvent . ToString ( ) } ",
1336- ex ) ;
1337- }
1340+ throw new RawFileParserException (
1341+ $ "Tribrid decision tree fix failed - cannot get reaction# { _precursorTree [ _precursorScanNumber ] . ReactionCount } from { scanEvent . ToString ( ) } ",
1342+ ex ) ;
13381343 }
1339-
1340- // Construct and set the precursor list element of the spectrum
1341- spectrum . precursorList =
1342- ConstructPrecursorList ( _precursorScanNumber , scanEvent , charge , monoisotopicMz , isolationWidth ,
1343- SPSMasses , out var reactionCount ) ;
1344-
1345- //save precursor information for later reference
1346- _precursorTree [ scanNumber ] = new PrecursorInfo ( _precursorScanNumber , msLevel , reactionCount , spectrum . precursorList . precursor ) ;
13471344 }
1348- catch ( Exception e )
1349- {
1350- var extra = ( e . InnerException is null ) ? "" : $ "\n { e . InnerException . StackTrace } ";
1351-
1352- Log . Warn ( $ "Failed creating precursor list for scan# { scanNumber } - precursor information for this and dependent scans will be empty\n Exception details:{ e . Message } \n { e . StackTrace } \n { extra } ") ;
1353- ParseInput . NewWarn ( ) ;
13541345
1355- _precursorTree [ scanNumber ] = new PrecursorInfo ( _precursorScanNumber , 1 , 0 , new PrecursorType [ 0 ] ) ;
1346+ // Construct and set the precursor list element of the spectrum
1347+ spectrum . precursorList =
1348+ ConstructPrecursorList ( _precursorScanNumber , scanEvent , charge , monoisotopicMz , isolationWidth ,
1349+ SPSMasses , out var reactionCount ) ;
13561350
1357- }
1358-
1351+ //save precursor information for later reference
1352+ _precursorTree [ scanNumber ] = new PrecursorInfo ( _precursorScanNumber , msLevel , reactionCount , spectrum . precursorList . precursor ) ;
13591353 }
1360- else
1354+ catch ( Exception e )
13611355 {
1362- spectrum . precursorList = ConstructPRMPrecursorList ( scanEvent , charge , isolationWidth ) ;
1356+ var extra = ( e . InnerException is null ) ? "" : $ "\n { e . InnerException . StackTrace } ";
1357+
1358+ Log . Warn ( $ "Failed creating precursor list for scan# { scanNumber } - precursor information for this and dependent scans will be empty\n Exception details:{ e . Message } \n { e . StackTrace } \n { extra } ") ;
1359+ ParseInput . NewWarn ( ) ;
1360+
1361+ _precursorTree [ scanNumber ] = new PrecursorInfo ( _precursorScanNumber , 1 , 0 , new PrecursorType [ 0 ] ) ;
1362+
13631363 }
13641364 }
13651365 else
@@ -1884,6 +1884,45 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
18841884 return spectrum ;
18851885 }
18861886
1887+ private int FindLastReaction ( IScanEvent scanEvent , int msLevel )
1888+ {
1889+ int lastReactionIndex = msLevel - 2 ;
1890+
1891+ //iteratively trying find the last available index for reaction
1892+ while ( true )
1893+ {
1894+ try
1895+ {
1896+ scanEvent . GetReaction ( lastReactionIndex + 1 ) ;
1897+ }
1898+ catch ( ArgumentOutOfRangeException )
1899+ {
1900+ //stop trying
1901+ break ;
1902+ }
1903+
1904+ lastReactionIndex ++ ;
1905+ }
1906+
1907+ //supplemental activation flag is on -> one of the levels (not necissirily the last one) used supplemental activation
1908+ //check last two activations
1909+ if ( scanEvent . SupplementalActivation == TriState . On )
1910+ {
1911+ var lastActivation = scanEvent . GetReaction ( lastReactionIndex ) . ActivationType ;
1912+ var beforeLastActivation = scanEvent . GetReaction ( lastReactionIndex - 1 ) . ActivationType ;
1913+
1914+ if ( ( beforeLastActivation == ActivationType . ElectronTransferDissociation || beforeLastActivation == ActivationType . ElectronCaptureDissociation ) &&
1915+ ( lastActivation == ActivationType . CollisionInducedDissociation || lastActivation == ActivationType . HigherEnergyCollisionalDissociation ) )
1916+ return lastReactionIndex - 1 ; //ETD or ECD followed by HCD or CID -> supplemental activation in the last level (move the last reaction one step back)
1917+ else
1918+ return lastReactionIndex ;
1919+ }
1920+ else //just use the last one
1921+ {
1922+ return lastReactionIndex ;
1923+ }
1924+ }
1925+
18871926 private SpectrumType ConstructPDASpectrum ( int scanNumber , int instrumentNumber )
18881927 {
18891928 // Get each scan from the RAW file
@@ -2146,20 +2185,24 @@ private PrecursorListType ConstructPrecursorList(int precursorScanNumber, IScanE
21462185 // Get precursors from earlier levels
21472186 var prevPrecursors = _precursorTree [ precursorScanNumber ] ;
21482187
2149- var spectrumRef = "" ;
2188+ string spectrumRef = null ;
21502189 int msLevel = ( int ) scanEvent . MSOrder ;
21512190 IReaction reaction = null ;
21522191 var precursorMz = 0.0 ;
21532192 reactionCount = prevPrecursors . ReactionCount ;
21542193
2155- spectrumRef = ConstructSpectrumTitle ( ( int ) Device . MS , 1 , precursorScanNumber ) ;
21562194 reaction = scanEvent . GetReaction ( reactionCount ) ;
2157-
2158- precursorMz = reaction . PrecursorMass ;
21592195
21602196 //if isolation width was not found in the trailer, try to get one from the reaction
21612197 if ( isolationWidth == null ) isolationWidth = reaction . IsolationWidth ;
2162-
2198+
2199+ precursorMz = reaction . PrecursorMass ;
2200+
2201+ if ( precursorScanNumber > 0 )
2202+ {
2203+ spectrumRef = ConstructSpectrumTitle ( ( int ) Device . MS , 1 , precursorScanNumber ) ;
2204+ }
2205+
21632206 var precursor = new PrecursorType
21642207 {
21652208 selectedIonList =
@@ -2196,7 +2239,7 @@ private PrecursorListType ConstructPrecursorList(int precursorScanNumber, IScanE
21962239 } ) ;
21972240 }
21982241
2199- if ( selectedIonMz > ZeroDelta )
2242+ if ( selectedIonMz > ZeroDelta && precursorScanNumber > 0 )
22002243 {
22012244 var selectedIonIntensity = CalculatePrecursorPeakIntensity ( _rawFile , precursorScanNumber , reaction . PrecursorMass , isolationWidth ,
22022245 ParseInput . NoPeakPicking . Contains ( msLevel - 1 ) ) ;
@@ -2417,156 +2460,10 @@ private PrecursorListType ConstructPrecursorList(int precursorScanNumber, IScanE
24172460
24182461 }
24192462
2420- /// <summary>
2421- /// Populate the precursor list element for PRM/MS2-only datasets (No MS1 scans)
2422- /// </summary>
2423- /// <param name="scanEvent">the scan event</param>
2424- /// <param name="charge">the charge from trailer</param>
2425- /// <param name="isolationWidth">the isolation width value from trailer</param>
2426- /// <returns>the precursor list</returns>
2427- private PrecursorListType ConstructPRMPrecursorList ( IScanEventBase scanEvent , int ? charge , double ? isolationWidth )
2428- {
2429- List < PrecursorType > precursors = new List < PrecursorType > ( ) ;
2430-
2431-
2432- int msLevel = ( int ) scanEvent . MSOrder ;
2433- IReaction reaction = scanEvent . GetReaction ( 0 ) ;
2434- double precursorMz = reaction . PrecursorMass ;
2435-
2436- //if isolation width was not found in the trailer, try to get one from the reaction
2437- if ( isolationWidth == null ) isolationWidth = reaction . IsolationWidth ;
2438-
2439- var precursor = new PrecursorType
2440- {
2441- selectedIonList =
2442- new SelectedIonListType { count = "1" , selectedIon = new ParamGroupType [ 1 ] } ,
2443- } ;
2444-
2445- precursor . selectedIonList . selectedIon [ 0 ] = new ParamGroupType ( ) ;
2446-
2447- var ionCvParams = new List < CVParamType >
2448- {
2449- new CVParamType
2450- {
2451- name = "selected ion m/z" ,
2452- value = precursorMz . ToString ( CultureInfo . InvariantCulture ) ,
2453- accession = "MS:1000744" ,
2454- cvRef = "MS" ,
2455- unitCvRef = "MS" ,
2456- unitAccession = "MS:1000040" ,
2457- unitName = "m/z"
2458- }
2459- } ;
2460-
2461- if ( charge != null )
2462- {
2463- ionCvParams . Add ( new CVParamType
2464- {
2465- name = "charge state" ,
2466- value = charge . ToString ( ) ,
2467- accession = "MS:1000041" ,
2468- cvRef = "MS"
2469- } ) ;
2470- }
2471- precursor . selectedIonList . selectedIon [ 0 ] . cvParam = ionCvParams . ToArray ( ) ;
2472-
2473- precursor . isolationWindow =
2474- new ParamGroupType
2475- {
2476- cvParam = new CVParamType [ 3 ]
2477- } ;
2478-
2479- precursor . isolationWindow . cvParam [ 0 ] =
2480- new CVParamType
2481- {
2482- accession = "MS:1000827" ,
2483- name = "isolation window target m/z" ,
2484- value = precursorMz . ToString ( CultureInfo . InvariantCulture ) ,
2485- cvRef = "MS" ,
2486- unitCvRef = "MS" ,
2487- unitAccession = "MS:1000040" ,
2488- unitName = "m/z"
2489- } ;
2490- if ( isolationWidth != null )
2491- {
2492- var offset = isolationWidth . Value / 2 + reaction . IsolationWidthOffset ;
2493- precursor . isolationWindow . cvParam [ 1 ] =
2494- new CVParamType
2495- {
2496- accession = "MS:1000828" ,
2497- name = "isolation window lower offset" ,
2498- value = ( isolationWidth . Value - offset ) . ToString ( CultureInfo . InvariantCulture ) ,
2499- cvRef = "MS" ,
2500- unitCvRef = "MS" ,
2501- unitAccession = "MS:1000040" ,
2502- unitName = "m/z"
2503- } ;
2504- precursor . isolationWindow . cvParam [ 2 ] =
2505- new CVParamType
2506- {
2507- accession = "MS:1000829" ,
2508- name = "isolation window upper offset" ,
2509- value = offset . ToString ( CultureInfo . InvariantCulture ) ,
2510- cvRef = "MS" ,
2511- unitCvRef = "MS" ,
2512- unitAccession = "MS:1000040" ,
2513- unitName = "m/z"
2514- } ;
2515- }
2516-
2517- // Activation
2518- var activationCvParams = new List < CVParamType > ( ) ;
2519- if ( reaction != null )
2520- {
2521- if ( reaction . CollisionEnergyValid )
2522- {
2523- activationCvParams . Add (
2524- new CVParamType
2525- {
2526- accession = "MS:1000045" ,
2527- name = "collision energy" ,
2528- cvRef = "MS" ,
2529- value = reaction . CollisionEnergy . ToString ( CultureInfo . InvariantCulture ) ,
2530- unitCvRef = "UO" ,
2531- unitAccession = "UO:0000266" ,
2532- unitName = "electronvolt"
2533- } ) ;
2534- }
2535-
2536- if ( ! OntologyMapping . DissociationTypes . TryGetValue ( reaction . ActivationType , out var activation ) )
2537- {
2538- activation = new CVParamType
2539- {
2540- accession = "MS:1000044" ,
2541- name = "Activation Method" ,
2542- cvRef = "MS" ,
2543- value = ""
2544- } ;
2545- }
2546-
2547- activationCvParams . Add ( activation ) ;
2548- }
2549-
2550- precursor . activation =
2551- new ParamGroupType
2552- {
2553- cvParam = activationCvParams . ToArray ( )
2554- } ;
2555-
2556- precursors . Add ( precursor ) ;
2557-
2558- return new PrecursorListType
2559- {
2560- count = precursors . Count . ToString ( ) ,
2561- precursor = precursors . ToArray ( )
2562- } ;
2563-
2564- }
2565-
25662463 private int GetParentFromScanString ( string scanString )
25672464 {
2568- var result = _filterStringIsolationMzPattern . Match ( scanString ) ;
2569- var parts = Regex . Split ( result . Groups [ 1 ] . Value , " " ) ;
2465+ // var result = _filterStringIsolationMzPattern.Match(scanString);
2466+ var parts = Regex . Split ( scanString , " " ) ;
25702467
25712468 //find the position of the first (from the end) precursor with a different mass
25722469 //to account for possible supplementary activations written in the filter
@@ -2584,7 +2481,7 @@ private int GetParentFromScanString(string scanString)
25842481 return _precursorScanNumbers [ parentFilter ] ;
25852482 }
25862483
2587- return - 1 ; //unsuccessful parsing
2484+ return - 2 ; //unsuccessful parsing
25882485 }
25892486
25902487 /// <summary>
0 commit comments