rustdf/data/
dda.rs

1use crate::data::acquisition::AcquisitionMode;
2use crate::data::handle::{IndexConverter, TimsData, TimsDataLoader};
3use crate::data::meta::{
4    read_dda_precursor_meta, read_global_meta_sql, read_meta_data_sql, read_pasef_frame_ms_ms_info,
5    DDAPrecursor, PasefMsMsMeta,
6};
7use mscore::timstof::frame::{RawTimsFrame, TimsFrame};
8use mscore::timstof::slice::TimsSlice;
9use rayon::prelude::*;
10use rayon::ThreadPoolBuilder;
11use std::collections::BTreeMap;
12
13#[derive(Clone)]
14pub struct PASEFDDAFragment {
15    pub frame_id: u32,
16    pub precursor_id: u32,
17    pub collision_energy: f64,
18    pub selected_fragment: TimsFrame,
19}
20
21pub struct TimsDatasetDDA {
22    pub loader: TimsDataLoader,
23}
24
25impl TimsDatasetDDA {
26    pub fn new(
27        bruker_lib_path: &str,
28        data_path: &str,
29        in_memory: bool,
30        use_bruker_sdk: bool,
31    ) -> Self {
32        // TODO: error handling
33        let global_meta_data = read_global_meta_sql(data_path).unwrap();
34        let meta_data = read_meta_data_sql(data_path).unwrap();
35
36        let scan_max_index = meta_data.iter().map(|x| x.num_scans).max().unwrap() as u32;
37        let im_lower = global_meta_data.one_over_k0_range_lower;
38        let im_upper = global_meta_data.one_over_k0_range_upper;
39
40        let tof_max_index = global_meta_data.tof_max_index;
41        let mz_lower = global_meta_data.mz_acquisition_range_lower;
42        let mz_upper = global_meta_data.mz_acquisition_range_upper;
43
44        let loader = match in_memory {
45            true => TimsDataLoader::new_in_memory(
46                bruker_lib_path,
47                data_path,
48                use_bruker_sdk,
49                scan_max_index,
50                im_lower,
51                im_upper,
52                tof_max_index,
53                mz_lower,
54                mz_upper,
55            ),
56            false => TimsDataLoader::new_lazy(
57                bruker_lib_path,
58                data_path,
59                use_bruker_sdk,
60                scan_max_index,
61                im_lower,
62                im_upper,
63                tof_max_index,
64                mz_lower,
65                mz_upper,
66            ),
67        };
68        TimsDatasetDDA { loader }
69    }
70
71    /*
72        #[derive(Debug, Clone)]
73    pub struct DDAPrecursor {
74        pub frame_id: i64,
75        pub precursor_id: i64,
76        pub mono_mz: f64,
77        pub highest_intensity_mz: f64,
78        pub average_mz: f64,
79        pub charge: Option<i64>,
80        pub inverse_ion_mobility: f64,
81        pub collision_energy: f64,
82        pub precuror_total_intensity: f64,
83        pub isolation_mz: f64,
84        pub isolation_width: f64,
85    }
86         */
87
88    pub fn get_selected_precursors(&self) -> Vec<DDAPrecursor> {
89        let precursor_meta = read_dda_precursor_meta(&self.loader.get_data_path()).unwrap();
90        let pasef_meta = self.get_pasef_frame_ms_ms_info();
91
92        let precursor_id_to_pasef_meta: BTreeMap<i64, &PasefMsMsMeta> = pasef_meta
93            .iter()
94            .map(|x| (x.precursor_id as i64, x))
95            .collect();
96
97        // go over all precursors and get the precursor meta data
98        let result: Vec<_> = precursor_meta
99            .iter()
100            .map(|precursor| {
101                let pasef_meta = precursor_id_to_pasef_meta
102                    .get(&precursor.precursor_id)
103                    .unwrap();
104                DDAPrecursor {
105                    frame_id: precursor.precursor_frame_id,
106                    precursor_id: precursor.precursor_id,
107                    mono_mz: precursor.precursor_mz_monoisotopic,
108                    highest_intensity_mz: precursor.precursor_mz_highest_intensity,
109                    average_mz: precursor.precursor_mz_average,
110                    charge: precursor.precursor_charge,
111                    inverse_ion_mobility: self.scan_to_inverse_mobility(
112                        precursor.precursor_frame_id as u32,
113                        &vec![precursor.precursor_average_scan_number as u32],
114                    )[0],
115                    collision_energy: pasef_meta.collision_energy,
116                    precuror_total_intensity: precursor.precursor_total_intensity,
117                    isolation_mz: pasef_meta.isolation_mz,
118                    isolation_width: pasef_meta.isolation_width,
119                }
120            })
121            .collect();
122
123        result
124    }
125
126    pub fn get_precursor_frames(
127        &self,
128        min_intensity: f64,
129        max_num_peaks: usize,
130        num_threads: usize,
131    ) -> Vec<TimsFrame> {
132        // get all precursor frames
133        let meta_data = read_meta_data_sql(&self.loader.get_data_path()).unwrap();
134
135        // get the precursor frames
136        let precursor_frames = meta_data.iter().filter(|x| x.ms_ms_type == 0);
137
138        let tims_silce =
139            self.get_slice(precursor_frames.map(|x| x.id as u32).collect(), num_threads);
140
141        let result: Vec<_> = tims_silce
142            .frames
143            .par_iter()
144            .map(|frame| {
145                frame
146                    .filter_ranged(0.0, 2000.0, 0, 2000, 0.0, 5.0, min_intensity, 1e9)
147                    .top_n(max_num_peaks)
148            })
149            .collect();
150
151        result
152    }
153
154    pub fn get_pasef_frame_ms_ms_info(&self) -> Vec<PasefMsMsMeta> {
155        read_pasef_frame_ms_ms_info(&self.loader.get_data_path()).unwrap()
156    }
157
158    /// Get the fragment spectra for all PASEF selected precursors
159    pub fn get_pasef_fragments(&self, num_threads: usize) -> Vec<PASEFDDAFragment> {
160        // extract fragment spectra information
161        let pasef_info = self.get_pasef_frame_ms_ms_info();
162
163        let pool = ThreadPoolBuilder::new()
164            .num_threads(num_threads)
165            .build()
166            .unwrap();
167
168        let filtered_frames = pool.install(|| {
169            let result: Vec<_> = pasef_info
170                .par_iter()
171                .map(|pasef_info| {
172                    // get the frame
173                    let frame = self.loader.get_frame(pasef_info.frame_id as u32);
174
175                    // get five percent of the scan range
176                    let scan_margin = (pasef_info.scan_num_end - pasef_info.scan_num_begin) / 20;
177
178                    // get the fragment spectrum by scan range
179                    let filtered_frame = frame.filter_ranged(
180                        0.0,
181                        2000.0,
182                        (pasef_info.scan_num_begin - scan_margin) as i32,
183                        (pasef_info.scan_num_end + scan_margin) as i32,
184                        0.0,
185                        5.0,
186                        0.0,
187                        1e9,
188                    );
189
190                    PASEFDDAFragment {
191                        frame_id: pasef_info.frame_id as u32,
192                        precursor_id: pasef_info.precursor_id as u32,
193                        collision_energy: pasef_info.collision_energy,
194                        // flatten the spectrum
195                        selected_fragment: filtered_frame,
196                    }
197                })
198                .collect();
199
200            result
201        });
202
203        filtered_frames
204    }
205}
206
207impl TimsData for TimsDatasetDDA {
208    fn get_frame(&self, frame_id: u32) -> TimsFrame {
209        self.loader.get_frame(frame_id)
210    }
211
212    fn get_raw_frame(&self, frame_id: u32) -> RawTimsFrame {
213        self.loader.get_raw_frame(frame_id)
214    }
215
216    fn get_slice(&self, frame_ids: Vec<u32>, num_threads: usize) -> TimsSlice {
217        self.loader.get_slice(frame_ids, num_threads)
218    }
219
220    fn get_acquisition_mode(&self) -> AcquisitionMode {
221        self.loader.get_acquisition_mode().clone()
222    }
223
224    fn get_frame_count(&self) -> i32 {
225        self.loader.get_frame_count()
226    }
227
228    fn get_data_path(&self) -> &str {
229        &self.loader.get_data_path()
230    }
231}
232
233impl IndexConverter for TimsDatasetDDA {
234    fn tof_to_mz(&self, frame_id: u32, tof_values: &Vec<u32>) -> Vec<f64> {
235        self.loader
236            .get_index_converter()
237            .tof_to_mz(frame_id, tof_values)
238    }
239
240    fn mz_to_tof(&self, frame_id: u32, mz_values: &Vec<f64>) -> Vec<u32> {
241        self.loader
242            .get_index_converter()
243            .mz_to_tof(frame_id, mz_values)
244    }
245
246    fn scan_to_inverse_mobility(&self, frame_id: u32, scan_values: &Vec<u32>) -> Vec<f64> {
247        self.loader
248            .get_index_converter()
249            .scan_to_inverse_mobility(frame_id, scan_values)
250    }
251
252    fn inverse_mobility_to_scan(
253        &self,
254        frame_id: u32,
255        inverse_mobility_values: &Vec<f64>,
256    ) -> Vec<u32> {
257        self.loader
258            .get_index_converter()
259            .inverse_mobility_to_scan(frame_id, inverse_mobility_values)
260    }
261}