molframe_core/annotation/
model.rs1use crate::column::{Presence, ValidityMask};
4use crate::limits::CapacityError;
5use crate::symbol::SymbolId;
6use std::collections::BTreeMap;
7use std::sync::Arc;
8
9pub const SEGMENT_ID_ANNOTATION: &str = "segid";
11pub const PARTIAL_CHARGE_ANNOTATION: &str = "charge";
13pub const ATOM_RADIUS_ANNOTATION: &str = "radius";
15pub const AUTODOCK_TYPE_ANNOTATION: &str = "autodock_type";
17pub const COMPONENT_KIND_ANNOTATION: &str = "component_kind";
19pub const POLYMER_ATOM_ROLE_ANNOTATION: &str = "polymer_atom_role";
21pub const AROMATIC_ATOM_ANNOTATION: &str = "aromatic";
23pub const FORMAL_CHARGE_ANNOTATION: &str = "formalcharge";
25pub const HBOND_DONOR_ANNOTATION: &str = "hbond_donor";
27pub const HBOND_ACCEPTOR_ANNOTATION: &str = "hbond_acceptor";
29pub const STEREO_CONFIGURATION_ANNOTATION: &str = "chirality";
31pub const PLDDT_ANNOTATION: &str = "plddt";
33pub const PAE_ANNOTATION: &str = "pae";
35
36#[derive(Clone, Debug)]
38pub struct AnnotationColumn<T> {
39 values: Arc<Vec<T>>,
40 validity: ValidityMask,
41}
42
43impl<T> AnnotationColumn<T> {
44 pub fn from_values(values: Vec<T>) -> Result<Self, CapacityError> {
50 let len =
51 u32::try_from(values.len()).map_err(|_| CapacityError::new("annotation column"))?;
52 let validity = ValidityMask::all_present(len);
53 Ok(Self {
54 values: Arc::new(values),
55 validity,
56 })
57 }
58
59 #[must_use]
61 pub fn len(&self) -> u32 {
62 self.validity.len()
63 }
64
65 #[must_use]
67 pub fn is_empty(&self) -> bool {
68 self.validity.is_empty()
69 }
70
71 #[must_use]
73 pub fn presence(&self, atom: u32) -> Presence {
74 self.validity.get(atom)
75 }
76
77 #[must_use]
79 pub fn values(&self) -> &[T] {
80 &self.values
81 }
82
83 pub(crate) fn filter(&self, keep: impl Fn(u32) -> bool) -> Result<Self, CapacityError>
84 where
85 T: Clone,
86 {
87 let entries = self
88 .values
89 .iter()
90 .enumerate()
91 .filter_map(|(position, value)| {
92 let position = u32::try_from(position).ok()?;
93 keep(position).then(|| (value.clone(), self.validity.get(position)))
94 });
95 Self::from_entries(entries)
96 }
97}
98
99impl<T> AnnotationColumn<T> {
100 pub fn from_entries(
106 entries: impl IntoIterator<Item = (T, Presence)>,
107 ) -> Result<Self, CapacityError> {
108 let (values, presences): (Vec<_>, Vec<_>) = entries.into_iter().unzip();
109 let validity = ValidityMask::try_from_iter(presences)
110 .ok_or_else(|| CapacityError::new("annotation column"))?;
111 Ok(Self {
112 values: Arc::new(values),
113 validity,
114 })
115 }
116}
117
118impl<T: Copy> AnnotationColumn<T> {
119 #[must_use]
121 pub fn get(&self, atom: u32) -> Option<(T, Presence)> {
122 self.values
123 .get(atom as usize)
124 .copied()
125 .map(|value| (value, self.validity.get(atom)))
126 }
127}
128
129#[derive(Clone, Debug)]
131#[non_exhaustive]
132pub enum AtomAnnotation {
133 Boolean(AnnotationColumn<bool>),
135 Integer(AnnotationColumn<i64>),
137 Real(AnnotationColumn<f64>),
139 Symbol(AnnotationColumn<SymbolId>),
141}
142
143impl AtomAnnotation {
144 #[must_use]
146 pub fn len(&self) -> u32 {
147 match self {
148 Self::Boolean(column) => column.len(),
149 Self::Integer(column) => column.len(),
150 Self::Real(column) => column.len(),
151 Self::Symbol(column) => column.len(),
152 }
153 }
154
155 #[must_use]
157 pub fn is_empty(&self) -> bool {
158 self.len() == 0
159 }
160
161 pub(crate) fn filter(&self, keep: impl Fn(u32) -> bool + Copy) -> Result<Self, CapacityError> {
162 Ok(match self {
163 Self::Boolean(column) => Self::Boolean(column.filter(keep)?),
164 Self::Integer(column) => Self::Integer(column.filter(keep)?),
165 Self::Real(column) => Self::Real(column.filter(keep)?),
166 Self::Symbol(column) => Self::Symbol(column.filter(keep)?),
167 })
168 }
169}
170
171#[derive(Clone, Debug, Default)]
173pub struct AtomAnnotations {
174 columns: BTreeMap<Box<str>, AtomAnnotation>,
175}
176
177impl AtomAnnotations {
178 #[must_use]
180 pub fn len(&self) -> usize {
181 self.columns.len()
182 }
183
184 #[must_use]
186 pub fn is_empty(&self) -> bool {
187 self.columns.is_empty()
188 }
189
190 #[must_use]
192 pub fn get(&self, name: &str) -> Option<&AtomAnnotation> {
193 self.columns.get(name)
194 }
195
196 pub fn iter(&self) -> impl Iterator<Item = (&str, &AtomAnnotation)> {
198 self.columns
199 .iter()
200 .map(|(name, column)| (name.as_ref(), column))
201 }
202
203 pub fn insert(
205 &mut self,
206 name: impl Into<Box<str>>,
207 column: AtomAnnotation,
208 ) -> Option<AtomAnnotation> {
209 self.columns.insert(name.into(), column)
210 }
211
212 pub fn remove(&mut self, name: &str) -> Option<AtomAnnotation> {
214 self.columns.remove(name)
215 }
216
217 pub(crate) fn filter(&self, keep: impl Fn(u32) -> bool + Copy) -> Result<Self, CapacityError> {
218 let columns = self
219 .columns
220 .iter()
221 .map(|(name, column)| Ok((name.clone(), column.filter(keep)?)))
222 .collect::<Result<_, CapacityError>>()?;
223 Ok(Self { columns })
224 }
225}
226
227#[cfg(test)]
228#[path = "model_tests.rs"]
229mod tests;