1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
use std::{borrow::Cow, ops::Range};
#[cfg(feature = "mzannotate")]
use mzcore::chemistry::MassOutputMode;
use mzcore::{
chemistry::AmbiguousMolecule,
sequence::{FlankingSequence, PeptidoformIonSet},
system::{Mass, MassOverCharge, Ratio, Time, isize::Charge},
};
use mzcv::Term;
use crate::{KnownFileFormat, ProteinMetaData, Reliability, SpectrumIds};
/// Generalised access to meta data of PSMs
pub trait PSMMetaData {
/// Get the peptidoform ion set, if present
fn peptidoform_ion_set(&self) -> Option<Cow<'_, PeptidoformIonSet>>;
/// Get the format and version for this peptidoform
fn format(&self) -> KnownFileFormat;
/// Get the numerical PSM identifier
fn numerical_id(&self) -> Option<usize>;
/// Get the PSM identifier
fn id(&self) -> String;
/// Get the search engine that identified this PSM
fn search_engine(&self) -> Option<Term>;
/// Get the normalised confidence, a score between -1 and 1 describing the confidence in the
/// entire PSM
fn confidence(&self) -> Option<f64>;
/// Get the normalised local confidence, a score between -1 and 1 for each amino acid in the
/// peptide
fn local_confidence(&self) -> Option<Cow<'_, [f64]>>;
/// Get the original confidence and the term identifying the type of original confidence
fn original_confidence(&self) -> Option<(f64, Term)>;
/// Get the original local confidence, a score for each amino acid in the peptide
fn original_local_confidence(&self) -> Option<&[f64]>;
/// The charge of the precursor/PSM, if known
fn charge(&self) -> Option<Charge>;
/// Which fragmentation mode was used, if known
fn mode(&self) -> Option<Cow<'_, str>>;
/// Which built-in fragmentation model this fragmentation mode matches to.
/// The default implementation matches on the textual output of [`MetaData::mode`].
/// If needed a custom implementation can be made.
#[cfg(feature = "mzannotate")]
fn fragmentation_model(
&self,
) -> Option<mzannotate::annotation::model::BuiltInFragmentationModel> {
self.mode().map(|m| m.as_ref().into())
}
/// The retention time, if known
fn retention_time(&self) -> Option<Time>;
/// The scans per rawfile that are at the basis for this PSM
fn scans(&self) -> SpectrumIds;
/// Get the mz as experimentally determined
fn experimental_mz(&self) -> Option<MassOverCharge>;
/// Get the mass as experimentally determined
fn experimental_mass(&self) -> Option<Mass>;
/// Get the absolute ppm error between the experimental and theoretical precursor mass, if there
/// are multiple masses possible returns the smallest ppm
fn ppm_error(&self) -> Option<Ratio> {
let exp_mass = self.experimental_mass()?;
self.peptidoform_ion_set().and_then(|f| {
f.formulas()
.iter()
.map(|theo_mass| theo_mass.monoisotopic_mass().ppm(exp_mass))
.min_by(|a, b| a.value.total_cmp(&b.value))
})
}
/// Get the absolute mass error between the experimental and theoretical precursor mass, if
/// there are multiple masses possible returns the smallest difference
fn mass_error(&self) -> Option<Mass> {
let exp_mass = self.experimental_mass()?;
self.peptidoform_ion_set().and_then(|f| {
f.formulas()
.iter()
.map(|theo_mass| (exp_mass - theo_mass.monoisotopic_mass()).abs())
.min_by(|a, b| a.value.total_cmp(&b.value))
})
}
/// The linked protein type
type Protein: ProteinMetaData + Default + Clone;
/// Get the linked protein
fn proteins(&self) -> Cow<'_, [Self::Protein]> {
Cow::Borrowed(&[])
}
/// Get the protein location if this was database matched data
fn protein_location(&self) -> Option<Range<u16>>;
/// Get the flanking sequences on the N and C terminal side.
/// The reported sequences are both in N to C direction.
fn flanking_sequences(&self) -> (&FlankingSequence, &FlankingSequence);
/// The database that was used for matching optionally with the version of the database
fn database(&self) -> Option<(&str, Option<&str>)>;
/// Get if this PSM is marked as a unique match for its database match, note that this might not
/// be true anymore if multiple streams of data are merged
fn unique(&self) -> Option<bool>;
/// Get the reliability of this PSM
fn reliability(&self) -> Option<Reliability>;
/// Get the URI for this PSM
fn uri(&self) -> Option<String>;
/// What mode is the spectrum stored in
#[cfg(feature = "mzannotate")]
type SpectrumOutputMode: MassOutputMode;
/// Get the annotated spectrum if this is encoded in the format
// TODO: built parsers for OPair, PLGS, MetaMorpheus
#[cfg(feature = "mzannotate")]
fn annotated_spectrum(
&self,
) -> Option<Cow<'_, mzannotate::spectrum::AnnotatedSpectrum<Self::SpectrumOutputMode>>> {
None
}
/// Check if this spectrum has an annotated spectrum available.
/// This can be overwritten to built a faster implementation if creating the spectrum needs to
/// happen at runtime.
#[cfg(feature = "mzannotate")]
fn has_annotated_spectrum(&self) -> bool {
self.annotated_spectrum().is_some()
}
}
macro_rules! impl_ref {
($t:ty) => {
impl<T: PSMMetaData> PSMMetaData for $t {
type Protein = T::Protein;
#[cfg(feature = "mzannotate")]
type SpectrumOutputMode = T::SpectrumOutputMode;
fn peptidoform_ion_set(&self) -> Option<Cow<'_, PeptidoformIonSet>> {
(**self).peptidoform_ion_set()
}
fn format(&self) -> KnownFileFormat {
(**self).format()
}
fn numerical_id(&self) -> Option<usize> {
(**self).numerical_id()
}
fn id(&self) -> String {
(**self).id()
}
fn search_engine(&self) -> Option<Term> {
(**self).search_engine()
}
fn confidence(&self) -> Option<f64> {
(**self).confidence()
}
fn local_confidence(&self) -> Option<Cow<'_, [f64]>> {
(**self).local_confidence()
}
fn original_confidence(&self) -> Option<(f64, Term)> {
(**self).original_confidence()
}
fn original_local_confidence(&self) -> Option<&[f64]> {
(**self).original_local_confidence()
}
fn charge(&self) -> Option<Charge> {
(**self).charge()
}
fn mode(&self) -> Option<Cow<'_, str>> {
(**self).mode()
}
fn retention_time(&self) -> Option<Time> {
(**self).retention_time()
}
fn scans(&self) -> SpectrumIds {
(**self).scans()
}
fn experimental_mz(&self) -> Option<MassOverCharge> {
(**self).experimental_mz()
}
fn experimental_mass(&self) -> Option<Mass> {
(**self).experimental_mass()
}
fn mass_error(&self) -> Option<Mass> {
(**self).mass_error()
}
fn ppm_error(&self) -> Option<Ratio> {
(**self).ppm_error()
}
fn proteins(&self) -> Cow<'_, [Self::Protein]> {
(**self).proteins()
}
fn protein_location(&self) -> Option<Range<u16>> {
(**self).protein_location()
}
fn flanking_sequences(&self) -> (&FlankingSequence, &FlankingSequence) {
(**self).flanking_sequences()
}
fn database(&self) -> Option<(&str, Option<&str>)> {
(**self).database()
}
fn unique(&self) -> Option<bool> {
(**self).unique()
}
fn reliability(&self) -> Option<Reliability> {
(**self).reliability()
}
fn uri(&self) -> Option<String> {
(**self).uri()
}
#[cfg(feature = "mzannotate")]
fn annotated_spectrum(
&self,
) -> Option<Cow<'_, mzannotate::spectrum::AnnotatedSpectrum<Self::SpectrumOutputMode>>>
{
(**self).annotated_spectrum()
}
#[cfg(feature = "mzannotate")]
fn has_annotated_spectrum(&self) -> bool {
(**self).has_annotated_spectrum()
}
}
};
}
impl_ref!(&T);
impl_ref!(std::rc::Rc<T>);
impl_ref!(std::sync::Arc<T>);