use mscore::data::peptide::PeptideProductIonSeriesCollection;
use mscore::data::spectrum::{IndexedMzSpectrum, MsType, MzSpectrum};
use mscore::timstof::collision::{TimsTofCollisionEnergy, TimsTofCollisionEnergyDIA};
use mscore::timstof::frame::TimsFrame;
use mscore::timstof::quadrupole::{IonTransmission, TimsTransmissionDDA, TimsTransmissionDIA};
use mscore::timstof::spectrum::TimsSpectrum;
use std::collections::{BTreeMap, HashSet};
use std::path::Path;
use std::sync::Arc;
use rayon::prelude::*;
use crate::sim::containers::{FragmentIonSim, FramesSim, IonSim, PeptidesSim, ScansSim};
use crate::sim::handle::TimsTofSyntheticsDataHandle;
pub struct TimsTofLazyFrameBuilderDIA {
pub db_path: String,
pub frames: Vec<FramesSim>,
pub scans: Vec<ScansSim>,
pub precursor_frame_id_set: HashSet<u32>,
pub frame_to_rt: BTreeMap<u32, f32>,
pub scan_to_mobility: BTreeMap<u32, f32>,
pub transmission_settings: TimsTransmissionDIA,
pub fragmentation_settings: TimsTofCollisionEnergyDIA,
pub num_threads: usize,
}
impl TimsTofLazyFrameBuilderDIA {
pub fn new(path: &Path, num_threads: usize) -> rusqlite::Result<Self> {
let handle = TimsTofSyntheticsDataHandle::new(path)?;
let frames = handle.read_frames()?;
let scans = handle.read_scans()?;
let precursor_frame_id_set = TimsTofSyntheticsDataHandle::build_precursor_frame_id_set(&frames);
let frame_to_rt = TimsTofSyntheticsDataHandle::build_frame_to_rt(&frames);
let scan_to_mobility = TimsTofSyntheticsDataHandle::build_scan_to_mobility(&scans);
let transmission_settings = handle.get_transmission_dia();
let fragmentation_settings = handle.get_collision_energy_dia();
Ok(Self {
db_path: path.to_str().unwrap().to_string(),
frames,
scans,
precursor_frame_id_set,
frame_to_rt,
scan_to_mobility,
transmission_settings,
fragmentation_settings,
num_threads,
})
}
fn load_data_for_frame_range(
&self,
frame_min: u32,
frame_max: u32,
) -> rusqlite::Result<(Vec<PeptidesSim>, Vec<IonSim>, Vec<FragmentIonSim>)> {
let path = Path::new(&self.db_path);
let handle = TimsTofSyntheticsDataHandle::new(path)?;
let peptides = handle.read_peptides_for_frame_range(frame_min, frame_max)?;
if peptides.is_empty() {
return Ok((Vec::new(), Vec::new(), Vec::new()));
}
let peptide_ids: Vec<u32> = peptides.iter().map(|p| p.peptide_id).collect();
let ions = handle.read_ions_for_peptides(&peptide_ids)?;
let fragment_ions = handle.read_fragment_ions_for_peptides(&peptide_ids)?;
Ok((peptides, ions, fragment_ions))
}
pub fn build_frames_lazy(
&self,
frame_ids: Vec<u32>,
fragmentation: bool,
mz_noise_precursor: bool,
uniform: bool,
precursor_noise_ppm: f64,
mz_noise_fragment: bool,
fragment_noise_ppm: f64,
right_drag: bool,
) -> Vec<TimsFrame> {
if frame_ids.is_empty() {
return Vec::new();
}
let frame_min = *frame_ids.iter().min().unwrap();
let frame_max = *frame_ids.iter().max().unwrap();
let (peptides, ions, fragment_ions) = match self.load_data_for_frame_range(frame_min, frame_max) {
Ok(data) => data,
Err(_) => return Vec::new(),
};
let peptide_map = TimsTofSyntheticsDataHandle::build_peptide_map(&peptides);
let peptide_to_ions = TimsTofSyntheticsDataHandle::build_peptide_to_ions(&ions);
let frame_to_abundances = TimsTofSyntheticsDataHandle::build_frame_to_abundances(&peptides);
let peptide_to_events = TimsTofSyntheticsDataHandle::build_peptide_to_events(&peptides);
let fragment_ions_map = if fragmentation {
Some(TimsTofSyntheticsDataHandle::build_fragment_ions(
&peptide_map,
&fragment_ions,
self.num_threads,
))
} else {
None
};
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(self.num_threads)
.build()
.unwrap();
pool.install(|| {
let mut tims_frames: Vec<TimsFrame> = Vec::with_capacity(frame_ids.len());
unsafe { tims_frames.set_len(frame_ids.len()); }
frame_ids.par_iter().enumerate().for_each(|(idx, frame_id)| {
let frame = self.build_single_frame(
*frame_id,
fragmentation,
mz_noise_precursor,
uniform,
precursor_noise_ppm,
mz_noise_fragment,
fragment_noise_ppm,
right_drag,
&peptide_map,
&peptide_to_ions,
&frame_to_abundances,
&peptide_to_events,
&fragment_ions_map,
);
unsafe {
let ptr = tims_frames.as_ptr() as *mut TimsFrame;
std::ptr::write(ptr.add(idx), frame);
}
});
tims_frames
})
}
#[allow(clippy::too_many_arguments)]
fn build_single_frame(
&self,
frame_id: u32,
fragmentation: bool,
mz_noise_precursor: bool,
uniform: bool,
precursor_noise_ppm: f64,
mz_noise_fragment: bool,
fragment_noise_ppm: f64,
right_drag: bool,
_peptide_map: &BTreeMap<u32, PeptidesSim>,
peptide_to_ions: &BTreeMap<u32, (Vec<f32>, Vec<Vec<u32>>, Vec<Vec<f32>>, Vec<i8>, Vec<MzSpectrum>)>,
frame_to_abundances: &BTreeMap<u32, (Vec<u32>, Vec<f32>)>,
peptide_to_events: &BTreeMap<u32, f32>,
fragment_ions_map: &Option<BTreeMap<(u32, i8, i32), (PeptideProductIonSeriesCollection, Vec<MzSpectrum>)>>,
) -> TimsFrame {
let is_precursor = self.precursor_frame_id_set.contains(&frame_id);
if is_precursor {
self.build_precursor_frame(
frame_id,
mz_noise_precursor,
uniform,
precursor_noise_ppm,
right_drag,
peptide_to_ions,
frame_to_abundances,
peptide_to_events,
)
} else {
self.build_fragment_frame(
frame_id,
fragmentation,
mz_noise_fragment,
uniform,
fragment_noise_ppm,
right_drag,
peptide_to_ions,
frame_to_abundances,
peptide_to_events,
fragment_ions_map,
)
}
}
#[allow(clippy::too_many_arguments)]
fn build_precursor_frame(
&self,
frame_id: u32,
mz_noise_precursor: bool,
uniform: bool,
precursor_noise_ppm: f64,
right_drag: bool,
peptide_to_ions: &BTreeMap<u32, (Vec<f32>, Vec<Vec<u32>>, Vec<Vec<f32>>, Vec<i8>, Vec<MzSpectrum>)>,
frame_to_abundances: &BTreeMap<u32, (Vec<u32>, Vec<f32>)>,
peptide_to_events: &BTreeMap<u32, f32>,
) -> TimsFrame {
let ms_type = MsType::Precursor;
let rt = *self.frame_to_rt.get(&frame_id).unwrap_or(&0.0) as f64;
let Some((peptide_ids, abundances)) = frame_to_abundances.get(&frame_id) else {
return TimsFrame::new(frame_id as i32, ms_type, rt, vec![], vec![], vec![], vec![], vec![]);
};
let estimated_capacity = peptide_ids.len() * 4;
let mut tims_spectra: Vec<TimsSpectrum> = Vec::with_capacity(estimated_capacity);
for (peptide_id, abundance) in peptide_ids.iter().zip(abundances.iter()) {
let Some((ion_abundances, scan_occurrences, scan_abundances, _, spectra)) =
peptide_to_ions.get(peptide_id)
else {
continue;
};
let total_events = *peptide_to_events.get(peptide_id).unwrap_or(&1.0);
for (index, ion_abundance) in ion_abundances.iter().enumerate() {
let scan_occurrence = &scan_occurrences[index];
let scan_abundance = &scan_abundances[index];
let spectrum = &spectra[index];
for (scan, scan_abu) in scan_occurrence.iter().zip(scan_abundance.iter()) {
let abundance_factor = abundance * ion_abundance * scan_abu * total_events;
let scaled_spec: MzSpectrum = spectrum.clone() * abundance_factor as f64;
let mz_spectrum = if mz_noise_precursor {
if uniform {
scaled_spec.add_mz_noise_uniform(precursor_noise_ppm, right_drag)
} else {
scaled_spec.add_mz_noise_normal(precursor_noise_ppm)
}
} else {
scaled_spec
};
let scan_mobility = *self.scan_to_mobility.get(scan).unwrap_or(&0.0) as f64;
let spectrum_len = mz_spectrum.mz.len();
tims_spectra.push(TimsSpectrum::new(
frame_id as i32,
*scan as i32,
rt,
scan_mobility,
ms_type.clone(),
IndexedMzSpectrum::from_mz_spectrum(
vec![0; spectrum_len],
mz_spectrum,
),
));
}
}
}
let mut filtered = TimsFrame::from_tims_spectra_filtered(
tims_spectra, 0.0, 10000.0, 0, 2000, 0.0, 10.0, 1.0, 1e9,
);
let intensities_rounded: Vec<f64> = filtered
.ims_frame
.intensity
.iter()
.map(|x| x.round())
.collect();
filtered.ims_frame.intensity = Arc::new(intensities_rounded);
filtered
}
#[allow(clippy::too_many_arguments)]
fn build_fragment_frame(
&self,
frame_id: u32,
fragmentation: bool,
mz_noise_fragment: bool,
uniform: bool,
fragment_noise_ppm: f64,
right_drag: bool,
peptide_to_ions: &BTreeMap<u32, (Vec<f32>, Vec<Vec<u32>>, Vec<Vec<f32>>, Vec<i8>, Vec<MzSpectrum>)>,
frame_to_abundances: &BTreeMap<u32, (Vec<u32>, Vec<f32>)>,
peptide_to_events: &BTreeMap<u32, f32>,
fragment_ions_map: &Option<BTreeMap<(u32, i8, i32), (PeptideProductIonSeriesCollection, Vec<MzSpectrum>)>>,
) -> TimsFrame {
let ms_type = MsType::FragmentDia;
let rt = *self.frame_to_rt.get(&frame_id).unwrap_or(&0.0) as f64;
if !fragmentation || fragment_ions_map.is_none() {
let precursor_frame = self.build_precursor_frame(
frame_id,
mz_noise_fragment,
uniform,
fragment_noise_ppm,
right_drag,
peptide_to_ions,
frame_to_abundances,
peptide_to_events,
);
let mut frame = self.transmission_settings.transmit_tims_frame(&precursor_frame, None);
frame.ms_type = MsType::FragmentDia;
return frame;
}
let fragment_ions = fragment_ions_map.as_ref().unwrap();
let Some((peptide_ids, frame_abundances)) = frame_to_abundances.get(&frame_id) else {
return TimsFrame::new(frame_id as i32, ms_type, rt, vec![], vec![], vec![], vec![], vec![]);
};
let estimated_capacity = peptide_ids.len() * 4;
let mut tims_spectra: Vec<TimsSpectrum> = Vec::with_capacity(estimated_capacity);
for (peptide_id, frame_abundance) in peptide_ids.iter().zip(frame_abundances.iter()) {
let Some((ion_abundances, scan_occurrences, scan_abundances, charges, spectra)) =
peptide_to_ions.get(peptide_id)
else {
continue;
};
let total_events = *peptide_to_events.get(peptide_id).unwrap_or(&1.0);
for (index, ion_abundance) in ion_abundances.iter().enumerate() {
let all_scan_occurrence = &scan_occurrences[index];
let all_scan_abundance = &scan_abundances[index];
let spectrum = &spectra[index];
let charge_state = charges[index];
for (scan, scan_abundance) in all_scan_occurrence.iter().zip(all_scan_abundance.iter()) {
if !self.transmission_settings.any_transmitted(
frame_id as i32,
*scan as i32,
&spectrum.mz,
None,
) {
continue;
}
let fraction_events = frame_abundance * scan_abundance * ion_abundance * total_events;
let collision_energy = self.fragmentation_settings.get_collision_energy(
frame_id as i32,
*scan as i32,
);
let collision_energy_quantized = (collision_energy * 1e1).round() as i32;
let Some((_, fragment_series_vec)) = fragment_ions.get(&(*peptide_id, charge_state, collision_energy_quantized)) else {
continue;
};
let scan_mobility = *self.scan_to_mobility.get(scan).unwrap_or(&0.0) as f64;
for fragment_ion_series in fragment_series_vec.iter() {
let scaled_spec = fragment_ion_series.clone() * fraction_events as f64;
let mz_spectrum = if mz_noise_fragment {
if uniform {
scaled_spec.add_mz_noise_uniform(fragment_noise_ppm, right_drag)
} else {
scaled_spec.add_mz_noise_normal(fragment_noise_ppm)
}
} else {
scaled_spec
};
let spectrum_len = mz_spectrum.mz.len();
tims_spectra.push(TimsSpectrum::new(
frame_id as i32,
*scan as i32,
rt,
scan_mobility,
ms_type.clone(),
IndexedMzSpectrum::from_mz_spectrum(
vec![0; spectrum_len],
mz_spectrum,
).filter_ranged(100.0, 1700.0, 1.0, 1e9),
));
}
}
}
}
if tims_spectra.is_empty() {
return TimsFrame::new(frame_id as i32, ms_type, rt, vec![], vec![], vec![], vec![], vec![]);
}
let mut filtered = TimsFrame::from_tims_spectra_filtered(
tims_spectra, 100.0, 1700.0, 0, 1000, 0.0, 10.0, 1.0, 1e9,
);
let intensities_rounded: Vec<f64> = filtered
.ims_frame
.intensity
.iter()
.map(|x| x.round())
.collect();
filtered.ims_frame.intensity = Arc::new(intensities_rounded);
filtered
}
pub fn num_frames(&self) -> usize {
self.frames.len()
}
pub fn frame_ids(&self) -> Vec<u32> {
self.frames.iter().map(|f| f.frame_id).collect()
}
pub fn precursor_frame_ids(&self) -> Vec<u32> {
self.precursor_frame_id_set.iter().cloned().collect()
}
pub fn fragment_frame_ids(&self) -> Vec<u32> {
self.frames
.iter()
.filter(|f| !self.precursor_frame_id_set.contains(&f.frame_id))
.map(|f| f.frame_id)
.collect()
}
}
impl TimsTofCollisionEnergy for TimsTofLazyFrameBuilderDIA {
fn get_collision_energy(&self, frame_id: i32, scan_id: i32) -> f64 {
self.fragmentation_settings.get_collision_energy(frame_id, scan_id)
}
}
pub struct TimsTofLazyFrameBuilderDDA {
pub db_path: String,
pub frames: Vec<FramesSim>,
pub scans: Vec<ScansSim>,
pub precursor_frame_id_set: HashSet<u32>,
pub frame_to_rt: BTreeMap<u32, f32>,
pub scan_to_mobility: BTreeMap<u32, f32>,
pub transmission_settings: TimsTransmissionDDA,
pub num_threads: usize,
}
impl TimsTofLazyFrameBuilderDDA {
pub fn new(path: &Path, num_threads: usize) -> rusqlite::Result<Self> {
let handle = TimsTofSyntheticsDataHandle::new(path)?;
let frames = handle.read_frames()?;
let scans = handle.read_scans()?;
let precursor_frame_id_set = TimsTofSyntheticsDataHandle::build_precursor_frame_id_set(&frames);
let frame_to_rt = TimsTofSyntheticsDataHandle::build_frame_to_rt(&frames);
let scan_to_mobility = TimsTofSyntheticsDataHandle::build_scan_to_mobility(&scans);
let transmission_settings = handle.get_transmission_dda();
Ok(Self {
db_path: path.to_str().unwrap().to_string(),
frames,
scans,
precursor_frame_id_set,
frame_to_rt,
scan_to_mobility,
transmission_settings,
num_threads,
})
}
fn load_data_for_frame_range(
&self,
frame_min: u32,
frame_max: u32,
) -> rusqlite::Result<(Vec<PeptidesSim>, Vec<IonSim>, Vec<FragmentIonSim>)> {
let path = Path::new(&self.db_path);
let handle = TimsTofSyntheticsDataHandle::new(path)?;
let peptides = handle.read_peptides_for_frame_range(frame_min, frame_max)?;
if peptides.is_empty() {
return Ok((Vec::new(), Vec::new(), Vec::new()));
}
let peptide_ids: Vec<u32> = peptides.iter().map(|p| p.peptide_id).collect();
let ions = handle.read_ions_for_peptides(&peptide_ids)?;
let fragment_ions = handle.read_fragment_ions_for_peptides(&peptide_ids)?;
Ok((peptides, ions, fragment_ions))
}
pub fn build_frames_lazy(
&self,
frame_ids: Vec<u32>,
fragmentation: bool,
mz_noise_precursor: bool,
uniform: bool,
precursor_noise_ppm: f64,
mz_noise_fragment: bool,
fragment_noise_ppm: f64,
right_drag: bool,
) -> Vec<TimsFrame> {
if frame_ids.is_empty() {
return Vec::new();
}
let frame_min = *frame_ids.iter().min().unwrap();
let frame_max = *frame_ids.iter().max().unwrap();
let (peptides, ions, fragment_ions) = match self.load_data_for_frame_range(frame_min, frame_max) {
Ok(data) => data,
Err(_) => return Vec::new(),
};
let peptide_map = TimsTofSyntheticsDataHandle::build_peptide_map(&peptides);
let peptide_to_ions = TimsTofSyntheticsDataHandle::build_peptide_to_ions(&ions);
let frame_to_abundances = TimsTofSyntheticsDataHandle::build_frame_to_abundances(&peptides);
let peptide_to_events = TimsTofSyntheticsDataHandle::build_peptide_to_events(&peptides);
let fragment_ions_map = if fragmentation {
Some(TimsTofSyntheticsDataHandle::build_fragment_ions(
&peptide_map,
&fragment_ions,
self.num_threads,
))
} else {
None
};
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(self.num_threads)
.build()
.unwrap();
pool.install(|| {
let mut tims_frames: Vec<TimsFrame> = Vec::with_capacity(frame_ids.len());
unsafe { tims_frames.set_len(frame_ids.len()); }
frame_ids.par_iter().enumerate().for_each(|(idx, frame_id)| {
let frame = self.build_single_frame(
*frame_id,
fragmentation,
mz_noise_precursor,
uniform,
precursor_noise_ppm,
mz_noise_fragment,
fragment_noise_ppm,
right_drag,
&peptide_to_ions,
&frame_to_abundances,
&peptide_to_events,
&fragment_ions_map,
);
unsafe {
let ptr = tims_frames.as_ptr() as *mut TimsFrame;
std::ptr::write(ptr.add(idx), frame);
}
});
tims_frames
})
}
#[allow(clippy::too_many_arguments)]
fn build_single_frame(
&self,
frame_id: u32,
fragmentation: bool,
mz_noise_precursor: bool,
uniform: bool,
precursor_noise_ppm: f64,
mz_noise_fragment: bool,
fragment_noise_ppm: f64,
right_drag: bool,
peptide_to_ions: &BTreeMap<u32, (Vec<f32>, Vec<Vec<u32>>, Vec<Vec<f32>>, Vec<i8>, Vec<MzSpectrum>)>,
frame_to_abundances: &BTreeMap<u32, (Vec<u32>, Vec<f32>)>,
peptide_to_events: &BTreeMap<u32, f32>,
fragment_ions_map: &Option<BTreeMap<(u32, i8, i32), (PeptideProductIonSeriesCollection, Vec<MzSpectrum>)>>,
) -> TimsFrame {
let is_precursor = self.precursor_frame_id_set.contains(&frame_id);
if is_precursor {
self.build_precursor_frame(
frame_id,
mz_noise_precursor,
uniform,
precursor_noise_ppm,
right_drag,
peptide_to_ions,
frame_to_abundances,
peptide_to_events,
)
} else {
self.build_fragment_frame(
frame_id,
fragmentation,
mz_noise_fragment,
uniform,
fragment_noise_ppm,
right_drag,
peptide_to_ions,
frame_to_abundances,
peptide_to_events,
fragment_ions_map,
)
}
}
#[allow(clippy::too_many_arguments)]
fn build_precursor_frame(
&self,
frame_id: u32,
mz_noise_precursor: bool,
uniform: bool,
precursor_noise_ppm: f64,
right_drag: bool,
peptide_to_ions: &BTreeMap<u32, (Vec<f32>, Vec<Vec<u32>>, Vec<Vec<f32>>, Vec<i8>, Vec<MzSpectrum>)>,
frame_to_abundances: &BTreeMap<u32, (Vec<u32>, Vec<f32>)>,
peptide_to_events: &BTreeMap<u32, f32>,
) -> TimsFrame {
let ms_type = MsType::Precursor;
let rt = *self.frame_to_rt.get(&frame_id).unwrap_or(&0.0) as f64;
let Some((peptide_ids, abundances)) = frame_to_abundances.get(&frame_id) else {
return TimsFrame::new(frame_id as i32, ms_type, rt, vec![], vec![], vec![], vec![], vec![]);
};
let estimated_capacity = peptide_ids.len() * 4;
let mut tims_spectra: Vec<TimsSpectrum> = Vec::with_capacity(estimated_capacity);
for (peptide_id, abundance) in peptide_ids.iter().zip(abundances.iter()) {
let Some((ion_abundances, scan_occurrences, scan_abundances, _, spectra)) =
peptide_to_ions.get(peptide_id)
else {
continue;
};
let total_events = *peptide_to_events.get(peptide_id).unwrap_or(&1.0);
for (index, ion_abundance) in ion_abundances.iter().enumerate() {
let scan_occurrence = &scan_occurrences[index];
let scan_abundance = &scan_abundances[index];
let spectrum = &spectra[index];
for (scan, scan_abu) in scan_occurrence.iter().zip(scan_abundance.iter()) {
let abundance_factor = abundance * ion_abundance * scan_abu * total_events;
let scaled_spec: MzSpectrum = spectrum.clone() * abundance_factor as f64;
let mz_spectrum = if mz_noise_precursor {
if uniform {
scaled_spec.add_mz_noise_uniform(precursor_noise_ppm, right_drag)
} else {
scaled_spec.add_mz_noise_normal(precursor_noise_ppm)
}
} else {
scaled_spec
};
let scan_mobility = *self.scan_to_mobility.get(scan).unwrap_or(&0.0) as f64;
let spectrum_len = mz_spectrum.mz.len();
tims_spectra.push(TimsSpectrum::new(
frame_id as i32,
*scan as i32,
rt,
scan_mobility,
ms_type.clone(),
IndexedMzSpectrum::from_mz_spectrum(
vec![0; spectrum_len],
mz_spectrum,
),
));
}
}
}
let mut filtered = TimsFrame::from_tims_spectra_filtered(
tims_spectra, 0.0, 10000.0, 0, 2000, 0.0, 10.0, 1.0, 1e9,
);
let intensities_rounded: Vec<f64> = filtered
.ims_frame
.intensity
.iter()
.map(|x| x.round())
.collect();
filtered.ims_frame.intensity = Arc::new(intensities_rounded);
filtered
}
#[allow(clippy::too_many_arguments)]
fn build_fragment_frame(
&self,
frame_id: u32,
fragmentation: bool,
mz_noise_fragment: bool,
uniform: bool,
fragment_noise_ppm: f64,
right_drag: bool,
peptide_to_ions: &BTreeMap<u32, (Vec<f32>, Vec<Vec<u32>>, Vec<Vec<f32>>, Vec<i8>, Vec<MzSpectrum>)>,
frame_to_abundances: &BTreeMap<u32, (Vec<u32>, Vec<f32>)>,
peptide_to_events: &BTreeMap<u32, f32>,
fragment_ions_map: &Option<BTreeMap<(u32, i8, i32), (PeptideProductIonSeriesCollection, Vec<MzSpectrum>)>>,
) -> TimsFrame {
let ms_type = MsType::FragmentDda;
let rt = *self.frame_to_rt.get(&frame_id).unwrap_or(&0.0) as f64;
let pasef_meta = self.transmission_settings.pasef_meta.get(&(frame_id as i32));
if pasef_meta.is_none() {
if !fragmentation || fragment_ions_map.is_none() {
let precursor_frame = self.build_precursor_frame(
frame_id,
mz_noise_fragment,
uniform,
fragment_noise_ppm,
right_drag,
peptide_to_ions,
frame_to_abundances,
peptide_to_events,
);
let mut frame = self.transmission_settings.transmit_tims_frame(&precursor_frame, None);
frame.ms_type = MsType::FragmentDda;
return frame;
}
return TimsFrame::new(frame_id as i32, ms_type, rt, vec![], vec![], vec![], vec![], vec![]);
}
let pasef_meta = pasef_meta.unwrap();
if !fragmentation || fragment_ions_map.is_none() {
let precursor_frame = self.build_precursor_frame(
frame_id,
mz_noise_fragment,
uniform,
fragment_noise_ppm,
right_drag,
peptide_to_ions,
frame_to_abundances,
peptide_to_events,
);
let mut frame = self.transmission_settings.transmit_tims_frame(&precursor_frame, None);
frame.ms_type = MsType::FragmentDda;
return frame;
}
let fragment_ions = fragment_ions_map.as_ref().unwrap();
let Some((peptide_ids, frame_abundances)) = frame_to_abundances.get(&frame_id) else {
return TimsFrame::new(frame_id as i32, ms_type, rt, vec![], vec![], vec![], vec![], vec![]);
};
let estimated_capacity = peptide_ids.len() * 4;
let mut tims_spectra: Vec<TimsSpectrum> = Vec::with_capacity(estimated_capacity);
for (peptide_id, frame_abundance) in peptide_ids.iter().zip(frame_abundances.iter()) {
let Some((ion_abundances, scan_occurrences, scan_abundances, charges, spectra)) =
peptide_to_ions.get(peptide_id)
else {
continue;
};
let total_events = *peptide_to_events.get(peptide_id).unwrap_or(&1.0);
for (index, ion_abundance) in ion_abundances.iter().enumerate() {
let all_scan_occurrence = &scan_occurrences[index];
let all_scan_abundance = &scan_abundances[index];
let spectrum = &spectra[index];
let charge_state = charges[index];
for (scan, scan_abundance) in all_scan_occurrence.iter().zip(all_scan_abundance.iter()) {
if !self.transmission_settings.any_transmitted(
frame_id as i32,
*scan as i32,
&spectrum.mz,
None,
) {
continue;
}
let fraction_events = frame_abundance * scan_abundance * ion_abundance * total_events;
let collision_energy: f64 = pasef_meta
.iter()
.find(|scan_meta| scan_meta.scan_start <= *scan as i32 && scan_meta.scan_end >= *scan as i32)
.map(|s| s.collision_energy)
.unwrap_or(0.0);
let collision_energy_quantized = (collision_energy * 1e1).round() as i32;
let Some((_, fragment_series_vec)) = fragment_ions.get(&(*peptide_id, charge_state, collision_energy_quantized)) else {
continue;
};
let scan_mobility = *self.scan_to_mobility.get(scan).unwrap_or(&0.0) as f64;
for fragment_ion_series in fragment_series_vec.iter() {
let scaled_spec = fragment_ion_series.clone() * fraction_events as f64;
let mz_spectrum = if mz_noise_fragment {
if uniform {
scaled_spec.add_mz_noise_uniform(fragment_noise_ppm, right_drag)
} else {
scaled_spec.add_mz_noise_normal(fragment_noise_ppm)
}
} else {
scaled_spec
};
let spectrum_len = mz_spectrum.mz.len();
tims_spectra.push(TimsSpectrum::new(
frame_id as i32,
*scan as i32,
rt,
scan_mobility,
ms_type.clone(),
IndexedMzSpectrum::from_mz_spectrum(
vec![0; spectrum_len],
mz_spectrum,
).filter_ranged(100.0, 1700.0, 1.0, 1e9),
));
}
}
}
}
if tims_spectra.is_empty() {
return TimsFrame::new(frame_id as i32, ms_type, rt, vec![], vec![], vec![], vec![], vec![]);
}
let mut filtered = TimsFrame::from_tims_spectra_filtered(
tims_spectra, 100.0, 1700.0, 0, 1000, 0.0, 10.0, 1.0, 1e9,
);
let intensities_rounded: Vec<f64> = filtered
.ims_frame
.intensity
.iter()
.map(|x| x.round())
.collect();
filtered.ims_frame.intensity = Arc::new(intensities_rounded);
filtered
}
pub fn get_collision_energy(&self, frame_id: i32, scan_id: i32) -> f64 {
self.transmission_settings.get_collision_energy(frame_id, scan_id).unwrap_or(0.0)
}
pub fn num_frames(&self) -> usize {
self.frames.len()
}
pub fn frame_ids(&self) -> Vec<u32> {
self.frames.iter().map(|f| f.frame_id).collect()
}
pub fn precursor_frame_ids(&self) -> Vec<u32> {
self.precursor_frame_id_set.iter().cloned().collect()
}
pub fn fragment_frame_ids(&self) -> Vec<u32> {
self.frames
.iter()
.filter(|f| !self.precursor_frame_id_set.contains(&f.frame_id))
.map(|f| f.frame_id)
.collect()
}
}