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