rustdf/sim/
dia.rs

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::collision::{TimsTofCollisionEnergy, TimsTofCollisionEnergyDIA};
7use mscore::timstof::frame::TimsFrame;
8use mscore::timstof::quadrupole::{IonTransmission, TimsTransmissionDIA};
9use mscore::timstof::spectrum::TimsSpectrum;
10use std::collections::{BTreeMap, HashSet};
11use std::path::Path;
12
13use rayon::prelude::*;
14use rayon::ThreadPoolBuilder;
15
16use crate::sim::handle::TimsTofSyntheticsDataHandle;
17use crate::sim::precursor::TimsTofSyntheticsPrecursorFrameBuilder;
18
19pub struct TimsTofSyntheticsFrameBuilderDIA {
20    pub path: String,
21    pub precursor_frame_builder: TimsTofSyntheticsPrecursorFrameBuilder,
22    pub transmission_settings: TimsTransmissionDIA,
23    pub fragmentation_settings: TimsTofCollisionEnergyDIA,
24    pub fragment_ions:
25        Option<BTreeMap<(u32, i8, i32), (PeptideProductIonSeriesCollection, Vec<MzSpectrum>)>>,
26    pub fragment_ions_annotated: Option<
27        BTreeMap<(u32, i8, i32), (PeptideProductIonSeriesCollection, Vec<MzSpectrumAnnotated>)>,
28    >,
29}
30
31impl TimsTofSyntheticsFrameBuilderDIA {
32    pub fn new(path: &Path, with_annotations: bool, num_threads: usize) -> rusqlite::Result<Self> {
33        let synthetics = TimsTofSyntheticsPrecursorFrameBuilder::new(path)?;
34        let handle = TimsTofSyntheticsDataHandle::new(path)?;
35
36        let fragment_ions = handle.read_fragment_ions()?;
37
38        // get collision energy settings per window group
39        let fragmentation_settings = handle.get_collision_energy_dia();
40        // get ion transmission settings per window group
41        let transmission_settings = handle.get_transmission_dia();
42
43        match with_annotations {
44            true => {
45                let fragment_ions =
46                    Some(TimsTofSyntheticsDataHandle::build_fragment_ions_annotated(
47                        &synthetics.peptides,
48                        &fragment_ions,
49                        num_threads,
50                    ));
51                Ok(Self {
52                    path: path.to_str().unwrap().to_string(),
53                    precursor_frame_builder: synthetics,
54                    transmission_settings,
55                    fragmentation_settings,
56                    fragment_ions: None,
57                    fragment_ions_annotated: fragment_ions,
58                })
59            }
60
61            false => {
62                let fragment_ions = Some(TimsTofSyntheticsDataHandle::build_fragment_ions(
63                    &synthetics.peptides,
64                    &fragment_ions,
65                    num_threads,
66                ));
67                Ok(Self {
68                    path: path.to_str().unwrap().to_string(),
69                    precursor_frame_builder: synthetics,
70                    transmission_settings,
71                    fragmentation_settings,
72                    fragment_ions,
73                    fragment_ions_annotated: None,
74                })
75            }
76        }
77    }
78
79    /// Build a frame for DIA synthetic experiment
80    ///
81    /// # Arguments
82    ///
83    /// * `frame_id` - The frame id
84    /// * `fragmentation` - A boolean indicating if fragmentation is enabled, if false, the frame has same mz distribution as the precursor frame but will be quadrupole filtered
85    ///
86    /// # Returns
87    ///
88    /// A TimsFrame
89    ///
90    pub fn build_frame(
91        &self,
92        frame_id: u32,
93        fragmentation: bool,
94        mz_noise_precursor: bool,
95        uniform: bool,
96        precursor_noise_ppm: f64,
97        mz_noise_fragment: bool,
98        fragment_noise_ppm: f64,
99        right_drag: bool,
100    ) -> TimsFrame {
101        // determine if the frame is a precursor frame
102        match self
103            .precursor_frame_builder
104            .precursor_frame_id_set
105            .contains(&frame_id)
106        {
107            true => self.build_ms1_frame(
108                frame_id,
109                mz_noise_precursor,
110                uniform,
111                precursor_noise_ppm,
112                right_drag,
113            ),
114            false => self.build_ms2_frame(
115                frame_id,
116                fragmentation,
117                mz_noise_fragment,
118                uniform,
119                fragment_noise_ppm,
120                right_drag,
121            ),
122        }
123    }
124
125    pub fn build_frame_annotated(
126        &self,
127        frame_id: u32,
128        fragmentation: bool,
129        mz_noise_precursor: bool,
130        uniform: bool,
131        precursor_noise_ppm: f64,
132        mz_noise_fragment: bool,
133        fragment_noise_ppm: f64,
134        right_drag: bool,
135    ) -> TimsFrameAnnotated {
136        match self
137            .precursor_frame_builder
138            .precursor_frame_id_set
139            .contains(&frame_id)
140        {
141            true => self.build_ms1_frame_annotated(
142                frame_id,
143                mz_noise_precursor,
144                uniform,
145                precursor_noise_ppm,
146                right_drag,
147            ),
148            false => self.build_ms2_frame_annotated(
149                frame_id,
150                fragmentation,
151                mz_noise_fragment,
152                uniform,
153                fragment_noise_ppm,
154                right_drag,
155            ),
156        }
157    }
158
159    pub fn get_fragment_ion_ids(&self, precursor_frame_ids: Vec<u32>) -> Vec<u32> {
160        let mut peptide_ids: HashSet<u32> = HashSet::new();
161        // get all peptide ids for the precursor frame ids
162        for frame_id in precursor_frame_ids {
163            for (peptide_id, peptide) in self.precursor_frame_builder.peptides.iter() {
164                if peptide.frame_start <= frame_id && peptide.frame_end >= frame_id {
165                    peptide_ids.insert(*peptide_id);
166                }
167            }
168        }
169        // get all ion ids for the peptide ids
170        let mut result: Vec<u32> = Vec::new();
171        for item in peptide_ids {
172            let ions = self.precursor_frame_builder.ions.get(&item).unwrap();
173            for ion in ions.iter() {
174                result.push(ion.ion_id);
175            }
176        }
177        result
178    }
179
180    pub fn build_frames(
181        &self,
182        frame_ids: Vec<u32>,
183        fragmentation: bool,
184        mz_noise_precursor: bool,
185        uniform: bool,
186        precursor_noise_ppm: f64,
187        mz_noise_fragment: bool,
188        fragment_noise_ppm: f64,
189        right_drag: bool,
190        num_threads: usize,
191    ) -> Vec<TimsFrame> {
192        let thread_pool = ThreadPoolBuilder::new()
193            .num_threads(num_threads)
194            .build()
195            .unwrap();
196        let mut tims_frames: Vec<TimsFrame> = Vec::new();
197
198        thread_pool.install(|| {
199            tims_frames = frame_ids
200                .par_iter()
201                .map(|frame_id| {
202                    self.build_frame(
203                        *frame_id,
204                        fragmentation,
205                        mz_noise_precursor,
206                        uniform,
207                        precursor_noise_ppm,
208                        mz_noise_fragment,
209                        fragment_noise_ppm,
210                        right_drag,
211                    )
212                })
213                .collect();
214        });
215
216        tims_frames.sort_by(|a, b| a.frame_id.cmp(&b.frame_id));
217
218        tims_frames
219    }
220
221    pub fn build_frames_annotated(
222        &self,
223        frame_ids: Vec<u32>,
224        fragmentation: bool,
225        mz_noise_precursor: bool,
226        uniform: bool,
227        precursor_noise_ppm: f64,
228        mz_noise_fragment: bool,
229        fragment_noise_ppm: f64,
230        right_drag: bool,
231        num_threads: usize,
232    ) -> Vec<TimsFrameAnnotated> {
233        let thread_pool = ThreadPoolBuilder::new()
234            .num_threads(num_threads)
235            .build()
236            .unwrap();
237        let mut tims_frames: Vec<TimsFrameAnnotated> = Vec::new();
238
239        thread_pool.install(|| {
240            tims_frames = frame_ids
241                .par_iter()
242                .map(|frame_id| {
243                    self.build_frame_annotated(
244                        *frame_id,
245                        fragmentation,
246                        mz_noise_precursor,
247                        uniform,
248                        precursor_noise_ppm,
249                        mz_noise_fragment,
250                        fragment_noise_ppm,
251                        right_drag,
252                    )
253                })
254                .collect();
255        });
256
257        tims_frames.sort_by(|a, b| a.frame_id.cmp(&b.frame_id));
258
259        tims_frames
260    }
261
262    fn build_ms1_frame(
263        &self,
264        frame_id: u32,
265        mz_noise_precursor: bool,
266        uniform: bool,
267        precursor_ppm: f64,
268        right_drag: bool,
269    ) -> TimsFrame {
270        let mut tims_frame = self.precursor_frame_builder.build_precursor_frame(
271            frame_id,
272            mz_noise_precursor,
273            uniform,
274            precursor_ppm,
275            right_drag,
276        );
277        let intensities_rounded = tims_frame
278            .ims_frame
279            .intensity
280            .iter()
281            .map(|x| x.round())
282            .collect::<Vec<_>>();
283        tims_frame.ims_frame.intensity = intensities_rounded;
284        tims_frame
285    }
286
287    fn build_ms1_frame_annotated(
288        &self,
289        frame_id: u32,
290        mz_noise_precursor: bool,
291        uniform: bool,
292        precursor_ppm: f64,
293        right_drag: bool,
294    ) -> TimsFrameAnnotated {
295        let mut tims_frame = self
296            .precursor_frame_builder
297            .build_precursor_frame_annotated(
298                frame_id,
299                mz_noise_precursor,
300                uniform,
301                precursor_ppm,
302                right_drag,
303            );
304        let intensities_rounded = tims_frame
305            .intensity
306            .iter()
307            .map(|x| x.round())
308            .collect::<Vec<_>>();
309        tims_frame.intensity = intensities_rounded;
310        tims_frame
311    }
312
313    fn build_ms2_frame(
314        &self,
315        frame_id: u32,
316        fragmentation: bool,
317        mz_noise_fragment: bool,
318        uniform: bool,
319        fragment_ppm: f64,
320        right_drag: bool,
321    ) -> TimsFrame {
322        match fragmentation {
323            false => {
324                let mut frame = self.transmission_settings.transmit_tims_frame(
325                    &self.build_ms1_frame(
326                        frame_id,
327                        mz_noise_fragment,
328                        uniform,
329                        fragment_ppm,
330                        right_drag,
331                    ),
332                    None,
333                );
334                let intensities_rounded = frame
335                    .ims_frame
336                    .intensity
337                    .iter()
338                    .map(|x| x.round())
339                    .collect::<Vec<_>>();
340                frame.ims_frame.intensity = intensities_rounded;
341                frame.ms_type = MsType::FragmentDia;
342                frame
343            }
344            true => {
345                let mut frame = self.build_fragment_frame(
346                    frame_id,
347                    &self.fragment_ions.as_ref().unwrap(),
348                    mz_noise_fragment,
349                    uniform,
350                    fragment_ppm,
351                    None,
352                    None,
353                    None,
354                    Some(right_drag),
355                );
356                let intensities_rounded = frame
357                    .ims_frame
358                    .intensity
359                    .iter()
360                    .map(|x| x.round())
361                    .collect::<Vec<_>>();
362                frame.ims_frame.intensity = intensities_rounded;
363                frame
364            }
365        }
366    }
367
368    fn build_ms2_frame_annotated(
369        &self,
370        frame_id: u32,
371        fragmentation: bool,
372        mz_noise_fragment: bool,
373        uniform: bool,
374        fragment_ppm: f64,
375        right_drag: bool,
376    ) -> TimsFrameAnnotated {
377        match fragmentation {
378            false => {
379                let mut frame = self.transmission_settings.transmit_tims_frame_annotated(
380                    &self.build_ms1_frame_annotated(
381                        frame_id,
382                        mz_noise_fragment,
383                        uniform,
384                        fragment_ppm,
385                        right_drag,
386                    ),
387                    None,
388                );
389                let intensities_rounded = frame
390                    .intensity
391                    .iter()
392                    .map(|x| x.round())
393                    .collect::<Vec<_>>();
394                frame.intensity = intensities_rounded;
395                frame.ms_type = MsType::FragmentDia;
396                frame
397            }
398            true => {
399                let mut frame = self.build_fragment_frame_annotated(
400                    frame_id,
401                    &self.fragment_ions_annotated.as_ref().unwrap(),
402                    mz_noise_fragment,
403                    uniform,
404                    fragment_ppm,
405                    None,
406                    None,
407                    None,
408                    Some(right_drag),
409                );
410                let intensities_rounded = frame
411                    .intensity
412                    .iter()
413                    .map(|x| x.round())
414                    .collect::<Vec<_>>();
415                frame.intensity = intensities_rounded;
416                frame
417            }
418        }
419    }
420
421    /// Build a fragment frame
422    ///
423    /// # Arguments
424    ///
425    /// * `frame_id` - The frame id
426    /// * `mz_min` - The minimum m/z value in fragment spectrum
427    /// * `mz_max` - The maximum m/z value in fragment spectrum
428    /// * `intensity_min` - The minimum intensity value in fragment spectrum
429    ///
430    /// # Returns
431    ///
432    /// A TimsFrame
433    ///
434    fn build_fragment_frame(
435        &self,
436        frame_id: u32,
437        fragment_ions: &BTreeMap<
438            (u32, i8, i32),
439            (PeptideProductIonSeriesCollection, Vec<MzSpectrum>),
440        >,
441        mz_noise_fragment: bool,
442        uniform: bool,
443        fragment_ppm: f64,
444        mz_min: Option<f64>,
445        mz_max: Option<f64>,
446        intensity_min: Option<f64>,
447        right_drag: Option<bool>,
448    ) -> TimsFrame {
449        // check frame id
450        let ms_type = match self
451            .precursor_frame_builder
452            .precursor_frame_id_set
453            .contains(&frame_id)
454        {
455            false => MsType::FragmentDia,
456            true => MsType::Unknown,
457        };
458
459        let mut tims_spectra: Vec<TimsSpectrum> = Vec::new();
460
461        // Frame might not have any peptides
462        if !self
463            .precursor_frame_builder
464            .frame_to_abundances
465            .contains_key(&frame_id)
466        {
467            return TimsFrame::new(
468                frame_id as i32,
469                ms_type.clone(),
470                *self
471                    .precursor_frame_builder
472                    .frame_to_rt
473                    .get(&frame_id)
474                    .unwrap() as f64,
475                vec![],
476                vec![],
477                vec![],
478                vec![],
479                vec![],
480            );
481        }
482
483        // Get the peptide ids and abundances for the frame, should now save to unwrap since we checked if the frame is in the map
484        let (peptide_ids, frame_abundances) = self
485            .precursor_frame_builder
486            .frame_to_abundances
487            .get(&frame_id)
488            .unwrap();
489
490        // Go over all peptides in the frame with their respective abundances
491        for (peptide_id, frame_abundance) in peptide_ids.iter().zip(frame_abundances.iter()) {
492            // jump to next peptide if the peptide_id is not in the peptide_to_ions map
493            if !self
494                .precursor_frame_builder
495                .peptide_to_ions
496                .contains_key(&peptide_id)
497            {
498                continue;
499            }
500
501            // get all the ions for the peptide
502            let (ion_abundances, scan_occurrences, scan_abundances, charges, spectra) = self
503                .precursor_frame_builder
504                .peptide_to_ions
505                .get(&peptide_id)
506                .unwrap();
507
508            for (index, ion_abundance) in ion_abundances.iter().enumerate() {
509                // occurrence and abundance of the ion in the scan
510                let all_scan_occurrence = scan_occurrences.get(index).unwrap();
511                let all_scan_abundance = scan_abundances.get(index).unwrap();
512
513                // get precursor spectrum for the ion
514                let spectrum = spectra.get(index).unwrap();
515
516                // go over occurrence and abundance of the ion in the scan
517                for (scan, scan_abundance) in
518                    all_scan_occurrence.iter().zip(all_scan_abundance.iter())
519                {
520                    // first, check if precursor is transmitted
521                    if !self.transmission_settings.any_transmitted(
522                        frame_id as i32,
523                        *scan as i32,
524                        &spectrum.mz,
525                        None,
526                    ) {
527                        continue;
528                    }
529
530                    // calculate abundance factor
531                    let total_events = self
532                        .precursor_frame_builder
533                        .peptide_to_events
534                        .get(&peptide_id)
535                        .unwrap();
536                    let fraction_events =
537                        frame_abundance * scan_abundance * ion_abundance * total_events;
538
539                    // get collision energy for the ion
540                    let collision_energy = self
541                        .fragmentation_settings
542                        .get_collision_energy(frame_id as i32, *scan as i32);
543                    let collision_energy_quantized = (collision_energy * 1e1).round() as i32;
544
545                    // get charge state for the ion
546                    let charge_state = charges.get(index).unwrap();
547                    // extract fragment ions for the peptide, charge state and collision energy
548                    let maybe_value = fragment_ions.get(&(
549                        *peptide_id,
550                        *charge_state,
551                        collision_energy_quantized,
552                    ));
553
554                    // jump to next peptide if the fragment_ions is None (can this happen?)
555                    if maybe_value.is_none() {
556                        continue;
557                    }
558
559                    // for each fragment ion series, create a spectrum and add it to the tims_spectra
560                    for fragment_ion_series in maybe_value.unwrap().1.iter() {
561                        let scaled_spec = fragment_ion_series.clone() * fraction_events as f64;
562                        let right_drag = right_drag.unwrap_or(false);
563
564                        let mz_spectrum = if mz_noise_fragment {
565                            match uniform {
566                                true => scaled_spec.add_mz_noise_uniform(fragment_ppm, right_drag),
567                                false => scaled_spec.add_mz_noise_normal(fragment_ppm),
568                            }
569                        } else {
570                            scaled_spec
571                        };
572
573                        tims_spectra.push(TimsSpectrum::new(
574                            frame_id as i32,
575                            *scan as i32,
576                            *self
577                                .precursor_frame_builder
578                                .frame_to_rt
579                                .get(&frame_id)
580                                .unwrap() as f64,
581                            *self
582                                .precursor_frame_builder
583                                .scan_to_mobility
584                                .get(&scan)
585                                .unwrap() as f64,
586                            ms_type.clone(),
587                            IndexedMzSpectrum::new(
588                                vec![0; mz_spectrum.mz.len()],
589                                mz_spectrum.mz,
590                                mz_spectrum.intensity,
591                            )
592                            .filter_ranged(100.0, 1700.0, 1.0, 1e9),
593                        ));
594                    }
595                }
596            }
597        }
598
599        if tims_spectra.is_empty() {
600            return TimsFrame::new(
601                frame_id as i32,
602                ms_type.clone(),
603                *self
604                    .precursor_frame_builder
605                    .frame_to_rt
606                    .get(&frame_id)
607                    .unwrap() as f64,
608                vec![],
609                vec![],
610                vec![],
611                vec![],
612                vec![],
613            );
614        }
615
616        let tims_frame = TimsFrame::from_tims_spectra(tims_spectra);
617        tims_frame.filter_ranged(
618            mz_min.unwrap_or(100.0),
619            mz_max.unwrap_or(1700.0),
620            0,
621            1000,
622            0.0,
623            10.0,
624            intensity_min.unwrap_or(1.0),
625            1e9,
626        )
627    }
628
629    pub fn build_fragment_frame_annotated(
630        &self,
631        frame_id: u32,
632        fragment_ions: &BTreeMap<
633            (u32, i8, i32),
634            (PeptideProductIonSeriesCollection, Vec<MzSpectrumAnnotated>),
635        >,
636        mz_noise_fragment: bool,
637        uniform: bool,
638        fragment_ppm: f64,
639        mz_min: Option<f64>,
640        mz_max: Option<f64>,
641        intensity_min: Option<f64>,
642        right_drag: Option<bool>,
643    ) -> TimsFrameAnnotated {
644        let ms_type = match self
645            .precursor_frame_builder
646            .precursor_frame_id_set
647            .contains(&frame_id)
648        {
649            false => MsType::FragmentDia,
650            true => MsType::Unknown,
651        };
652
653        let mut tims_spectra: Vec<TimsSpectrumAnnotated> = Vec::new();
654
655        if !self
656            .precursor_frame_builder
657            .frame_to_abundances
658            .contains_key(&frame_id)
659        {
660            return TimsFrameAnnotated::new(
661                frame_id as i32,
662                *self
663                    .precursor_frame_builder
664                    .frame_to_rt
665                    .get(&frame_id)
666                    .unwrap() as f64,
667                ms_type.clone(),
668                vec![],
669                vec![],
670                vec![],
671                vec![],
672                vec![],
673                vec![],
674            );
675        }
676
677        let (peptide_ids, frame_abundances) = self
678            .precursor_frame_builder
679            .frame_to_abundances
680            .get(&frame_id)
681            .unwrap();
682
683        for (peptide_id, frame_abundance) in peptide_ids.iter().zip(frame_abundances.iter()) {
684            if !self
685                .precursor_frame_builder
686                .peptide_to_ions
687                .contains_key(&peptide_id)
688            {
689                continue;
690            }
691
692            let (ion_abundances, scan_occurrences, scan_abundances, charges, _) = self
693                .precursor_frame_builder
694                .peptide_to_ions
695                .get(&peptide_id)
696                .unwrap();
697
698            for (index, ion_abundance) in ion_abundances.iter().enumerate() {
699                let all_scan_occurrence = scan_occurrences.get(index).unwrap();
700                let all_scan_abundance = scan_abundances.get(index).unwrap();
701
702                let peptide = self
703                    .precursor_frame_builder
704                    .peptides
705                    .get(peptide_id)
706                    .unwrap();
707                let ion = PeptideIon::new(
708                    peptide.sequence.sequence.clone(),
709                    charges[index] as i32,
710                    *ion_abundance as f64,
711                    Some(*peptide_id as i32),
712                );
713                // TODO: make this configurable
714                let spectrum = ion.calculate_isotopic_spectrum_annotated(1e-3, 1e-8, 200, 1e-4);
715
716                for (scan, scan_abundance) in
717                    all_scan_occurrence.iter().zip(all_scan_abundance.iter())
718                {
719                    if !self.transmission_settings.any_transmitted(
720                        frame_id as i32,
721                        *scan as i32,
722                        &spectrum.mz,
723                        None,
724                    ) {
725                        continue;
726                    }
727
728                    let total_events = self
729                        .precursor_frame_builder
730                        .peptide_to_events
731                        .get(&peptide_id)
732                        .unwrap();
733                    let fraction_events =
734                        frame_abundance * scan_abundance * ion_abundance * total_events;
735
736                    let collision_energy = self
737                        .fragmentation_settings
738                        .get_collision_energy(frame_id as i32, *scan as i32);
739                    let collision_energy_quantized = (collision_energy * 1e1).round() as i32;
740
741                    let charge_state = charges.get(index).unwrap();
742                    let maybe_value = fragment_ions.get(&(
743                        *peptide_id,
744                        *charge_state,
745                        collision_energy_quantized,
746                    ));
747
748                    if maybe_value.is_none() {
749                        continue;
750                    }
751
752                    for fragment_ion_series in maybe_value.unwrap().1.iter() {
753                        let scaled_spec = fragment_ion_series.clone() * fraction_events as f64;
754                        let right_drag = right_drag.unwrap_or(false);
755
756                        let mz_spectrum = if mz_noise_fragment {
757                            match uniform {
758                                true => scaled_spec.add_mz_noise_uniform(fragment_ppm, right_drag),
759                                false => scaled_spec.add_mz_noise_normal(fragment_ppm),
760                            }
761                        } else {
762                            scaled_spec
763                        };
764
765                        tims_spectra.push(TimsSpectrumAnnotated::new(
766                            frame_id as i32,
767                            *scan,
768                            *self
769                                .precursor_frame_builder
770                                .frame_to_rt
771                                .get(&frame_id)
772                                .unwrap() as f64,
773                            *self
774                                .precursor_frame_builder
775                                .scan_to_mobility
776                                .get(&scan)
777                                .unwrap() as f64,
778                            ms_type.clone(),
779                            vec![0; mz_spectrum.mz.len()],
780                            mz_spectrum,
781                        ));
782                    }
783                }
784            }
785        }
786
787        if tims_spectra.is_empty() {
788            return TimsFrameAnnotated::new(
789                frame_id as i32,
790                *self
791                    .precursor_frame_builder
792                    .frame_to_rt
793                    .get(&frame_id)
794                    .unwrap() as f64,
795                ms_type.clone(),
796                vec![],
797                vec![],
798                vec![],
799                vec![],
800                vec![],
801                vec![],
802            );
803        }
804
805        let tims_frame = TimsFrameAnnotated::from_tims_spectra_annotated(tims_spectra);
806
807        tims_frame.filter_ranged(
808            mz_min.unwrap_or(100.0),
809            mz_max.unwrap_or(1700.0),
810            0.0,
811            10.0,
812            0,
813            1000,
814            intensity_min.unwrap_or(1.0),
815            1e9,
816        )
817    }
818
819    pub fn get_ion_transmission_matrix(
820        &self,
821        peptide_id: u32,
822        charge: i8,
823        include_precursor_frames: bool,
824    ) -> Vec<Vec<f32>> {
825        let maybe_peptide_sim = self.precursor_frame_builder.peptides.get(&peptide_id);
826
827        let mut frame_ids = match maybe_peptide_sim {
828            Some(maybe_peptide_sim) => maybe_peptide_sim.frame_distribution.occurrence.clone(),
829            _ => vec![],
830        };
831
832        if !include_precursor_frames {
833            frame_ids = frame_ids
834                .iter()
835                .filter(|frame_id| {
836                    !self
837                        .precursor_frame_builder
838                        .precursor_frame_id_set
839                        .contains(frame_id)
840                })
841                .cloned()
842                .collect();
843        }
844
845        let ion = self
846            .precursor_frame_builder
847            .ions
848            .get(&peptide_id)
849            .unwrap()
850            .iter()
851            .find(|ion| ion.charge == charge)
852            .unwrap();
853        let spectrum = ion.simulated_spectrum.clone();
854        let scan_distribution = &ion.scan_distribution;
855
856        let mut transmission_matrix =
857            vec![vec![0.0; frame_ids.len()]; scan_distribution.occurrence.len()];
858
859        for (frame_index, frame) in frame_ids.iter().enumerate() {
860            for (scan_index, scan) in scan_distribution.occurrence.iter().enumerate() {
861                if self.transmission_settings.all_transmitted(
862                    *frame as i32,
863                    *scan as i32,
864                    &spectrum.mz,
865                    None,
866                ) {
867                    transmission_matrix[scan_index][frame_index] = 1.0;
868                } else if self.transmission_settings.any_transmitted(
869                    *frame as i32,
870                    *scan as i32,
871                    &spectrum.mz,
872                    None,
873                ) {
874                    let transmitted_spectrum = self.transmission_settings.transmit_spectrum(
875                        *frame as i32,
876                        *scan as i32,
877                        spectrum.clone(),
878                        None,
879                    );
880                    let percentage_transmitted = transmitted_spectrum.intensity.iter().sum::<f64>()
881                        / spectrum.intensity.iter().sum::<f64>();
882                    transmission_matrix[scan_index][frame_index] = percentage_transmitted as f32;
883                }
884            }
885        }
886
887        transmission_matrix
888    }
889
890    pub fn count_number_transmissions(&self, peptide_id: u32, charge: i8) -> (usize, usize) {
891        let frame_ids: Vec<_> = self
892            .precursor_frame_builder
893            .peptides
894            .get(&peptide_id)
895            .unwrap()
896            .frame_distribution
897            .occurrence
898            .clone()
899            .iter()
900            .filter(|frame_id| {
901                !self
902                    .precursor_frame_builder
903                    .precursor_frame_id_set
904                    .contains(frame_id)
905            })
906            .cloned()
907            .collect();
908        let ion = self
909            .precursor_frame_builder
910            .ions
911            .get(&peptide_id)
912            .unwrap()
913            .iter()
914            .find(|ion| ion.charge == charge)
915            .unwrap();
916        let spectrum = ion.simulated_spectrum.clone();
917        let scan_distribution = &ion.scan_distribution;
918
919        let mut frame_count = 0;
920        let mut scan_count = 0;
921
922        for frame in frame_ids.iter() {
923            let mut frame_transmitted = false;
924            for scan in scan_distribution.occurrence.iter() {
925                if self.transmission_settings.any_transmitted(
926                    *frame as i32,
927                    *scan as i32,
928                    &spectrum.mz,
929                    None,
930                ) {
931                    frame_transmitted = true;
932                    scan_count += 1;
933                }
934            }
935            if frame_transmitted {
936                frame_count += 1;
937            }
938        }
939
940        (frame_count, scan_count)
941    }
942
943    pub fn count_number_transmissions_parallel(
944        &self,
945        peptide_ids: Vec<u32>,
946        charge: Vec<i8>,
947        num_threads: usize,
948    ) -> Vec<(usize, usize)> {
949        let thread_pool = ThreadPoolBuilder::new()
950            .num_threads(num_threads)
951            .build()
952            .unwrap();
953        let result: Vec<(usize, usize)> = thread_pool.install(|| {
954            peptide_ids
955                .par_iter()
956                .zip(charge.par_iter())
957                .map(|(peptide_id, charge)| self.count_number_transmissions(*peptide_id, *charge))
958                .collect()
959        });
960
961        result
962    }
963}
964
965impl TimsTofCollisionEnergy for TimsTofSyntheticsFrameBuilderDIA {
966    fn get_collision_energy(&self, frame_id: i32, scan_id: i32) -> f64 {
967        self.fragmentation_settings
968            .get_collision_energy(frame_id, scan_id)
969    }
970}