1use mscore::data::peptide::{PeptideIon, PeptideProductIonSeriesCollection};
2use mscore::data::spectrum::{IndexedMzSpectrum, MsType, MzSpectrum};
3use mscore::simulation::annotation::{
4 MzSpectrumAnnotated, TimsFrameAnnotated, TimsSpectrumAnnotated,
5};
6use mscore::timstof::frame::TimsFrame;
7use mscore::timstof::quadrupole::{IonTransmission, TimsTransmissionDDA};
8use mscore::timstof::spectrum::TimsSpectrum;
9use std::collections::{BTreeMap, HashSet};
10use std::path::Path;
11
12use rayon::prelude::*;
13use rayon::ThreadPoolBuilder;
14use crate::sim::handle::TimsTofSyntheticsDataHandle;
15use crate::sim::precursor::TimsTofSyntheticsPrecursorFrameBuilder;
16
17pub struct TimsTofSyntheticsFrameBuilderDDA {
18 pub path: String,
19 pub precursor_frame_builder: TimsTofSyntheticsPrecursorFrameBuilder,
20 pub transmission_settings: TimsTransmissionDDA,
21 pub fragment_ions:
22 Option<BTreeMap<(u32, i8, i32), (PeptideProductIonSeriesCollection, Vec<MzSpectrum>)>>,
23 pub fragment_ions_annotated: Option<
24 BTreeMap<(u32, i8, i32), (PeptideProductIonSeriesCollection, Vec<MzSpectrumAnnotated>)>,
25 >,
26}
27
28impl TimsTofSyntheticsFrameBuilderDDA {
29 pub fn new(path: &Path, with_annotations: bool, num_threads: usize) -> Self {
30
31 let handle = TimsTofSyntheticsDataHandle::new(path).unwrap();
32 let fragment_ions = handle.read_fragment_ions().unwrap();
33 let transmission_settings = handle.get_transmission_dda();
34
35 let synthetics = TimsTofSyntheticsPrecursorFrameBuilder::new(path).unwrap();
36
37 match with_annotations {
38 true => {
39 let fragment_ions =
40 Some(TimsTofSyntheticsDataHandle::build_fragment_ions_annotated(
41 &synthetics.peptides,
42 &fragment_ions,
43 num_threads,
44 ));
45 Self {
46 path: path.to_str().unwrap().to_string(),
47 precursor_frame_builder: synthetics,
48 transmission_settings,
49 fragment_ions: None,
50 fragment_ions_annotated: fragment_ions,
51 }
52 }
53 false => {
54 let fragment_ions = Some(TimsTofSyntheticsDataHandle::build_fragment_ions(
55 &synthetics.peptides,
56 &fragment_ions,
57 num_threads,
58 ));
59 Self {
60 path: path.to_str().unwrap().to_string(),
61 precursor_frame_builder: synthetics,
62 transmission_settings,
63 fragment_ions,
64 fragment_ions_annotated: None,
65 }
66 }
67 }
68 }
69 pub fn build_frame(
81 &self,
82 frame_id: u32,
83 fragmentation: bool,
84 mz_noise_precursor: bool,
85 uniform: bool,
86 precursor_noise_ppm: f64,
87 mz_noise_fragment: bool,
88 fragment_noise_ppm: f64,
89 right_drag: bool,
90 ) -> TimsFrame {
91 match self
93 .precursor_frame_builder
94 .precursor_frame_id_set
95 .contains(&frame_id)
96 {
97 true => self.build_ms1_frame(
98 frame_id,
99 mz_noise_precursor,
100 uniform,
101 precursor_noise_ppm,
102 right_drag,
103 ),
104 false => self.build_ms2_frame(
105 frame_id,
106 fragmentation,
107 mz_noise_fragment,
108 uniform,
109 fragment_noise_ppm,
110 right_drag,
111 ),
112 }
113 }
114
115 pub fn build_frame_annotated(
116 &self,
117 frame_id: u32,
118 fragmentation: bool,
119 mz_noise_precursor: bool,
120 uniform: bool,
121 precursor_noise_ppm: f64,
122 mz_noise_fragment: bool,
123 fragment_noise_ppm: f64,
124 right_drag: bool,
125 ) -> TimsFrameAnnotated {
126 match self
127 .precursor_frame_builder
128 .precursor_frame_id_set
129 .contains(&frame_id)
130 {
131 true => self.build_ms1_frame_annotated(
132 frame_id,
133 mz_noise_precursor,
134 uniform,
135 precursor_noise_ppm,
136 right_drag,
137 ),
138 false => self.build_ms2_frame_annotated(
139 frame_id,
140 fragmentation,
141 mz_noise_fragment,
142 uniform,
143 fragment_noise_ppm,
144 right_drag,
145 ),
146 }
147 }
148
149 pub fn get_fragment_ion_ids(&self, precursor_frame_ids: Vec<u32>) -> Vec<u32> {
150 let mut peptide_ids: HashSet<u32> = HashSet::new();
151 for frame_id in precursor_frame_ids {
153 for (peptide_id, peptide) in self.precursor_frame_builder.peptides.iter() {
154 if peptide.frame_start <= frame_id && peptide.frame_end >= frame_id {
155 peptide_ids.insert(*peptide_id);
156 }
157 }
158 }
159 let mut result: Vec<u32> = Vec::new();
161 for item in peptide_ids {
162 let ions = self.precursor_frame_builder.ions.get(&item).unwrap();
163 for ion in ions.iter() {
164 result.push(ion.ion_id);
165 }
166 }
167 result
168 }
169
170 pub fn build_frames(
171 &self,
172 frame_ids: Vec<u32>,
173 fragmentation: bool,
174 mz_noise_precursor: bool,
175 uniform: bool,
176 precursor_noise_ppm: f64,
177 mz_noise_fragment: bool,
178 fragment_noise_ppm: f64,
179 right_drag: bool,
180 num_threads: usize,
181 ) -> Vec<TimsFrame> {
182 let thread_pool = ThreadPoolBuilder::new()
183 .num_threads(num_threads)
184 .build()
185 .unwrap();
186 let mut tims_frames: Vec<TimsFrame> = Vec::new();
187
188 thread_pool.install(|| {
189 tims_frames = frame_ids
190 .par_iter()
191 .map(|frame_id| {
192 self.build_frame(
193 *frame_id,
194 fragmentation,
195 mz_noise_precursor,
196 uniform,
197 precursor_noise_ppm,
198 mz_noise_fragment,
199 fragment_noise_ppm,
200 right_drag,
201 )
202 })
203 .collect();
204 });
205
206 tims_frames.sort_by(|a, b| a.frame_id.cmp(&b.frame_id));
207
208 tims_frames
209 }
210 pub fn build_frames_annotated(
211 &self,
212 frame_ids: Vec<u32>,
213 fragmentation: bool,
214 mz_noise_precursor: bool,
215 uniform: bool,
216 precursor_noise_ppm: f64,
217 mz_noise_fragment: bool,
218 fragment_noise_ppm: f64,
219 right_drag: bool,
220 num_threads: usize,
221 ) -> Vec<TimsFrameAnnotated> {
222 let thread_pool = ThreadPoolBuilder::new()
223 .num_threads(num_threads)
224 .build()
225 .unwrap();
226 let mut tims_frames: Vec<TimsFrameAnnotated> = Vec::new();
227
228 thread_pool.install(|| {
229 tims_frames = frame_ids
230 .par_iter()
231 .map(|frame_id| {
232 self.build_frame_annotated(
233 *frame_id,
234 fragmentation,
235 mz_noise_precursor,
236 uniform,
237 precursor_noise_ppm,
238 mz_noise_fragment,
239 fragment_noise_ppm,
240 right_drag,
241 )
242 })
243 .collect();
244 });
245
246 tims_frames.sort_by(|a, b| a.frame_id.cmp(&b.frame_id));
247
248 tims_frames
249 }
250
251 fn build_ms1_frame(
252 &self,
253 frame_id: u32,
254 mz_noise_precursor: bool,
255 uniform: bool,
256 precursor_ppm: f64,
257 right_drag: bool,
258 ) -> TimsFrame {
259 let mut tims_frame = self.precursor_frame_builder.build_precursor_frame(
260 frame_id,
261 mz_noise_precursor,
262 uniform,
263 precursor_ppm,
264 right_drag,
265 );
266 let intensities_rounded = tims_frame
267 .ims_frame
268 .intensity
269 .iter()
270 .map(|x| x.round())
271 .collect::<Vec<_>>();
272 tims_frame.ims_frame.intensity = intensities_rounded;
273 tims_frame
274 }
275
276 fn build_ms1_frame_annotated(
277 &self,
278 frame_id: u32,
279 mz_noise_precursor: bool,
280 uniform: bool,
281 precursor_ppm: f64,
282 right_drag: bool,
283 ) -> TimsFrameAnnotated {
284 let mut tims_frame = self
285 .precursor_frame_builder
286 .build_precursor_frame_annotated(
287 frame_id,
288 mz_noise_precursor,
289 uniform,
290 precursor_ppm,
291 right_drag,
292 );
293 let intensities_rounded = tims_frame
294 .intensity
295 .iter()
296 .map(|x| x.round())
297 .collect::<Vec<_>>();
298 tims_frame.intensity = intensities_rounded;
299 tims_frame
300 }
301
302 fn build_ms2_frame(
303 &self,
304 frame_id: u32,
305 fragmentation: bool,
306 mz_noise_fragment: bool,
307 uniform: bool,
308 fragment_ppm: f64,
309 right_drag: bool,
310 ) -> TimsFrame {
311 match fragmentation {
312 false => {
313 let mut frame = self.transmission_settings.transmit_tims_frame(
314 &self.build_ms1_frame(
315 frame_id,
316 mz_noise_fragment,
317 uniform,
318 fragment_ppm,
319 right_drag,
320 ),
321 None,
322 );
323 let intensities_rounded = frame
324 .ims_frame
325 .intensity
326 .iter()
327 .map(|x| x.round())
328 .collect::<Vec<_>>();
329 frame.ims_frame.intensity = intensities_rounded;
330 frame.ms_type = MsType::FragmentDia;
331 frame
332 }
333 true => {
334 let mut frame = self.build_fragment_frame(
335 frame_id,
336 &self.fragment_ions.as_ref().unwrap(),
337 mz_noise_fragment,
338 uniform,
339 fragment_ppm,
340 None,
341 None,
342 None,
343 Some(right_drag),
344 );
345 let intensities_rounded = frame
346 .ims_frame
347 .intensity
348 .iter()
349 .map(|x| x.round())
350 .collect::<Vec<_>>();
351 frame.ims_frame.intensity = intensities_rounded;
352 frame
353 }
354 }
355 }
356
357 fn build_ms2_frame_annotated(
358 &self,
359 frame_id: u32,
360 fragmentation: bool,
361 mz_noise_fragment: bool,
362 uniform: bool,
363 fragment_ppm: f64,
364 right_drag: bool,
365 ) -> TimsFrameAnnotated {
366 match fragmentation {
367 false => {
368 let mut frame = self.transmission_settings.transmit_tims_frame_annotated(
369 &self.build_ms1_frame_annotated(
370 frame_id,
371 mz_noise_fragment,
372 uniform,
373 fragment_ppm,
374 right_drag,
375 ),
376 None,
377 );
378 let intensities_rounded = frame
379 .intensity
380 .iter()
381 .map(|x| x.round())
382 .collect::<Vec<_>>();
383 frame.intensity = intensities_rounded;
384 frame.ms_type = MsType::FragmentDia;
385 frame
386 }
387 true => {
388 let mut frame = self.build_fragment_frame_annotated(
389 frame_id,
390 &self.fragment_ions_annotated.as_ref().unwrap(),
391 mz_noise_fragment,
392 uniform,
393 fragment_ppm,
394 None,
395 None,
396 None,
397 Some(right_drag),
398 );
399 let intensities_rounded = frame
400 .intensity
401 .iter()
402 .map(|x| x.round())
403 .collect::<Vec<_>>();
404 frame.intensity = intensities_rounded;
405 frame
406 }
407 }
408 }
409
410 fn build_fragment_frame(
424 &self,
425 frame_id: u32,
426 fragment_ions: &BTreeMap<
427 (u32, i8, i32),
428 (PeptideProductIonSeriesCollection, Vec<MzSpectrum>),
429 >,
430 mz_noise_fragment: bool,
431 uniform: bool,
432 fragment_ppm: f64,
433 mz_min: Option<f64>,
434 mz_max: Option<f64>,
435 intensity_min: Option<f64>,
436 right_drag: Option<bool>,
437 ) -> TimsFrame {
438 let ms_type = match self
440 .precursor_frame_builder
441 .precursor_frame_id_set
442 .contains(&frame_id)
443 {
444 false => MsType::FragmentDda,
445 true => MsType::Unknown,
446 };
447
448 let mut tims_spectra: Vec<TimsSpectrum> = Vec::new();
449
450 if !self
452 .precursor_frame_builder
453 .frame_to_abundances
454 .contains_key(&frame_id)
455 {
456 return TimsFrame::new(
457 frame_id as i32,
458 ms_type.clone(),
459 *self
460 .precursor_frame_builder
461 .frame_to_rt
462 .get(&frame_id)
463 .unwrap() as f64,
464 vec![],
465 vec![],
466 vec![],
467 vec![],
468 vec![],
469 );
470 }
471
472 let (peptide_ids, frame_abundances) = self
474 .precursor_frame_builder
475 .frame_to_abundances
476 .get(&frame_id)
477 .unwrap();
478
479 for (peptide_id, frame_abundance) in peptide_ids.iter().zip(frame_abundances.iter()) {
481 if !self
483 .precursor_frame_builder
484 .peptide_to_ions
485 .contains_key(&peptide_id)
486 {
487 continue;
488 }
489
490 let (ion_abundances, scan_occurrences, scan_abundances, charges, spectra) = self
492 .precursor_frame_builder
493 .peptide_to_ions
494 .get(&peptide_id)
495 .unwrap();
496
497 for (index, ion_abundance) in ion_abundances.iter().enumerate() {
498 let all_scan_occurrence = scan_occurrences.get(index).unwrap();
500 let all_scan_abundance = scan_abundances.get(index).unwrap();
501
502 let spectrum = spectra.get(index).unwrap();
504
505 for (scan, scan_abundance) in
507 all_scan_occurrence.iter().zip(all_scan_abundance.iter())
508 {
509 if !self.transmission_settings.any_transmitted(
511 frame_id as i32,
512 *scan as i32,
513 &spectrum.mz,
514 None,
515 ) {
516 continue;
517 }
518
519 let total_events = self
521 .precursor_frame_builder
522 .peptide_to_events
523 .get(&peptide_id)
524 .unwrap();
525 let fraction_events =
526 frame_abundance * scan_abundance * ion_abundance * total_events;
527
528 let maybe_pasef_meta = self.transmission_settings.pasef_meta.get(&(frame_id as i32));
530
531 let collision_energy: f64 = match maybe_pasef_meta {
532 Some(pasef_meta) => {
533 pasef_meta
534 .iter()
535 .find(|scan_meta| scan_meta.scan_start <= *scan as i32 && scan_meta.scan_end >= *scan as i32)
536 .map(|s| s.collision_energy)
537 .unwrap_or(0.0)
538 },
539 None => 0.0
540 };
541
542 let collision_energy_quantized = (collision_energy * 1e1).round() as i32;
543
544 let charge_state = charges.get(index).unwrap();
546 let maybe_value = fragment_ions.get(&(
548 *peptide_id,
549 *charge_state,
550 collision_energy_quantized,
551 ));
552
553 if maybe_value.is_none() {
555 continue;
556 }
557
558 for fragment_ion_series in maybe_value.unwrap().1.iter() {
560 let scaled_spec = fragment_ion_series.clone() * fraction_events as f64;
561 let right_drag = right_drag.unwrap_or(false);
562
563 let mz_spectrum = if mz_noise_fragment {
564 match uniform {
565 true => scaled_spec.add_mz_noise_uniform(fragment_ppm, right_drag),
566 false => scaled_spec.add_mz_noise_normal(fragment_ppm),
567 }
568 } else {
569 scaled_spec
570 };
571
572 tims_spectra.push(TimsSpectrum::new(
573 frame_id as i32,
574 *scan as i32,
575 *self
576 .precursor_frame_builder
577 .frame_to_rt
578 .get(&frame_id)
579 .unwrap() as f64,
580 *self
581 .precursor_frame_builder
582 .scan_to_mobility
583 .get(&scan)
584 .unwrap() as f64,
585 ms_type.clone(),
586 IndexedMzSpectrum::new(
587 vec![0; mz_spectrum.mz.len()],
588 mz_spectrum.mz,
589 mz_spectrum.intensity,
590 )
591 .filter_ranged(100.0, 1700.0, 1.0, 1e9),
592 ));
593 }
594 }
595 }
596 }
597
598 if tims_spectra.is_empty() {
599 return TimsFrame::new(
600 frame_id as i32,
601 ms_type.clone(),
602 *self
603 .precursor_frame_builder
604 .frame_to_rt
605 .get(&frame_id)
606 .unwrap() as f64,
607 vec![],
608 vec![],
609 vec![],
610 vec![],
611 vec![],
612 );
613 }
614
615 let tims_frame = TimsFrame::from_tims_spectra(tims_spectra);
616 tims_frame.filter_ranged(
617 mz_min.unwrap_or(100.0),
618 mz_max.unwrap_or(1700.0),
619 0,
620 1000,
621 0.0,
622 10.0,
623 intensity_min.unwrap_or(1.0),
624 1e9,
625 )
626 }
627
628 pub fn build_fragment_frame_annotated(
629 &self,
630 frame_id: u32,
631 fragment_ions: &BTreeMap<
632 (u32, i8, i32),
633 (PeptideProductIonSeriesCollection, Vec<MzSpectrumAnnotated>),
634 >,
635 mz_noise_fragment: bool,
636 uniform: bool,
637 fragment_ppm: f64,
638 mz_min: Option<f64>,
639 mz_max: Option<f64>,
640 intensity_min: Option<f64>,
641 right_drag: Option<bool>,
642 ) -> TimsFrameAnnotated {
643 let ms_type = match self
644 .precursor_frame_builder
645 .precursor_frame_id_set
646 .contains(&frame_id)
647 {
648 false => MsType::FragmentDia,
649 true => MsType::Unknown,
650 };
651
652 let mut tims_spectra: Vec<TimsSpectrumAnnotated> = Vec::new();
653
654 if !self
655 .precursor_frame_builder
656 .frame_to_abundances
657 .contains_key(&frame_id)
658 {
659 return TimsFrameAnnotated::new(
660 frame_id as i32,
661 *self
662 .precursor_frame_builder
663 .frame_to_rt
664 .get(&frame_id)
665 .unwrap() as f64,
666 ms_type.clone(),
667 vec![],
668 vec![],
669 vec![],
670 vec![],
671 vec![],
672 vec![],
673 );
674 }
675
676 let (peptide_ids, frame_abundances) = self
677 .precursor_frame_builder
678 .frame_to_abundances
679 .get(&frame_id)
680 .unwrap();
681
682 for (peptide_id, frame_abundance) in peptide_ids.iter().zip(frame_abundances.iter()) {
683 if !self
684 .precursor_frame_builder
685 .peptide_to_ions
686 .contains_key(&peptide_id)
687 {
688 continue;
689 }
690
691 let (ion_abundances, scan_occurrences, scan_abundances, charges, _) = self
692 .precursor_frame_builder
693 .peptide_to_ions
694 .get(&peptide_id)
695 .unwrap();
696
697 for (index, ion_abundance) in ion_abundances.iter().enumerate() {
698 let all_scan_occurrence = scan_occurrences.get(index).unwrap();
699 let all_scan_abundance = scan_abundances.get(index).unwrap();
700
701 let peptide = self
702 .precursor_frame_builder
703 .peptides
704 .get(peptide_id)
705 .unwrap();
706 let ion = PeptideIon::new(
707 peptide.sequence.sequence.clone(),
708 charges[index] as i32,
709 *ion_abundance as f64,
710 Some(*peptide_id as i32),
711 );
712 let spectrum = ion.calculate_isotopic_spectrum_annotated(1e-3, 1e-8, 200, 1e-4);
714
715 for (scan, scan_abundance) in
716 all_scan_occurrence.iter().zip(all_scan_abundance.iter())
717 {
718 if !self.transmission_settings.any_transmitted(
719 frame_id as i32,
720 *scan as i32,
721 &spectrum.mz,
722 None,
723 ) {
724 continue;
725 }
726
727 let total_events = self
728 .precursor_frame_builder
729 .peptide_to_events
730 .get(&peptide_id)
731 .unwrap();
732 let fraction_events =
733 frame_abundance * scan_abundance * ion_abundance * total_events;
734
735 let maybe_pasef_meta = self.transmission_settings.pasef_meta.get(&(frame_id as i32));
737
738 let collision_energy: f64 = match maybe_pasef_meta {
739 Some(pasef_meta) => {
740 pasef_meta
741 .iter()
742 .find(|scan_meta| scan_meta.scan_start <= *scan as i32 && scan_meta.scan_end >= *scan as i32)
743 .map(|s| s.collision_energy)
744 .unwrap_or(0.0)
745 },
746 None => 0.0
747 };
748
749 let collision_energy_quantized = (collision_energy * 1e1).round() as i32;
750
751 let charge_state = charges.get(index).unwrap();
752 let maybe_value = fragment_ions.get(&(
753 *peptide_id,
754 *charge_state,
755 collision_energy_quantized,
756 ));
757
758 if maybe_value.is_none() {
759 continue;
760 }
761
762 for fragment_ion_series in maybe_value.unwrap().1.iter() {
763 let scaled_spec = fragment_ion_series.clone() * fraction_events as f64;
764 let right_drag = right_drag.unwrap_or(false);
765
766 let mz_spectrum = if mz_noise_fragment {
767 match uniform {
768 true => scaled_spec.add_mz_noise_uniform(fragment_ppm, right_drag),
769 false => scaled_spec.add_mz_noise_normal(fragment_ppm),
770 }
771 } else {
772 scaled_spec
773 };
774
775 tims_spectra.push(TimsSpectrumAnnotated::new(
776 frame_id as i32,
777 *scan,
778 *self
779 .precursor_frame_builder
780 .frame_to_rt
781 .get(&frame_id)
782 .unwrap() as f64,
783 *self
784 .precursor_frame_builder
785 .scan_to_mobility
786 .get(&scan)
787 .unwrap() as f64,
788 ms_type.clone(),
789 vec![0; mz_spectrum.mz.len()],
790 mz_spectrum,
791 ));
792 }
793 }
794 }
795 }
796
797 if tims_spectra.is_empty() {
798 return TimsFrameAnnotated::new(
799 frame_id as i32,
800 *self
801 .precursor_frame_builder
802 .frame_to_rt
803 .get(&frame_id)
804 .unwrap() as f64,
805 ms_type.clone(),
806 vec![],
807 vec![],
808 vec![],
809 vec![],
810 vec![],
811 vec![],
812 );
813 }
814
815 let tims_frame = TimsFrameAnnotated::from_tims_spectra_annotated(tims_spectra);
816
817 tims_frame.filter_ranged(
818 mz_min.unwrap_or(100.0),
819 mz_max.unwrap_or(1700.0),
820 0.0,
821 10.0,
822 0,
823 1000,
824 intensity_min.unwrap_or(1.0),
825 1e9,
826 )
827 }
828
829 pub fn get_collision_energy(&self, frame_id: i32, scan_id: i32) -> f64 {
830 self.transmission_settings.get_collision_energy(frame_id, scan_id).unwrap_or(0.0)
831 }
832
833 pub fn get_collision_energies(&self, frame_ids: Vec<i32>, scan_ids: Vec<i32>) -> Vec<f64> {
834 let mut collision_energies: Vec<f64> = Vec::new();
835 for frame_id in frame_ids {
836 for scan_id in &scan_ids {
837 collision_energies.push(self.get_collision_energy(frame_id, *scan_id));
838 }
839 }
840 collision_energies
841 }
842}