rustdf/data/
handle.rs

1use crate::data::meta::{read_global_meta_sql, read_meta_data_sql, FrameMeta, GlobalMetaData};
2use crate::data::raw::BrukerTimsDataLibrary;
3use crate::data::utility::{
4    flatten_scan_values, parse_decompressed_bruker_binary_data, zstd_decompress,
5};
6use byteorder::{LittleEndian, ReadBytesExt};
7use mscore::data::spectrum::MsType;
8use mscore::timstof::frame::{ImsFrame, RawTimsFrame, TimsFrame};
9use mscore::timstof::slice::TimsSlice;
10use std::fs::File;
11use std::io::{Cursor, Read, Seek, SeekFrom};
12use std::path::PathBuf;
13
14use crate::data::acquisition::AcquisitionMode;
15use rayon::prelude::*;
16use rayon::ThreadPoolBuilder;
17
18use std::error::Error;
19
20fn lzf_decompress(data: &[u8], max_output_size: usize) -> Result<Vec<u8>, Box<dyn Error>> {
21    let decompressed_data = lzf::decompress(data, max_output_size)
22        .map_err(|e| format!("LZF decompression failed: {}", e))?;
23    Ok(decompressed_data)
24}
25
26fn parse_decompressed_bruker_binary_type1(
27    decompressed_bytes: &[u8],
28    scan_indices: &mut [i64],
29    tof_indices: &mut [u32],
30    intensities: &mut [u16],
31    scan_start: usize,
32    scan_index: usize,
33) -> usize {
34    // Interpret decompressed_bytes as a slice of i32
35    let int_count = decompressed_bytes.len() / 4;
36    let buffer =
37        unsafe { std::slice::from_raw_parts(decompressed_bytes.as_ptr() as *const i32, int_count) };
38
39    let mut tof_index = 0i32;
40    let mut previous_was_intensity = true;
41    let mut current_index = scan_start;
42
43    for &value in buffer {
44        if value >= 0 {
45            // positive value => intensity
46            if previous_was_intensity {
47                tof_index += 1;
48            }
49            tof_indices[current_index] = tof_index as u32;
50            intensities[current_index] = value as u16;
51            previous_was_intensity = true;
52            current_index += 1;
53        } else {
54            // negative value => indicates a jump in tof_index
55            tof_index -= value; // value is negative, so this adds |value| to tof_index
56            previous_was_intensity = false;
57        }
58    }
59
60    let scan_size = current_index - scan_start;
61    scan_indices[scan_index] = scan_size as i64;
62    scan_size
63}
64
65pub struct TimsRawDataLayout {
66    pub raw_data_path: String,
67    pub global_meta_data: GlobalMetaData,
68    pub frame_meta_data: Vec<FrameMeta>,
69    pub max_scan_count: i64,
70    pub frame_id_ptr: Vec<i64>,
71    pub tims_offset_values: Vec<i64>,
72    pub acquisition_mode: AcquisitionMode,
73}
74
75impl TimsRawDataLayout {
76    pub fn new(data_path: &str) -> Self {
77        // get the global and frame meta data
78        let global_meta_data = read_global_meta_sql(data_path).unwrap();
79        let frame_meta_data = read_meta_data_sql(data_path).unwrap();
80
81        // get the max scan count
82        let max_scan_count = frame_meta_data.iter().map(|x| x.num_scans).max().unwrap();
83
84        let mut frame_id_ptr: Vec<i64> = Vec::new();
85        frame_id_ptr.resize(frame_meta_data.len() + 1, 0);
86
87        // get the frame id_ptr values
88        for (i, row) in frame_meta_data.iter().enumerate() {
89            frame_id_ptr[i + 1] = row.num_peaks + frame_id_ptr[i];
90        }
91
92        // get the tims offset values
93        let tims_offset_values = frame_meta_data
94            .iter()
95            .map(|x| x.tims_id)
96            .collect::<Vec<i64>>();
97
98        // get the acquisition mode
99        let acquisition_mode = match frame_meta_data[0].scan_mode {
100            8 => AcquisitionMode::DDA,
101            9 => AcquisitionMode::DIA,
102            _ => AcquisitionMode::Unknown,
103        };
104
105        TimsRawDataLayout {
106            raw_data_path: data_path.to_string(),
107            global_meta_data,
108            frame_meta_data,
109            max_scan_count,
110            frame_id_ptr,
111            tims_offset_values,
112            acquisition_mode,
113        }
114    }
115}
116
117pub trait TimsData {
118    fn get_frame(&self, frame_id: u32) -> TimsFrame;
119    fn get_raw_frame(&self, frame_id: u32) -> RawTimsFrame;
120    fn get_slice(&self, frame_ids: Vec<u32>, num_threads: usize) -> TimsSlice;
121    fn get_acquisition_mode(&self) -> AcquisitionMode;
122    fn get_frame_count(&self) -> i32;
123    fn get_data_path(&self) -> &str;
124}
125
126pub trait IndexConverter {
127    fn tof_to_mz(&self, frame_id: u32, tof_values: &Vec<u32>) -> Vec<f64>;
128    fn mz_to_tof(&self, frame_id: u32, mz_values: &Vec<f64>) -> Vec<u32>;
129    fn scan_to_inverse_mobility(&self, frame_id: u32, scan_values: &Vec<u32>) -> Vec<f64>;
130    fn inverse_mobility_to_scan(
131        &self,
132        frame_id: u32,
133        inverse_mobility_values: &Vec<f64>,
134    ) -> Vec<u32>;
135}
136
137pub struct BrukerLibTimsDataConverter {
138    pub bruker_lib: BrukerTimsDataLibrary,
139}
140
141impl BrukerLibTimsDataConverter {
142    pub fn new(bruker_lib_path: &str, data_path: &str) -> Self {
143        let bruker_lib = BrukerTimsDataLibrary::new(bruker_lib_path, data_path).unwrap();
144        BrukerLibTimsDataConverter { bruker_lib }
145    }
146}
147impl IndexConverter for BrukerLibTimsDataConverter {
148    /// translate tof to mz values calling the bruker library
149    ///
150    /// # Arguments
151    ///
152    /// * `frame_id` - A u32 that holds the frame id
153    /// * `tof` - A vector of u32 that holds the tof values
154    ///
155    /// # Returns
156    ///
157    /// * `mz_values` - A vector of f64 that holds the mz values
158    ///
159    fn tof_to_mz(&self, frame_id: u32, tof: &Vec<u32>) -> Vec<f64> {
160        let mut dbl_tofs: Vec<f64> = Vec::new();
161        dbl_tofs.resize(tof.len(), 0.0);
162
163        for (i, &val) in tof.iter().enumerate() {
164            dbl_tofs[i] = val as f64;
165        }
166
167        let mut mz_values: Vec<f64> = Vec::new();
168        mz_values.resize(tof.len(), 0.0);
169
170        self.bruker_lib
171            .tims_index_to_mz(frame_id, &dbl_tofs, &mut mz_values)
172            .expect("Bruker binary call failed at: tims_index_to_mz;");
173
174        mz_values
175    }
176
177    fn mz_to_tof(&self, frame_id: u32, mz: &Vec<f64>) -> Vec<u32> {
178        let mut dbl_mz: Vec<f64> = Vec::new();
179        dbl_mz.resize(mz.len(), 0.0);
180
181        for (i, &val) in mz.iter().enumerate() {
182            dbl_mz[i] = val;
183        }
184
185        let mut tof_values: Vec<f64> = Vec::new();
186        tof_values.resize(mz.len(), 0.0);
187
188        self.bruker_lib
189            .tims_mz_to_index(frame_id, &dbl_mz, &mut tof_values)
190            .expect("Bruker binary call failed at: tims_mz_to_index;");
191
192        tof_values.iter().map(|&x| x.round() as u32).collect()
193    }
194
195    /// translate scan to inverse mobility values calling the bruker library
196    ///
197    /// # Arguments
198    ///
199    /// * `frame_id` - A u32 that holds the frame id
200    /// * `scan` - A vector of i32 that holds the scan values
201    ///
202    /// # Returns
203    ///
204    /// * `inv_mob` - A vector of f64 that holds the inverse mobility values
205    ///
206    fn scan_to_inverse_mobility(&self, frame_id: u32, scan: &Vec<u32>) -> Vec<f64> {
207        let mut dbl_scans: Vec<f64> = Vec::new();
208        dbl_scans.resize(scan.len(), 0.0);
209
210        for (i, &val) in scan.iter().enumerate() {
211            dbl_scans[i] = val as f64;
212        }
213
214        let mut inv_mob: Vec<f64> = Vec::new();
215        inv_mob.resize(scan.len(), 0.0);
216
217        self.bruker_lib
218            .tims_scan_to_inv_mob(frame_id, &dbl_scans, &mut inv_mob)
219            .expect("Bruker binary call failed at: tims_scannum_to_oneoverk0;");
220
221        inv_mob
222    }
223
224    /// translate inverse mobility to scan values calling the bruker library
225    ///
226    /// # Arguments
227    ///
228    /// * `frame_id` - A u32 that holds the frame id
229    /// * `inv_mob` - A vector of f64 that holds the inverse mobility values
230    ///
231    /// # Returns
232    ///
233    /// * `scan_values` - A vector of i32 that holds the scan values
234    ///
235    fn inverse_mobility_to_scan(&self, frame_id: u32, inv_mob: &Vec<f64>) -> Vec<u32> {
236        let mut dbl_inv_mob: Vec<f64> = Vec::new();
237        dbl_inv_mob.resize(inv_mob.len(), 0.0);
238
239        for (i, &val) in inv_mob.iter().enumerate() {
240            dbl_inv_mob[i] = val;
241        }
242
243        let mut scan_values: Vec<f64> = Vec::new();
244        scan_values.resize(inv_mob.len(), 0.0);
245
246        self.bruker_lib
247            .inv_mob_to_tims_scan(frame_id, &dbl_inv_mob, &mut scan_values)
248            .expect("Bruker binary call failed at: tims_oneoverk0_to_scannum;");
249
250        scan_values.iter().map(|&x| x.round() as u32).collect()
251    }
252}
253
254pub enum TimsIndexConverter {
255    Simple(SimpleIndexConverter),
256    BrukerLib(BrukerLibTimsDataConverter),
257}
258
259impl IndexConverter for TimsIndexConverter {
260    fn tof_to_mz(&self, frame_id: u32, tof_values: &Vec<u32>) -> Vec<f64> {
261        match self {
262            TimsIndexConverter::Simple(converter) => converter.tof_to_mz(frame_id, tof_values),
263            TimsIndexConverter::BrukerLib(converter) => converter.tof_to_mz(frame_id, tof_values),
264        }
265    }
266
267    fn mz_to_tof(&self, frame_id: u32, mz_values: &Vec<f64>) -> Vec<u32> {
268        match self {
269            TimsIndexConverter::Simple(converter) => converter.mz_to_tof(frame_id, mz_values),
270            TimsIndexConverter::BrukerLib(converter) => converter.mz_to_tof(frame_id, mz_values),
271        }
272    }
273
274    fn scan_to_inverse_mobility(&self, frame_id: u32, scan_values: &Vec<u32>) -> Vec<f64> {
275        match self {
276            TimsIndexConverter::Simple(converter) => {
277                converter.scan_to_inverse_mobility(frame_id, scan_values)
278            }
279            TimsIndexConverter::BrukerLib(converter) => {
280                converter.scan_to_inverse_mobility(frame_id, scan_values)
281            }
282        }
283    }
284
285    fn inverse_mobility_to_scan(
286        &self,
287        frame_id: u32,
288        inverse_mobility_values: &Vec<f64>,
289    ) -> Vec<u32> {
290        match self {
291            TimsIndexConverter::Simple(converter) => {
292                converter.inverse_mobility_to_scan(frame_id, inverse_mobility_values)
293            }
294            TimsIndexConverter::BrukerLib(converter) => {
295                converter.inverse_mobility_to_scan(frame_id, inverse_mobility_values)
296            }
297        }
298    }
299}
300
301pub struct TimsLazyLoder {
302    pub raw_data_layout: TimsRawDataLayout,
303    pub index_converter: TimsIndexConverter,
304}
305
306impl TimsData for TimsLazyLoder {
307    fn get_frame(&self, frame_id: u32) -> TimsFrame {
308        let frame_index = (frame_id - 1) as usize;
309
310        // turns out, there can be empty frames in the data, check for that, if so, return an empty frame
311        let num_peaks = self.raw_data_layout.frame_meta_data[frame_index].num_peaks;
312
313        if num_peaks == 0 {
314
315            let ms_type_raw = self.raw_data_layout.frame_meta_data[frame_index].ms_ms_type;
316
317            let ms_type = match ms_type_raw {
318                0 => MsType::Precursor,
319                8 => MsType::FragmentDda,
320                9 => MsType::FragmentDia,
321                _ => MsType::Unknown,
322            };
323
324            return TimsFrame {
325                frame_id: frame_id as i32,
326                ms_type,
327                scan: Vec::new(),
328                tof: Vec::new(),
329                ims_frame: ImsFrame {
330                    retention_time: self.raw_data_layout.frame_meta_data[(frame_id - 1) as usize]
331                        .time,
332                    mobility: Vec::new(),
333                    mz: Vec::new(),
334                    intensity: Vec::new(),
335                },
336            };
337        }
338
339        let offset = self.raw_data_layout.tims_offset_values[frame_index] as u64;
340
341        let mut file_path = PathBuf::from(&self.raw_data_layout.raw_data_path);
342        file_path.push("analysis.tdf_bin");
343        let mut infile = File::open(&file_path).unwrap();
344
345        infile.seek(SeekFrom::Start(offset)).unwrap();
346
347        let mut bin_buffer = [0u8; 4];
348        infile.read_exact(&mut bin_buffer).unwrap();
349        let bin_size = Cursor::new(bin_buffer).read_i32::<LittleEndian>().unwrap();
350
351        infile.read_exact(&mut bin_buffer).unwrap();
352
353        match self.raw_data_layout.global_meta_data.tims_compression_type {
354            1 => {
355                let scan_count =
356                    self.raw_data_layout.frame_meta_data[frame_index].num_scans as usize;
357                let num_peaks = num_peaks as usize;
358                let compression_offset = 8 + (scan_count + 1) * 4;
359
360                let mut scan_offsets_buffer = vec![0u8; (scan_count + 1) * 4];
361                infile.read_exact(&mut scan_offsets_buffer).unwrap();
362
363                let mut scan_offsets = Vec::with_capacity(scan_count + 1);
364                {
365                    let mut rdr = Cursor::new(&scan_offsets_buffer);
366                    for _ in 0..(scan_count + 1) {
367                        scan_offsets.push(rdr.read_i32::<LittleEndian>().unwrap());
368                    }
369                }
370
371                for offs in &mut scan_offsets {
372                    *offs -= compression_offset as i32;
373                }
374
375                let remaining_size = (bin_size as usize - compression_offset) as usize;
376                let mut compressed_data = vec![0u8; remaining_size];
377                infile.read_exact(&mut compressed_data).unwrap();
378
379                let mut scan_indices_ = vec![0i64; scan_count];
380                let mut tof_indices_ = vec![0u32; num_peaks];
381                let mut intensities_ = vec![0u16; num_peaks];
382
383                let mut scan_start = 0usize;
384
385                for scan_index in 0..scan_count {
386                    let start = scan_offsets[scan_index] as usize;
387                    let end = scan_offsets[scan_index + 1] as usize;
388
389                    if start == end {
390                        continue;
391                    }
392
393                    let max_output_size = num_peaks * 8;
394                    let decompressed_bytes =
395                        lzf_decompress(&compressed_data[start..end], max_output_size)
396                            .expect("LZF decompression failed.");
397
398                    scan_start += parse_decompressed_bruker_binary_type1(
399                        &decompressed_bytes,
400                        &mut scan_indices_,
401                        &mut tof_indices_,
402                        &mut intensities_,
403                        scan_start,
404                        scan_index,
405                    );
406                }
407
408                // Create a flat scan vector to match what flatten_scan_values expects
409                let mut scan = Vec::with_capacity(num_peaks);
410                {
411                    let mut current_scan_index = 0u32;
412                    for &size in &scan_indices_ {
413                        let sz = size as usize;
414                        for _ in 0..sz {
415                            scan.push(current_scan_index);
416                        }
417                        current_scan_index += 1;
418                    }
419                }
420
421                let intensity_dbl = intensities_.iter().map(|&x| x as f64).collect::<Vec<f64>>();
422                let tof_i32 = tof_indices_.iter().map(|&x| x as i32).collect::<Vec<i32>>();
423
424                let mz = self.index_converter.tof_to_mz(frame_id, &tof_indices_);
425                let inv_mobility = self
426                    .index_converter
427                    .scan_to_inverse_mobility(frame_id, &scan);
428
429                let ms_type_raw = self.raw_data_layout.frame_meta_data[frame_index].ms_ms_type;
430                let ms_type = match ms_type_raw {
431                    0 => MsType::Precursor,
432                    8 => MsType::FragmentDda,
433                    9 => MsType::FragmentDia,
434                    _ => MsType::Unknown,
435                };
436
437                TimsFrame {
438                    frame_id: frame_id as i32,
439                    ms_type,
440                    scan: scan.iter().map(|&x| x as i32).collect(),
441                    tof: tof_i32,
442                    ims_frame: ImsFrame {
443                        retention_time: self.raw_data_layout.frame_meta_data[frame_index].time,
444                        mobility: inv_mobility,
445                        mz,
446                        intensity: intensity_dbl,
447                    },
448                }
449            }
450
451            // Existing handling of Type 2
452            2 => {
453                let mut compressed_data = vec![0u8; bin_size as usize - 8];
454                infile.read_exact(&mut compressed_data).unwrap();
455
456                let decompressed_bytes = zstd_decompress(&compressed_data).unwrap();
457
458                let (scan, tof, intensity) =
459                    parse_decompressed_bruker_binary_data(&decompressed_bytes).unwrap();
460                let intensity_dbl = intensity.iter().map(|&x| x as f64).collect();
461                let tof_i32 = tof.iter().map(|&x| x as i32).collect();
462                let scan = flatten_scan_values(&scan, true);
463
464                let mz = self.index_converter.tof_to_mz(frame_id, &tof);
465                let inv_mobility = self
466                    .index_converter
467                    .scan_to_inverse_mobility(frame_id, &scan);
468
469                let ms_type_raw = self.raw_data_layout.frame_meta_data[frame_index].ms_ms_type;
470
471                let ms_type = match ms_type_raw {
472                    0 => MsType::Precursor,
473                    8 => MsType::FragmentDda,
474                    9 => MsType::FragmentDia,
475                    _ => MsType::Unknown,
476                };
477
478                TimsFrame {
479                    frame_id: frame_id as i32,
480                    ms_type,
481                    scan: scan.iter().map(|&x| x as i32).collect(),
482                    tof: tof_i32,
483                    ims_frame: ImsFrame {
484                        retention_time: self.raw_data_layout.frame_meta_data[frame_index].time,
485                        mobility: inv_mobility,
486                        mz,
487                        intensity: intensity_dbl,
488                    },
489                }
490            }
491
492            _ => {
493                panic!("TimsCompressionType is not 1 or 2.")
494            }
495        }
496    }
497
498    fn get_raw_frame(&self, frame_id: u32) -> RawTimsFrame {
499        let frame_index = (frame_id - 1) as usize;
500        let offset = self.raw_data_layout.tims_offset_values[frame_index] as u64;
501
502        // turns out, there can be empty frames in the data, check for that, if so, return an empty frame
503        let num_peaks = self.raw_data_layout.frame_meta_data[frame_index].num_peaks;
504
505        if num_peaks == 0 {
506            return RawTimsFrame {
507                frame_id: frame_id as i32,
508                retention_time: self.raw_data_layout.frame_meta_data[(frame_id - 1) as usize].time,
509                ms_type: MsType::Unknown,
510                scan: Vec::new(),
511                tof: Vec::new(),
512                intensity: Vec::new(),
513            };
514        }
515
516        let mut file_path = PathBuf::from(&self.raw_data_layout.raw_data_path);
517        file_path.push("analysis.tdf_bin");
518        let mut infile = File::open(&file_path).unwrap();
519
520        infile.seek(SeekFrom::Start(offset)).unwrap();
521
522        let mut bin_buffer = [0u8; 4];
523        infile.read_exact(&mut bin_buffer).unwrap();
524        let bin_size = Cursor::new(bin_buffer).read_i32::<LittleEndian>().unwrap();
525
526        infile.read_exact(&mut bin_buffer).unwrap();
527
528        match self.raw_data_layout.global_meta_data.tims_compression_type {
529            _ if self.raw_data_layout.global_meta_data.tims_compression_type == 1 => {
530                panic!("Decompression Type1 not implemented.");
531            }
532
533            // Extract from ZSTD compressed binary
534            _ if self.raw_data_layout.global_meta_data.tims_compression_type == 2 => {
535                let mut compressed_data = vec![0u8; bin_size as usize - 8];
536                infile.read_exact(&mut compressed_data).unwrap();
537
538                let decompressed_bytes = zstd_decompress(&compressed_data).unwrap();
539
540                let (scan, tof, intensity) =
541                    parse_decompressed_bruker_binary_data(&decompressed_bytes).unwrap();
542
543                let ms_type_raw = self.raw_data_layout.frame_meta_data[frame_index].ms_ms_type;
544
545                let ms_type = match ms_type_raw {
546                    0 => MsType::Precursor,
547                    8 => MsType::FragmentDda,
548                    9 => MsType::FragmentDia,
549                    _ => MsType::Unknown,
550                };
551
552                let frame = RawTimsFrame {
553                    frame_id: frame_id as i32,
554                    retention_time: self.raw_data_layout.frame_meta_data[(frame_id - 1) as usize]
555                        .time,
556                    ms_type,
557                    scan,
558                    tof,
559                    intensity: intensity.iter().map(|&x| x as f64).collect(),
560                };
561
562                return frame;
563            }
564
565            // Error on unknown compression algorithm
566            _ => {
567                panic!("TimsCompressionType is not 1 or 2.")
568            }
569        }
570    }
571
572    fn get_slice(&self, frame_ids: Vec<u32>, _num_threads: usize) -> TimsSlice {
573        let result: Vec<TimsFrame> = frame_ids.into_iter().map(|f| self.get_frame(f)).collect();
574
575        TimsSlice { frames: result }
576    }
577
578    fn get_acquisition_mode(&self) -> AcquisitionMode {
579        self.raw_data_layout.acquisition_mode.clone()
580    }
581
582    fn get_frame_count(&self) -> i32 {
583        self.raw_data_layout.frame_meta_data.len() as i32
584    }
585
586    fn get_data_path(&self) -> &str {
587        &self.raw_data_layout.raw_data_path
588    }
589}
590
591pub struct TimsInMemoryLoader {
592    pub raw_data_layout: TimsRawDataLayout,
593    pub index_converter: TimsIndexConverter,
594    compressed_data: Vec<u8>,
595}
596
597impl TimsData for TimsInMemoryLoader {
598    fn get_frame(&self, frame_id: u32) -> TimsFrame {
599        let raw_frame = self.get_raw_frame(frame_id);
600
601        let raw_frame = match raw_frame.ms_type {
602            MsType::FragmentDda => raw_frame.smooth(1).centroid(1),
603            _ => raw_frame,
604        };
605
606        // if raw frame is empty, return an empty frame
607        if raw_frame.scan.is_empty() {
608            return TimsFrame::default();
609        }
610
611        let tof_i32 = raw_frame.tof.iter().map(|&x| x as i32).collect();
612        let scan = flatten_scan_values(&raw_frame.scan, true);
613
614        let mz = self.index_converter.tof_to_mz(frame_id, &raw_frame.tof);
615        let inverse_mobility = self
616            .index_converter
617            .scan_to_inverse_mobility(frame_id, &scan);
618
619        let ims_frame = ImsFrame {
620            retention_time: raw_frame.retention_time,
621            mz,
622            intensity: raw_frame.intensity,
623            mobility: inverse_mobility,
624        };
625
626        TimsFrame {
627            frame_id: frame_id as i32,
628            ms_type: raw_frame.ms_type,
629            scan: scan.iter().map(|&x| x as i32).collect(),
630            tof: tof_i32,
631            ims_frame,
632        }
633    }
634
635    fn get_raw_frame(&self, frame_id: u32) -> RawTimsFrame {
636        let frame_index = (frame_id - 1) as usize;
637        let offset = self.raw_data_layout.tims_offset_values[frame_index] as usize;
638
639        let bin_size_offset = offset + 4; // Assuming the size is stored immediately before the frame data
640        let bin_size = Cursor::new(&self.compressed_data[offset..bin_size_offset])
641            .read_i32::<LittleEndian>()
642            .unwrap();
643
644        let data_offset = bin_size_offset + 4; // Adjust based on actual structure
645        let frame_data = &self.compressed_data[data_offset..data_offset + bin_size as usize - 8];
646
647        let decompressed_bytes = zstd_decompress(&frame_data).unwrap();
648
649        let (scan, tof, intensity) =
650            parse_decompressed_bruker_binary_data(&decompressed_bytes).unwrap();
651
652        let ms_type_raw = self.raw_data_layout.frame_meta_data[frame_index].ms_ms_type;
653
654        let ms_type = match ms_type_raw {
655            0 => MsType::Precursor,
656            8 => MsType::FragmentDda,
657            9 => MsType::FragmentDia,
658            _ => MsType::Unknown,
659        };
660
661        let raw_frame = RawTimsFrame {
662            frame_id: frame_id as i32,
663            retention_time: self.raw_data_layout.frame_meta_data[(frame_id - 1) as usize].time,
664            ms_type,
665            scan,
666            tof,
667            intensity: intensity.iter().map(|&x| x as f64).collect(),
668        };
669
670        raw_frame
671    }
672
673    fn get_slice(&self, frame_ids: Vec<u32>, num_threads: usize) -> TimsSlice {
674        let pool = ThreadPoolBuilder::new()
675            .num_threads(num_threads)
676            .build()
677            .unwrap();
678        let frames = pool.install(|| {
679            frame_ids
680                .par_iter()
681                .map(|&frame_id| self.get_frame(frame_id))
682                .collect()
683        });
684
685        TimsSlice { frames }
686    }
687
688    fn get_acquisition_mode(&self) -> AcquisitionMode {
689        self.raw_data_layout.acquisition_mode.clone()
690    }
691
692    fn get_frame_count(&self) -> i32 {
693        self.raw_data_layout.frame_meta_data.len() as i32
694    }
695
696    fn get_data_path(&self) -> &str {
697        &self.raw_data_layout.raw_data_path
698    }
699}
700
701pub enum TimsDataLoader {
702    InMemory(TimsInMemoryLoader),
703    Lazy(TimsLazyLoder),
704}
705
706impl TimsDataLoader {
707    pub fn new_lazy(
708        bruker_lib_path: &str,
709        data_path: &str,
710        use_bruker_sdk: bool,
711        scan_max_index: u32,
712        im_lower: f64,
713        im_upper: f64,
714        tof_max_index: u32,
715        mz_lower: f64,
716        mz_upper: f64,
717    ) -> Self {
718        let raw_data_layout = TimsRawDataLayout::new(data_path);
719
720        let index_converter = match use_bruker_sdk {
721            true => TimsIndexConverter::BrukerLib(BrukerLibTimsDataConverter::new(
722                bruker_lib_path,
723                data_path,
724            )),
725            false => TimsIndexConverter::Simple(SimpleIndexConverter::from_boundaries(
726                mz_lower,
727                mz_upper,
728                tof_max_index,
729                im_lower,
730                im_upper,
731                scan_max_index,
732            )),
733        };
734
735        TimsDataLoader::Lazy(TimsLazyLoder {
736            raw_data_layout,
737            index_converter,
738        })
739    }
740
741    pub fn new_in_memory(
742        bruker_lib_path: &str,
743        data_path: &str,
744        use_bruker_sdk: bool,
745        scan_max_index: u32,
746        im_lower: f64,
747        im_upper: f64,
748        tof_max_index: u32,
749        mz_lower: f64,
750        mz_upper: f64,
751    ) -> Self {
752        let raw_data_layout = TimsRawDataLayout::new(data_path);
753
754        let index_converter = match use_bruker_sdk {
755            true => TimsIndexConverter::BrukerLib(BrukerLibTimsDataConverter::new(
756                bruker_lib_path,
757                data_path,
758            )),
759            false => TimsIndexConverter::Simple(SimpleIndexConverter::from_boundaries(
760                mz_lower,
761                mz_upper,
762                tof_max_index,
763                im_lower,
764                im_upper,
765                scan_max_index,
766            )),
767        };
768
769        let mut file_path = PathBuf::from(data_path);
770        file_path.push("analysis.tdf_bin");
771        let mut infile = File::open(file_path).unwrap();
772        let mut data = Vec::new();
773        infile.read_to_end(&mut data).unwrap();
774
775        TimsDataLoader::InMemory(TimsInMemoryLoader {
776            raw_data_layout,
777            index_converter,
778            compressed_data: data,
779        })
780    }
781    pub fn get_index_converter(&self) -> &dyn IndexConverter {
782        match self {
783            TimsDataLoader::InMemory(loader) => &loader.index_converter,
784            TimsDataLoader::Lazy(loader) => &loader.index_converter,
785        }
786    }
787}
788
789impl TimsData for TimsDataLoader {
790    fn get_frame(&self, frame_id: u32) -> TimsFrame {
791        match self {
792            TimsDataLoader::InMemory(loader) => loader.get_frame(frame_id),
793            TimsDataLoader::Lazy(loader) => loader.get_frame(frame_id),
794        }
795    }
796    fn get_raw_frame(&self, frame_id: u32) -> RawTimsFrame {
797        match self {
798            TimsDataLoader::InMemory(loader) => loader.get_raw_frame(frame_id),
799            TimsDataLoader::Lazy(loader) => loader.get_raw_frame(frame_id),
800        }
801    }
802
803    fn get_slice(&self, frame_ids: Vec<u32>, num_threads: usize) -> TimsSlice {
804        match self {
805            TimsDataLoader::InMemory(loader) => loader.get_slice(frame_ids, num_threads),
806            TimsDataLoader::Lazy(loader) => loader.get_slice(frame_ids, num_threads),
807        }
808    }
809
810    fn get_acquisition_mode(&self) -> AcquisitionMode {
811        match self {
812            TimsDataLoader::InMemory(loader) => loader.get_acquisition_mode(),
813            TimsDataLoader::Lazy(loader) => loader.get_acquisition_mode(),
814        }
815    }
816
817    fn get_frame_count(&self) -> i32 {
818        match self {
819            TimsDataLoader::InMemory(loader) => loader.get_frame_count(),
820            TimsDataLoader::Lazy(loader) => loader.get_frame_count(),
821        }
822    }
823
824    fn get_data_path(&self) -> &str {
825        match self {
826            TimsDataLoader::InMemory(loader) => loader.get_data_path(),
827            TimsDataLoader::Lazy(loader) => loader.get_data_path(),
828        }
829    }
830}
831
832pub struct SimpleIndexConverter {
833    pub tof_intercept: f64,
834    pub tof_slope: f64,
835    pub scan_intercept: f64,
836    pub scan_slope: f64,
837}
838
839impl SimpleIndexConverter {
840    pub fn from_boundaries(
841        mz_min: f64,
842        mz_max: f64,
843        tof_max_index: u32,
844        im_min: f64,
845        im_max: f64,
846        scan_max_index: u32,
847    ) -> Self {
848        let tof_intercept: f64 = mz_min.sqrt();
849        let tof_slope: f64 = (mz_max.sqrt() - tof_intercept) / tof_max_index as f64;
850
851        let scan_intercept: f64 = im_max;
852        let scan_slope: f64 = (im_min - scan_intercept) / scan_max_index as f64;
853        Self {
854            tof_intercept,
855            tof_slope,
856            scan_intercept,
857            scan_slope,
858        }
859    }
860}
861
862impl IndexConverter for SimpleIndexConverter {
863    fn tof_to_mz(&self, _frame_id: u32, _tof_values: &Vec<u32>) -> Vec<f64> {
864        let mut mz_values: Vec<f64> = Vec::new();
865        mz_values.resize(_tof_values.len(), 0.0);
866
867        for (i, &val) in _tof_values.iter().enumerate() {
868            mz_values[i] = (self.tof_intercept + self.tof_slope * val as f64).powi(2);
869        }
870
871        mz_values
872    }
873
874    fn mz_to_tof(&self, _frame_id: u32, _mz_values: &Vec<f64>) -> Vec<u32> {
875        let mut tof_values: Vec<u32> = Vec::new();
876        tof_values.resize(_mz_values.len(), 0);
877
878        for (i, &val) in _mz_values.iter().enumerate() {
879            tof_values[i] = ((val.sqrt() - self.tof_intercept) / self.tof_slope) as u32;
880        }
881
882        tof_values
883    }
884
885    fn scan_to_inverse_mobility(&self, _frame_id: u32, _scan_values: &Vec<u32>) -> Vec<f64> {
886        let mut inv_mobility_values: Vec<f64> = Vec::new();
887        inv_mobility_values.resize(_scan_values.len(), 0.0);
888
889        for (i, &val) in _scan_values.iter().enumerate() {
890            inv_mobility_values[i] = self.scan_intercept + self.scan_slope * val as f64;
891        }
892
893        inv_mobility_values
894    }
895
896    fn inverse_mobility_to_scan(
897        &self,
898        _frame_id: u32,
899        _inverse_mobility_values: &Vec<f64>,
900    ) -> Vec<u32> {
901        let mut scan_values: Vec<u32> = Vec::new();
902        scan_values.resize(_inverse_mobility_values.len(), 0);
903
904        for (i, &val) in _inverse_mobility_values.iter().enumerate() {
905            scan_values[i] = ((val - self.scan_intercept) / self.scan_slope) as u32;
906        }
907
908        scan_values
909    }
910}