molframe_core/chunk/
atom.rs1use super::parent::ParentMapping;
9use super::stats::AtomChunkStats;
10use crate::column::{EncodedColumn, Presence, ValidityMask};
11use crate::coords::CoordinateBlock;
12use crate::element::Element;
13use crate::index::ResidueIndex;
14use crate::optional::OptionalSymbol;
15use crate::symbol::{AltId, SymbolId};
16use crate::topology::ResidueTable;
17use std::ops::Range;
18
19#[derive(Clone, Copy, Debug)]
21pub struct AtomRecord {
22 pub position: Option<[f32; 3]>,
24 pub element: Element,
26 pub atom_name: SymbolId,
28 pub auth_atom_name: OptionalSymbol,
30 pub alternate_component_id: OptionalSymbol,
35 pub alt_id: AltId,
37 pub residue: ResidueIndex,
39 pub occupancy: (f32, Presence),
41 pub b_factor: (f32, Presence),
43 pub formal_charge: (i8, Presence),
45 pub atom_site_id: u32,
47}
48
49#[derive(Clone, Debug)]
51pub struct AtomChunk {
52 pub(super) atoms: Range<u32>,
53 pub(super) model: u32,
54 pub(super) element: EncodedColumn<u8>,
55 pub(super) atom_name: EncodedColumn<SymbolId>,
56 pub(super) auth_atom_name: Option<EncodedColumn<u32>>,
57 pub(super) alternate_component_id: Option<EncodedColumn<u32>>,
58 pub(super) alt_id: EncodedColumn<u32>,
59 pub(super) residue: ParentMapping,
60 pub(super) occupancy: EncodedColumn<f32>,
61 pub(super) occupancy_validity: ValidityMask,
62 pub(super) b_factor: EncodedColumn<f32>,
63 pub(super) b_factor_validity: ValidityMask,
64 pub(super) formal_charge: EncodedColumn<i8>,
65 pub(super) formal_charge_validity: ValidityMask,
66 pub(super) atom_site_id: EncodedColumn<u32>,
67 pub(super) coord_validity: ValidityMask,
68 pub(super) stats: AtomChunkStats,
69}
70
71impl AtomChunk {
72 #[must_use]
75 pub fn len(&self) -> u32 {
76 self.atoms.end - self.atoms.start
77 }
78
79 #[must_use]
81 pub fn is_empty(&self) -> bool {
82 self.len() == 0
83 }
84
85 #[must_use]
89 pub fn atoms(&self) -> Range<u32> {
90 self.atom_range()
91 }
92
93 #[must_use]
95 pub const fn stats(&self) -> &AtomChunkStats {
96 &self.stats
97 }
98
99 #[must_use]
101 pub const fn model(&self) -> u32 {
102 self.model
103 }
104
105 #[must_use]
107 pub fn positions<'a>(&self, coords: &'a CoordinateBlock) -> Option<&'a [[f32; 3]]> {
108 coords.range(self.atom_range())
109 }
110
111 #[must_use]
113 pub fn element(&self, local: u32) -> Option<Element> {
114 self.element.get(local).map(Element::from_atomic_number)
115 }
116
117 #[must_use]
119 pub fn atom_name(&self, local: u32) -> Option<SymbolId> {
120 self.atom_name.get(local)
121 }
122
123 #[must_use]
125 pub fn atom_names_plain(&self) -> Option<&[SymbolId]> {
126 self.atom_name.as_slice()
127 }
128
129 #[must_use]
131 pub fn auth_atom_name(&self, local: u32) -> Option<SymbolId> {
132 optional_symbol(self.auth_atom_name.as_ref()?.get(local)?)
133 }
134
135 #[must_use]
137 pub fn alternate_component_id(&self, local: u32) -> Option<SymbolId> {
138 optional_symbol(self.alternate_component_id.as_ref()?.get(local)?)
139 }
140
141 #[must_use]
143 pub fn alt_id(&self, local: u32) -> Option<AltId> {
144 self.alt_id.get(local).map(AltId::from_raw)
145 }
146
147 #[must_use]
149 pub fn residue(&self, local: u32, residues: &ResidueTable) -> Option<ResidueIndex> {
150 self.residue.resolve(local, self.atoms.start, residues)
151 }
152
153 #[must_use]
155 pub fn occupancy(&self, local: u32) -> Option<(f32, Presence)> {
156 with_presence(self.occupancy.get(local), &self.occupancy_validity, local)
157 }
158
159 #[must_use]
161 pub fn occupancies_plain(&self) -> Option<&[f32]> {
162 self.occupancy.as_slice()
163 }
164
165 #[must_use]
167 pub fn b_factor(&self, local: u32) -> Option<(f32, Presence)> {
168 with_presence(self.b_factor.get(local), &self.b_factor_validity, local)
169 }
170
171 #[must_use]
173 pub fn b_factors_plain(&self) -> Option<&[f32]> {
174 self.b_factor.as_slice()
175 }
176
177 #[must_use]
179 pub fn formal_charge(&self, local: u32) -> Option<(i8, Presence)> {
180 with_presence(
181 self.formal_charge.get(local),
182 &self.formal_charge_validity,
183 local,
184 )
185 }
186
187 #[must_use]
189 pub fn atom_site_id(&self, local: u32) -> Option<u32> {
190 self.atom_site_id.get(local)
191 }
192
193 #[must_use]
195 pub fn has_position(&self, local: u32) -> bool {
196 self.coord_validity.get(local).is_present()
197 }
198
199 #[must_use]
204 pub fn record(
205 &self,
206 local: u32,
207 residues: &ResidueTable,
208 position: Option<[f32; 3]>,
209 ) -> Option<AtomRecord> {
210 let position = if self.has_position(local) {
211 Some(position?)
212 } else {
213 None
214 };
215 Some(AtomRecord {
216 position,
217 element: self.element(local)?,
218 atom_name: self.atom_name(local)?,
219 auth_atom_name: optional(self.auth_atom_name(local)),
220 alternate_component_id: optional(self.alternate_component_id(local)),
221 alt_id: self.alt_id(local)?,
222 residue: self.residue(local, residues)?,
223 occupancy: self.occupancy(local)?,
224 b_factor: self.b_factor(local)?,
225 formal_charge: self.formal_charge(local)?,
226 atom_site_id: self.atom_site_id(local)?,
227 })
228 }
229
230 fn atom_range(&self) -> Range<u32> {
231 self.atoms.start..self.atoms.end
232 }
233}
234
235fn with_presence<T>(
236 value: Option<T>,
237 validity: &ValidityMask,
238 local: u32,
239) -> Option<(T, Presence)> {
240 value.map(|value| (value, validity.get(local)))
241}
242
243fn optional_symbol(raw: u32) -> Option<SymbolId> {
244 (raw != u32::MAX).then(|| SymbolId::from_raw(raw))
245}
246
247fn optional(symbol: Option<SymbolId>) -> OptionalSymbol {
248 match symbol {
249 Some(symbol) => OptionalSymbol::some(symbol),
250 None => OptionalSymbol::NONE,
251 }
252}