use super::ExtensionStore;
use crate::annotation::AtomAnnotations;
use crate::chunk::AtomChunk;
use crate::coords::{CoordinateBlock, CoordinateGeneration};
use crate::index::ModelIndex;
use crate::secondary::SecondaryStructure;
use crate::symbol::{Interner, SymbolId};
use crate::topology::Topology;
use std::fmt;
use std::sync::Arc;
const PLACEHOLDER_TOLERANCE: f64 = 1e-6;
#[derive(Clone, Debug, Default)]
pub struct EntryMetadata {
pub id: Option<Box<str>>,
pub title: Option<Box<str>>,
pub method: Option<Box<str>>,
pub resolution: Option<f32>,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct UnitCell {
pub lengths: [f64; 3],
pub angles: [f64; 3],
}
impl UnitCell {
#[must_use]
pub fn is_placeholder(&self) -> bool {
values_are_near(&self.lengths, 1.0) && values_are_near(&self.angles, 90.0)
}
}
#[derive(Clone, Debug)]
pub enum CoordinateStore {
Single(CoordinateBlock),
Dense {
frames: Vec<CoordinateBlock>,
},
Ragged {
models: Vec<Structure>,
},
}
impl CoordinateStore {
pub(crate) fn make_unique_in(
&mut self,
context: &crate::ExecutionContext,
) -> Result<(), crate::MemoryBudgetError> {
match self {
Self::Single(block) => block.make_unique_in(context),
Self::Dense { frames } => {
for frame in frames {
frame.make_unique_in(context)?;
}
Ok(())
}
Self::Ragged { .. } => Ok(()),
}
}
#[must_use]
pub fn model_count(&self) -> usize {
match self {
Self::Single(_) => 1,
Self::Dense { frames } => frames.len(),
Self::Ragged { models } => models.len(),
}
}
#[must_use]
pub fn block(&self, model: ModelIndex) -> Option<&CoordinateBlock> {
match self {
Self::Single(block) => (model.get() == 0).then_some(block),
Self::Dense { frames } => frames.get(model.as_usize()),
Self::Ragged { .. } => None,
}
}
#[must_use]
pub const fn is_dense(&self) -> bool {
matches!(self, Self::Single(_) | Self::Dense { .. })
}
#[must_use]
pub fn ragged_models(&self) -> Option<&[Structure]> {
match self {
Self::Ragged { models } => Some(models),
Self::Single(_) | Self::Dense { .. } => None,
}
}
}
#[derive(Clone, Debug)]
pub struct StructureData {
pub entry: EntryMetadata,
pub topology: Topology,
pub chunks: Arc<Vec<AtomChunk>>,
pub bonds: crate::bond::BondTable,
pub anisotropy: crate::anisotropy::AnisotropyTable,
pub secondary_structure: Arc<Vec<SecondaryStructure>>,
pub annotations: AtomAnnotations,
pub extensions: ExtensionStore,
pub coords: CoordinateStore,
pub dictionary: Interner,
pub cell: Option<UnitCell>,
pub generation: CoordinateGeneration,
pub(crate) reservation: Option<Arc<crate::MemoryReservation>>,
}
#[repr(C)]
#[derive(Clone, Debug)]
pub struct Structure(Arc<StructureData>);
impl StructureData {
#[must_use]
pub fn empty() -> Self {
Self {
reservation: None,
entry: EntryMetadata::default(),
topology: Topology::default(),
chunks: Arc::new(Vec::new()),
bonds: crate::bond::BondTable::default(),
anisotropy: crate::anisotropy::AnisotropyTable::default(),
secondary_structure: Arc::new(Vec::new()),
annotations: AtomAnnotations::default(),
extensions: ExtensionStore::new(),
coords: CoordinateStore::Single(CoordinateBlock::new()),
dictionary: Interner::new(),
cell: None,
generation: CoordinateGeneration::INITIAL,
}
}
pub(crate) fn atom_count(&self) -> u32 {
match self.chunks.last() {
Some(chunk) => chunk.atoms().end,
None => 0,
}
}
}
impl Structure {
#[must_use]
pub fn new(data: StructureData) -> Self {
Self(Arc::new(data))
}
pub(crate) fn retain_reservation(mut self, reservation: Arc<crate::MemoryReservation>) -> Self {
let data = Arc::make_mut(&mut self.0);
if let CoordinateStore::Ragged { models } = &mut data.coords {
for model in models {
*model = model.clone().retain_reservation(Arc::clone(&reservation));
}
}
data.reservation = Some(reservation);
self
}
#[must_use]
pub fn data(&self) -> &StructureData {
&self.0
}
#[must_use]
pub fn shared(&self) -> Arc<StructureData> {
Arc::clone(&self.0)
}
#[must_use]
pub fn atom_count(&self) -> u32 {
self.0.atom_count()
}
#[must_use]
pub fn atom(&self, atom: crate::index::AtomIndex) -> Option<super::AtomRef<'_>> {
self.0.atom(atom)
}
#[must_use]
pub fn model(&self, model: ModelIndex) -> Option<super::ModelRef<'_>> {
self.0.model(model)
}
#[must_use]
pub fn model_snapshot(&self, model: ModelIndex) -> Option<(Self, ModelIndex)> {
match &self.0.coords {
CoordinateStore::Ragged { models } => models
.get(model.as_usize())
.cloned()
.map(|structure| (structure, ModelIndex::new(0))),
CoordinateStore::Single(_) | CoordinateStore::Dense { .. } => self
.model(model)
.map(|_| (self.clone(), model))
.or_else(|| {
(model.get() == 0 && self.model_count() == 1).then(|| (self.clone(), model))
}),
}
}
#[must_use]
pub fn chain(&self, chain: crate::index::ChainIndex) -> Option<super::ChainRef<'_>> {
self.0.chain(chain)
}
#[must_use]
pub fn residue(&self, residue: crate::index::ResidueIndex) -> Option<super::ResidueRef<'_>> {
self.0.residue(residue)
}
#[must_use]
pub fn model_count(&self) -> usize {
self.0.coords.model_count()
}
#[must_use]
pub fn ragged_models(&self) -> Option<&[Self]> {
self.0.coords.ragged_models()
}
#[must_use]
pub fn chain_count(&self) -> usize {
self.0.topology.chains.len()
}
#[must_use]
pub fn residue_count(&self) -> usize {
self.0.topology.residues.len()
}
#[must_use]
pub fn entity_count(&self) -> usize {
self.0.topology.entities.len()
}
#[must_use]
pub fn positions(&self) -> &[[f32; 3]] {
match self.model_positions(ModelIndex::new(0)) {
Some(positions) => positions,
None => &[],
}
}
#[must_use]
pub fn model_positions(&self, model: ModelIndex) -> Option<&[[f32; 3]]> {
self.0.coords.block(model).map(CoordinateBlock::as_slice)
}
#[must_use]
pub fn generation(&self) -> CoordinateGeneration {
self.0.generation
}
#[must_use]
pub fn annotations(&self) -> &AtomAnnotations {
&self.0.annotations
}
#[must_use]
pub fn secondary_structure(&self) -> &[SecondaryStructure] {
&self.0.secondary_structure
}
#[must_use]
pub fn extensions(&self) -> &ExtensionStore {
&self.0.extensions
}
#[must_use]
pub fn with_extension<T>(&self, key: &'static str, value: T) -> Self
where
T: std::any::Any + Send + Sync + std::panic::RefUnwindSafe,
{
let mut data = self.data().clone();
data.extensions.insert(key, value);
Self::new(data)
}
#[must_use]
pub fn without_extensions(&self) -> Self {
if self.0.extensions.is_empty() {
return self.clone();
}
let mut data = self.data().clone();
data.extensions.clear();
Self::new(data)
}
#[must_use]
pub fn resolve(&self, symbol: SymbolId) -> Option<&str> {
self.0.dictionary.resolve(symbol)
}
}
impl From<StructureData> for Structure {
fn from(data: StructureData) -> Self {
Self::new(data)
}
}
fn values_are_near(values: &[f64], expected: f64) -> bool {
values
.iter()
.all(|value| (value - expected).abs() < PLACEHOLDER_TOLERANCE)
}
impl fmt::Display for Structure {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Structure(models={}, chains={}, residues={}, atoms={})",
self.model_count(),
self.chain_count(),
self.residue_count(),
self.atom_count(),
)
}
}
#[cfg(test)]
#[path = "data_tests.rs"]
mod tests;