PathMovers and MoveStrategies

If you want to add new kinds of Monte Carlo moves, you need to know a little more about the PathMover object. If you would like those Monte Carlo moves to be compatible with the rest of the OPS path sampling structure, you should learn more about MoveStrategies.



Many of the new path sampling movers that people invent involve propagating the dynamics in some way. To simplify this, we have an abstract object called an EngineMover. Shooting moves are built based on the EngineMover.

The big picture is that the EngineMover takes a Sample from a given ensemble, and propagates the trajectory can no longer satisfy a given target_ensemble. Details of subclasses depend mainly on the ensemble, the target_ensemble, and the direction.

For example, shooting moves have the same ensemble and target_ensemble. However, for extension moves (e.g., in the MinusMover), the initial ensemble is different from the final target_ensemble.

Before re-implementing anything, you should consider whether your needs are met by the built-in subclasses of EngineMover. A few examples might help to show how moves can be thought of as including an EngineMover, rather than needing to subclass one:



The MoveStrategy object acts as a factory for PathMovers. This makes it much easier for the user to mix and match various move types to create an overall custom scheme for the simulation.

When creating a new move strategy, you’ll mainly need to override the make_movers() method. You’ll also need to decide the level of the strategy. The different levels are essentially priorities. When a MoveScheme builds its movers, the strategies are applied in an order sorted first by level, and second by order the strategy was added to the scheme. Identifying the correct level for you strategy is by far the most complicated part of adding a move strategy, so the rest of this section will explain it.

Let’s start with the way that movers are organized with a MoveScheme. Every scheme has organizes its movers into “groups.” These groups should define the “canonical” move types (that is, the way you normally think about a move in path space: shooting, replica exchange, etc.) The process of building a move scheme goes in the following order:

  1. For each group, decide what ensembles will be involved as input and output (SIGNATURE level).

  2. Create the specific path movers for those ensembles (MOVER level).

  3. Create the groups that will organize the move scheme. (GROUP level).

  4. If necessary, change the kind of mover or reorganize the group (SUPERGROUP level).

  5. Finally, create the global organization of the move scheme (organize the groups themselves, GLOBAL level).

The differences between GROUP and SUPERGROUP are pretty flexible, and many strategies could work for either one. Here are some examples of when to use each level:

  • levels.SIGNATURE: Use this if your mover changes which ensembles are involved. For example, different replica exchange strategies include “nearest neighbor,” “all possible,” and “specific selected.” These don’t change the nature of the move, but do change which ensembles are involved.

  • levels.MOVER: Use this if the nature of the move changes, but not its fundamental purpose. For example, if you are implemented a different kind of shooting strategy (a different approach for shooting point selection, or two-way shooting instead of one-way), this would be the correct approach. It changes the movers without changing the input and output ensembles of each move.

  • levels.GROUP: Use this if you’re creating a new group of movers from movers in other groups. For example, rather than randomly selected which moves to do from a group, you might create a new group where you combine them

  • levels.SUPERGROUP: Use this if you’re

  • levels.GLOBAL: Use this to organize the global structure of

In most cases, you’ll probably be adding a new type of mover. In that case, you should use levels.MOVER. The best approach is, as always, to find an example in the code that does something similar to what you want to do.

Technically, each level is associated with an integer value, and you can add other levels between (much like Python’s logging facilities). The levels object just gives convenient access to specific values (10, 30, 50, 70, 90). However, we don’t recommend straying from those default levels unless you’re very certain that you must.