Direct API for Engines
The direct API works when your underlying engine has an API to directly control the dynamics. For example, there’s a Python function you can use to tell the engine to run for 10 time steps, and give you the result. The OPS direct engine API is then just a wrapper to translate between OPS and the underlying engine’s API.
Direct Engine API Overview
If your engine will use direct control, then your engine class should
DynamicsEngine. More documentation for the methods
discussed below can be found in the documentation for that class.
Engines take an
options dictionary to provide details of how they run.
All direct control engines need to be able to use the options:
n_steps_per_frame: Number of internal time steps per saved frame (snapshot).
n_frames_max: Maximum number of snapshots allowed in a trajectory before the simulation stops.
You will need to implement these methods:
@property): Getter and setter for the current snapshot. We assume that the current state of the system (e.g., positions and velocities) exists inside your engine; this is the code for translating that internal representation to/from the OPS representation as a
generate_next_frame(): Takes the (previously set)
current_snapshotand generates the next saved frame, i.e., it performs
n_steps_per_frameindividual time steps.
Optionally, you can override several other methods:
start(snapshot=None): Run before each trajectory.
stop(trajectory): Run after each trajectory ends.
generate_n_frames(n_frames=1): Generate a fixed number of frames at once; this can be used for performance in some cases. By default, this just calls
generate_next_frame, but calls it
is_valid_snapshot(snapshot): Check whether the snapshot is physically valid (can be used to check types, look for NaNs, etc.)
Note that you are likely to also need to override the
from_dict methods; see the storage documentation for more details.
How the Direct Engine API Runs
This subsection gives an overview of what order the methods that make up the direct engine API are called in the course of a trajectory. A simulation can consist of multiple trajectories, so
Start of simulation: Code that needs to be run before performing any dynamics should be part of the standard
__init__of your engine subclass.
Start of dynamics: Before a specific dynamics run, we (1) set the snapshot with
engine.current_snapshot = snapshot, using the current snapshot property’s setter; and (2) call
During dynamics: During the dynamics, we call
generate_n_frames), which will typically call the getter for
engine.current_snapshotinternally (to return the snapshot). The logic within OPS determines whether to ask for more frames from the engine.
After dynamics: When the OPS logic says that no more frames are required from the engine, we call
trajectoryis the trajectory we have created. This is a good place to do any per-trajectory clean-up that you need, but may simply be a no-op for many direct API engines.
Direct Engine API Step-by-Step
Or: “So you want to make a direct API engine?”
Direct API engines are generally straightforward.
Start by determining what
featuresyour subclass of
Snapshotwill require. Define the snapshot class. See Snapshot Features for details.
Create the class, and set up its intialization. You will need to:
Add any options that you use should have some, with appropriate default values, in the
_default_optionsclass variable (a dictionary).
SnapshotDescriptorinstance for your engine instance (and pass it to the
Write required methods:
current_snapshot(getter and setter) and
from_dict(), if needed. See documentation on storage for more.
If the optional methods are needed for your engine, write them.