Skip to main content

timsrust_core/
frames.rs

1use rayon::iter::IntoParallelIterator;
2use timsrust_utils::{
3    custom_error,
4    reader::{IndexedReader, Reader},
5};
6// use timsrust_utils::reader::{IndexedReader, Reader};
7
8use crate::{Intensity, IntensityIndex, Mz, TofIndex, coordinates::Converter};
9
10use super::{AcquisitionType, QuadrupoleSettings};
11use rayon::prelude::*;
12use std::sync::Arc;
13
14/// A frame with all unprocessed data as it was acquired.
15#[derive(Clone, Debug, Default, PartialEq)]
16pub struct Frame {
17    ions: FrameIons,
18    info: FrameInfo,
19}
20
21impl Frame {
22    pub fn get_corrected_intensity(&self, index: usize) -> f64 {
23        let v = u32::from(self.ions.intensities[index]) as f64;
24        self.info.intensity_correction_factor * v
25    }
26
27    pub fn info(&self) -> &FrameInfo {
28        &self.info
29    }
30
31    pub fn ions(&self) -> &FrameIons {
32        &self.ions
33    }
34
35    pub fn set_data(
36        &mut self,
37        scan_offsets: Vec<usize>,
38        tof_indices: Vec<TofIndex>,
39        intensities: Vec<IntensityIndex>,
40    ) {
41        self.ions = FrameIons::new(scan_offsets, tof_indices, intensities);
42    }
43
44    pub fn set_ions(&mut self, ions: FrameIons) {
45        self.ions = ions;
46    }
47
48    pub fn set_info(&mut self, info: FrameInfo) {
49        self.info = info;
50    }
51
52    pub fn len(&self) -> usize {
53        self.ions.intensities.len()
54    }
55
56    pub fn is_empty(&self) -> bool {
57        self.ions.is_empty()
58    }
59
60    pub fn index(&self) -> usize {
61        self.info().index
62    }
63}
64
65/// The MS level used.
66#[derive(Debug, PartialEq, Default, Clone, Copy)]
67pub enum MSLevel {
68    MS1,
69    MS2,
70    /// Default value.
71    #[default]
72    Unknown,
73}
74
75impl MSLevel {
76    pub fn read_from_msms_type(msms_type: u8) -> MSLevel {
77        match msms_type {
78            0 => MSLevel::MS1,
79            8 => MSLevel::MS2,
80            9 => MSLevel::MS2,
81            _ => MSLevel::Unknown,
82        }
83    }
84}
85
86#[derive(Clone, Debug, PartialEq, Default)]
87pub struct FrameIons {
88    scan_offsets: Vec<usize>,
89    tof_indices: Vec<TofIndex>,
90    intensities: Vec<IntensityIndex>,
91}
92
93impl FrameIons {
94    pub fn new(
95        scan_offsets: Vec<usize>,
96        tof_indices: Vec<TofIndex>,
97        intensities: Vec<IntensityIndex>,
98    ) -> Self {
99        Self {
100            scan_offsets,
101            tof_indices,
102            intensities,
103        }
104    }
105
106    pub fn is_empty(&self) -> bool {
107        self.intensities.is_empty()
108    }
109
110    pub fn scan_offsets(&self) -> &Vec<usize> {
111        &self.scan_offsets
112    }
113
114    pub fn tof_indices(&self) -> &Vec<TofIndex> {
115        &self.tof_indices
116    }
117
118    pub fn mz_values<C: Converter<TofIndex, Mz>>(
119        &self,
120        converter: &C,
121    ) -> Vec<Mz> {
122        converter.batch_convert(&self.tof_indices)
123    }
124
125    pub fn intensities(&self) -> &Vec<IntensityIndex> {
126        &self.intensities
127    }
128
129    pub fn intensity_values<C: Converter<IntensityIndex, Intensity>>(
130        &self,
131        converter: &C,
132    ) -> Vec<Intensity> {
133        converter.batch_convert(&self.intensities)
134    }
135
136    pub fn read_scan(
137        &self,
138        scan_index: usize,
139    ) -> impl Iterator<Item = (TofIndex, IntensityIndex)> + '_ {
140        let (start, end) = if scan_index >= self.scan_count() {
141            (0, 0)
142        } else {
143            (
144                self.scan_offsets[scan_index],
145                self.scan_offsets[scan_index + 1],
146            )
147        };
148        (start..end).map(move |index| {
149            (self.tof_indices[index], self.intensities[index])
150        })
151    }
152
153    pub fn scan_count(&self) -> usize {
154        self.scan_offsets.len() - 1
155    }
156
157    pub fn add_info(self, info: FrameInfo) -> Frame {
158        Frame { ions: self, info }
159    }
160}
161
162#[derive(Clone, Debug, Default, PartialEq)]
163pub struct FrameInfo {
164    quadrupole_settings: Arc<QuadrupoleSettings>,
165    index: usize,
166    rt_in_seconds: f64,
167    intensity_correction_factor: f64,
168    acquisition_type: AcquisitionType,
169    ms_level: MSLevel,
170    window_group: u8,
171    cycle_index: Option<usize>,
172}
173
174impl FrameInfo {
175    #[allow(clippy::too_many_arguments)]
176    pub fn new(
177        quadrupole_settings: Arc<QuadrupoleSettings>,
178        index: usize,
179        rt_in_seconds: f64,
180        intensity_correction_factor: f64,
181        acquisition_type: AcquisitionType,
182        ms_level: MSLevel,
183        window_group: u8,
184        cycle_index: Option<usize>,
185    ) -> Self {
186        Self {
187            quadrupole_settings,
188            index,
189            rt_in_seconds,
190            intensity_correction_factor,
191            acquisition_type,
192            ms_level,
193            window_group,
194            cycle_index,
195        }
196    }
197
198    pub fn quadrupole_settings(&self) -> &Arc<QuadrupoleSettings> {
199        &self.quadrupole_settings
200    }
201
202    pub fn index(&self) -> usize {
203        self.index
204    }
205
206    pub fn cycle_index(&self) -> Option<usize> {
207        self.cycle_index
208    }
209
210    pub fn rt_in_seconds(&self) -> f64 {
211        self.rt_in_seconds
212    }
213
214    pub fn intensity_correction_factor(&self) -> f64 {
215        self.intensity_correction_factor
216    }
217
218    pub fn acquisition_type(&self) -> AcquisitionType {
219        self.acquisition_type
220    }
221
222    pub fn ms_level(&self) -> MSLevel {
223        self.ms_level
224    }
225
226    pub fn window_group(&self) -> u8 {
227        self.window_group
228    }
229
230    pub fn add_ions(self, ions: FrameIons) -> Frame {
231        Frame { ions, info: self }
232    }
233}
234#[derive(Debug)]
235pub struct FrameReader<IonReader, InfoReader> {
236    ion_reader: IonReader,
237    info_reader: InfoReader,
238}
239
240impl<IonReader, InfoReader> FrameReader<IonReader, InfoReader>
241where
242    IonReader: Reader<FrameIons>,
243    InfoReader: Reader<FrameInfo> + IndexedReader<FrameInfo>,
244{
245    pub fn new(ion_reader: IonReader, info_reader: InfoReader) -> Self {
246        Self {
247            ion_reader,
248            info_reader,
249        }
250    }
251
252    pub fn ion_reader(&self) -> &IonReader {
253        &self.ion_reader
254    }
255
256    pub fn info_reader(&self) -> &InfoReader {
257        &self.info_reader
258    }
259
260    pub fn ion_reader_index(
261        &self,
262        index: usize,
263    ) -> Result<usize, FrameReaderError> {
264        Ok(index)
265    }
266
267    pub fn info_reader_index(
268        &self,
269        index: usize,
270    ) -> Result<usize, FrameReaderError> {
271        Ok(index)
272    }
273
274    pub fn get_ions(
275        &self,
276        index: usize,
277    ) -> Result<FrameIons, FrameReaderError> {
278        let index = self.ion_reader_index(index)?;
279        let result = self
280            .ion_reader()
281            .get(index)
282            .map_err(|e| FrameReaderError::new(e.to_string()))?;
283        Ok(result)
284    }
285
286    pub fn get_info(
287        &self,
288        index: usize,
289    ) -> Result<FrameInfo, FrameReaderError> {
290        let index = self.info_reader_index(index)?;
291        let result = self
292            .info_reader()
293            .get(index)
294            .map_err(|e| FrameReaderError::new(e.to_string()))?;
295        Ok(result)
296    }
297
298    pub fn get_frame(&self, index: usize) -> Result<Frame, FrameReaderError> {
299        let info = self.get_info(index)?;
300        let ions = self.get_ions(index)?;
301        let mut frame = Frame::default();
302        frame.set_ions(ions);
303        frame.set_info(info);
304        Ok(frame)
305    }
306
307    /// Return a [`Frame`] populated with only [`FrameInfo`] (no ion data loaded).
308    pub fn get_partial_frame_without_ions(
309        &self,
310        index: usize,
311    ) -> Result<Frame, FrameReaderError> {
312        let info = self.get_info(index)?;
313        let mut frame = Frame::default();
314        frame.set_info(info);
315        Ok(frame)
316    }
317
318    pub fn len(&self) -> usize {
319        self.iter_indices().count()
320    }
321
322    pub fn is_empty(&self) -> bool {
323        self.len() == 0
324    }
325
326    // pub fn size(&self) -> Option<usize> {
327    //     self.info_reader().size()
328    // }
329
330    pub fn iter_indices(&self) -> impl Iterator<Item = usize> {
331        self.info_reader().iter()
332    }
333
334    pub fn filter<'a, F: Fn(&Frame) -> bool + Sync + Send + 'a>(
335        &'a self,
336        predicate: F,
337    ) -> impl Iterator<Item = Result<Frame, FrameReaderError>> {
338        self.iter_indices().filter_map(move |x| {
339            match self.info_reader().get(x) {
340                Ok(frame_info) => {
341                    let mut partial_frame = Frame::default();
342                    partial_frame.set_info(frame_info);
343                    if predicate(&partial_frame) {
344                        let ions = self.get_ions(x).ok()?;
345                        partial_frame.set_ions(ions);
346                        Some(Ok(partial_frame))
347                    } else {
348                        None
349                    }
350                },
351                Err(e) => Some(Err(FrameReaderError::new(e.to_string()))),
352            }
353        })
354    }
355
356    pub fn parallel_filter<'a, F: Fn(&Frame) -> bool + Sync + Send + 'a>(
357        &'a self,
358        predicate: F,
359    ) -> impl ParallelIterator<Item = Result<Frame, FrameReaderError>> + 'a
360    where
361        Self: Sync + Send + 'a,
362    {
363        self.iter_indices()
364            .collect::<Vec<_>>()
365            .into_par_iter()
366            .filter_map(move |x| match self.info_reader().get(x) {
367                Ok(frame_info) => {
368                    let mut partial_frame = Frame::default();
369                    partial_frame.set_info(frame_info);
370                    if predicate(&partial_frame) {
371                        let ions = self.get_ions(x).ok()?;
372                        partial_frame.set_ions(ions);
373                        Some(Ok(partial_frame))
374                    } else {
375                        None
376                    }
377                },
378                Err(e) => Some(Err(FrameReaderError::new(e.to_string()))),
379            })
380    }
381}
382
383custom_error!(pub FrameReaderError);