1use std::convert::TryFrom;
2use std::{borrow::Cow, ops::Index};
3
4use mzdata_param::curie;
5use mzpeaks::Mass;
6use thiserror::Error;
7
8use mzpeaks::{
9 peak_set::PeakSetVec, prelude::*, CentroidLike, CentroidPeak, DeconvolutedCentroidLike,
10 DeconvolutedPeak, MZPeakSetType, MassPeakSetType, PeakCollection, PeakSet, MZ,
11};
12
13#[cfg(feature = "mzsignal")]
14use mzsignal::{
15 denoise::{denoise, DenoisingError},
16 peak_picker::{PeakFitType, PeakPicker, PeakPickerError},
17 reprofile::{self, PeakShape, PeakShapeModel},
18 FittedPeak,
19};
20
21use mzdata_param::{ParamDescribed, Unit, Value};
22
23#[allow(unused)]
24use mzdata_bindata::{
25 ArrayRetrievalError, ArrayType, BinaryArrayMap, BinaryDataArrayType, BuildArrayMapFrom,
26 BuildFromArrayMap, DataArray,
27};
28
29use crate::peaks::{PeakDataLevel, RefPeakDataLevel, SpectrumSummary};
30use crate::scan_properties::{
31 Acquisition, IonMobilityMeasure, Precursor, ScanPolarity, SignalContinuity, SpectrumDescription,
32};
33
34use crate::utils::HasIonMobility;
35
36pub trait CentroidPeakAdapting: CentroidLike + From<CentroidPeak> {}
38impl<C: CentroidLike + From<CentroidPeak>> CentroidPeakAdapting for C {}
39
40pub trait DeconvolutedPeakAdapting: DeconvolutedCentroidLike + From<DeconvolutedPeak> {}
42impl<D: DeconvolutedCentroidLike + From<DeconvolutedPeak>> DeconvolutedPeakAdapting for D {}
43
44pub trait SpectrumLike<
46 C: CentroidLike = CentroidPeak,
47 D: DeconvolutedCentroidLike = DeconvolutedPeak,
48>: ParamDescribed
49{
50 fn description(&self) -> &SpectrumDescription;
53
54 fn description_mut(&mut self) -> &mut SpectrumDescription;
56
57 #[inline]
59 fn acquisition(&self) -> &Acquisition {
60 &self.description().acquisition
61 }
62
63 #[inline]
65 fn precursor(&self) -> Option<&Precursor> {
66 let desc = self.description();
67 desc.precursor.first()
68 }
69
70 fn precursor_iter(&self) -> impl Iterator<Item = &Precursor> + ExactSizeIterator {
72 let desc = self.description();
73 desc.precursor.iter()
74 }
75
76 fn precursor_count(&self) -> usize {
78 self.description().precursor.len()
79 }
80
81 fn precursor_mut(&mut self) -> Option<&mut Precursor> {
83 let desc = self.description_mut();
84 desc.precursor.first_mut()
85 }
86
87 fn precursor_iter_mut(&mut self) -> impl Iterator<Item = &mut Precursor> + ExactSizeIterator {
89 let desc = self.description_mut();
90 desc.precursor.iter_mut()
91 }
92
93 fn add_precursor(&mut self, precursor: Precursor) {
101 self.description_mut().precursor.push(precursor);
102 }
103
104 fn remove_precursor(&mut self, index: usize) -> Precursor {
110 self.description_mut().precursor.remove(index)
111 }
112
113 #[inline]
115 fn start_time(&self) -> f64 {
116 let acq = self.acquisition();
117 match acq.scans.first() {
118 Some(evt) => evt.start_time,
119 None => 0.0,
120 }
121 }
122
123 #[inline]
125 fn ms_level(&self) -> u8 {
126 self.description().ms_level
127 }
128
129 #[inline]
131 fn id(&self) -> &str {
132 &self.description().id
133 }
134
135 #[inline]
137 fn index(&self) -> usize {
138 self.description().index
139 }
140
141 #[inline]
144 fn signal_continuity(&self) -> SignalContinuity {
145 self.description().signal_continuity
146 }
147
148 #[inline]
150 fn polarity(&self) -> ScanPolarity {
151 self.description().polarity
152 }
153
154 fn ion_mobility(&self) -> Option<f64> {
157 self.acquisition()
158 .iter()
159 .flat_map(|s| s.ion_mobility())
160 .next()
161 }
162
163 fn has_ion_mobility(&self) -> bool {
166 self.ion_mobility().is_some()
167 }
168
169 fn peaks(&'_ self) -> RefPeakDataLevel<'_, C, D>;
171
172 fn into_peaks_and_description(self) -> (PeakDataLevel<C, D>, SpectrumDescription);
179
180 fn raw_arrays(&'_ self) -> Option<&'_ BinaryArrayMap>;
184
185 fn has_ion_mobility_dimension(&self) -> bool {
188 self.raw_arrays()
189 .map(|a| a.has_ion_mobility())
190 .unwrap_or_default()
191 }
192
193 fn has_ion_mobility_class(&self) -> HasIonMobility {
195 if self.has_ion_mobility_dimension() {
196 HasIonMobility::Dimension
197 } else if self.has_ion_mobility() {
198 HasIonMobility::Point
199 } else {
200 HasIonMobility::None
201 }
202 }
203
204 fn spectrum_type(&self) -> Option<mzdata_meta::SpectrumType> {
211 self.description().spectrum_type()
212 }
213
214 fn set_spectrum_type(&mut self, spectrum_type: mzdata_meta::SpectrumType) {
216 self.description_mut().set_spectrum_type(spectrum_type);
217 }
218
219 fn update_summaries(&mut self) {
224 let SpectrumSummary {
225 tic,
226 base_peak: bp,
227 mz_range,
228 count: _,
229 } = self.peaks().fetch_summaries();
230
231 let desc = self.description_mut();
232 let params = desc.params_mut();
233 let tic_curie = curie!(MS:1000285);
234
235 if let Some(p) = params.iter_mut().find(|p| **p == tic_curie) {
236 p.value = Value::Float(tic as f64);
237 } else {
238 let mut p = tic_curie.as_param();
239 p.name = "total ion current".to_string();
240 p.value = Value::Float(tic as f64);
241 p.unit = Unit::DetectorCounts;
242 params.push(p)
243 }
244
245 let bpmz_curie = curie!(MS:1000504);
246 if let Some(p) = params.iter_mut().find(|p| **p == bpmz_curie) {
247 p.value = Value::Float(bp.mz());
248 } else {
249 let mut p = bpmz_curie.as_param();
250 p.name = "base peak m/z".to_string();
251 p.value = Value::Float(bp.mz());
252 p.unit = Unit::MZ;
253 params.push(p);
254 }
255
256 let lowest_mz_curie = curie!(MS:1000528);
257 if let Some(p) = params.iter_mut().find(|p| **p == lowest_mz_curie) {
258 p.value = Value::Float(mz_range.0);
259 } else {
260 let mut p = lowest_mz_curie.as_param();
261 p.name = "lowest observed m/z".to_string();
262 p.value = Value::Float(mz_range.0);
263 p.unit = Unit::MZ;
264 params.push(p);
265 }
266
267 let highest_mz_curie = curie!(MS:1000527);
268 if let Some(p) = params.iter_mut().find(|p| **p == highest_mz_curie) {
269 p.value = Value::Float(mz_range.0);
270 } else {
271 let mut p = highest_mz_curie.as_param();
272 p.name = "highest observed m/z".to_string();
273 p.value = Value::Float(mz_range.1);
274 p.unit = Unit::MZ;
275 params.push(p);
276 }
277
278 let bpint_curie = curie!(MS:1000505);
279 if let Some(p) = params.iter_mut().find(|p| **p == bpint_curie) {
280 p.value = Value::Float(bp.intensity() as f64);
281 } else {
282 let mut p = bpint_curie.as_param();
283 p.name = "base peak intensity".to_string();
284 p.value = Value::Float(bp.intensity() as f64);
285 p.unit = Unit::DetectorCounts;
286 params.push(p);
287 }
288 }
289}
290
291#[derive(Default, Debug, Clone)]
292#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
293pub struct RawSpectrum {
297 pub description: SpectrumDescription,
299 pub arrays: BinaryArrayMap,
302}
303
304impl ParamDescribed for RawSpectrum {
305 fn params(&self) -> &[crate::params::Param] {
306 <SpectrumDescription as ParamDescribed>::params(&self.description)
307 }
308
309 fn params_mut(&mut self) -> &mut crate::params::ParamList {
310 <SpectrumDescription as ParamDescribed>::params_mut(&mut self.description)
311 }
312}
313
314#[derive(Debug, Clone, PartialEq, Error)]
316pub enum SpectrumConversionError {
317 #[error("m/z array does not match size of intensity array")]
318 MZIntensityArraySizeMismatch,
319 #[error("Operation expected charge-deconvolved data but did not find it")]
320 NotDeconvoluted,
321 #[error("Operation expected centroided data but did not find it")]
322 NotCentroided,
323 #[error("No peak data of any kind was found")]
324 NoPeakData,
325 #[error("An error occurred while accessing raw data arrays: {0}")]
326 ArrayRetrievalError(
327 #[from]
328 #[source]
329 ArrayRetrievalError,
330 ),
331}
332
333#[derive(Debug, Clone, thiserror::Error)]
335pub enum SpectrumProcessingError {
336 #[cfg(feature = "mzsignal")]
337 #[error("An error occurred while denoising: {0:?}")]
338 DenoisingError(
339 #[from]
340 #[source]
341 DenoisingError,
342 ),
343
344 #[cfg(feature = "mzsignal")]
345 #[error("An error occurred while peak picking: {0:?}")]
346 PeakPickerError(
347 #[from]
348 #[source]
349 PeakPickerError,
350 ),
351
352 #[error("An error occurred while trying to convert spectrum types: {0}")]
353 SpectrumConversionError(
354 #[from]
355 #[source]
356 SpectrumConversionError,
357 ),
358
359 #[error("An error occurred while accessing raw data arrays: {0}")]
360 ArrayRetrievalError(
361 #[from]
362 #[source]
363 ArrayRetrievalError,
364 ),
365}
366
367impl<'transient, 'lifespan: 'transient> RawSpectrum {
368 pub fn new(description: SpectrumDescription, arrays: BinaryArrayMap) -> Self {
369 Self {
370 description,
371 arrays,
372 }
373 }
374
375 pub fn into_centroid<C>(self) -> Result<CentroidSpectrumType<C>, SpectrumConversionError>
384 where
385 C: BuildFromArrayMap + CentroidLike,
386 {
387 if !matches!(
388 self.description.signal_continuity,
389 SignalContinuity::Centroid
390 ) {
391 return Err(SpectrumConversionError::NotCentroided);
392 }
393
394 let peaks = match C::try_from_arrays(&self.arrays) {
395 Ok(peaks) => peaks.into(),
396 Err(e) => return Err(e.into()),
397 };
398 let mut centroid = CentroidSpectrumType::<C> {
399 description: self.description,
400 peaks,
401 };
402 centroid.description.signal_continuity = SignalContinuity::Centroid;
403
404 Ok(centroid)
405 }
406
407 pub fn mzs(&'lifespan self) -> Cow<'transient, [f64]> {
413 self.arrays.mzs().unwrap()
414 }
415
416 pub fn intensities(&'lifespan self) -> Cow<'transient, [f32]> {
422 self.arrays.intensities().unwrap()
423 }
424
425 pub fn mzs_mut(&mut self) -> Result<&mut [f64], ArrayRetrievalError> {
426 self.arrays.mzs_mut()
427 }
428
429 pub fn intensities_mut(&mut self) -> Result<&mut [f32], ArrayRetrievalError> {
430 self.arrays.intensities_mut()
431 }
432
433 pub fn decode_all_arrays(&mut self) -> Result<(), ArrayRetrievalError> {
439 self.arrays.decode_all_arrays()
440 }
441
442 pub fn into_spectrum<C, D>(self) -> Result<MultiLayerSpectrum<C, D>, SpectrumConversionError>
444 where
445 C: BuildFromArrayMap,
446 C: CentroidLike,
447 D: DeconvolutedCentroidLike,
448 {
449 Ok(MultiLayerSpectrum::<C, D> {
450 arrays: Some(self.arrays),
451 description: self.description,
452 ..Default::default()
453 })
454 }
455
456 #[cfg(feature = "mzsignal")]
459 pub fn denoise(&mut self, scale: f32) -> Result<(), SpectrumProcessingError> {
460 let mut intensities_copy = self.arrays.intensities()?.into_owned();
461 let mz_array = self.arrays.mzs()?;
462 match denoise(&mz_array, &mut intensities_copy, scale) {
463 Ok(_) => {
464 let view = self.arrays.get_mut(&ArrayType::IntensityArray).unwrap();
465 view.store_as(BinaryDataArrayType::Float32)
466 .expect("Failed to reformat intensity array");
467 view.update_buffer(&intensities_copy)
468 .expect("Failed to update intensity array buffer");
469 Ok(())
470 }
471 Err(err) => Err(SpectrumProcessingError::DenoisingError(err)),
472 }
473 }
474
475 #[cfg(feature = "mzsignal")]
481 pub fn pick_peaks_with_into<
482 C: CentroidPeakAdapting + BuildFromArrayMap + From<FittedPeak>,
483 D: DeconvolutedPeakAdapting + BuildFromArrayMap,
484 >(
485 self,
486 peak_picker: &PeakPicker,
487 ) -> Result<MultiLayerSpectrum<C, D>, SpectrumProcessingError> {
488 let mut result = self.into_spectrum()?;
489 match result.pick_peaks_with(peak_picker) {
490 Ok(_) => Ok(result),
491 Err(err) => Err(err),
492 }
493 }
494
495 #[cfg(feature = "mzsignal")]
501 pub fn pick_peaks_into<
502 C: CentroidPeakAdapting + BuildFromArrayMap + From<FittedPeak>,
503 D: DeconvolutedPeakAdapting + BuildFromArrayMap,
504 >(
505 self,
506 signal_to_noise_threshold: f32,
507 ) -> Result<MultiLayerSpectrum<C, D>, SpectrumProcessingError> {
508 let peak_picker = PeakPicker {
509 fit_type: PeakFitType::Quadratic,
510 signal_to_noise_threshold,
511 ..Default::default()
512 };
513 self.pick_peaks_with_into(&peak_picker)
514 }
515
516 #[cfg(feature = "mzsignal")]
522 pub fn pick_peaks_in_intervals_into<
523 C: CentroidPeakAdapting + BuildFromArrayMap + From<FittedPeak>,
524 D: DeconvolutedPeakAdapting + BuildFromArrayMap,
525 >(
526 self,
527 signal_to_noise_threshold: f32,
528 intervals: &[(f64, f64)],
529 ) -> Result<MultiLayerSpectrum<C, D>, SpectrumProcessingError> {
530 let mut result = self.into_spectrum()?;
531 match result.pick_peaks_in_intervals(signal_to_noise_threshold, intervals) {
532 Ok(_) => Ok(result),
533 Err(err) => Err(err),
534 }
535 }
536}
537
538impl RawSpectrum {
539 pub fn description(&self) -> &SpectrumDescription {
542 <RawSpectrum as SpectrumLike<CentroidPeak, DeconvolutedPeak>>::description(self)
543 }
544
545 pub fn description_mut(&mut self) -> &mut SpectrumDescription {
547 <RawSpectrum as SpectrumLike<CentroidPeak, DeconvolutedPeak>>::description_mut(self)
548 }
549
550 #[inline]
552 pub fn acquisition(&self) -> &Acquisition {
553 &self.description().acquisition
554 }
555
556 #[inline]
558 pub fn precursor(&self) -> Option<&Precursor> {
559 let desc = self.description();
560 desc.precursor.first()
561 }
562
563 pub fn precursor_iter(&self) -> impl Iterator<Item = &Precursor> {
565 let desc = self.description();
566 desc.precursor.iter()
567 }
568
569 pub fn precursor_mut(&mut self) -> Option<&mut Precursor> {
571 let desc = self.description_mut();
572 desc.precursor.first_mut()
573 }
574
575 pub fn precursor_iter_mut(&mut self) -> impl Iterator<Item = &mut Precursor> {
577 let desc = self.description_mut();
578 desc.precursor.iter_mut()
579 }
580
581 #[inline]
583 pub fn start_time(&self) -> f64 {
584 let acq = self.acquisition();
585 match acq.scans.first() {
586 Some(evt) => evt.start_time,
587 None => 0.0,
588 }
589 }
590
591 #[inline]
593 pub fn ms_level(&self) -> u8 {
594 self.description().ms_level
595 }
596
597 #[inline]
599 pub fn id(&self) -> &str {
600 &self.description().id
601 }
602
603 #[inline]
605 pub fn index(&self) -> usize {
606 self.description().index
607 }
608
609 #[inline]
612 pub fn signal_continuity(&self) -> SignalContinuity {
613 self.description().signal_continuity
614 }
615
616 #[inline]
618 pub fn polarity(&self) -> ScanPolarity {
619 self.description().polarity
620 }
621
622 pub fn ion_mobility(&self) -> Option<f64> {
625 <RawSpectrum as SpectrumLike<CentroidPeak, DeconvolutedPeak>>::ion_mobility(self)
626 }
627
628 pub fn has_ion_mobility(&self) -> bool {
631 <RawSpectrum as SpectrumLike<CentroidPeak, DeconvolutedPeak>>::ion_mobility(self).is_some()
632 }
633
634 pub fn peaks(&'_ self) -> RefPeakDataLevel<'_, CentroidPeak, DeconvolutedPeak> {
636 <RawSpectrum as SpectrumLike<CentroidPeak, DeconvolutedPeak>>::peaks(self)
637 }
638
639 pub fn into_peaks_and_description(
640 self,
641 ) -> (
642 PeakDataLevel<CentroidPeak, DeconvolutedPeak>,
643 SpectrumDescription,
644 ) {
645 <RawSpectrum as SpectrumLike<CentroidPeak, DeconvolutedPeak>>::into_peaks_and_description(
646 self,
647 )
648 }
649
650 pub fn raw_arrays(&'_ self) -> Option<&'_ BinaryArrayMap> {
654 <RawSpectrum as SpectrumLike<CentroidPeak, DeconvolutedPeak>>::raw_arrays(self)
655 }
656
657 pub fn has_ion_mobility_dimension(&self) -> bool {
660 self.raw_arrays()
661 .map(|a| a.has_ion_mobility())
662 .unwrap_or_default()
663 }
664
665 pub fn update_summaries(&mut self) {
670 <RawSpectrum as SpectrumLike<CentroidPeak, DeconvolutedPeak>>::update_summaries(self)
671 }
672}
673
674impl<C: CentroidLike, D: DeconvolutedCentroidLike> SpectrumLike<C, D> for RawSpectrum {
675 #[inline]
676 fn description(&self) -> &SpectrumDescription {
677 &self.description
678 }
679
680 fn description_mut(&mut self) -> &mut SpectrumDescription {
681 &mut self.description
682 }
683
684 fn peaks(&'_ self) -> RefPeakDataLevel<'_, C, D> {
685 RefPeakDataLevel::RawData(&self.arrays)
686 }
687
688 fn raw_arrays(&'_ self) -> Option<&'_ BinaryArrayMap> {
689 Some(&self.arrays)
690 }
691
692 fn into_peaks_and_description(self) -> (PeakDataLevel<C, D>, SpectrumDescription) {
693 (PeakDataLevel::RawData(self.arrays), self.description)
694 }
695}
696
697#[derive(Debug, Clone)]
698#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
699pub struct CentroidSpectrumType<C: CentroidLike> {
704 pub description: SpectrumDescription,
706 pub peaks: MZPeakSetType<C>,
708}
709
710impl<C: CentroidLike> Default for CentroidSpectrumType<C> {
711 fn default() -> Self {
712 Self {
713 description: Default::default(),
714 peaks: PeakSetVec::empty(),
715 }
716 }
717}
718
719#[cfg(feature = "mzsignal")]
722impl<C: CentroidLike + BuildArrayMapFrom + BuildFromArrayMap> CentroidSpectrumType<C> {
723 pub fn reprofile_with_shape_into(
731 self,
732 dx: f64,
733 fwhm: f32,
734 ) -> Result<MultiLayerSpectrum<C, DeconvolutedPeak>, SpectrumProcessingError> {
735 let mut spectrum = self.into_spectrum()?;
736 spectrum.reprofile_with_shape(dx, fwhm)?;
737 Ok(spectrum)
738 }
739}
740
741impl<C: CentroidLike> ParamDescribed for CentroidSpectrumType<C> {
742 fn params(&self) -> &[crate::params::Param] {
743 <SpectrumDescription as ParamDescribed>::params(&self.description)
744 }
745
746 fn params_mut(&mut self) -> &mut crate::params::ParamList {
747 <SpectrumDescription as ParamDescribed>::params_mut(&mut self.description)
748 }
749}
750
751impl<C: CentroidLike> SpectrumLike<C> for CentroidSpectrumType<C> {
753 #[inline]
754 fn description(&self) -> &SpectrumDescription {
755 &self.description
756 }
757
758 fn description_mut(&mut self) -> &mut SpectrumDescription {
759 &mut self.description
760 }
761
762 fn peaks(&'_ self) -> RefPeakDataLevel<'_, C, DeconvolutedPeak> {
763 RefPeakDataLevel::Centroid(&self.peaks)
764 }
765
766 fn raw_arrays(&'_ self) -> Option<&'_ BinaryArrayMap> {
767 None
768 }
769
770 fn into_peaks_and_description(
771 self,
772 ) -> (PeakDataLevel<C, DeconvolutedPeak>, SpectrumDescription) {
773 (PeakDataLevel::Centroid(self.peaks), self.description)
774 }
775}
776
777impl<C: CentroidLike> CentroidSpectrumType<C> {
778 pub fn new(description: SpectrumDescription, peaks: MZPeakSetType<C>) -> Self {
779 Self { description, peaks }
780 }
781
782 pub fn into_spectrum<D>(self) -> Result<MultiLayerSpectrum<C, D>, SpectrumConversionError>
785 where
786 D: DeconvolutedCentroidLike,
787 {
788 let val = MultiLayerSpectrum::<C, D> {
789 peaks: Some(self.peaks),
790 description: self.description,
791 ..Default::default()
792 };
793 Ok(val)
794 }
795
796 pub fn reinterpret<C1, D1>(self) -> Result<MultiLayerSpectrum<C1, D1>, SpectrumConversionError>
802 where
803 C1: CentroidLike + BuildArrayMapFrom + BuildFromArrayMap,
804 D1: DeconvolutedCentroidLike + BuildArrayMapFrom + BuildFromArrayMap,
805 C: BuildArrayMapFrom,
806 {
807 let arrays = C::as_arrays(self.peaks.as_slice());
808 Ok(MultiLayerSpectrum::new(
809 self.description,
810 Some(arrays),
811 None,
812 None,
813 ))
814 }
815}
816
817pub type CentroidSpectrum = CentroidSpectrumType<CentroidPeak>;
818
819impl<C: CentroidPeakAdapting> Index<usize> for CentroidSpectrumType<C> {
820 type Output = <MZPeakSetType<C> as Index<usize>>::Output;
821
822 fn index(&self, index: usize) -> &Self::Output {
823 self.peaks.index(index)
824 }
825}
826
827impl<C: CentroidPeakAdapting> PeakCollection<C, MZ> for CentroidSpectrumType<C>
828where
829 MZPeakSetType<C>: PeakCollection<C, MZ>,
830 <MZPeakSetType<C> as Index<usize>>::Output: CoordinateLike<MZ>,
831{
832 fn len(&self) -> usize {
833 self.peaks.len()
834 }
835
836 fn get_item(&self, i: usize) -> &C {
837 self.peaks.get_item(i)
838 }
839
840 fn get_slice(&self, i: std::ops::Range<usize>) -> &[C] {
841 self.peaks.get_slice(i)
842 }
843
844 fn search_by(&self, query: f64) -> Result<usize, usize> {
845 self.peaks.search_by(query)
846 }
847
848 fn iter<'a>(&'a self) -> impl Iterator<Item = &'a C>
849 where
850 C: 'a,
851 {
852 self.peaks.iter()
853 }
854}
855
856#[derive(Debug, Clone)]
860#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
861pub struct DeconvolutedSpectrumType<D: DeconvolutedCentroidLike> {
862 pub description: SpectrumDescription,
864 pub deconvoluted_peaks: MassPeakSetType<D>,
867}
868
869impl<D: DeconvolutedCentroidLike> Default for DeconvolutedSpectrumType<D> {
870 fn default() -> Self {
871 Self {
872 description: Default::default(),
873 deconvoluted_peaks: PeakSetVec::empty(),
874 }
875 }
876}
877
878impl<D: DeconvolutedCentroidLike> DeconvolutedSpectrumType<D> {
879 pub fn new(description: SpectrumDescription, deconvoluted_peaks: MassPeakSetType<D>) -> Self {
880 Self {
881 description,
882 deconvoluted_peaks,
883 }
884 }
885
886 pub fn into_spectrum<C>(self) -> Result<MultiLayerSpectrum<C, D>, SpectrumConversionError>
889 where
890 C: CentroidLike,
891 {
892 let val = MultiLayerSpectrum::<C, D> {
893 deconvoluted_peaks: Some(self.deconvoluted_peaks),
894 description: self.description,
895 ..Default::default()
896 };
897 Ok(val)
898 }
899
900 pub fn reinterpret<C1, D1>(self) -> MultiLayerSpectrum<C1, D1>
904 where
905 C1: CentroidLike + BuildArrayMapFrom + BuildFromArrayMap,
906 D1: DeconvolutedCentroidLike + BuildArrayMapFrom + BuildFromArrayMap,
907 D: BuildArrayMapFrom,
908 {
909 let arrays = D::as_arrays(self.deconvoluted_peaks.as_slice());
910 MultiLayerSpectrum::new(self.description, Some(arrays), None, None)
911 }
912}
913
914impl<D: DeconvolutedCentroidLike> ParamDescribed for DeconvolutedSpectrumType<D> {
915 fn params(&self) -> &[crate::params::Param] {
916 <SpectrumDescription as ParamDescribed>::params(&self.description)
917 }
918
919 fn params_mut(&mut self) -> &mut crate::params::ParamList {
920 <SpectrumDescription as ParamDescribed>::params_mut(&mut self.description)
921 }
922}
923
924impl<D: DeconvolutedCentroidLike> SpectrumLike<CentroidPeak, D> for DeconvolutedSpectrumType<D> {
926 #[inline]
927 fn description(&self) -> &SpectrumDescription {
928 &self.description
929 }
930
931 fn description_mut(&mut self) -> &mut SpectrumDescription {
932 &mut self.description
933 }
934
935 fn peaks(&'_ self) -> RefPeakDataLevel<'_, CentroidPeak, D> {
936 RefPeakDataLevel::Deconvoluted(&self.deconvoluted_peaks)
937 }
938
939 fn raw_arrays(&'_ self) -> Option<&'_ BinaryArrayMap> {
940 None
941 }
942
943 fn into_peaks_and_description(self) -> (PeakDataLevel<CentroidPeak, D>, SpectrumDescription) {
944 (
945 PeakDataLevel::Deconvoluted(self.deconvoluted_peaks),
946 self.description,
947 )
948 }
949}
950
951pub type DeconvolutedSpectrum = DeconvolutedSpectrumType<DeconvolutedPeak>;
952
953impl<D: DeconvolutedPeakAdapting> Index<usize> for DeconvolutedSpectrumType<D> {
954 type Output = <MassPeakSetType<D> as Index<usize>>::Output;
955
956 fn index(&self, index: usize) -> &Self::Output {
957 self.deconvoluted_peaks.index(index)
958 }
959}
960
961impl<D: DeconvolutedPeakAdapting> PeakCollection<D, Mass> for DeconvolutedSpectrumType<D>
962where
963 MassPeakSetType<D>: PeakCollection<D, Mass>,
964 <MassPeakSetType<D> as Index<usize>>::Output: CoordinateLike<Mass>,
965{
966 fn len(&self) -> usize {
967 self.deconvoluted_peaks.len()
968 }
969
970 fn get_item(&self, i: usize) -> &D {
971 self.deconvoluted_peaks.get_item(i)
972 }
973
974 fn get_slice(&self, i: std::ops::Range<usize>) -> &[D] {
975 self.deconvoluted_peaks.get_slice(i)
976 }
977
978 fn search_by(&self, query: f64) -> Result<usize, usize> {
979 self.deconvoluted_peaks.search_by(query)
980 }
981
982 fn iter<'a>(&'a self) -> impl Iterator<Item = &'a D>
983 where
984 D: 'a,
985 {
986 self.deconvoluted_peaks.iter()
987 }
988}
989
990#[derive(Debug, Clone)]
991#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
992pub struct MultiLayerSpectrum<
998 C: CentroidLike = CentroidPeak,
999 D: DeconvolutedCentroidLike = DeconvolutedPeak,
1000> {
1001 pub description: SpectrumDescription,
1003
1004 pub arrays: Option<BinaryArrayMap>,
1007
1008 pub peaks: Option<MZPeakSetType<C>>,
1010
1011 pub deconvoluted_peaks: Option<MassPeakSetType<D>>,
1013}
1014
1015impl<C: CentroidLike, D: DeconvolutedCentroidLike> MultiLayerSpectrum<C, D> {
1016 pub fn new(
1020 description: SpectrumDescription,
1021 arrays: Option<BinaryArrayMap>,
1022 peaks: Option<MZPeakSetType<C>>,
1023 deconvoluted_peaks: Option<MassPeakSetType<D>>,
1024 ) -> Self {
1025 Self {
1026 description,
1027 arrays,
1028 peaks,
1029 deconvoluted_peaks,
1030 }
1031 }
1032}
1033
1034impl<C: CentroidLike, D: DeconvolutedCentroidLike> Default for MultiLayerSpectrum<C, D> {
1035 fn default() -> Self {
1036 Self {
1037 description: Default::default(),
1038 arrays: None,
1039 peaks: None,
1040 deconvoluted_peaks: None,
1041 }
1042 }
1043}
1044
1045impl<C: CentroidLike, D: DeconvolutedCentroidLike> ParamDescribed for MultiLayerSpectrum<C, D> {
1046 fn params(&self) -> &[crate::params::Param] {
1047 <SpectrumDescription as ParamDescribed>::params(&self.description)
1048 }
1049
1050 fn params_mut(&mut self) -> &mut crate::params::ParamList {
1051 <SpectrumDescription as ParamDescribed>::params_mut(&mut self.description)
1052 }
1053}
1054
1055impl<C: CentroidLike, D: DeconvolutedCentroidLike> SpectrumLike<C, D> for MultiLayerSpectrum<C, D> {
1056 #[inline]
1057 fn description(&self) -> &SpectrumDescription {
1058 &self.description
1059 }
1060
1061 fn description_mut(&mut self) -> &mut SpectrumDescription {
1062 &mut self.description
1063 }
1064
1065 fn peaks(&'_ self) -> RefPeakDataLevel<'_, C, D> {
1066 if let Some(peaks) = &self.deconvoluted_peaks {
1067 RefPeakDataLevel::Deconvoluted(peaks)
1068 } else if let Some(peaks) = &self.peaks {
1069 RefPeakDataLevel::Centroid(peaks)
1070 } else if let Some(arrays) = &self.arrays {
1071 RefPeakDataLevel::RawData(arrays)
1072 } else {
1073 RefPeakDataLevel::Missing
1074 }
1075 }
1076
1077 fn raw_arrays(&'_ self) -> Option<&'_ BinaryArrayMap> {
1078 self.arrays.as_ref()
1079 }
1080
1081 fn into_peaks_and_description(self) -> (PeakDataLevel<C, D>, SpectrumDescription) {
1082 if let Some(peaks) = self.deconvoluted_peaks {
1083 (PeakDataLevel::Deconvoluted(peaks), self.description)
1084 } else if let Some(peaks) = self.peaks {
1085 (PeakDataLevel::Centroid(peaks), self.description)
1086 } else if let Some(arrays) = self.arrays {
1087 (PeakDataLevel::RawData(arrays), self.description)
1088 } else {
1089 (PeakDataLevel::Missing, self.description)
1090 }
1091 }
1092}
1093
1094impl<C: CentroidLike, D: DeconvolutedCentroidLike> MultiLayerSpectrum<C, D>
1095where
1096 C: BuildFromArrayMap + BuildArrayMapFrom,
1097 D: BuildFromArrayMap + BuildArrayMapFrom,
1098{
1099 pub fn into_centroid(self) -> Result<CentroidSpectrumType<C>, SpectrumConversionError> {
1108 if let Some(peaks) = self.peaks {
1109 let mut result = CentroidSpectrumType::<C> {
1110 peaks,
1111 description: self.description,
1112 };
1113 result.description.signal_continuity = SignalContinuity::Centroid;
1114 return Ok(result);
1115 } else if self.signal_continuity() == SignalContinuity::Centroid {
1116 if let Some(arrays) = &self.arrays {
1117 let peaks = C::try_from_arrays(arrays)?.into();
1118 let mut centroid = CentroidSpectrumType::<C> {
1119 description: self.description,
1120 peaks,
1121 };
1122 centroid.description.signal_continuity = SignalContinuity::Centroid;
1123 return Ok(centroid);
1124 } else {
1125 let mut result = CentroidSpectrumType::<C> {
1126 peaks: MZPeakSetType::<C>::empty(),
1127 description: self.description,
1128 };
1129 result.description.signal_continuity = SignalContinuity::Centroid;
1130 return Ok(result);
1131 }
1132 }
1133 Err(SpectrumConversionError::NotCentroided)
1134 }
1135
1136 pub fn into_raw(self) -> Result<RawSpectrum, SpectrumConversionError> {
1138 if let Some(arrays) = self.arrays {
1139 Ok(RawSpectrum {
1140 arrays,
1141 description: self.description,
1142 })
1143 } else if let Some(peaks) = self.deconvoluted_peaks {
1144 let arrays = D::as_arrays(&peaks[0..]);
1145 let mut result = RawSpectrum {
1146 arrays,
1147 description: self.description,
1148 };
1149 result.description.signal_continuity = SignalContinuity::Centroid;
1150 Ok(result)
1151 } else if let Some(peaks) = self.peaks {
1152 let arrays = C::as_arrays(&peaks[0..]);
1153
1154 let mut result = RawSpectrum {
1155 arrays,
1156 description: self.description,
1157 };
1158 result.description.signal_continuity = SignalContinuity::Centroid;
1159 Ok(result)
1160 } else {
1161 let arrays = BinaryArrayMap::from(&PeakSet::empty());
1162 Ok(RawSpectrum {
1163 arrays,
1164 description: self.description,
1165 })
1166 }
1167 }
1168
1169 pub fn from_arrays_and_description(
1171 arrays: BinaryArrayMap,
1172 description: SpectrumDescription,
1173 ) -> Self {
1174 Self {
1175 description,
1176 arrays: Some(arrays),
1177 ..Default::default()
1178 }
1179 }
1180
1181 pub fn from_description(description: SpectrumDescription) -> Self {
1183 Self {
1184 description,
1185 ..Default::default()
1186 }
1187 }
1188
1189 pub fn from_peaks_data_levels_and_description(
1193 peaks: PeakDataLevel<C, D>,
1194 description: SpectrumDescription,
1195 ) -> Self {
1196 match peaks {
1197 PeakDataLevel::Missing => Self::new(description, None, None, None),
1198 PeakDataLevel::RawData(arrays) => Self::new(description, Some(arrays), None, None),
1199 PeakDataLevel::Centroid(peaks) => Self::new(description, None, Some(peaks), None),
1200 PeakDataLevel::Deconvoluted(peaks) => Self::new(description, None, None, Some(peaks)),
1201 }
1202 }
1203
1204 pub fn from_spectrum_like<S: SpectrumLike<C, D>>(spectrum: S) -> Self {
1209 let (peaks, description) = spectrum.into_peaks_and_description();
1210 Self::from_peaks_data_levels_and_description(peaks, description)
1211 }
1212
1213 pub fn reinterpret<C1, D1>(self) -> MultiLayerSpectrum<C1, D1>
1221 where
1222 C1: CentroidLike + BuildArrayMapFrom + BuildFromArrayMap,
1223 D1: DeconvolutedCentroidLike + BuildArrayMapFrom + BuildFromArrayMap,
1224 C: BuildArrayMapFrom,
1225 D: BuildArrayMapFrom,
1226 {
1227 let arrays = if let Some(peaks) = self.deconvoluted_peaks {
1228 D::as_arrays(peaks.as_slice())
1229 } else if let Some(peaks) = self.peaks {
1230 C::as_arrays(peaks.as_slice())
1231 } else if let Some(arrays) = self.arrays {
1232 arrays
1233 } else {
1234 BinaryArrayMap::new()
1235 };
1236
1237 MultiLayerSpectrum::new(self.description, Some(arrays), None, None)
1238 }
1239
1240 #[cfg(feature = "mzsignal")]
1241 pub fn denoise(&mut self, scale: f32) -> Result<(), SpectrumProcessingError> {
1244 match &mut self.arrays {
1245 Some(arrays) => {
1246 let mut intensities_copy = arrays.intensities()?.into_owned();
1247 let mz_array = arrays.mzs()?;
1248 match denoise(&mz_array, &mut intensities_copy, scale) {
1249 Ok(_) => {
1250 let view = arrays.get_mut(&ArrayType::IntensityArray).unwrap();
1251 view.store_as(BinaryDataArrayType::Float32)?;
1252 view.update_buffer(&intensities_copy)?;
1253 Ok(())
1254 }
1255 Err(err) => Err(SpectrumProcessingError::DenoisingError(err)),
1256 }
1257 }
1258 None => Err(SpectrumProcessingError::SpectrumConversionError(
1259 SpectrumConversionError::NoPeakData,
1260 )),
1261 }
1262 }
1263
1264 #[cfg(feature = "mzsignal")]
1265 pub fn reprofile_with_shape(
1281 &mut self,
1282 dx: f64,
1283 fwhm: f32,
1284 ) -> Result<(), SpectrumProcessingError> {
1285 if let Some(peaks) = self.peaks.as_ref() {
1286 let reprofiler = reprofile::PeakSetReprofiler::new(
1287 peaks.first().map(|p| p.mz() - 1.0).unwrap_or_default(),
1288 peaks.last().map(|p| p.mz() + 1.0).unwrap_or_default(),
1289 dx,
1290 );
1291
1292 let models: Vec<_> = peaks
1293 .iter()
1294 .map(|p| {
1295 PeakShapeModel::from_centroid(p.mz(), p.intensity(), fwhm, PeakShape::Gaussian)
1296 })
1297 .collect();
1298
1299 let pair = reprofiler.reprofile_from_models(&models);
1300
1301 let arrays = match self.arrays.as_mut() {
1302 Some(arrays) => arrays,
1303 None => {
1304 self.arrays = Some(BinaryArrayMap::new());
1305 self.arrays.as_mut().unwrap()
1306 }
1307 };
1308 arrays.add(DataArray::wrap(
1309 &ArrayType::MZArray,
1310 BinaryDataArrayType::Float64,
1311 pair.mz_array.iter().flat_map(|i| i.to_le_bytes()).collect(),
1312 ));
1313
1314 arrays.add(DataArray::wrap(
1315 &ArrayType::IntensityArray,
1316 BinaryDataArrayType::Float32,
1317 pair.intensity_array
1318 .iter()
1319 .flat_map(|i| i.to_le_bytes())
1320 .collect(),
1321 ));
1322 Ok(())
1323 } else {
1324 Err(SpectrumConversionError::NoPeakData.into())
1325 }
1326 }
1327}
1328
1329impl<C: CentroidLike, D: DeconvolutedCentroidLike> MultiLayerSpectrum<C, D>
1330where
1331 C: BuildFromArrayMap,
1332 D: BuildFromArrayMap,
1333{
1334 pub fn try_build_peaks(
1341 &mut self,
1342 ) -> Result<RefPeakDataLevel<'_, C, D>, SpectrumConversionError> {
1343 if self.peaks.is_some() || self.deconvoluted_peaks.is_some() {
1344 return Ok(self.peaks());
1345 }
1346 if matches!(self.signal_continuity(), SignalContinuity::Centroid) {
1347 if let Some(arrays) = self.arrays.as_ref() {
1348 let peak_data: PeakDataLevel<C, D> = PeakDataLevel::try_from(arrays)?;
1349 match peak_data {
1350 PeakDataLevel::Missing => Ok(RefPeakDataLevel::Missing),
1351 PeakDataLevel::RawData(_) => panic!("not possible"),
1352 PeakDataLevel::Centroid(peak_set_vec) => {
1353 self.peaks = Some(peak_set_vec);
1354 Ok(self.peaks())
1355 }
1356 PeakDataLevel::Deconvoluted(peak_set_vec) => {
1357 self.deconvoluted_peaks = Some(peak_set_vec);
1358 Ok(self.peaks())
1359 }
1360 }
1361 } else {
1362 Ok(self.peaks())
1363 }
1364 } else {
1365 Ok(self.peaks())
1366 }
1367 }
1368
1369 pub fn try_build_centroids(&mut self) -> Result<&MZPeakSetType<C>, SpectrumConversionError> {
1370 if self.peaks.is_some() {
1371 Ok(self.peaks.as_ref().unwrap())
1372 } else if let Some(arrays) = self.arrays.as_ref() {
1373 match self.signal_continuity() {
1374 SignalContinuity::Centroid => {
1375 let peaks = C::try_from_arrays(arrays)?.into();
1376 self.peaks = Some(peaks);
1377 Ok(self.peaks.as_ref().unwrap())
1378 }
1379 _ => Err(SpectrumConversionError::NotCentroided),
1380 }
1381 } else {
1382 Err(SpectrumConversionError::NoPeakData)
1383 }
1384 }
1385
1386 pub fn try_build_deconvoluted_centroids(
1387 &mut self,
1388 ) -> Result<&MassPeakSetType<D>, SpectrumConversionError> {
1389 if let Some(ref peaks) = self.deconvoluted_peaks {
1390 Ok(peaks)
1391 } else if let Some(ref arrays) = self.arrays {
1392 match self.signal_continuity() {
1393 SignalContinuity::Centroid => {
1394 let peaks = D::try_from_arrays(arrays)?.into();
1395 self.deconvoluted_peaks = Some(peaks);
1396 Ok(self.deconvoluted_peaks.as_ref().unwrap())
1397 }
1398 _ => Err(SpectrumConversionError::NotCentroided),
1399 }
1400 } else {
1401 Err(SpectrumConversionError::NoPeakData)
1402 }
1403 }
1404}
1405
1406#[cfg(feature = "mzsignal")]
1411impl<C: CentroidLike + From<FittedPeak>, D: DeconvolutedCentroidLike> MultiLayerSpectrum<C, D> {
1412 pub fn pick_peaks_with(
1421 &mut self,
1422 peak_picker: &PeakPicker,
1423 ) -> Result<(), SpectrumProcessingError> {
1424 if let Some(arrays) = &self.arrays {
1425 let mz_array = arrays.mzs()?;
1426 let intensity_array = arrays.intensities()?;
1427
1428 if matches!(self.signal_continuity(), SignalContinuity::Centroid) {
1429 let mut peaks: MZPeakSetType<C> = mz_array
1430 .iter()
1431 .zip(intensity_array.iter())
1432 .map(|(mz, inten)| FittedPeak::new(*mz, *inten, 0, 0.0, 0.0).into())
1433 .collect();
1434 peaks.sort();
1435 self.peaks = Some(peaks);
1436 Ok(())
1437 } else {
1438 let mut acc = Vec::new();
1439 match peak_picker.discover_peaks(&mz_array, &intensity_array, &mut acc) {
1440 Ok(_) => {
1441 let peaks: MZPeakSetType<C> = acc.into_iter().map(|p| C::from(p)).collect();
1442 self.peaks = Some(peaks);
1443 Ok(())
1444 }
1445 Err(err) => Err(SpectrumProcessingError::PeakPickerError(err)),
1446 }
1447 }
1448 } else {
1449 Err(SpectrumProcessingError::SpectrumConversionError(
1450 SpectrumConversionError::NoPeakData,
1451 ))
1452 }
1453 }
1454
1455 pub fn pick_peaks(
1461 &mut self,
1462 signal_to_noise_threshold: f32,
1463 ) -> Result<(), SpectrumProcessingError> {
1464 let peak_picker = PeakPicker {
1465 fit_type: PeakFitType::Quadratic,
1466 signal_to_noise_threshold,
1467 ..Default::default()
1468 };
1469 self.pick_peaks_with(&peak_picker)
1470 }
1471
1472 pub fn pick_peaks_in_intervals(
1483 &mut self,
1484 signal_to_noise_threshold: f32,
1485 intervals: &[(f64, f64)],
1486 ) -> Result<(), SpectrumProcessingError> {
1487 let peak_picker = PeakPicker {
1488 fit_type: PeakFitType::Quadratic,
1489 signal_to_noise_threshold,
1490 ..Default::default()
1491 };
1492
1493 if matches!(self.signal_continuity(), SignalContinuity::Centroid) {
1494 self.pick_peaks_with(&peak_picker)?;
1495 let peaks = self
1496 .peaks
1497 .take()
1498 .unwrap()
1499 .into_iter()
1500 .filter(|p| {
1501 let mz = p.mz();
1502 intervals.iter().any(|(lo, hi)| *lo <= mz && mz < *hi)
1503 })
1504 .collect();
1505 self.peaks = Some(peaks);
1506 Ok(())
1507 } else if let Some(arrays) = &self.arrays {
1508 let mz_array = arrays.mzs()?;
1509 let intensity_array = arrays.intensities()?;
1510 let mut acc = Vec::new();
1511 for (mz_start, mz_end) in intervals {
1512 match peak_picker.discover_peaks_in_interval(
1513 &mz_array,
1514 &intensity_array,
1515 &mut acc,
1516 *mz_start,
1517 *mz_end,
1518 ) {
1519 Ok(_) => {}
1520 Err(err) => return Err(SpectrumProcessingError::PeakPickerError(err)),
1521 }
1522 }
1523 let peaks: MZPeakSetType<C> = acc.into_iter().map(|p| C::from(p)).collect();
1524 self.peaks = Some(peaks);
1525 return Ok(());
1526 } else {
1527 return Err(SpectrumProcessingError::SpectrumConversionError(
1528 SpectrumConversionError::NoPeakData,
1529 ));
1530 }
1531 }
1532}
1533
1534impl<C: CentroidLike, D: DeconvolutedCentroidLike> TryFrom<MultiLayerSpectrum<C, D>>
1535 for CentroidSpectrumType<C>
1536where
1537 C: BuildFromArrayMap + BuildArrayMapFrom,
1538 D: BuildFromArrayMap + BuildArrayMapFrom,
1539{
1540 type Error = SpectrumConversionError;
1541
1542 fn try_from(
1543 spectrum: MultiLayerSpectrum<C, D>,
1544 ) -> Result<CentroidSpectrumType<C>, Self::Error> {
1545 spectrum.into_centroid()
1546 }
1547}
1548
1549pub type Spectrum = MultiLayerSpectrum<CentroidPeak, DeconvolutedPeak>;
1552
1553impl<C: CentroidLike, D: DeconvolutedCentroidLike> From<CentroidSpectrumType<C>>
1554 for MultiLayerSpectrum<C, D>
1555{
1556 fn from(spectrum: CentroidSpectrumType<C>) -> MultiLayerSpectrum<C, D> {
1557 spectrum.into_spectrum().unwrap()
1558 }
1559}
1560
1561impl<C: CentroidLike, D: DeconvolutedCentroidLike> From<RawSpectrum> for MultiLayerSpectrum<C, D>
1562where
1563 C: BuildFromArrayMap,
1564 D: BuildFromArrayMap,
1565{
1566 fn from(spectrum: RawSpectrum) -> MultiLayerSpectrum<C, D> {
1567 spectrum.into_spectrum().unwrap()
1568 }
1569}
1570
1571impl<C: CentroidLike, D: DeconvolutedCentroidLike> From<MultiLayerSpectrum<C, D>> for RawSpectrum
1572where
1573 C: BuildFromArrayMap + BuildArrayMapFrom,
1574 D: BuildFromArrayMap + BuildArrayMapFrom,
1575{
1576 fn from(spectrum: MultiLayerSpectrum<C, D>) -> RawSpectrum {
1577 spectrum.into_raw().unwrap()
1578 }
1579}
1580
1581impl<C: CentroidLike> TryFrom<RawSpectrum> for CentroidSpectrumType<C>
1582where
1583 C: BuildFromArrayMap,
1584{
1585 type Error = SpectrumConversionError;
1586
1587 fn try_from(spectrum: RawSpectrum) -> Result<CentroidSpectrumType<C>, Self::Error> {
1588 spectrum.into_centroid()
1589 }
1590}
1591
1592impl<C: CentroidLike, D: DeconvolutedCentroidLike> TryFrom<MultiLayerSpectrum<C, D>>
1593 for DeconvolutedSpectrumType<D>
1594where
1595 C: BuildFromArrayMap + BuildArrayMapFrom,
1596 D: BuildFromArrayMap + BuildArrayMapFrom,
1597{
1598 type Error = SpectrumConversionError;
1599
1600 fn try_from(value: MultiLayerSpectrum<C, D>) -> Result<Self, Self::Error> {
1601 match value.peaks() {
1602 RefPeakDataLevel::Deconvoluted(_) => {
1603 let peaks = value.deconvoluted_peaks.unwrap();
1604 Ok(DeconvolutedSpectrumType {
1605 description: value.description,
1606 deconvoluted_peaks: peaks,
1607 })
1608 }
1609 RefPeakDataLevel::RawData(arrays) => {
1610 let peaks = match D::try_from_arrays(arrays) {
1611 Ok(peaks) => peaks,
1612 Err(e) => return Err(SpectrumConversionError::ArrayRetrievalError(e)),
1613 };
1614 Ok(DeconvolutedSpectrumType {
1615 description: value.description,
1616 deconvoluted_peaks: PeakSetVec::new(peaks),
1617 })
1618 }
1619 _ => Err(SpectrumConversionError::NotDeconvoluted),
1620 }
1621 }
1622}