Source code for sknano.core.atoms._trajectory
# -*- coding: utf-8 -*-
"""
===============================================================================
Trajectory class for MD simulations (:mod:`sknano.core.atoms._trajectory`)
===============================================================================
Classes for analyzing the atom trajectories of molecular dynamics simulations.
.. currentmodule:: sknano.core.atoms._trajectory
"""
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
__docformat__ = 'restructuredtext en'
from operator import attrgetter
import numpy as np
from sknano.core import BaseClass, UserList
from ._md_atoms import MDAtom as Atom, MDAtoms as Atoms
__all__ = ['Snapshot', 'Trajectory']
class AtomSelection:
""":class:`Trajectory` atom selection class.
Parameters
----------
traj : :class:`Trajectory`
"""
def __init__(self, traj):
self.traj = traj
def all(self, ts=None):
"""Select all atoms for all snapshots or snapshot at given timestep.
Parameters
----------
ts : {None, int}, optional
"""
if ts is None:
for snapshot in self.traj:
if not snapshot.selected:
continue
for i in range(snapshot.Natoms):
snapshot.atom_selection[i] = True
snapshot.nselected = snapshot.Natoms
else:
snapshot = self.traj.get_snapshot(ts)
for i in range(snapshot.Natoms):
snapshot.atom_selection[i] = True
snapshot.nselected = snapshot.Natoms
class TimeSelection:
""":class:`Trajectory` time selection class.
Parameters
----------
traj : :class:`Trajectory`
"""
def __init__(self, traj):
self.traj = traj
def all(self, ts=None):
"""Select all trajectory snapshots/timesteps."""
[setattr(snapshot, 'selected', True) for snapshot in self.traj]
self.traj.nselected = self.traj.Nsnaps
self.traj.atom_selection.all()
self.print_fraction_selected()
def one(self, ts):
"""Select only timestep `ts`."""
[setattr(snapshot, 'selected', False) for snapshot in self.traj]
try:
self.traj.get_snapshot(ts).selected = True
self.traj.nselected = 1
except AttributeError:
pass
self.traj.atom_selection.all()
self.print_fraction_selected()
def none(self):
"""Deselect all timesteps."""
[setattr(snapshot, 'selected', False) for snapshot in self.traj]
self.traj.nselected = 0
self.print_fraction_selected()
def skip(self, n):
"""Select every `n`\ th timestep from currently selected timesteps."""
count = n - 1
for snapshot in self.traj:
if not snapshot.selected:
continue
count += 1
if count == n:
count = 0
continue
snapshot.selected = False
self.traj.nselected -= 1
self.traj.atom_selection.all()
self.print_fraction_selected()
def print_fraction_selected(self):
print('{}/{} snapshots selected'.format(
self.traj.nselected, self.traj.Nsnaps))
[docs]class Snapshot(BaseClass):
"""Container class for :class:`Trajectory` data at single timestep"""
def __init__(self, trajectory=None):
super().__init__()
self.trajectory = trajectory
self.atomattrs = None
self.attr_dtypes = None
self.timestep = None
self._atoms = None
self.fmtstr = "trajectory={trajectory!r}"
@property
def atoms(self):
"""Snapshot atoms."""
atoms = Atoms()
for atom in self._atoms:
try:
reference_atom = \
self.trajectory.reference_atoms.get_atom(
int(atom[self.atomattrs.index('id')]))
except AttributeError:
reference_atom = None
try:
t0_atom = self.trajectory.t0_atoms.get_atom(
int(atom[self.atomattrs.index('id')]))
except AttributeError:
t0_atom = None
attrs = [dtype(value) for dtype, value in
zip(self.attr_dtypes, atom)]
atoms.append(Atom(reference_atom=reference_atom,
t0_atom=t0_atom,
**dict(list(zip(self.atomattrs, attrs)))))
return atoms
@atoms.setter
def atoms(self, value):
self._atoms = value
@property
def atom_selection(self):
""":class:`~numpy:numpy.ndarray` boolean array."""
return self._atom_selection
@atom_selection.setter
def atom_selection(self, value):
if not isinstance(value, (list, np.ndarray)):
raise ValueError('Expected an array_like object.')
self._atom_selection = np.asarray(value, dtype=bool)
@property
def aselect(self):
"""Alias for :attr:`Snapshot.atom_selection`."""
return self.atom_selection
@aselect.setter
def aselect(self, value):
self.atom_selection = value
@property
def selected(self):
"""True/False if this snapshot is selected."""
return self._selected
@selected.setter
def selected(self, value):
self._selected = bool(value)
@property
def tselect(self):
"""Alias for :attr:`Snapshot.selected`."""
return self.selected
@tselect.setter
def tselect(self, value):
self.selected = value
@property
def nselected(self):
"""Number of selected atoms in this snapshot."""
return self._nselected
@nselected.setter
def nselected(self, value):
self._nselected = int(value)
@property
def nselect(self):
"""Alias for :attr:`Snapshot.nselected`."""
return self.nselected
@nselect.setter
def nselect(self, value):
self.nselected = value
[docs] def get_atoms(self, asarray=False):
"""Get atoms.
Parameters
----------
asarray : :class:`~python:bool`
Returns
-------
:class:`~numpy:numpy.ndarray` or :class:`MDAtoms`
if `asarray` is `True`, the atoms are returned as an
:class:`~numpy:numpy.ndarray`, otherwise an :class:`MDAtoms`
instance is returned.
"""
if asarray:
return self._atoms
return self.atoms
[docs] def todict(self):
return dict(trajectory=self.trajectory)
[docs]class Trajectory(BaseClass, UserList):
"""Base class for trajectory analysis."""
def __init__(self, snapshots=None):
super().__init__(initlist=snapshots)
self.fmtstr = "snapshots={snapshots!r}"
self.time_selection = TimeSelection(self)
self.atom_selection = AtomSelection(self)
self.nselected = 0
self.reference_atoms = None
self._reference_snapshot = None
self.t0_atoms = None
self._t0_snapshot = None
@property
def Nsnaps(self):
"""Number of :class:`Snapshot`\ s in `Trajectory`."""
return len(self.data)
@property
def atom_selection(self):
"""`AtomSelection` class."""
return self._atom_selection
@atom_selection.setter
def atom_selection(self, value):
if not isinstance(value, AtomSelection):
raise ValueError('Expected an `AtomSelection` instance.')
self._atom_selection = value
@property
def time_selection(self):
return self._time_selection
@time_selection.setter
def time_selection(self, value):
if not isinstance(value, TimeSelection):
raise ValueError('Expected a `TimeSelection instance.')
self._time_selection = value
@property
def aselect(self):
"""Alias for :attr:`Trajectory.atom_selection`."""
return self.atom_selection
@aselect.setter
def aselect(self, value):
self.atom_selection = value
@property
def tselect(self):
"""Alias for :attr:`Trajectory.time_selection`."""
return self.time_selection
@tselect.setter
def tselect(self, value):
self.time_selection = value
@property
def nselected(self):
"""Number of selected snapshots."""
return self._nselected
@nselected.setter
def nselected(self, value):
self._nselected = int(value)
@property
def nselect(self):
"""Alias for :attr:`Trajectory.nselected`."""
return self.nselected
@nselect.setter
def nselect(self, value):
self.nselected = value
@property
def snapshots(self):
"""Returns the list of :class:`Snapshot`\ s."""
return self.data
[docs] def sort(self, key=attrgetter('timestep'), reverse=False):
"""Sort the trajectory :class:`Snapshot`\ s."""
super().sort(key=key, reverse=reverse)
[docs] def cull(self):
"""Remove duplicate timesteps from `Trajectory`."""
i = 1
while i < len(self.data):
if self.data[i].timestep == self.data[i-1].timestep:
del self.data[i]
else:
i += 1
[docs] def get_snapshot(self, ts):
"""Return :class:`Snapshot` with timestep `ts`."""
for snapshot in self:
if snapshot.timestep == ts:
return snapshot
print("No snapshot at ts={:d} exists".format(ts))
[docs] def timestep_index(self, ts):
"""Return index of :class:`Snapshot` with timestep `ts`."""
for i, snapshot in enumerate(self):
if snapshot.timestep == ts:
return i
print("No timestep {:d} exists".format(ts))
@property
def reference_snapshot(self):
return self._reference_snapshot
@reference_snapshot.setter
def reference_snapshot(self, value):
if not isinstance(value, Snapshot):
raise TypeError('Expected a `Snapshot` instance.')
self._reference_snapshot = value
self.reference_atoms = self.reference_snapshot.atoms
self.reference_atoms.update_attrs()
@property
def t0_snapshot(self):
return self._t0_snapshot
@t0_snapshot.setter
def t0_snapshot(self, value):
if not isinstance(value, Snapshot):
raise TypeError('Expected a `Snapshot` instance.')
self._t0_snapshot = value
self.t0_atoms = self.t0_snapshot.atoms
self.t0_atoms.update_attrs()
@property
def timesteps(self):
v = np.zeros(self.nselected, dtype=int)
for i, snapshot in enumerate(self.data):
if snapshot.selected:
v[i] = snapshot.timestep
return v
[docs] def todict(self):
return dict(snapshots=self.data)