@@ -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 />
@@ -1416,61 +1418,52 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
14161418 _precursorScanNumber = GetParentFromScanString ( result . Groups [ 1 ] . Value ) ;
14171419 }
14181420
1419- if ( _precursorScanNumber > 0 )
1421+ //finding precursor scan failed
1422+ if ( _precursorScanNumber == - 2 )
14201423 {
1424+ Log . Warn ( $ "Cannot find precursor scan for scan# { scanNumber } ") ;
1425+ _precursorTree [ - 2 ] = new PrecursorInfo ( 0 , msLevel , FindLastReaction ( scanEvent , msLevel ) , new PrecursorType [ 0 ] ) ;
1426+ }
14211427
1422- try
1428+ try
1429+ {
1430+ try //since there is no direct way to get the number of reactions available, it is necessary to try and fail
1431+ {
1432+ scanEvent . GetReaction ( _precursorTree [ _precursorScanNumber ] . ReactionCount ) ;
1433+ }
1434+ catch ( ArgumentOutOfRangeException ex )
14231435 {
1424- try //since there is no direct way to get the number of reactions available, it is necessary to try and fail
1436+ Log . Debug ( $ "Using Tribrid decision tree fix for scan# { scanNumber } ") ;
1437+ //Is it a decision tree scheduled scan on tribrid?
1438+ if ( msLevel == _precursorTree [ _precursorScanNumber ] . MSLevel )
14251439 {
1426- scanEvent . GetReaction ( _precursorTree [ _precursorScanNumber ] . ReactionCount ) ;
1440+ _precursorScanNumber = GetParentFromScanString ( result . Groups [ 1 ] . Value ) ;
14271441 }
1428- catch ( ArgumentOutOfRangeException ex )
1442+ else
14291443 {
1430- Log . Debug ( $ "Using Tribrid decision tree fix for scan# { scanNumber } ") ;
1431- //Is it a decision tree scheduled scan on tribrid?
1432- if ( msLevel == _precursorTree [ _precursorScanNumber ] . MSLevel )
1433- {
1434- _precursorScanNumber = GetParentFromScanString ( result . Groups [ 1 ] . Value ) ;
1435- }
1436- else
1437- {
1438- throw new RawFileParserException (
1439- $ "Tribrid decision tree fix failed - cannot get reaction# { _precursorTree [ _precursorScanNumber ] . ReactionCount } from { scanEvent . ToString ( ) } ",
1440- ex ) ;
1441- }
1444+ throw new RawFileParserException (
1445+ $ "Tribrid decision tree fix failed - cannot get reaction# { _precursorTree [ _precursorScanNumber ] . ReactionCount } from { scanEvent . ToString ( ) } ",
1446+ ex ) ;
14421447 }
1443-
1444- // Construct and set the precursor list element of the spectrum
1445- spectrum . precursorList =
1446- ConstructPrecursorList ( _precursorScanNumber , scanEvent , charge , monoisotopicMz , isolationWidth ,
1447- SPSMasses , out var reactionCount ) ;
1448-
1449- //save precursor information for later reference
1450- _precursorTree [ scanNumber ] = new PrecursorInfo ( _precursorScanNumber , msLevel , reactionCount , spectrum . precursorList . precursor ) ;
14511448 }
1452- catch ( Exception e )
1453- {
1454- var extra = ( e . InnerException is null ) ? "" : $ "\n { e . InnerException . StackTrace } ";
14551449
1456- 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 } ") ;
1457- ParseInput . NewWarn ( ) ;
1458-
1459- _precursorTree [ scanNumber ] = new PrecursorInfo ( _precursorScanNumber , 1 , 0 , new PrecursorType [ 0 ] ) ;
1460-
1461- }
1450+ // Construct and set the precursor list element of the spectrum
1451+ spectrum . precursorList =
1452+ ConstructPrecursorList ( _precursorScanNumber , scanEvent , charge , monoisotopicMz , isolationWidth ,
1453+ SPSMasses , out var reactionCount ) ;
14621454
1455+ //save precursor information for later reference
1456+ _precursorTree [ scanNumber ] = new PrecursorInfo ( _precursorScanNumber , msLevel , reactionCount , spectrum . precursorList . precursor ) ;
14631457 }
1464- else
1458+ catch ( Exception e )
14651459 {
1466- spectrum . precursorList = new PrecursorListType
1467- {
1468- count = "0" ,
1469- precursor = new PrecursorType [ 0 ]
1470- } ;
1460+ var extra = ( e . InnerException is null ) ? "" : $ "\n { e . InnerException . StackTrace } ";
1461+
1462+ 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 } ") ;
1463+ ParseInput . NewWarn ( ) ;
1464+
1465+ _precursorTree [ scanNumber ] = new PrecursorInfo ( _precursorScanNumber , 1 , 0 , new PrecursorType [ 0 ] ) ;
14711466
1472- Log . Error ( $ "Failed finding precursor for { scanNumber } ") ;
1473- ParseInput . NewError ( ) ;
14741467 }
14751468 }
14761469 else
@@ -1995,6 +1988,45 @@ private SpectrumType ConstructMSSpectrum(int scanNumber)
19951988 return spectrum ;
19961989 }
19971990
1991+ private int FindLastReaction ( IScanEvent scanEvent , int msLevel )
1992+ {
1993+ int lastReactionIndex = msLevel - 2 ;
1994+
1995+ //iteratively trying find the last available index for reaction
1996+ while ( true )
1997+ {
1998+ try
1999+ {
2000+ scanEvent . GetReaction ( lastReactionIndex + 1 ) ;
2001+ }
2002+ catch ( ArgumentOutOfRangeException )
2003+ {
2004+ //stop trying
2005+ break ;
2006+ }
2007+
2008+ lastReactionIndex ++ ;
2009+ }
2010+
2011+ //supplemental activation flag is on -> one of the levels (not necissirily the last one) used supplemental activation
2012+ //check last two activations
2013+ if ( scanEvent . SupplementalActivation == TriState . On )
2014+ {
2015+ var lastActivation = scanEvent . GetReaction ( lastReactionIndex ) . ActivationType ;
2016+ var beforeLastActivation = scanEvent . GetReaction ( lastReactionIndex - 1 ) . ActivationType ;
2017+
2018+ if ( ( beforeLastActivation == ActivationType . ElectronTransferDissociation || beforeLastActivation == ActivationType . ElectronCaptureDissociation ) &&
2019+ ( lastActivation == ActivationType . CollisionInducedDissociation || lastActivation == ActivationType . HigherEnergyCollisionalDissociation ) )
2020+ return lastReactionIndex - 1 ; //ETD or ECD followed by HCD or CID -> supplemental activation in the last level (move the last reaction one step back)
2021+ else
2022+ return lastReactionIndex ;
2023+ }
2024+ else //just use the last one
2025+ {
2026+ return lastReactionIndex ;
2027+ }
2028+ }
2029+
19982030 private SpectrumType ConstructPDASpectrum ( int scanNumber , int instrumentNumber )
19992031 {
20002032 // Get each scan from the RAW file
@@ -2257,19 +2289,24 @@ private PrecursorListType ConstructPrecursorList(int precursorScanNumber, IScanE
22572289 // Get precursors from earlier levels
22582290 var prevPrecursors = _precursorTree [ precursorScanNumber ] ;
22592291
2260- var spectrumRef = "" ;
2292+ string spectrumRef = null ;
22612293 int msLevel = ( int ) scanEvent . MSOrder ;
22622294 IReaction reaction = null ;
22632295 var precursorMz = 0.0 ;
22642296 reactionCount = prevPrecursors . ReactionCount ;
22652297
2266- spectrumRef = ConstructSpectrumTitle ( ( int ) Device . MS , 1 , precursorScanNumber ) ;
22672298 reaction = scanEvent . GetReaction ( reactionCount ) ;
22682299
2269- precursorMz = reaction . PrecursorMass ;
2270-
22712300 //if isolation width was not found in the trailer, try to get one from the reaction
22722301 if ( isolationWidth == null ) isolationWidth = reaction . IsolationWidth ;
2302+ if ( isolationWidth < 0 ) isolationWidth = null ;
2303+
2304+ precursorMz = reaction . PrecursorMass ;
2305+
2306+ if ( precursorScanNumber > 0 )
2307+ {
2308+ spectrumRef = ConstructSpectrumTitle ( ( int ) Device . MS , 1 , precursorScanNumber ) ;
2309+ }
22732310
22742311 var precursor = new PrecursorType
22752312 {
@@ -2307,7 +2344,7 @@ private PrecursorListType ConstructPrecursorList(int precursorScanNumber, IScanE
23072344 } ) ;
23082345 }
23092346
2310- if ( selectedIonMz > ZeroDelta )
2347+ if ( selectedIonMz > ZeroDelta && precursorScanNumber > 0 )
23112348 {
23122349 var selectedIonIntensity = CalculatePrecursorPeakIntensity ( _rawFile , precursorScanNumber , reaction . PrecursorMass , isolationWidth ,
23132350 ParseInput . NoPeakPicking . Contains ( msLevel - 1 ) ) ;
@@ -2488,6 +2525,51 @@ private PrecursorListType ConstructPrecursorList(int precursorScanNumber, IScanE
24882525 spectrumRef = spectrumRef
24892526 } ;
24902527
2528+ //Isolation window for SPS masses is the same as for the first precursor
2529+ SPSPrecursor . isolationWindow =
2530+ new ParamGroupType
2531+ {
2532+ cvParam = new CVParamType [ 3 ]
2533+ } ;
2534+
2535+ SPSPrecursor . isolationWindow . cvParam [ 0 ] =
2536+ new CVParamType
2537+ {
2538+ accession = "MS:1000827" ,
2539+ name = "isolation window target m/z" ,
2540+ value = SPSMasses [ n ] . ToString ( CultureInfo . InvariantCulture ) ,
2541+ cvRef = "MS" ,
2542+ unitCvRef = "MS" ,
2543+ unitAccession = "MS:1000040" ,
2544+ unitName = "m/z"
2545+ } ;
2546+ if ( isolationWidth != null )
2547+ {
2548+ var offset = isolationWidth . Value / 2 + reaction . IsolationWidthOffset ;
2549+ SPSPrecursor . isolationWindow . cvParam [ 1 ] =
2550+ new CVParamType
2551+ {
2552+ accession = "MS:1000828" ,
2553+ name = "isolation window lower offset" ,
2554+ value = ( isolationWidth . Value - offset ) . ToString ( CultureInfo . InvariantCulture ) ,
2555+ cvRef = "MS" ,
2556+ unitCvRef = "MS" ,
2557+ unitAccession = "MS:1000040" ,
2558+ unitName = "m/z"
2559+ } ;
2560+ SPSPrecursor . isolationWindow . cvParam [ 2 ] =
2561+ new CVParamType
2562+ {
2563+ accession = "MS:1000829" ,
2564+ name = "isolation window upper offset" ,
2565+ value = offset . ToString ( CultureInfo . InvariantCulture ) ,
2566+ cvRef = "MS" ,
2567+ unitCvRef = "MS" ,
2568+ unitAccession = "MS:1000040" ,
2569+ unitName = "m/z"
2570+ } ;
2571+ }
2572+
24912573 // Selected ion MZ only
24922574 SPSPrecursor . selectedIonList . selectedIon [ 0 ] =
24932575 new ParamGroupType
@@ -2530,8 +2612,7 @@ private PrecursorListType ConstructPrecursorList(int precursorScanNumber, IScanE
25302612
25312613 private int GetParentFromScanString ( string scanString )
25322614 {
2533- var result = _filterStringIsolationMzPattern . Match ( scanString ) ;
2534- var parts = Regex . Split ( result . Groups [ 1 ] . Value , " " ) ;
2615+ var parts = Regex . Split ( scanString , " " ) ;
25352616
25362617 //find the position of the first (from the end) precursor with a different mass
25372618 //to account for possible supplementary activations written in the filter
@@ -2549,7 +2630,7 @@ private int GetParentFromScanString(string scanString)
25492630 return _precursorScanNumbers [ parentFilter ] ;
25502631 }
25512632
2552- return - 1 ; //unsuccessful parsing
2633+ return - 2 ; //unsuccessful parsing
25532634 }
25542635
25552636 /// <summary>
0 commit comments