"""
@author: JD Chodera
@author: JH Prinz
"""
import abc
from openpathsampling.netcdfplus import StorableObject
from . import features as feats
# =============================================================================
# ABSTRACT SNAPSHOT (IMPLEMENTS ONLY REVERSED SNAPSHOTS)
# =============================================================================
[docs]class BaseSnapshot(StorableObject):
"""
Simulation snapshot. Contains references to a configuration and momentum
"""
__metaclass__ = abc.ABCMeta
def __init__(self, topology=None):
"""
Attributes
----------
topology : openpathsamping.Topology, default: None
The corresponding topology used with this Snapshot. Can also be None
and means no topology is specified.
"""
super(BaseSnapshot, self).__init__()
self._reversed = None
self.topology = topology
# def __eq__(self, other):
# if self is other:
# return True
#
# if isinstance(other, BaseSnapshot):
# return self.__uuid__ == other.__uuid__
#
# return NotImplemented
#
# def __ne__(self, other):
# return not self == other
# def __hash__(self):
# return self.__uuid__ & 1152921504606846975
@property
def reversed(self):
"""
Get the reversed copy.
Returns
-------
:class:`openpathsampling.snapshots.AbstractSnapshot`
the reversed partner of the current snapshot
Snapshots exist in pairs and this returns the reversed counter part.
The actual implementation takes care the the reversed version have
reversed momenta, etc. Usually these will not be stored separately but
flipped when requested.
"""
if self._reversed is None:
self._reversed = self.create_reversed()
return self._reversed
[docs] def __neg__(self):
"""
Access the reversed snapshot using `-`
Returns
-------
:class:`BaseSnapshot`
the reversed copy
"""
return self.reversed
# ==========================================================================
# Utility functions
# ==========================================================================
[docs] def copy(self):
"""
Returns a shallow copy of the instance itself. The contained
configuration and momenta are not copied.
Returns
-------
:class:`openpathsampling.BaseSnapshot`
the shallow copy
Notes
-----
Shallow here means that content will not be copied but only referenced.
Hence if you store the shallow copy it will be stored under a different
idx, but the content (e.g. Configuration object) will not.
"""
this = self.__class__.__new__(self.__class__)
BaseSnapshot.__init__(this, topology=self.topology)
return this
def copy_with_replacement(self, **kwargs):
cp = self.copy() # this will copy all, but it is simple
for key, value in kwargs.items():
if hasattr(cp, key):
setattr(cp, key, value)
else:
raise TypeError("copy_with_replacement() got an "
"unexpected keyword argument '%s'" % key)
return cp
def create_reversed(self):
this = self.copy()
this._reversed = self
return this
[docs]def SnapshotFactory(
name,
features,
description=None,
use_lazy_reversed=False,
base_class=None):
"""
Helper to create a new Snapshot class
Parameters
----------
name : str
name of the Snapshot class
features : list of :obj:`openpathsampling.features`
the features used to build the snapshot
description : str
the string to be used as basis for the docstring of the new class
it will be merged with the docs for the features
use_lazy_reversed : bool
still in there for legacy reasons. It will make the .reversed attribute
into a descriptor than can treat LoaderProxy objects. This feature is
not relly used anymore and can in the best case only save little memory
with slowing down construction, etc. Using `False` is faster
base_class : :obj:`openpathsampling.BaseSnapshot`
The base class the Snapshot is derived from.
Default is the `BaseSnapshot` class.
Returns
-------
:class:`openpathsampling.Snapshot`
the created `Snapshot` class
"""
if base_class is None:
base_class = BaseSnapshot
if type(base_class) is not tuple:
base_class = (base_class,)
cls = type(name, base_class, {})
if description is not None:
cls.__doc__ = description
cls = feats.attach_features(
features,
use_lazy_reversed=use_lazy_reversed)(cls)
return cls
class SnapshotDescriptor(frozenset, StorableObject):
def __init__(self, contents):
StorableObject.__init__(self)
frozenset.__init__(contents)
self._dimensions = dict(self)
self._cls = self._dimensions['class']
del self._dimensions['class']
@property
def snapshot_class(self):
return self._cls
@property
def dimensions(self):
return self._dimensions
@classmethod
def from_dict(cls, dct):
return cls(dct.items())
def to_dict(self):
return dict(self)
@staticmethod
def construct(snapshot_class, snapshot_dimensions):
d = {'class': snapshot_class}
if set(snapshot_class.__features__.dimensions) > \
set(snapshot_dimensions.keys()):
raise RuntimeError(
('Snapshot of type %s needs %s as dimensions, '
'you only provided %s') %
(
snapshot_class.__class__.__name__,
str(set(snapshot_class.__features__['dimensions'])),
str(set(snapshot_dimensions.keys()))
)
)
d.update(snapshot_dimensions)
return SnapshotDescriptor(d.items())