1use std::collections::{BTreeMap, HashMap, HashSet};
2use std::f64;
3use std::f64::consts::E;
4use itertools::izip;
5use crate::data::spectrum::MzSpectrum;
6use crate::simulation::annotation::{MzSpectrumAnnotated, TimsFrameAnnotated};
7use crate::timstof::frame::TimsFrame;
8
9pub fn smooth_step(x: &Vec<f64>, up_start: f64, up_end: f64, k: f64) -> Vec<f64> {
33 let m = (up_start + up_end) / 2.0;
34 x.iter().map(|&xi| 1.0 / (1.0 + E.powf(-k * (xi - m)))).collect()
35}
36
37pub fn smooth_step_up_down(x: &Vec<f64>, up_start: f64, up_end: f64, down_start: f64, down_end: f64, k: f64) -> Vec<f64> {
63 let step_up = smooth_step(x, up_start, up_end, k);
64 let step_down = smooth_step(x, down_start, down_end, k);
65 step_up.iter().zip(step_down.iter()).map(|(&u, &d)| u - d).collect()
66}
67
68pub fn ion_transition_function_midpoint(midpoint: f64, window_length: f64, k: f64) -> impl Fn(Vec<f64>) -> Vec<f64> {
92 let half_window = window_length / 2.0;
93
94 let up_start = midpoint - half_window - 0.5;
95 let up_end = midpoint - half_window;
96 let down_start = midpoint + half_window;
97 let down_end = midpoint + half_window + 0.5;
98
99 move |mz: Vec<f64>| -> Vec<f64> {
101 smooth_step_up_down(&mz, up_start, up_end, down_start, down_end, k)
102 }
103}
104
105pub fn apply_transmission(midpoint: f64, window_length: f64, k: f64, mz: Vec<f64>) -> Vec<f64> {
129 ion_transition_function_midpoint(midpoint, window_length, k)(mz)
130}
131
132#[derive(Clone, Copy, Debug)]
144pub struct WindowTransmission {
145 pub center_mz: f64,
146 pub width_mz: f64,
147 pub k: f64,
149}
150
151impl WindowTransmission {
152 pub fn new(center_mz: f64, width_mz: f64, k: f64) -> Self {
153 WindowTransmission { center_mz, width_mz, k }
154 }
155
156 pub fn probabilities(&self, mz: &[f64]) -> Vec<f64> {
159 apply_transmission(self.center_mz, self.width_mz, self.k, mz.to_vec())
160 }
161
162 pub fn any_transmitted(&self, mz: &[f64], min_proba: Option<f64>) -> bool {
165 let cutoff = min_proba.unwrap_or(0.5);
166 self.probabilities(mz).iter().any(|&p| p > cutoff)
167 }
168
169 pub fn transmitted_set(&self, mz: &[f64], min_proba: Option<f64>) -> HashSet<usize> {
172 let cutoff = min_proba.unwrap_or(0.5);
173 let p = self.probabilities(mz);
174 mz.iter()
175 .enumerate()
176 .filter(|&(i, _)| p[i] > cutoff)
177 .map(|(i, _)| i)
178 .collect()
179 }
180}
181
182pub trait IonTransmission {
183 fn apply_transmission(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>) -> Vec<f64>;
184
185 fn transmit_spectrum(&self, frame_id: i32, scan_id: i32, spectrum: MzSpectrum, min_probability: Option<f64>) -> MzSpectrum {
199
200 let probability_cutoff = min_probability.unwrap_or(0.5);
201 let transmission_probability = self.apply_transmission(frame_id, scan_id, &spectrum.mz);
202
203 let mut filtered_mz = Vec::new();
204 let mut filtered_intensity = Vec::new();
205
206 for (i, (mz, intensity)) in spectrum.mz.iter().zip(spectrum.intensity.iter()).enumerate() {
208 if transmission_probability[i] > probability_cutoff {
209 filtered_mz.push(*mz);
210 filtered_intensity.push(*intensity* transmission_probability[i]);
211 }
212 }
213
214 MzSpectrum::new(filtered_mz, filtered_intensity)
215 }
216
217 fn transmit_annotated_spectrum(&self, frame_id: i32, scan_id: i32, spectrum: MzSpectrumAnnotated, min_probability: Option<f64>) -> MzSpectrumAnnotated {
231 let probability_cutoff = min_probability.unwrap_or(0.5);
232 let transmission_probability = self.apply_transmission(frame_id, scan_id, &spectrum.mz);
233
234 let mut filtered_mz = Vec::new();
235 let mut filtered_intensity = Vec::new();
236 let mut filtered_annotation = Vec::new();
237
238 for (i, (mz, intensity, annotation)) in izip!(spectrum.mz.iter(), spectrum.intensity.iter(), spectrum.annotations.iter()).enumerate() {
240 if transmission_probability[i] > probability_cutoff {
241 filtered_mz.push(*mz);
242 filtered_intensity.push(*intensity* transmission_probability[i]);
243 filtered_annotation.push(annotation.clone());
244 }
245 }
246
247 MzSpectrumAnnotated {
248 mz: filtered_mz,
249 intensity: filtered_intensity,
250 annotations: filtered_annotation,
251 }
252 }
253
254 fn transmit_ion(&self, frame_ids: Vec<i32>, scan_ids: Vec<i32>, spec: MzSpectrum, min_proba: Option<f64>) -> Vec<Vec<MzSpectrum>> {
255
256 let mut result: Vec<Vec<MzSpectrum>> = Vec::new();
257
258 for frame_id in frame_ids.iter() {
259 let mut frame_result: Vec<MzSpectrum> = Vec::new();
260 for scan_id in scan_ids.iter() {
261 let transmitted_spectrum = self.transmit_spectrum(*frame_id, *scan_id, spec.clone(), min_proba);
262 frame_result.push(transmitted_spectrum);
263 }
264 result.push(frame_result);
265 }
266 result
267 }
268
269 fn get_transmission_set(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>, min_proba: Option<f64>) -> HashSet<usize> {
283 let probability_cutoff = min_proba.unwrap_or(0.5);
285 let transmission_probability = self.apply_transmission(frame_id, scan_id, mz);
286 mz.iter().enumerate().filter(|&(i, _)| transmission_probability[i] > probability_cutoff).map(|(i, _)| i).collect()
287 }
288
289 fn all_transmitted(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>, min_proba: Option<f64>) -> bool {
303 let probability_cutoff = min_proba.unwrap_or(0.5);
304 let transmission_probability = self.apply_transmission(frame_id, scan_id, mz);
305 transmission_probability.iter().all(|&p| p > probability_cutoff)
306 }
307
308 fn is_transmitted(&self, frame_id: i32, scan_id: i32, mz: f64, min_proba: Option<f64>) -> bool {
322 let probability_cutoff = min_proba.unwrap_or(0.5);
323 let transmission_probability = self.apply_transmission(frame_id, scan_id, &vec![mz]);
324 transmission_probability[0] > probability_cutoff
325 }
326
327 fn any_transmitted(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>, min_proba: Option<f64>) -> bool {
341 let probability_cutoff = min_proba.unwrap_or(0.5);
342 let transmission_probability = self.apply_transmission(frame_id, scan_id, mz);
343 transmission_probability.iter().any(|&p| p > probability_cutoff)
344 }
345
346 fn transmit_tims_frame(&self, frame: &TimsFrame, min_probability: Option<f64>) -> TimsFrame {
348 let spectra = frame.to_tims_spectra();
349 let mut filtered_spectra = Vec::new();
350
351 for mut spectrum in spectra {
352 let filtered_spectrum = self.transmit_spectrum(frame.frame_id, spectrum.scan, spectrum.spectrum.mz_spectrum, min_probability);
353 if filtered_spectrum.mz.len() > 0 {
354 spectrum.spectrum.mz_spectrum = filtered_spectrum;
355 filtered_spectra.push(spectrum);
356 }
357 }
358
359 if filtered_spectra.len() > 0 {
360 TimsFrame::from_tims_spectra(filtered_spectra)
361 } else {
362 TimsFrame::new(
363 frame.frame_id,
364 frame.ms_type.clone(),
365 0.0,
366 vec![],
367 vec![],
368 vec![],
369 vec![],
370 vec![]
371 )
372 }
373 }
374
375 fn transmit_tims_frame_annotated(&self, frame: &TimsFrameAnnotated, min_probability: Option<f64>) -> TimsFrameAnnotated {
387 let spectra = frame.to_tims_spectra_annotated();
388 let mut filtered_spectra = Vec::new();
389
390 for mut spectrum in spectra {
391 let filtered_spectrum = self.transmit_annotated_spectrum(frame.frame_id, spectrum.scan as i32, spectrum.spectrum.clone(), min_probability);
392 if filtered_spectrum.mz.len() > 0 {
393 spectrum.spectrum = filtered_spectrum;
394 filtered_spectra.push(spectrum);
395 }
396 }
397
398 if filtered_spectra.len() > 0 {
399 TimsFrameAnnotated::from_tims_spectra_annotated(filtered_spectra)
400 } else {
401 TimsFrameAnnotated::new(
402 frame.frame_id,
403 frame.retention_time,
404 frame.ms_type.clone(),
405 vec![],
406 vec![],
407 vec![],
408 vec![],
409 vec![],
410 vec![]
411 )
412 }
413 }
414
415 fn isotopes_transmitted(&self, frame_id: i32, scan_id: i32, mz_mono: f64, isotopic_envelope: &Vec<f64>, min_probability: Option<f64>) -> (f64, Vec<(f64, f64)>) {
416
417 let probability_cutoff = min_probability.unwrap_or(0.5);
418 let transmission_probability = self.apply_transmission(frame_id, scan_id, &isotopic_envelope);
419 let mut result: Vec<(f64, f64)> = Vec::new();
420
421 for (mz, p) in isotopic_envelope.iter().zip(transmission_probability.iter()) {
422 if *p > probability_cutoff {
423 result.push((*mz - mz_mono, *p));
424 }
425 }
426
427 (mz_mono, result)
428 }
429}
430
431#[derive(Clone, Debug)]
432pub struct TimsTransmissionDIA {
433 frame_to_window_group: HashMap<i32, i32>,
434 window_group_settings: HashMap<(i32, i32), (f64, f64)>,
435 k: f64,
436}
437
438impl TimsTransmissionDIA {
439 pub fn new(
440 frame: Vec<i32>,
441 frame_window_group: Vec<i32>,
442 window_group: Vec<i32>,
443 scan_start: Vec<i32>,
444 scan_end: Vec<i32>,
445 isolation_mz: Vec<f64>,
446 isolation_width: Vec<f64>,
447 k: Option<f64>,
448 ) -> Self {
449 let frame_to_window_group = frame.iter().zip(frame_window_group.iter()).map(|(&f, &wg)| (f, wg)).collect::<HashMap<i32, i32>>();
451 let mut window_group_settings: HashMap<(i32, i32), (f64, f64)> = HashMap::new();
452
453 for (index, &wg) in window_group.iter().enumerate() {
454 let scan_start = scan_start[index];
455 let scan_end = scan_end[index];
456 let isolation_mz = isolation_mz[index];
457 let isolation_width = isolation_width[index];
458
459 let value = (isolation_mz, isolation_width);
460
461 for scan in scan_start..scan_end + 1 {
462 let key = (wg, scan);
463 window_group_settings.insert(key, value);
464 }
465 }
466
467 Self {
468 frame_to_window_group,
469 window_group_settings,
470 k: k.unwrap_or(15.0),
471 }
472 }
473
474 pub fn frame_to_window_group(&self, frame_id: i32) -> i32 {
475 let window_group = self.frame_to_window_group.get(&frame_id);
476 match window_group {
477 Some(&wg) => wg,
478 None => -1,
479 }
480 }
481
482 pub fn get_setting(&self, window_group: i32, scan_id: i32) -> Option<&(f64, f64)> {
483 let setting = self.window_group_settings.get(&(window_group, scan_id));
484 match setting {
485 Some(s) => Some(s),
486 None => None,
487 }
488 }
489
490 pub fn is_precursor(&self, frame_id: i32) -> bool {
492 match self.frame_to_window_group.contains_key(&frame_id) {
494 true => false,
495 false => true,
496 }
497 }
498}
499
500impl IonTransmission for TimsTransmissionDIA {
501 fn apply_transmission(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>) -> Vec<f64> {
502
503 let setting = self.get_setting(self.frame_to_window_group(frame_id), scan_id);
504 let is_precursor = self.is_precursor(frame_id);
505
506 match setting {
507 Some((isolation_mz, isolation_width)) => {
508 apply_transmission(*isolation_mz, *isolation_width, self.k, mz.clone())
509 },
510 None => match is_precursor {
511 true => vec![1.0; mz.len()],
512 false => vec![0.0; mz.len()],
513 }
514 }
515 }
516}
517
518#[derive(Clone, Debug)]
519pub struct PASEFMeta {
520 pub frame: i32,
521 pub scan_start: i32,
522 pub scan_end: i32,
523 pub isolation_mz: f64,
524 pub isolation_width: f64,
525 pub collision_energy: f64,
526 pub precursor: i32,
527}
528
529impl PASEFMeta {
530 pub fn new(frame: i32, scan_start: i32, scan_end: i32, isolation_mz: f64, isolation_width: f64, collision_energy: f64, precursor: i32) -> Self {
531 Self {
532 frame,
533 scan_start,
534 scan_end,
535 isolation_mz,
536 isolation_width,
537 collision_energy,
538 precursor,
539 }
540 }
541}
542
543#[derive(Clone, Debug)]
544pub struct TimsTransmissionDDA {
545 pub pasef_meta: BTreeMap<i32, Vec<PASEFMeta>>,
547 pub k: f64,
548}
549
550impl TimsTransmissionDDA {
551 pub fn new(pasef_meta: Vec<PASEFMeta>, k: Option<f64>) -> Self {
552 let mut pasef_map: BTreeMap<i32, Vec<PASEFMeta>> = BTreeMap::new();
553 for meta in pasef_meta {
554 let entry = pasef_map.entry(meta.frame).or_insert(Vec::new());
555 entry.push(meta);
556 }
557 Self {
558 pasef_meta: pasef_map,
559 k: k.unwrap_or(15.0),
560 }
561 }
562
563 pub fn get_collision_energy(&self, frame_id: i32, scan_id: i32) -> Option<f64> {
564 let frame_meta = self.pasef_meta.get(&frame_id);
565 match frame_meta {
566 Some(meta) => {
567 for m in meta {
568 if scan_id >= m.scan_start && scan_id <= m.scan_end {
569 return Some(m.collision_energy);
570 }
571 }
572 None
573 },
574 None => None,
575 }
576 }
577
578 pub fn get_selections_for_precursor(&self, precursor_id: i32) -> Vec<(i32, f64)> {
581 let mut selections = Vec::new();
582 for (frame_id, meta_list) in &self.pasef_meta {
583 for meta in meta_list {
584 if meta.precursor == precursor_id {
585 selections.push((*frame_id, meta.collision_energy));
586 }
587 }
588 }
589 selections
590 }
591}
592
593impl IonTransmission for TimsTransmissionDDA {
594 fn apply_transmission(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>) -> Vec<f64> {
595
596 let meta = self.pasef_meta.get(&frame_id);
598
599 match meta {
600 Some(meta) => {
601 let mut transmission = vec![0.0; mz.len()];
602
603 for m in meta {
604 if scan_id >= m.scan_start && scan_id <= m.scan_end {
606 let transmission_prob = apply_transmission(m.isolation_mz, m.isolation_width, self.k, mz.clone());
608 for (i, p) in transmission_prob.iter().enumerate() {
610 transmission[i] = p.max(transmission[i]);
611 }
612 }
613 }
614 transmission
615 },
616 None => vec![0.0; mz.len()],
618 }
619 }
620}
621#[cfg(test)]
622mod p6b_window_transmission_tests {
623 use super::*;
624
625 #[test]
629 fn window_transmission_matches_scan_indexed_dia() {
630 let dia = TimsTransmissionDIA::new(
633 vec![2], vec![1], vec![1], vec![0], vec![50], vec![700.0], vec![20.0], None, );
642 let win = WindowTransmission::new(700.0, 20.0, 15.0);
643
644 let mz = vec![650.0, 689.0, 691.0, 700.0, 709.0, 711.0, 760.0];
646 let scan = 25; assert_eq!(dia.apply_transmission(2, scan, &mz), win.probabilities(&mz));
650 assert_eq!(
652 dia.any_transmitted(2, scan, &mz, None),
653 win.any_transmitted(&mz, None)
654 );
655 assert_eq!(
656 dia.get_transmission_set(2, scan, &mz, Some(0.5)),
657 win.transmitted_set(&mz, Some(0.5))
658 );
659 assert!(win.any_transmitted(&[700.0], None));
661 assert!(!win.any_transmitted(&[650.0], None));
662 }
663}