1use crate::data::acquisition::AcquisitionMode;
2use crate::data::handle::{IndexConverter, TimsData, TimsDataLoader};
3use crate::data::meta::{read_dda_precursor_meta, read_global_meta_sql, read_meta_data_sql, read_pasef_frame_ms_ms_info, DDAPrecursor, DDAPrecursorMeta, PasefMsMsMeta};
4use mscore::timstof::frame::{ImsFrame, RawTimsFrame, TimsFrame};
5use mscore::timstof::slice::TimsSlice;
6use mscore::timstof::spectrum_processing::{
7 PASEFFragmentData, PreprocessedSpectrum, SpectrumProcessingConfig,
8 process_pasef_fragments_batch,
9};
10use rayon::prelude::*;
11use rayon::ThreadPoolBuilder;
12use std::collections::BTreeMap;
13use rand::prelude::IteratorRandom;
14use mscore::data::spectrum::MsType;
15use std::collections::HashMap;
16
17#[derive(Clone)]
18pub struct PASEFDDAFragment {
19 pub frame_id: u32,
20 pub precursor_id: u32,
21 pub collision_energy: f64,
22 pub selected_fragment: TimsFrame,
23}
24
25#[derive(Clone, Debug, Default)]
27pub struct SignalMoments {
28 pub mean: f64,
29 pub variance: f64,
30 pub skewness: f64,
31 pub apex: f64,
32 pub fwhm: f64,
33 pub total_intensity: f64,
34}
35
36impl SignalMoments {
37 pub fn from_signal(coords: &[f64], intensities: &[f64]) -> Self {
39 if coords.is_empty() || intensities.iter().sum::<f64>() == 0.0 {
40 return Self::default();
41 }
42
43 let total: f64 = intensities.iter().sum();
44
45 let mean: f64 = coords.iter()
47 .zip(intensities.iter())
48 .map(|(c, i)| c * i / total)
49 .sum();
50
51 let variance: f64 = coords.iter()
53 .zip(intensities.iter())
54 .map(|(c, i)| i / total * (c - mean).powi(2))
55 .sum();
56
57 let std = variance.sqrt().max(1e-10);
59 let skewness: f64 = coords.iter()
60 .zip(intensities.iter())
61 .map(|(c, i)| i / total * ((c - mean) / std).powi(3))
62 .sum();
63
64 let apex_idx = intensities.iter()
66 .enumerate()
67 .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
68 .map(|(i, _)| i)
69 .unwrap_or(0);
70 let apex = coords.get(apex_idx).copied().unwrap_or(0.0);
71
72 let half_max = intensities.get(apex_idx).copied().unwrap_or(0.0) / 2.0;
74 let above_half: Vec<f64> = coords.iter()
75 .zip(intensities.iter())
76 .filter(|(_, i)| **i >= half_max)
77 .map(|(c, _)| *c)
78 .collect();
79 let fwhm = if above_half.len() >= 2 {
80 above_half.last().unwrap_or(&0.0) - above_half.first().unwrap_or(&0.0)
81 } else {
82 2.355 * std };
84
85 SignalMoments {
86 mean,
87 variance,
88 skewness,
89 apex,
90 fwhm,
91 total_intensity: total,
92 }
93 }
94}
95
96#[derive(Clone, Debug)]
98pub struct PrecursorMS1Signal {
99 pub precursor_id: u32,
100
101 pub rt_coords: Vec<f64>, pub rt_intensities: Vec<f64>,
104 pub rt_moments: SignalMoments,
105
106 pub im_coords: Vec<f64>, pub im_intensities: Vec<f64>,
109 pub im_moments: SignalMoments,
110
111 pub isotope_mz: Vec<f64>,
113 pub isotope_intensity: Vec<f64>,
114 pub mz_moments: SignalMoments,
115
116 pub raw_rt: Vec<f64>, pub raw_mz: Vec<f64>, pub raw_mobility: Vec<f64>, pub raw_intensity: Vec<f64>, }
122
123#[derive(Clone, Debug)]
125pub struct PrecursorCoord {
126 pub precursor_id: u32,
127 pub mz: f64, pub mono_mz: f64, pub rt_seconds: f64,
130 pub mobility: f64, pub im_start: f64, pub im_end: f64, pub charge: i32,
134}
135
136pub struct TimsDatasetDDA {
137 pub loader: TimsDataLoader,
138 pub pasef_meta: Vec<PasefMsMsMeta>,
139}
140
141impl TimsDatasetDDA {
142 pub fn new(
143 bruker_lib_path: &str,
144 data_path: &str,
145 in_memory: bool,
146 use_bruker_sdk: bool,
147 ) -> Self {
148 let global_meta_data = read_global_meta_sql(data_path).unwrap();
150 let meta_data = read_meta_data_sql(data_path).unwrap();
151
152 let scan_max_index = meta_data.iter().map(|x| x.num_scans).max().unwrap() as u32;
153 let im_lower = global_meta_data.one_over_k0_range_lower;
154 let im_upper = global_meta_data.one_over_k0_range_upper;
155
156 let tof_max_index = global_meta_data.tof_max_index;
157 let mz_lower = global_meta_data.mz_acquisition_range_lower;
158 let mz_upper = global_meta_data.mz_acquisition_range_upper;
159
160 let loader = match in_memory {
161 true => TimsDataLoader::new_in_memory(
162 bruker_lib_path,
163 data_path,
164 use_bruker_sdk,
165 scan_max_index,
166 im_lower,
167 im_upper,
168 tof_max_index,
169 mz_lower,
170 mz_upper,
171 ),
172 false => TimsDataLoader::new_lazy(
173 bruker_lib_path,
174 data_path,
175 use_bruker_sdk,
176 scan_max_index,
177 im_lower,
178 im_upper,
179 tof_max_index,
180 mz_lower,
181 mz_upper,
182 ),
183 };
184
185 let pasef_meta = read_pasef_frame_ms_ms_info(data_path).unwrap();
186
187 TimsDatasetDDA { loader, pasef_meta }
188 }
189
190 pub fn new_with_calibration(
206 data_path: &str,
207 in_memory: bool,
208 bruker_lib_path: &str,
209 im_lookup: Vec<f64>,
210 ) -> Self {
211 let global_meta_data = read_global_meta_sql(data_path).unwrap();
212
213 let tof_max_index = global_meta_data.tof_max_index;
214 let mz_lower = global_meta_data.mz_acquisition_range_lower;
215 let mz_upper = global_meta_data.mz_acquisition_range_upper;
216
217 let loader = match in_memory {
218 true => TimsDataLoader::new_in_memory_with_calibration(
219 data_path,
220 bruker_lib_path,
221 tof_max_index,
222 mz_lower,
223 mz_upper,
224 im_lookup,
225 ),
226 false => TimsDataLoader::new_lazy_with_calibration(
227 data_path,
228 bruker_lib_path,
229 tof_max_index,
230 mz_lower,
231 mz_upper,
232 im_lookup,
233 ),
234 };
235
236 let pasef_meta = read_pasef_frame_ms_ms_info(data_path).unwrap();
237
238 TimsDatasetDDA { loader, pasef_meta }
239 }
240
241 pub fn new_with_mz_calibration(
255 data_path: &str,
256 in_memory: bool,
257 tof_intercept: f64,
258 tof_slope: f64,
259 ) -> Self {
260 let global_meta_data = read_global_meta_sql(data_path).unwrap();
261 let frame_meta = read_meta_data_sql(data_path).unwrap();
262
263 let scan_max_index = frame_meta.iter().map(|x| x.num_scans).max().unwrap() as u32;
264 let im_lower = global_meta_data.one_over_k0_range_lower;
265 let im_upper = global_meta_data.one_over_k0_range_upper;
266
267 let loader = match in_memory {
268 true => TimsDataLoader::new_in_memory_with_mz_calibration(
269 data_path,
270 tof_intercept,
271 tof_slope,
272 im_lower,
273 im_upper,
274 scan_max_index,
275 ),
276 false => TimsDataLoader::new_lazy_with_mz_calibration(
277 data_path,
278 tof_intercept,
279 tof_slope,
280 im_lower,
281 im_upper,
282 scan_max_index,
283 ),
284 };
285
286 let pasef_meta = read_pasef_frame_ms_ms_info(data_path).unwrap();
287
288 TimsDatasetDDA { loader, pasef_meta }
289 }
290
291 pub fn new_with_bruker_formula(
299 data_path: &str,
300 in_memory: bool,
301 calibration_frame_id: u32,
302 ) -> Self {
303 let loader = match in_memory {
304 true => TimsDataLoader::new_in_memory_with_bruker_formula(
305 data_path,
306 calibration_frame_id,
307 ),
308 false => {
309 TimsDataLoader::new_lazy_with_bruker_formula(data_path, calibration_frame_id)
310 }
311 };
312 let pasef_meta = read_pasef_frame_ms_ms_info(data_path).unwrap();
313 TimsDatasetDDA { loader, pasef_meta }
314 }
315
316 pub fn uses_bruker_sdk(&self) -> bool {
319 self.loader.uses_bruker_sdk()
320 }
321
322 pub fn get_selected_precursors(&self) -> Vec<DDAPrecursor> {
323 let precursor_meta = read_dda_precursor_meta(&self.loader.get_data_path()).unwrap();
324 let pasef_meta = &self.pasef_meta;
325
326 let precursor_id_to_pasef_meta: BTreeMap<i64, &PasefMsMsMeta> = pasef_meta
327 .iter()
328 .map(|x| (x.precursor_id as i64, x))
329 .collect();
330
331 let result: Vec<_> = precursor_meta
333 .iter()
334 .map(|precursor| {
335 let pasef_meta = precursor_id_to_pasef_meta
336 .get(&precursor.precursor_id)
337 .unwrap();
338 DDAPrecursor {
339 frame_id: precursor.precursor_frame_id,
340 precursor_id: precursor.precursor_id,
341 mono_mz: precursor.precursor_mz_monoisotopic,
342 highest_intensity_mz: precursor.precursor_mz_highest_intensity,
343 average_mz: precursor.precursor_mz_average,
344 charge: precursor.precursor_charge,
345 inverse_ion_mobility: self.scan_to_inverse_mobility(
346 precursor.precursor_frame_id as u32,
347 &vec![precursor.precursor_average_scan_number as u32],
348 )[0],
349 collision_energy: pasef_meta.collision_energy,
350 precuror_total_intensity: precursor.precursor_total_intensity,
351 isolation_mz: pasef_meta.isolation_mz,
352 isolation_width: pasef_meta.isolation_width,
353 }
354 })
355 .collect();
356
357 result
358 }
359
360 pub fn get_precursor_frames(
361 &self,
362 min_intensity: f64,
363 max_num_peaks: usize,
364 num_threads: usize,
365 ) -> Vec<TimsFrame> {
366 let meta_data = read_meta_data_sql(&self.loader.get_data_path()).unwrap();
368
369 let precursor_frames = meta_data.iter().filter(|x| x.ms_ms_type == 0);
371
372 let tims_silce =
373 self.get_slice(precursor_frames.map(|x| x.id as u32).collect(), num_threads);
374
375 let result: Vec<_> = tims_silce
376 .frames
377 .par_iter()
378 .map(|frame| {
379 frame
380 .filter_ranged(0.0, 2000.0, 0, 2000, 0.0, 5.0, min_intensity, 1e9, 0, i32::MAX)
381 .top_n(max_num_peaks)
382 })
383 .collect();
384
385 result
386 }
387
388 pub fn extract_precursor_ms1_signals(
409 &self,
410 precursor_coords: Vec<PrecursorCoord>,
411 rt_window_sec: f64,
412 mz_tol_ppm: f64,
413 im_window: f64,
414 n_isotopes: usize,
415 num_threads: usize,
416 ) -> Vec<PrecursorMS1Signal> {
417 if precursor_coords.is_empty() {
418 return Vec::new();
419 }
420
421 let meta_data = read_meta_data_sql(&self.loader.get_data_path()).unwrap();
423
424 let mut ms1_frame_info: Vec<(u32, f64)> = meta_data
426 .iter()
427 .filter(|f| f.ms_ms_type == 0)
428 .map(|f| (f.id as u32, f.time))
429 .collect();
430 ms1_frame_info.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
431
432 let ms1_times: Vec<f64> = ms1_frame_info.iter().map(|(_, t)| *t).collect();
433
434 let mut sorted_coords: Vec<(usize, &PrecursorCoord)> = precursor_coords
436 .iter()
437 .enumerate()
438 .collect();
439 sorted_coords.sort_by(|a, b| a.1.rt_seconds.partial_cmp(&b.1.rt_seconds).unwrap());
440
441 let batch_size_sec = 300.0;
443 let mut results: Vec<(usize, PrecursorMS1Signal)> = Vec::with_capacity(precursor_coords.len());
444
445 let mut batch_start = 0;
446 while batch_start < sorted_coords.len() {
447 let batch_rt_start = sorted_coords[batch_start].1.rt_seconds;
449 let batch_rt_end = batch_rt_start + batch_size_sec;
450
451 let mut batch_end = batch_start;
452 while batch_end < sorted_coords.len() && sorted_coords[batch_end].1.rt_seconds < batch_rt_end {
453 batch_end += 1;
454 }
455
456 let frame_rt_min = batch_rt_start - rt_window_sec;
458 let frame_rt_max = batch_rt_end + rt_window_sec;
459
460 let frame_start_idx = ms1_times.partition_point(|t| *t < frame_rt_min);
461 let frame_end_idx = ms1_times.partition_point(|t| *t <= frame_rt_max);
462
463 let batch_frame_ids: Vec<u32> = ms1_frame_info[frame_start_idx..frame_end_idx]
465 .iter()
466 .map(|(id, _)| *id)
467 .collect();
468
469 let batch_frames = if !batch_frame_ids.is_empty() {
470 self.loader.get_slice(batch_frame_ids, num_threads)
471 } else {
472 TimsSlice { frames: Vec::new() }
473 };
474
475 let batch_times: Vec<f64> = ms1_times[frame_start_idx..frame_end_idx].to_vec();
476
477 let batch_coords = &sorted_coords[batch_start..batch_end];
479
480 let pool = ThreadPoolBuilder::new()
481 .num_threads(num_threads)
482 .build()
483 .unwrap();
484
485 let batch_results: Vec<(usize, PrecursorMS1Signal)> = pool.install(|| {
486 batch_coords.par_iter().map(|(orig_idx, coord)| {
487 let signal = Self::extract_single_precursor(
488 coord,
489 &batch_frames.frames,
490 &batch_times,
491 rt_window_sec,
492 mz_tol_ppm,
493 im_window,
494 n_isotopes,
495 );
496 (*orig_idx, signal)
497 }).collect()
498 });
499
500 results.extend(batch_results);
501 batch_start = batch_end;
502 }
503
504 results.sort_by_key(|(idx, _)| *idx);
506 results.into_iter().map(|(_, signal)| signal).collect()
507 }
508
509 fn extract_single_precursor(
511 coord: &PrecursorCoord,
512 frames: &[TimsFrame],
513 frame_times: &[f64],
514 rt_window_sec: f64,
515 mz_tol_ppm: f64,
516 im_window: f64,
517 n_isotopes: usize,
518 ) -> PrecursorMS1Signal {
519 let rt_sec = coord.rt_seconds;
520
521 let rt_min = rt_sec - rt_window_sec / 2.0;
523 let rt_max = rt_sec + rt_window_sec / 2.0;
524
525 let start_idx = frame_times.partition_point(|t| *t < rt_min);
526 let end_idx = frame_times.partition_point(|t| *t <= rt_max);
527
528 let isotope_spacing = 1.003355 / (coord.charge.max(1) as f64);
530 let n_isotopes_to_extract = 4.min(n_isotopes); let has_mono_mz = coord.mono_mz > 0.0;
536 let base_mz = if has_mono_mz { coord.mono_mz } else { coord.mz };
537 let mz_tol = base_mz * mz_tol_ppm / 1e6;
538
539 let isotope_mz_values: Vec<f64> = (0..n_isotopes_to_extract)
541 .map(|i| base_mz + (i as f64) * isotope_spacing)
542 .collect();
543
544 let (xic_mz_min, xic_mz_max) = if has_mono_mz {
548 (base_mz - mz_tol, base_mz + ((n_isotopes_to_extract - 1) as f64) * isotope_spacing + mz_tol)
550 } else {
551 (coord.mz - mz_tol, coord.mz + mz_tol)
553 };
554
555 let im_min = coord.mobility - im_window / 2.0;
557 let im_max = coord.mobility + im_window / 2.0;
558
559 let n_frames = end_idx.saturating_sub(start_idx);
561 let mut rt_coords = Vec::with_capacity(n_frames);
562 let mut rt_intensities = Vec::with_capacity(n_frames);
563 let mut im_dict: HashMap<i64, f64> = HashMap::new();
564 let mut isotope_intensity = vec![0.0f64; n_isotopes]; let mut raw_rt = Vec::new();
568 let mut raw_mz = Vec::new();
569 let mut raw_mobility = Vec::new();
570 let mut raw_intensity = Vec::new();
571
572 for idx in start_idx..end_idx.min(frames.len()) {
574 let frame_time = frame_times[idx];
575 let frame = &frames[idx];
576
577 let xic_filtered = frame.filter_ranged(
583 xic_mz_min, xic_mz_max,
584 0, i32::MAX,
585 im_min, im_max,
586 0.0, 1e9,
587 0, i32::MAX,
588 );
589
590 let xic_intensity: f64 = xic_filtered.ims_frame.intensity.iter().sum();
592 rt_coords.push(frame_time);
593 rt_intensities.push(xic_intensity);
594
595 for (mob, inten) in xic_filtered.ims_frame.mobility.iter().zip(xic_filtered.ims_frame.intensity.iter()) {
597 let mob_bin = (*mob * 1000.0).round() as i64;
598 *im_dict.entry(mob_bin).or_insert(0.0) += *inten;
599 }
600
601 for (iso_idx, iso_mz) in isotope_mz_values.iter().enumerate() {
604 let iso_peak_min = iso_mz - mz_tol;
605 let iso_peak_max = iso_mz + mz_tol;
606 let iso_intensity_sum: f64 = xic_filtered.ims_frame.mz.iter()
607 .zip(xic_filtered.ims_frame.intensity.iter())
608 .filter(|(mz, _)| **mz >= iso_peak_min && **mz <= iso_peak_max)
609 .map(|(_, i)| *i)
610 .sum();
611 isotope_intensity[iso_idx] += iso_intensity_sum;
612 }
613
614 let n_peaks = xic_filtered.ims_frame.mz.len();
616 for i in 0..n_peaks {
617 raw_rt.push(frame_time);
618 raw_mz.push(xic_filtered.ims_frame.mz[i]);
619 raw_mobility.push(xic_filtered.ims_frame.mobility[i]);
620 raw_intensity.push(xic_filtered.ims_frame.intensity[i]);
621 }
622 }
623
624 let mut im_entries: Vec<(i64, f64)> = im_dict.into_iter().collect();
626 im_entries.sort_by_key(|(k, _)| *k);
627 let im_coords: Vec<f64> = im_entries.iter().map(|(k, _)| *k as f64 / 1000.0).collect();
628 let im_intensities: Vec<f64> = im_entries.iter().map(|(_, v)| *v).collect();
629
630 let rt_moments = SignalMoments::from_signal(&rt_coords, &rt_intensities);
632 let im_moments = SignalMoments::from_signal(&im_coords, &im_intensities);
633 let mz_moments = SignalMoments::from_signal(&isotope_mz_values, &isotope_intensity);
634
635 PrecursorMS1Signal {
636 precursor_id: coord.precursor_id,
637 rt_coords,
638 rt_intensities,
639 rt_moments,
640 im_coords,
641 im_intensities,
642 im_moments,
643 isotope_mz: isotope_mz_values,
644 isotope_intensity,
645 mz_moments,
646 raw_rt,
647 raw_mz,
648 raw_mobility,
649 raw_intensity,
650 }
651 }
652
653 pub fn get_pasef_frame_ms_ms_info(&self) -> Vec<PasefMsMsMeta> {
654 read_pasef_frame_ms_ms_info(&self.loader.get_data_path()).unwrap()
655 }
656
657 pub fn get_pasef_fragments(&self, num_threads: usize) -> Vec<PASEFDDAFragment> {
659 self.get_pasef_fragments_for_precursors(None, num_threads)
661 }
662
663 pub fn get_pasef_fragments_for_precursors(
667 &self,
668 precursor_ids: Option<&[u32]>,
669 num_threads: usize,
670 ) -> Vec<PASEFDDAFragment> {
671 let pasef_info = self.get_pasef_frame_ms_ms_info();
673
674 let filtered_pasef_info: Vec<&PasefMsMsMeta> = match precursor_ids {
676 Some(ids) => {
677 let id_set: std::collections::HashSet<u32> = ids.iter().copied().collect();
679 pasef_info.iter()
680 .filter(|info| id_set.contains(&(info.precursor_id as u32)))
681 .collect()
682 }
683 None => pasef_info.iter().collect(),
684 };
685
686 let uses_bruker_sdk = self.loader.uses_bruker_sdk();
689
690 let process_fragment = |pasef_info: &PasefMsMsMeta| -> PASEFDDAFragment {
692 let frame = self.loader.get_frame(pasef_info.frame_id as u32);
694
695 let scan_margin = (pasef_info.scan_num_end - pasef_info.scan_num_begin) / 20;
697
698 let filtered_frame = frame.filter_ranged(
700 0.0,
701 2000.0,
702 (pasef_info.scan_num_begin - scan_margin) as i32,
703 (pasef_info.scan_num_end + scan_margin) as i32,
704 0.0,
705 5.0,
706 0.0,
707 1e9,
708 0,
709 i32::MAX,
710 );
711
712 PASEFDDAFragment {
713 frame_id: pasef_info.frame_id as u32,
714 precursor_id: pasef_info.precursor_id as u32,
715 collision_energy: pasef_info.collision_energy,
716 selected_fragment: filtered_frame,
718 }
719 };
720
721 if uses_bruker_sdk {
722 filtered_pasef_info.iter().map(|info| process_fragment(info)).collect()
724 } else {
725 let pool = ThreadPoolBuilder::new()
727 .num_threads(num_threads)
728 .build()
729 .unwrap();
730
731 pool.install(|| {
732 filtered_pasef_info.par_iter().map(|info| process_fragment(info)).collect()
733 })
734 }
735 }
736
737 pub fn get_preprocessed_pasef_fragments(
752 &self,
753 dataset_name: &str,
754 config: SpectrumProcessingConfig,
755 num_threads: usize,
756 ) -> Vec<PreprocessedSpectrum> {
757 let pasef_info = self.get_pasef_frame_ms_ms_info();
759
760 let precursor_meta = read_dda_precursor_meta(&self.loader.get_data_path()).unwrap_or_default();
762 let frame_meta = read_meta_data_sql(&self.loader.get_data_path()).unwrap_or_default();
763
764 let precursor_map: BTreeMap<i64, &DDAPrecursorMeta> = precursor_meta
766 .iter()
767 .map(|p| (p.precursor_id, p))
768 .collect();
769
770 let frame_time_map: BTreeMap<i64, f64> = frame_meta
771 .iter()
772 .map(|f| (f.id, f.time / 60.0)) .collect();
774
775 let mut pasef_by_precursor: BTreeMap<i64, Vec<&PasefMsMsMeta>> = BTreeMap::new();
778 for info in &pasef_info {
779 pasef_by_precursor
780 .entry(info.precursor_id)
781 .or_insert_with(Vec::new)
782 .push(info);
783 }
784
785 let uses_bruker_sdk = self.loader.uses_bruker_sdk();
789
790 let process_precursor = |(precursor_id, pasef_infos): (&i64, &Vec<&PasefMsMsMeta>)| -> Option<PASEFFragmentData> {
792 let precursor = precursor_map.get(precursor_id)?;
794
795 let first_pasef = pasef_infos.first()?;
797
798 let scan_start_time = frame_time_map.get(&first_pasef.frame_id).copied().unwrap_or(0.0);
800
801 let mut combined_scan = Vec::new();
803 let mut combined_mobility = Vec::new();
804 let mut combined_tof = Vec::new();
805 let mut combined_mz = Vec::new();
806 let mut combined_intensity = Vec::new();
807
808 for pasef_info in pasef_infos {
809 let frame = self.loader.get_frame(pasef_info.frame_id as u32);
811
812 let scan_margin = (pasef_info.scan_num_end - pasef_info.scan_num_begin) / 20;
814
815 let filtered_frame = frame.filter_ranged(
817 0.0,
818 2000.0,
819 (pasef_info.scan_num_begin - scan_margin) as i32,
820 (pasef_info.scan_num_end + scan_margin) as i32,
821 0.0,
822 5.0,
823 0.0,
824 1e9,
825 0,
826 i32::MAX,
827 );
828
829 combined_scan.extend(filtered_frame.scan.iter());
831 combined_mobility.extend(filtered_frame.ims_frame.mobility.iter());
832 combined_tof.extend(filtered_frame.tof.iter());
833 combined_mz.extend(filtered_frame.ims_frame.mz.iter());
834 combined_intensity.extend(filtered_frame.ims_frame.intensity.iter());
835 }
836
837 if combined_mz.is_empty() {
838 return None;
839 }
840
841 let precursor_mz = precursor.precursor_mz_monoisotopic
843 .unwrap_or(precursor.precursor_mz_highest_intensity);
844
845 Some(PASEFFragmentData {
846 frame_id: first_pasef.frame_id as u32,
847 precursor_id: *precursor_id as u32,
848 collision_energy: first_pasef.collision_energy,
849 scan_start_time,
850 scan: combined_scan,
851 mobility: combined_mobility,
852 tof: combined_tof,
853 mz: combined_mz,
854 intensity: combined_intensity,
855 precursor_mz,
856 precursor_charge: precursor.precursor_charge.map(|c| c as i32),
857 precursor_intensity: precursor.precursor_total_intensity,
858 isolation_mz: first_pasef.isolation_mz,
859 isolation_width: first_pasef.isolation_width,
860 })
861 };
862
863 let fragment_data: Vec<PASEFFragmentData> = if uses_bruker_sdk {
864 pasef_by_precursor
866 .iter()
867 .filter_map(process_precursor)
868 .collect()
869 } else {
870 let pool = ThreadPoolBuilder::new()
872 .num_threads(num_threads)
873 .build()
874 .unwrap();
875
876 pool.install(|| {
877 pasef_by_precursor
878 .par_iter()
879 .filter_map(|item| process_precursor(item))
880 .collect()
881 })
882 };
883
884 process_pasef_fragments_batch(fragment_data, dataset_name, &config, num_threads)
886 }
887
888 pub fn sample_pasef_fragment_random(
889 &self,
890 target_scan_apex: i32,
891 experiment_max_scan: i32,
892 ) -> TimsFrame {
893 let pasef_meta = &self.pasef_meta;
894 let random_index = rand::random::<usize>() % pasef_meta.len();
895 let pasef_info = &pasef_meta[random_index];
896
897 let frame = self.loader.get_frame(pasef_info.frame_id as u32);
899
900 let scan_margin = (pasef_info.scan_num_end - pasef_info.scan_num_begin) / 20;
902
903 let mut filtered = frame.filter_ranged(
905 0.0,
906 2000.0,
907 (pasef_info.scan_num_begin - scan_margin) as i32,
908 (pasef_info.scan_num_end + scan_margin) as i32,
909 0.0,
910 5.0,
911 0.0,
912 1e9,
913 0,
914 i32::MAX,
915 );
916
917 if filtered.scan.is_empty() {
919 return filtered;
920 }
921
922 let mut scan_copy = filtered.scan.clone();
924 scan_copy.sort_unstable();
925 let median_scan = scan_copy[scan_copy.len() / 2];
926
927 let scan_shift = target_scan_apex - median_scan;
929
930 for s in filtered.scan.iter_mut() {
932 *s += scan_shift;
933 }
934
935 let re_filtered = filtered.filter_ranged(
937 0.0,
938 2000.0,
939 0,
940 experiment_max_scan,
941 0.0,
942 5.0,
943 0.0,
944 1e9,
945 0,
946 i32::MAX,
947 );
948
949 re_filtered
950 }
951
952 pub fn sample_pasef_fragments_random(
953 &self,
954 target_scan_apex_values: Vec<i32>,
955 experiment_max_scan: i32,
956 ) -> TimsFrame {
957
958 if target_scan_apex_values.is_empty() {
960 return TimsFrame {
961 frame_id: 0, ms_type: MsType::FragmentDda,
963 scan: Vec::new(),
964 tof: Vec::new(),
965 ims_frame: ImsFrame::default(), }
967 }
968
969 let mut pasef_frames = Vec::new();
970
971 for target_scan_apex in target_scan_apex_values {
972 let pasef_frame = self.sample_pasef_fragment_random(target_scan_apex, experiment_max_scan);
973 pasef_frames.push(pasef_frame);
974 }
975
976 let mut combined_frame = pasef_frames[0].clone();
978
979 for frame in pasef_frames.iter().skip(1) {
980 combined_frame = combined_frame + frame.clone();
981 }
982
983 let im_values = self.scan_to_inverse_mobility(
985 combined_frame.frame_id as u32,
986 &combined_frame.scan.iter().map(|x| *x as u32).collect(),
987 );
988
989 combined_frame.ims_frame.mobility = std::sync::Arc::new(im_values);
991
992 combined_frame
993 }
994
995 pub fn sample_precursor_signal(
996 &self,
997 num_frames: usize,
998 max_intensity: f64,
999 take_probability: f64,
1000 ) -> TimsFrame {
1001 let meta_data = read_meta_data_sql(&self.loader.get_data_path()).unwrap();
1003 let precursor_frames = meta_data.iter().filter(|x| x.ms_ms_type == 0);
1004
1005 let mut rng = rand::thread_rng();
1007 let mut sampled_frames: Vec<TimsFrame> = Vec::new();
1008
1009 for frame in precursor_frames.choose_multiple(&mut rng, num_frames) {
1011 let frame_id = frame.id;
1012 let frame_data = self
1013 .loader
1014 .get_frame(frame_id as u32)
1015 .filter_ranged(0.0, 2000.0, 0, 1000, 0.0, 5.0, 1.0, max_intensity, 0, i32::MAX)
1016 .generate_random_sample(take_probability);
1017 sampled_frames.push(frame_data);
1018 }
1019
1020 let mut sampled_frame = sampled_frames.remove(0);
1022
1023 for frame in sampled_frames {
1025 sampled_frame = sampled_frame + frame;
1026 }
1027
1028 sampled_frame
1029 }
1030}
1031
1032impl TimsData for TimsDatasetDDA {
1033 fn get_frame(&self, frame_id: u32) -> TimsFrame {
1034 self.loader.get_frame(frame_id)
1035 }
1036
1037 fn get_raw_frame(&self, frame_id: u32) -> RawTimsFrame {
1038 self.loader.get_raw_frame(frame_id)
1039 }
1040
1041 fn get_slice(&self, frame_ids: Vec<u32>, num_threads: usize) -> TimsSlice {
1042 self.loader.get_slice(frame_ids, num_threads)
1043 }
1044
1045 fn get_acquisition_mode(&self) -> AcquisitionMode {
1046 self.loader.get_acquisition_mode().clone()
1047 }
1048
1049 fn get_frame_count(&self) -> i32 {
1050 self.loader.get_frame_count()
1051 }
1052
1053 fn get_data_path(&self) -> &str {
1054 &self.loader.get_data_path()
1055 }
1056}
1057
1058impl IndexConverter for TimsDatasetDDA {
1059 fn tof_to_mz(&self, frame_id: u32, tof_values: &Vec<u32>) -> Vec<f64> {
1060 self.loader
1061 .get_index_converter()
1062 .tof_to_mz(frame_id, tof_values)
1063 }
1064
1065 fn mz_to_tof(&self, frame_id: u32, mz_values: &Vec<f64>) -> Vec<u32> {
1066 self.loader
1067 .get_index_converter()
1068 .mz_to_tof(frame_id, mz_values)
1069 }
1070
1071 fn scan_to_inverse_mobility(&self, frame_id: u32, scan_values: &Vec<u32>) -> Vec<f64> {
1072 self.loader
1073 .get_index_converter()
1074 .scan_to_inverse_mobility(frame_id, scan_values)
1075 }
1076
1077 fn inverse_mobility_to_scan(
1078 &self,
1079 frame_id: u32,
1080 inverse_mobility_values: &Vec<f64>,
1081 ) -> Vec<u32> {
1082 self.loader
1083 .get_index_converter()
1084 .inverse_mobility_to_scan(frame_id, inverse_mobility_values)
1085 }
1086}