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 const fn occupancy_validity(&self) -> &ValidityMask {
162 &self.occupancy_validity
163 }
164
165 #[must_use]
167 pub fn occupancies_plain(&self) -> Option<&[f32]> {
168 self.occupancy.as_slice()
169 }
170
171 #[must_use]
173 pub fn b_factor(&self, local: u32) -> Option<(f32, Presence)> {
174 with_presence(self.b_factor.get(local), &self.b_factor_validity, local)
175 }
176
177 #[must_use]
179 pub const fn b_factor_validity(&self) -> &ValidityMask {
180 &self.b_factor_validity
181 }
182
183 #[must_use]
185 pub fn b_factors_plain(&self) -> Option<&[f32]> {
186 self.b_factor.as_slice()
187 }
188
189 #[must_use]
191 pub fn formal_charge(&self, local: u32) -> Option<(i8, Presence)> {
192 with_presence(
193 self.formal_charge.get(local),
194 &self.formal_charge_validity,
195 local,
196 )
197 }
198
199 #[must_use]
201 pub fn atom_site_id(&self, local: u32) -> Option<u32> {
202 self.atom_site_id.get(local)
203 }
204
205 #[must_use]
207 pub fn has_position(&self, local: u32) -> bool {
208 self.coord_validity.get(local).is_present()
209 }
210
211 #[must_use]
216 pub fn record(
217 &self,
218 local: u32,
219 residues: &ResidueTable,
220 position: Option<[f32; 3]>,
221 ) -> Option<AtomRecord> {
222 let position = if self.has_position(local) {
223 Some(position?)
224 } else {
225 None
226 };
227 Some(AtomRecord {
228 position,
229 element: self.element(local)?,
230 atom_name: self.atom_name(local)?,
231 auth_atom_name: optional(self.auth_atom_name(local)),
232 alternate_component_id: optional(self.alternate_component_id(local)),
233 alt_id: self.alt_id(local)?,
234 residue: self.residue(local, residues)?,
235 occupancy: self.occupancy(local)?,
236 b_factor: self.b_factor(local)?,
237 formal_charge: self.formal_charge(local)?,
238 atom_site_id: self.atom_site_id(local)?,
239 })
240 }
241
242 fn atom_range(&self) -> Range<u32> {
243 self.atoms.start..self.atoms.end
244 }
245}
246
247fn with_presence<T>(
248 value: Option<T>,
249 validity: &ValidityMask,
250 local: u32,
251) -> Option<(T, Presence)> {
252 value.map(|value| (value, validity.get(local)))
253}
254
255fn optional_symbol(raw: u32) -> Option<SymbolId> {
256 (raw != u32::MAX).then(|| SymbolId::from_raw(raw))
257}
258
259fn optional(symbol: Option<SymbolId>) -> OptionalSymbol {
260 match symbol {
261 Some(symbol) => OptionalSymbol::some(symbol),
262 None => OptionalSymbol::NONE,
263 }
264}