def test_synced(self):
     test_list = SyncedList(lambda x: x)
     assert not test_list._synced
     test_list._sync(None, [])
     assert test_list._synced
     test_list._unsync()
     assert not test_list._synced
Example #2
0
class Operations(Collection):
    """A mutable collection of operations which act on a `hoomd.Simulation`.

    The `Operations` class contains all the operations acting on a
    simulation. These operations are classes that perform various actions on a
    `hoomd.Simulation`. Operations can be added and removed at any point from a
    `hoomd.Operations` instance. The class provides the interface defined by
    `collections.abc.Collection`. Other methods for manipulating instances
    attempt to mimic Python objects where possible, but the class is not
    simply a mutable list or set. Since there are multiple types of operations
    in HOOMD-blue, `Operations` objects manage multiple independent
    sequences described below.

    The types of operations which can be added to an `Operations` object are
    tuners, updaters, integrators, writers, and computes. An `Operations` can
    only ever hold one integrator at a time. On the other hand, an `Operations`
    object can hold any number of tuners, updaters, writers, or computes. To
    see examples of these types of operations see `hoomd.tune` (tuners),
    `hoomd.update` (updaters), `hoomd.hpmc.integrate` or `hoomd.md.integrate`
    (integrators), , `hoomd.write` (writers), and `hoomd.md.thermo`
    (computes).

    A given instance of an operation class can only be added to a single
    `Operations` container. Likewise, a single instance cannot be added to the
    same `Operations` container more than once.

    All `Operations` instances start with a `hoomd.tune.ParticleSorter` instance
    in their ``tuners`` attribute. This increases simulation
    performance. However, users can choose to modify or remove this tuner if
    desired.

    Note:
        An `Operations` object is created by default when a new simulation is
        created.
    """
    def __init__(self):
        self._compute = list()
        self._scheduled = False
        self._updaters = SyncedList(OnlyType(Updater),
                                    _triggered_op_conversion)
        self._writers = SyncedList(OnlyType(Writer), _triggered_op_conversion)
        self._tuners = SyncedList(OnlyType(Tuner), lambda x: x._cpp_obj)
        self._computes = SyncedList(OnlyType(Compute), lambda x: x._cpp_obj)
        self._integrator = None

        self._tuners.append(ParticleSorter())

    def _get_proper_container(self, operation):
        if isinstance(operation, Updater):
            return self._updaters
        elif isinstance(operation, Writer):
            return self._writers
        elif isinstance(operation, Tuner):
            return self._tuners
        elif isinstance(operation, Compute):
            return self._computes
        else:
            raise TypeError(
                f"{type(operation)} is not a valid operation type.")

    def add(self, operation):
        """Add an operation to this container.

        Adds the provided operation to the appropriate attribute of the
        `Operations` instance.

        Args:
            operation (`hoomd.operation.Operation`): A HOOMD-blue tuner,
                updater, integrator, writer, or compute,  to add to the
                collection.

        Raises:
            ValueError: If ``operation`` already belongs to this or another
                `Operations` instance.
            TypeError: If ``operation`` is not of a valid type.

        Note:
            Since only one integrator can be associated with an `Operations`
            object at a time, this removes the current integrator when called
            with an integrator operation. Also, the ``integrator`` property
            cannot be set to ``None`` using this function. Use
            ``operations.integrator = None`` explicitly for this.
        """
        # calling _add is handled by the synced lists and integrator property.
        # we raise this error here to provide a more clear error message.
        if operation._added:
            raise ValueError("The provided operation has already been added "
                             "to an Operations instance.")
        if isinstance(operation, hoomd.integrate.BaseIntegrator):
            self.integrator = operation
        else:
            try:
                container = self._get_proper_container(operation)
            except TypeError:
                raise TypeError(f"Type {type(operation)} is not a valid "
                                f"type to add to Operations.")
            container.append(operation)

    def __iadd__(self, operation):
        """Works the same as `Operations.add`.

        Args:
            operation (`hoomd.operation.Operation`): A HOOMD-blue tuner,
                updater, integrator, writer, or compute to add to the object.
        """
        self.add(operation)
        return self

    def remove(self, operation):
        """Remove an operation from the `Operations` object.

        Remove the item from the collection whose id is the same as
        ``operation``. See
        `<https://docs.python.org/3/library/functions.html#id>`_ for the concept
        of a Python object id.

        Args:
            operation (`hoomd.operation.Operation`): A HOOMD-blue integrator,
                tuner, updater, integrator, or compute to remove from the
                container.

        Raises:
            ValueError: If ``operation`` is not found in this container.
            TypeError: If ``operation`` is not of a valid type.
        """
        if isinstance(operation, hoomd.integrate.BaseIntegrator):
            self.integrator = None
        else:
            try:
                container = self._get_proper_container(operation)
            except TypeError:
                raise TypeError(f"Type {type(operation)} is not a valid "
                                f"type to remove from Operations.")
            container.remove(operation)

    def __isub__(self, operation):
        """Works the same as `Operations.remove`.

        Args:
            operation (`hoomd.operation.Operation`): A HOOMD-blue integrator,
                tuner, updater, integrator, analzyer, or compute to remove from
                the collection.
        """
        self.remove(operation)
        return self

    @property
    def _sys_init(self):
        if self._simulation is None or self._simulation.state is None:
            return False
        else:
            return True

    def _schedule(self):
        """Prepares all operations for a `hoomd.Simulation.run` call.

        Creates the internal C++ objects for all operations.

        Raises:
            RuntimeError: raises when not associated with a `hoomd.Simulation`
                object.
        """
        if not self._sys_init:
            raise RuntimeError("System not initialized yet")
        sim = self._simulation
        if not (self.integrator is None or self.integrator._attached):
            self.integrator._attach()
        if not self.updaters._synced:
            self.updaters._sync(sim, sim._cpp_sys.updaters)
        if not self.writers._synced:
            self.writers._sync(sim, sim._cpp_sys.analyzers)
        if not self.tuners._synced:
            self.tuners._sync(sim, sim._cpp_sys.tuners)
        if not self.computes._synced:
            self.computes._sync(sim, sim._cpp_sys.computes)
        self._scheduled = True

    def _unschedule(self):
        """Undo the effects of `Operations._schedule`."""
        self._integrator._detach()
        self._writers._unsync()
        self._updaters._unsync()
        self._tuners._unsync()
        self._computes._unsync()
        self._scheduled = False

    def _store_reader(self, reader):
        # TODO
        pass

    def __contains__(self, operation):
        """Whether an operation is contained in this container.

        Args:
            operation: Returns whether this exact operation is
                contained in the collection.
        """
        return any(op is operation for op in self)

    def __iter__(self):
        """Iterates through all contained operations."""
        integrator = (self._integrator, ) if self._integrator else []
        yield from chain(self._tuners, self._updaters, integrator,
                         self._writers, self._computes)

    def __len__(self):
        """Return the number of operations contained in this collection."""
        return len(list(self))

    @property
    def integrator(self):
        """`hoomd.integrate.BaseIntegrator`: An MD or HPMC integrator object.

        `Operations` objects have an initial ``integrator`` property of
        ``None``. Can be set to MD or HPMC integrators. The property can also be
        set to ``None``.
        """
        return self._integrator

    @integrator.setter
    def integrator(self, op):
        if op is not None:
            if not isinstance(op, hoomd.integrate.BaseIntegrator):
                raise TypeError("Cannot set integrator to a type not derived "
                                "from hoomd.integrate.BaseIntegrator")
            if op._added:
                raise RuntimeError("Integrator cannot be added to twice to "
                                   "Operations collection.")
            else:
                op._add(self._simulation)

        old_ref = self.integrator
        self._integrator = op
        # Handle attaching and detaching integrators dealing with None values
        if self._scheduled:
            if op is not None:
                op._attach()
        if old_ref is not None:
            old_ref._notify_disconnect(self._simulation)
            old_ref._detach()
            old_ref._remove()

    @property
    def updaters(self):
        """list[`hoomd.operation.Updater`]: A list of updater operations.

        Holds the list of updaters associated with this collection. The list can
        be modified as a standard Python list.
        """
        return self._updaters

    @property
    def writers(self):
        """list[`hoomd.operation.Writer`]: A list of writer operations.

        Holds the list of writers associated with this collection. The list
        can be modified as a standard Python list.
        """
        return self._writers

    @property
    def tuners(self):
        """list[`hoomd.operation.Tuner`]: A list of tuner operations.

        Holds the list of tuners associated with this collection. The list can
        be modified as a standard Python list.
        """
        return self._tuners

    @property
    def computes(self):
        """list[`hoomd.operation.Compute`]: A list of tuner operations.

        Holds the list of tuners associated with this collection. The list can
        be modified as a standard Python list.
        """
        return self._computes