1717using System . Diagnostics ;
1818using System . Security . Permissions ;
1919using System . ComponentModel ;
20+ using System . Runtime . CompilerServices ;
2021
2122namespace Readers
22- {
23+ {
2324 public class TimsTofFileReader : MsDataFile , IDisposable
2425 {
2526 // timsTOF instruments collect frames, packets of ions collected by the tims, then analyzed
2627 // over multiple scans with each scan corresponding to the same retention time but different
2728 // ion mobility valuess. When reading the file, multiple scans from the same frame are collapsed into
2829 // a single spectrum
2930
30- public TimsTofFileReader ( string filePath ) : base ( filePath ) { }
31+ public TimsTofFileReader ( string filePath ) : base ( filePath )
32+ {
33+ FaultyFrameIds = new ( ) ;
34+ }
3135
3236 private UInt64 ? _fileHandle ;
3337 private Object _fileLock ;
@@ -36,7 +40,12 @@ public TimsTofFileReader(string filePath) : base (filePath) { }
3640 public int NumberOfFrames { get ; private set ; }
3741 public List < long > Ms1FrameIds { get ; private set ; }
3842 internal FrameProxyFactory FrameProxyFactory { get ; private set ; }
39-
43+
44+ internal List < long > FaultyFrameIds { get ; }
45+
46+ public string ? Warnings => FaultyFrameIds . Count > 0 ?
47+ "The following frames were not read correctly: " + String . Join ( ", " , FaultyFrameIds ) : null ;
48+
4049 // I don't know what the default scan range is, and at this point I'm too afraid to ask...
4150 private MzRange ? _scanWindow ;
4251 public MzRange ScanWindow => _scanWindow ??= new MzRange ( 20 , 2000 ) ;
@@ -49,6 +58,9 @@ public override void InitiateDynamicConnection()
4958 throw new FileNotFoundException ( "Data file is missing .tdf and/or .tdf_bin file" ) ;
5059 }
5160
61+ if ( Scans . IsNotNullOrEmpty ( ) && Scans . All ( s => s != null ) ) // If all scans have been loaded, then don't reload
62+ return ;
63+
5264 OpenSqlConnection ( ) ;
5365
5466 if ( _fileHandle != null ) tims_close ( ( UInt64 ) _fileHandle ) ;
@@ -57,6 +69,8 @@ public override void InitiateDynamicConnection()
5769
5870 CountFrames ( ) ;
5971 BuildProxyFactory ( ) ;
72+ CountMS1Frames ( ) ;
73+ CountPrecursors ( ) ;
6074 }
6175
6276 internal void OpenSqlConnection ( )
@@ -89,6 +103,7 @@ public override void CloseDynamicConnection()
89103 {
90104 if ( _sqlConnection ? . State == ConnectionState . Open ) _sqlConnection . Close ( ) ;
91105 _sqlConnection ? . Dispose ( ) ;
106+ _sqlConnection = null ;
92107 if ( _fileHandle != null )
93108 {
94109 tims_close ( ( UInt64 ) _fileHandle ) ;
@@ -232,9 +247,6 @@ public override MsDataFile LoadAllStaticData(FilteringParams filteringParams = n
232247 {
233248 InitiateDynamicConnection ( ) ;
234249
235- CountMS1Frames ( ) ;
236- CountPrecursors ( ) ;
237-
238250 _maxThreads = maxThreads ;
239251 Ms1ScansNoPrecursorsBag = new ( ) ;
240252 Parallel . ForEach (
@@ -315,6 +327,11 @@ internal void AssignOneBasedPrecursorsToPasefScans()
315327 internal void BuildAllScans ( long frameId , FilteringParams filteringParams )
316328 {
317329 FrameProxy frame = FrameProxyFactory . GetFrameProxy ( frameId ) ;
330+ if ( frame == null || ! frame . IsFrameValid ( ) )
331+ {
332+ FaultyFrameIds . Add ( frameId ) ;
333+ return ; // If the frame is null, then we can't build any scans for it
334+ }
318335 var records = GetMs1Records ( frameId ) ;
319336 foreach ( Ms1Record record in records )
320337 {
@@ -453,40 +470,7 @@ internal List<TimsDataScan> BuildPasefScanFromPrecursor(IEnumerable<int> precurs
453470 pasefScans . Add ( dataScan ) ;
454471 }
455472
456- // Grab all fragmentation spectra for each precursor
457- // Each TimsDataScan in pasefScans corresponds to one precursor.
458- // A precursor can be isolated and fragmented in multiple pasef frames
459- // Here, we iterate through each frame, averaging the scans that correspond to each precursor
460- foreach ( long frameId in allFrames )
461- {
462- FrameProxy frame = FrameProxyFactory . GetFrameProxy ( frameId ) ;
463- //Iterate through all the datascans created above with this frame
464- foreach ( var scan in pasefScans )
465- {
466- if ( scan . FrameIds . Contains ( frameId ) )
467- {
468- List < uint [ ] > indexArrays = new ( ) ;
469- List < int [ ] > intensityArrays = new ( ) ;
470- for ( int mobilityScanIdx = scan . ScanNumberStart ; mobilityScanIdx < scan . ScanNumberEnd ; mobilityScanIdx ++ )
471- {
472- indexArrays . Add ( frame . GetScanIndices ( mobilityScanIdx - 1 ) ) ;
473- intensityArrays . Add ( frame . GetScanIntensities ( mobilityScanIdx - 1 ) ) ;
474- }
475- // Perform frame level averaging, where all scans from one frame associated with a given precursor are merged and centroided
476- // Need to convert indexArrays to one uint[] and intensityArrays to one int[]
477- ( double [ ] Mzs , int [ ] Intensities ) summedArrays = TofSpectraMerger . MergeArraysToMzArray ( indexArrays , intensityArrays , FrameProxyFactory ) ;
478- scan . AddComponentArrays ( summedArrays . Mzs , summedArrays . Intensities ) ;
479- }
480- }
481- }
482-
483- // Now, we average the fragmentation spectra (each spectra originating in a different frame)
484- // to yield one spectrum per precursor
485- foreach ( TimsDataScan scan in pasefScans )
486- {
487- scan . AverageComponentSpectra ( FrameProxyFactory , filteringParams ) ;
488- }
489-
473+ PopulateSpectraForPasefScans ( pasefScans , allFrames , filteringParams ) ;
490474 return pasefScans ;
491475 }
492476
@@ -530,6 +514,54 @@ internal IEnumerable<PasefRecord> GetPasefRecords(IEnumerable<int> precursorIds)
530514 }
531515 }
532516
517+ /// <summary>
518+ /// Grab all fragmentation spectra for each precursor
519+ /// Each TimsDataScan in pasefScansWithNullSpectra corresponds to one precursor.
520+ /// A precursor can be isolated and fragmented in multiple pasef frames
521+ /// Here, we iterate through each frame, averaging the scans that correspond to each precursor
522+ /// </summary>
523+ /// <param name="pasefScansWithNullSpectra">List of timsDataScans with metadata but no MzSpectrum</param>
524+ /// <param name="relevantFrameIds">Frames that contains scans to average for the given pasefScans</param>
525+ /// <param name="filteringParams">Filtering params that specify MS2 spectrum filtering options</param>
526+ internal void PopulateSpectraForPasefScans ( List < TimsDataScan > pasefScansWithNullSpectra , IEnumerable < long > relevantFrameIds , FilteringParams filteringParams )
527+ {
528+ foreach ( long frameId in relevantFrameIds )
529+ {
530+ FrameProxy frame = FrameProxyFactory . GetFrameProxy ( frameId ) ;
531+ if ( frame == null || ! frame . IsFrameValid ( ) )
532+ {
533+ FaultyFrameIds . Add ( frameId ) ;
534+ continue ; // If the frame is null, then we can't build any scans for it
535+ }
536+ //Iterate through all the datascans created above with this frame
537+ foreach ( var scan in pasefScansWithNullSpectra )
538+ {
539+ if ( scan . FrameIds . Contains ( frameId ) )
540+ {
541+ List < uint [ ] > indexArrays = new ( ) ;
542+ List < int [ ] > intensityArrays = new ( ) ;
543+ for ( int mobilityScanIdx = scan . ScanNumberStart ; mobilityScanIdx < scan . ScanNumberEnd ; mobilityScanIdx ++ )
544+ {
545+ indexArrays . Add ( frame . GetScanIndices ( mobilityScanIdx - 1 ) ) ;
546+ intensityArrays . Add ( frame . GetScanIntensities ( mobilityScanIdx - 1 ) ) ;
547+ }
548+ // Perform frame level averaging, where all scans from one frame associated with a given precursor are merged and centroided
549+ // Need to convert indexArrays to one uint[] and intensityArrays to one int[]
550+ ( double [ ] Mzs , int [ ] Intensities ) summedArrays = TofSpectraMerger . MergeArraysToMzArray ( indexArrays , intensityArrays , FrameProxyFactory ) ;
551+ scan . AddComponentArrays ( summedArrays . Mzs , summedArrays . Intensities ) ;
552+ }
553+ }
554+ }
555+
556+ // Now, we average the fragmentation spectra (each spectra originating in a different frame)
557+ // to yield one spectrum per precursor
558+ foreach ( TimsDataScan scan in pasefScansWithNullSpectra )
559+ {
560+ scan . SumComponentSpectra ( FrameProxyFactory , filteringParams ) ;
561+ }
562+ pasefScansWithNullSpectra . RemoveAll ( scan => scan . MassSpectrum == null || scan . MassSpectrum . Size < 1 ) ;
563+ }
564+
533565 private const string nativeIdFormat = "Frame ID + scan number range format" ;
534566 private const string massSpecFileFormat = ".D format" ;
535567 public override SourceFile GetSourceFile ( )
0 commit comments