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
132pub trait IonTransmission {
133 fn apply_transmission(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>) -> Vec<f64>;
134
135 fn transmit_spectrum(&self, frame_id: i32, scan_id: i32, spectrum: MzSpectrum, min_probability: Option<f64>) -> MzSpectrum {
149
150 let probability_cutoff = min_probability.unwrap_or(0.5);
151 let transmission_probability = self.apply_transmission(frame_id, scan_id, &spectrum.mz);
152
153 let mut filtered_mz = Vec::new();
154 let mut filtered_intensity = Vec::new();
155
156 for (i, (mz, intensity)) in spectrum.mz.iter().zip(spectrum.intensity.iter()).enumerate() {
158 if transmission_probability[i] > probability_cutoff {
159 filtered_mz.push(*mz);
160 filtered_intensity.push(*intensity* transmission_probability[i]);
161 }
162 }
163
164 MzSpectrum {
165 mz: filtered_mz,
166 intensity: filtered_intensity,
167 }
168 }
169
170 fn transmit_annotated_spectrum(&self, frame_id: i32, scan_id: i32, spectrum: MzSpectrumAnnotated, min_probability: Option<f64>) -> MzSpectrumAnnotated {
184 let probability_cutoff = min_probability.unwrap_or(0.5);
185 let transmission_probability = self.apply_transmission(frame_id, scan_id, &spectrum.mz);
186
187 let mut filtered_mz = Vec::new();
188 let mut filtered_intensity = Vec::new();
189 let mut filtered_annotation = Vec::new();
190
191 for (i, (mz, intensity, annotation)) in izip!(spectrum.mz.iter(), spectrum.intensity.iter(), spectrum.annotations.iter()).enumerate() {
193 if transmission_probability[i] > probability_cutoff {
194 filtered_mz.push(*mz);
195 filtered_intensity.push(*intensity* transmission_probability[i]);
196 filtered_annotation.push(annotation.clone());
197 }
198 }
199
200 MzSpectrumAnnotated {
201 mz: filtered_mz,
202 intensity: filtered_intensity,
203 annotations: filtered_annotation,
204 }
205 }
206
207 fn transmit_ion(&self, frame_ids: Vec<i32>, scan_ids: Vec<i32>, spec: MzSpectrum, min_proba: Option<f64>) -> Vec<Vec<MzSpectrum>> {
208
209 let mut result: Vec<Vec<MzSpectrum>> = Vec::new();
210
211 for frame_id in frame_ids.iter() {
212 let mut frame_result: Vec<MzSpectrum> = Vec::new();
213 for scan_id in scan_ids.iter() {
214 let transmitted_spectrum = self.transmit_spectrum(*frame_id, *scan_id, spec.clone(), min_proba);
215 frame_result.push(transmitted_spectrum);
216 }
217 result.push(frame_result);
218 }
219 result
220 }
221
222 fn get_transmission_set(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>, min_proba: Option<f64>) -> HashSet<usize> {
236 let probability_cutoff = min_proba.unwrap_or(0.5);
238 let transmission_probability = self.apply_transmission(frame_id, scan_id, mz);
239 mz.iter().enumerate().filter(|&(i, _)| transmission_probability[i] > probability_cutoff).map(|(i, _)| i).collect()
240 }
241
242 fn all_transmitted(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>, min_proba: Option<f64>) -> bool {
256 let probability_cutoff = min_proba.unwrap_or(0.5);
257 let transmission_probability = self.apply_transmission(frame_id, scan_id, mz);
258 transmission_probability.iter().all(|&p| p > probability_cutoff)
259 }
260
261 fn is_transmitted(&self, frame_id: i32, scan_id: i32, mz: f64, min_proba: Option<f64>) -> bool {
275 let probability_cutoff = min_proba.unwrap_or(0.5);
276 let transmission_probability = self.apply_transmission(frame_id, scan_id, &vec![mz]);
277 transmission_probability[0] > probability_cutoff
278 }
279
280 fn any_transmitted(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>, min_proba: Option<f64>) -> bool {
294 let probability_cutoff = min_proba.unwrap_or(0.5);
295 let transmission_probability = self.apply_transmission(frame_id, scan_id, mz);
296 transmission_probability.iter().any(|&p| p > probability_cutoff)
297 }
298
299 fn transmit_tims_frame(&self, frame: &TimsFrame, min_probability: Option<f64>) -> TimsFrame {
301 let spectra = frame.to_tims_spectra();
302 let mut filtered_spectra = Vec::new();
303
304 for mut spectrum in spectra {
305 let filtered_spectrum = self.transmit_spectrum(frame.frame_id, spectrum.scan, spectrum.spectrum.mz_spectrum, min_probability);
306 if filtered_spectrum.mz.len() > 0 {
307 spectrum.spectrum.mz_spectrum = filtered_spectrum;
308 filtered_spectra.push(spectrum);
309 }
310 }
311
312 if filtered_spectra.len() > 0 {
313 TimsFrame::from_tims_spectra(filtered_spectra)
314 } else {
315 TimsFrame::new(
316 frame.frame_id,
317 frame.ms_type.clone(),
318 0.0,
319 vec![],
320 vec![],
321 vec![],
322 vec![],
323 vec![]
324 )
325 }
326 }
327
328 fn transmit_tims_frame_annotated(&self, frame: &TimsFrameAnnotated, min_probability: Option<f64>) -> TimsFrameAnnotated {
340 let spectra = frame.to_tims_spectra_annotated();
341 let mut filtered_spectra = Vec::new();
342
343 for mut spectrum in spectra {
344 let filtered_spectrum = self.transmit_annotated_spectrum(frame.frame_id, spectrum.scan as i32, spectrum.spectrum.clone(), min_probability);
345 if filtered_spectrum.mz.len() > 0 {
346 spectrum.spectrum = filtered_spectrum;
347 filtered_spectra.push(spectrum);
348 }
349 }
350
351 if filtered_spectra.len() > 0 {
352 TimsFrameAnnotated::from_tims_spectra_annotated(filtered_spectra)
353 } else {
354 TimsFrameAnnotated::new(
355 frame.frame_id,
356 frame.retention_time,
357 frame.ms_type.clone(),
358 vec![],
359 vec![],
360 vec![],
361 vec![],
362 vec![],
363 vec![]
364 )
365 }
366 }
367
368 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)>) {
369
370 let probability_cutoff = min_probability.unwrap_or(0.5);
371 let transmission_probability = self.apply_transmission(frame_id, scan_id, &isotopic_envelope);
372 let mut result: Vec<(f64, f64)> = Vec::new();
373
374 for (mz, p) in isotopic_envelope.iter().zip(transmission_probability.iter()) {
375 if *p > probability_cutoff {
376 result.push((*mz - mz_mono, *p));
377 }
378 }
379
380 (mz_mono, result)
381 }
382}
383
384#[derive(Clone, Debug)]
385pub struct TimsTransmissionDIA {
386 frame_to_window_group: HashMap<i32, i32>,
387 window_group_settings: HashMap<(i32, i32), (f64, f64)>,
388 k: f64,
389}
390
391impl TimsTransmissionDIA {
392 pub fn new(
393 frame: Vec<i32>,
394 frame_window_group: Vec<i32>,
395 window_group: Vec<i32>,
396 scan_start: Vec<i32>,
397 scan_end: Vec<i32>,
398 isolation_mz: Vec<f64>,
399 isolation_width: Vec<f64>,
400 k: Option<f64>,
401 ) -> Self {
402 let frame_to_window_group = frame.iter().zip(frame_window_group.iter()).map(|(&f, &wg)| (f, wg)).collect::<HashMap<i32, i32>>();
404 let mut window_group_settings: HashMap<(i32, i32), (f64, f64)> = HashMap::new();
405
406 for (index, &wg) in window_group.iter().enumerate() {
407 let scan_start = scan_start[index];
408 let scan_end = scan_end[index];
409 let isolation_mz = isolation_mz[index];
410 let isolation_width = isolation_width[index];
411
412 let value = (isolation_mz, isolation_width);
413
414 for scan in scan_start..scan_end + 1 {
415 let key = (wg, scan);
416 window_group_settings.insert(key, value);
417 }
418 }
419
420 Self {
421 frame_to_window_group,
422 window_group_settings,
423 k: k.unwrap_or(15.0),
424 }
425 }
426
427 pub fn frame_to_window_group(&self, frame_id: i32) -> i32 {
428 let window_group = self.frame_to_window_group.get(&frame_id);
429 match window_group {
430 Some(&wg) => wg,
431 None => -1,
432 }
433 }
434
435 pub fn get_setting(&self, window_group: i32, scan_id: i32) -> Option<&(f64, f64)> {
436 let setting = self.window_group_settings.get(&(window_group, scan_id));
437 match setting {
438 Some(s) => Some(s),
439 None => None,
440 }
441 }
442
443 pub fn is_precursor(&self, frame_id: i32) -> bool {
445 match self.frame_to_window_group.contains_key(&frame_id) {
447 true => false,
448 false => true,
449 }
450 }
451}
452
453impl IonTransmission for TimsTransmissionDIA {
454 fn apply_transmission(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>) -> Vec<f64> {
455
456 let setting = self.get_setting(self.frame_to_window_group(frame_id), scan_id);
457 let is_precursor = self.is_precursor(frame_id);
458
459 match setting {
460 Some((isolation_mz, isolation_width)) => {
461 apply_transmission(*isolation_mz, *isolation_width, self.k, mz.clone())
462 },
463 None => match is_precursor {
464 true => vec![1.0; mz.len()],
465 false => vec![0.0; mz.len()],
466 }
467 }
468 }
469}
470
471#[derive(Clone, Debug)]
472pub struct PASEFMeta {
473 pub frame: i32,
474 pub scan_start: i32,
475 pub scan_end: i32,
476 pub isolation_mz: f64,
477 pub isolation_width: f64,
478 pub collision_energy: f64,
479 pub precursor: i32,
480}
481
482impl PASEFMeta {
483 pub fn new(frame: i32, scan_start: i32, scan_end: i32, isolation_mz: f64, isolation_width: f64, collision_energy: f64, precursor: i32) -> Self {
484 Self {
485 frame,
486 scan_start,
487 scan_end,
488 isolation_mz,
489 isolation_width,
490 collision_energy,
491 precursor,
492 }
493 }
494}
495
496#[derive(Clone, Debug)]
497pub struct TimsTransmissionDDA {
498 pub pasef_meta: BTreeMap<i32, Vec<PASEFMeta>>,
500 pub k: f64,
501}
502
503impl TimsTransmissionDDA {
504 pub fn new(pasef_meta: Vec<PASEFMeta>, k: Option<f64>) -> Self {
505 let mut pasef_map: BTreeMap<i32, Vec<PASEFMeta>> = BTreeMap::new();
506 for meta in pasef_meta {
507 let entry = pasef_map.entry(meta.frame).or_insert(Vec::new());
508 entry.push(meta);
509 }
510 Self {
511 pasef_meta: pasef_map,
512 k: k.unwrap_or(15.0),
513 }
514 }
515
516 pub fn get_collision_energy(&self, frame_id: i32, scan_id: i32) -> Option<f64> {
517 let frame_meta = self.pasef_meta.get(&frame_id);
518 match frame_meta {
519 Some(meta) => {
520 for m in meta {
521 if scan_id >= m.scan_start && scan_id <= m.scan_end {
522 return Some(m.collision_energy);
523 }
524 }
525 None
526 },
527 None => None,
528 }
529 }
530}
531
532impl IonTransmission for TimsTransmissionDDA {
533 fn apply_transmission(&self, frame_id: i32, scan_id: i32, mz: &Vec<f64>) -> Vec<f64> {
534
535 let meta = self.pasef_meta.get(&frame_id);
537
538 match meta {
539 Some(meta) => {
540 let mut transmission = vec![0.0; mz.len()];
541
542 for m in meta {
543 if scan_id >= m.scan_start && scan_id <= m.scan_end {
545 let transmission_prob = apply_transmission(m.isolation_mz, m.isolation_width, self.k, mz.clone());
547 for (i, p) in transmission_prob.iter().enumerate() {
549 transmission[i] = p.max(transmission[i]);
550 }
551 }
552 }
553 transmission
554 },
555 None => vec![0.0; mz.len()],
557 }
558 }
559}