use super::parent::ParentMapping;
use super::stats::AtomChunkStats;
use crate::column::{EncodedColumn, Presence, ValidityMask};
use crate::coords::CoordinateBlock;
use crate::element::Element;
use crate::index::ResidueIndex;
use crate::optional::OptionalSymbol;
use crate::symbol::{AltId, SymbolId};
use crate::topology::ResidueTable;
use std::ops::Range;
#[derive(Clone, Copy, Debug)]
pub struct AtomRecord {
pub position: Option<[f32; 3]>,
pub element: Element,
pub atom_name: SymbolId,
pub auth_atom_name: OptionalSymbol,
pub alternate_component_id: OptionalSymbol,
pub alt_id: AltId,
pub residue: ResidueIndex,
pub occupancy: (f32, Presence),
pub b_factor: (f32, Presence),
pub formal_charge: (i8, Presence),
pub atom_site_id: u32,
}
#[derive(Clone, Debug)]
pub struct AtomChunk {
pub(super) atoms: Range<u32>,
pub(super) model: u32,
pub(super) element: EncodedColumn<u8>,
pub(super) atom_name: EncodedColumn<SymbolId>,
pub(super) auth_atom_name: Option<EncodedColumn<u32>>,
pub(super) alternate_component_id: Option<EncodedColumn<u32>>,
pub(super) alt_id: EncodedColumn<u32>,
pub(super) residue: ParentMapping,
pub(super) occupancy: EncodedColumn<f32>,
pub(super) occupancy_validity: ValidityMask,
pub(super) b_factor: EncodedColumn<f32>,
pub(super) b_factor_validity: ValidityMask,
pub(super) formal_charge: EncodedColumn<i8>,
pub(super) formal_charge_validity: ValidityMask,
pub(super) atom_site_id: EncodedColumn<u32>,
pub(super) coord_validity: ValidityMask,
pub(super) stats: AtomChunkStats,
}
impl AtomChunk {
#[must_use]
pub fn len(&self) -> u32 {
self.atoms.end - self.atoms.start
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn atoms(&self) -> Range<u32> {
self.atom_range()
}
#[must_use]
pub const fn stats(&self) -> &AtomChunkStats {
&self.stats
}
#[must_use]
pub const fn model(&self) -> u32 {
self.model
}
#[must_use]
pub fn positions<'a>(&self, coords: &'a CoordinateBlock) -> Option<&'a [[f32; 3]]> {
coords.range(self.atom_range())
}
#[must_use]
pub fn element(&self, local: u32) -> Option<Element> {
self.element.get(local).map(Element::from_atomic_number)
}
#[must_use]
pub fn atom_name(&self, local: u32) -> Option<SymbolId> {
self.atom_name.get(local)
}
#[must_use]
pub fn atom_names_plain(&self) -> Option<&[SymbolId]> {
self.atom_name.as_slice()
}
#[must_use]
pub fn auth_atom_name(&self, local: u32) -> Option<SymbolId> {
optional_symbol(self.auth_atom_name.as_ref()?.get(local)?)
}
#[must_use]
pub fn alternate_component_id(&self, local: u32) -> Option<SymbolId> {
optional_symbol(self.alternate_component_id.as_ref()?.get(local)?)
}
#[must_use]
pub fn alt_id(&self, local: u32) -> Option<AltId> {
self.alt_id.get(local).map(AltId::from_raw)
}
#[must_use]
pub fn residue(&self, local: u32, residues: &ResidueTable) -> Option<ResidueIndex> {
self.residue.resolve(local, self.atoms.start, residues)
}
#[must_use]
pub fn occupancy(&self, local: u32) -> Option<(f32, Presence)> {
with_presence(self.occupancy.get(local), &self.occupancy_validity, local)
}
#[must_use]
pub const fn occupancy_validity(&self) -> &ValidityMask {
&self.occupancy_validity
}
#[must_use]
pub fn occupancies_plain(&self) -> Option<&[f32]> {
self.occupancy.as_slice()
}
#[must_use]
pub fn b_factor(&self, local: u32) -> Option<(f32, Presence)> {
with_presence(self.b_factor.get(local), &self.b_factor_validity, local)
}
#[must_use]
pub const fn b_factor_validity(&self) -> &ValidityMask {
&self.b_factor_validity
}
#[must_use]
pub fn b_factors_plain(&self) -> Option<&[f32]> {
self.b_factor.as_slice()
}
#[must_use]
pub fn formal_charge(&self, local: u32) -> Option<(i8, Presence)> {
with_presence(
self.formal_charge.get(local),
&self.formal_charge_validity,
local,
)
}
#[must_use]
pub fn atom_site_id(&self, local: u32) -> Option<u32> {
self.atom_site_id.get(local)
}
#[must_use]
pub fn has_position(&self, local: u32) -> bool {
self.coord_validity.get(local).is_present()
}
#[must_use]
pub fn record(
&self,
local: u32,
residues: &ResidueTable,
position: Option<[f32; 3]>,
) -> Option<AtomRecord> {
let position = if self.has_position(local) {
Some(position?)
} else {
None
};
Some(AtomRecord {
position,
element: self.element(local)?,
atom_name: self.atom_name(local)?,
auth_atom_name: optional(self.auth_atom_name(local)),
alternate_component_id: optional(self.alternate_component_id(local)),
alt_id: self.alt_id(local)?,
residue: self.residue(local, residues)?,
occupancy: self.occupancy(local)?,
b_factor: self.b_factor(local)?,
formal_charge: self.formal_charge(local)?,
atom_site_id: self.atom_site_id(local)?,
})
}
fn atom_range(&self) -> Range<u32> {
self.atoms.start..self.atoms.end
}
}
fn with_presence<T>(
value: Option<T>,
validity: &ValidityMask,
local: u32,
) -> Option<(T, Presence)> {
value.map(|value| (value, validity.get(local)))
}
fn optional_symbol(raw: u32) -> Option<SymbolId> {
(raw != u32::MAX).then(|| SymbolId::from_raw(raw))
}
fn optional(symbol: Option<SymbolId>) -> OptionalSymbol {
match symbol {
Some(symbol) => OptionalSymbol::some(symbol),
None => OptionalSymbol::NONE,
}
}