1use std::marker::PhantomData;
2use std::mem;
3
4use mzdeisotope::charge::ChargeRange;
5use mzdeisotope::isotopic_model::CachingIsotopicModel;
6use mzdeisotope::scorer::{IsotopicFitFilter, IsotopicPatternScorer};
7use mzpeaks::feature::Feature;
8use mzpeaks::{coordinate::MZ, feature_map::FeatureMap};
9use mzpeaks::{prelude::*, Mass};
10
11use crate::DeconvolutionError;
12use crate::{processor::FeatureProcessor, FeatureSearchParams, solution::DeconvolvedSolutionFeature};
13use mzdeisotope::IsotopicModelLike;
14
15#[derive(Debug, Clone)]
16pub struct FeatureDeconvolutionEngine<
17 'lifespan,
18 T: Clone + Default,
19 C: FeatureLike<MZ, T> + Clone + Default,
20 S: IsotopicPatternScorer,
21 F: IsotopicFitFilter,
22> {
23 isotopic_params: FeatureSearchParams,
25 isotopic_model: Option<IsotopicModelLike<'lifespan>>,
28 scorer: Option<S>,
30 fit_filter: Option<F>,
32 peak_type: PhantomData<C>,
33 time_dim: PhantomData<T>,
34}
35
36impl<
37 'lifespan,
38 T: Clone + Default,
39 C: FeatureLike<MZ, T> + Clone + Default,
40 S: IsotopicPatternScorer,
41 F: IsotopicFitFilter,
42 > FeatureDeconvolutionEngine<'lifespan, T, C, S, F>
43{
44 pub fn new<I: Into<IsotopicModelLike<'lifespan>>>(
45 isotopic_params: FeatureSearchParams,
46 isotopic_model: I,
47 scorer: S,
48 fit_filter: F,
49 ) -> Self {
50 Self {
51 isotopic_params,
52 isotopic_model: Some(isotopic_model.into()),
53 scorer: Some(scorer),
54 fit_filter: Some(fit_filter),
55 peak_type: PhantomData,
56 time_dim: PhantomData,
57 }
58 }
59
60
61 pub fn populate_isotopic_model_cache(
71 &mut self,
72 min_mz: f64,
73 max_mz: f64,
74 min_charge: i32,
75 max_charge: i32,
76 ) {
77 if let Some(cache) = self.isotopic_model.as_mut() {
78 match cache {
79 IsotopicModelLike::SingleModel(cache) => {
80 cache.populate_cache_params(
81 min_mz,
82 max_mz,
83 min_charge,
84 max_charge,
85 self.isotopic_params.as_isotopic_params(),
86 );
87 }
88 IsotopicModelLike::MultipleModels(caches) => {
89 for cache in caches {
90 cache.populate_cache_params(
91 min_mz,
92 max_mz,
93 min_charge,
94 max_charge,
95 self.isotopic_params.as_isotopic_params(),
96 );
97 }
98 }
99 }
100 }
101 }
102
103 #[allow(clippy::too_many_arguments)]
104 pub fn deconvolute_features(
105 &mut self,
106 features: FeatureMap<MZ, T, C>,
107 error_tolerance: Tolerance,
108 charge_range: ChargeRange,
109 minimum_size: usize,
110 maximum_time_gap: f64,
111 minimum_intensity: f32,
112 max_missed_peaks: usize,
113 ) -> Result<FeatureMap<Mass, T, DeconvolvedSolutionFeature<T>>, DeconvolutionError> {
114 let output = match mem::take(&mut self.isotopic_model).unwrap() {
115 IsotopicModelLike::SingleModel(model) => {
116 let mut deconvoluter =
117 FeatureProcessor::<T, CachingIsotopicModel<'lifespan>, S, F>::new(
118 features.into_iter().map(|f| {
119 let f: Feature<MZ, T> = f.iter().collect();
120 f
121 }).collect(),
122 model,
123 mem::take(&mut self.scorer).unwrap(),
124 mem::take(&mut self.fit_filter).unwrap(),
125 minimum_size,
126 maximum_time_gap,
127 minimum_intensity,
128 true
129 );
130
131 let mut params = self.isotopic_params;
132 params.max_missed_peaks = max_missed_peaks;
133
134 let output = deconvoluter.deconvolve(
135 error_tolerance,
136 charge_range,
137 1,
138 0,
139 &self.isotopic_params,
140 1e-3,
141 10,
142 );
143
144 self.isotopic_model = Some(deconvoluter.isotopic_model.into());
145 self.scorer = Some(deconvoluter.scorer);
146 self.fit_filter = Some(deconvoluter.fit_filter);
147 output
148 }
149 _ => {
150 todo!("not yet implemented")
151 }
152 };
153 output
154 }
155}
156
157#[allow(clippy::too_many_arguments)]
158pub fn deconvolute_features<'a, T: Clone + Default, C: FeatureLike<MZ, T> + Clone + Default, I: Into<IsotopicModelLike<'a>>, S: IsotopicPatternScorer, F: IsotopicFitFilter>(
159 features: FeatureMap<MZ, T, C>,
160 isotopic_params: FeatureSearchParams,
161 isotopic_model: I,
162 scorer: S,
163 fit_filter: F,
164 error_tolerance: Tolerance,
165 charge_range: ChargeRange,
166 minimum_size: usize,
167 maximum_time_gap: f64,
168 minimum_intensity: f32,
169 max_missed_peaks: usize,
170) -> Result<FeatureMap<Mass, T, DeconvolvedSolutionFeature<T>>, DeconvolutionError> {
171 let mut engine = FeatureDeconvolutionEngine::new(isotopic_params, isotopic_model, scorer, fit_filter);
172
173 engine.deconvolute_features(features, error_tolerance, charge_range, minimum_size, maximum_time_gap, minimum_intensity, max_missed_peaks)
174}