Site navigation:


1. Basic event concepts

1.1. Terminal and non-terminal events

Discrete zero-crossing events provide the basis for stopping integration of an ODE or the computation of a difference equation / map, or for simply marking times at which a defined condition is met (without stopping generation of the trajectory). The first kind of events are known as terminal, the latter are known as non-terminal.

Terminal events form the basis of transitions in hybrid dynamical systems (see HybridSystems). Non-terminal events are useful for monitoring discrete changes in your dynamics, or for setting up the numerical computation of Poincare (return) maps and symbolic dynamics from continuous flows.

1.2. Arbitrary precision

For continuous dynamical systems, events can be resolved to arbitrary precision (precise events) or only to the precision of the trajectory generator's current step-size (non-precise events). For discrete DSs the step-size inherently corresponds to the maximal event precision.

The VODE integrator uses refined integration steps to find an event precisely, whereas Dopri and Radau use interpolation to the same order as the integration method itself.

Note that the precision of event detection can only be provided in terms of a tolerance for the independent variable associated with the event (e.g., time). Event precision cannot be specified in terms of a tolerance on the magnitude of the value of the zero-crossing function associated with the event.

1.3. Activation of events

Events that have been defined for a dynamical system can be made active or inactive, which controls whether or not those events are detected during generation of a trajectory. This can be specified at the time of definition, and then changed later through the eventstruct attribute of the Generator (see below). If they are temporarily not needed, setting events to be inactive makes integration more efficient.

2. Defining events

Events are defined much in the same way as the other elements of a Generator.

The recommended manner to create events for all compatible generators is the function makeZeroCrossEvent, exported from This function takes as arguments a general string expression, a direction, a dictionary of algorithmic arguments, followed by 'declarations' of any names used in the string expression, including variables, parameters, and inputs. Auxiliary functions can be called in the string definition, but auxiliary variables may not be accessed. For instance,

ev_args_nonterm = {'name': 'monitor',
           'eventtol': 1e-4,
           'eventdelay': 1e-5,
           'starttime': 0,
           'active': True,
           'term': False,
           'precise': True}

thresh_ev_nonterm = Events.makeZeroCrossEvent('in', 0,
                        ev_args_nonterm, inputnames=['in'],

defines a non-terminal python event that monitors an external input signal in for zero crossings in either direction (given by 0 in the second argument). For all variables, parameters, inputs, or auxiliary functions used in the specification of the event, we must declare them in the argument list, using the keywords varnames, parnames, inputnames, and fnspecs for functions. fnspecs must actually be a dictionary mapping function names to their definitions. For convenience, these declarations can be taken from the original Generator-creating arguments, and can contain superfluous definitions. I.e. they do not need to be filtered to only include declarations that are used in the event definition.

The default target language is Python, so the targetlang option was not strictly necessary, but this indicates the syntax used when specifying it. The other alternative for targetlang is 'c' when using any non-Python based integrator (Dopri, Radau, CUDA, ADMC++, etc).

An older and deprecated alternative, for Python-based generators only, is makePythonStateZeroCrossEvent, which is also only able to make events for state variable threshold crossings. The sole advantage of this function is its simpler syntax, but we may phase this function out in future releases.

Events can be defined to trigger only when the event boundary is crossed in one direction or another, or in both directions. This is specified by an integer in the second argument to makeZeroCrossing, taking values -1 (decreasing), 0 (either), and 1 (increasing).

2.1. Other event parameters

Parameters not already described:

As exemplified in, if you want to be able to detect an event that is expected to occur in a single time step, then currently you have to set eventdelay=0.

2.2. High- and low-level events [advanced topic]

Users must specify which target language is being used when creating events, as this is not currently done automatically. The result is that th Event object created will be one of two sub-classes: high level events HighLevelEvent (specified only using Python code), and low level events LowLevelEvent (specified using non-Python code, for either C or Matlab language targets). For convenience, low level events keep a version of their event specified in Python code, in case a user wishes to work with the event interactively and away from an integration run.

3. Working with events

When trajectories are created, any active events that were detected will be recorded as part of that trajectory. To see which points those are, there are two options. From the trajectory directly, one is to use the 'getEventTimes' or 'getEvents' methods of the trajectory object. The generator or model also has similarly-named methods to extract the event data from the most recent integration. Trajectory methods for this:

If a trajectory is sampled into a Pointset, the pointset will also contain a record of the events detected via its labels, e.g. for some sampled pointset, pts, accessing pts.labels.by_label['Event:monitor'] will return a dictionary of pointset indices mapping to event times for an event named 'monitor'. If you are not sure which events have occurred, you can first fetch a list of valid labels with pts.labels.getLabels(). To get the state variable values at those events, you merely have to index into the pointset with the indices provided by the dictionary keys.

3.1. Changing or viewing event properties

Once defined, events are passed to the initialization call to a generator in order to be incorporated into the generator. Events are contained in the eventstruct attribute of a generator. This attribute is an instance of the EventStruct class. This structure is used to maintain and query the events associated with the generator. With it, events can be added to or deleted from the structure. However, once a generator is created, only one that uses python as its target language will reflect such changes in its behaviour. C-based generators have their events committed at compile-time. The events attribute of the eventstruct contains a dictionary of all the defined events, but the following methods provide conveninent access to them for most purposes.

Eventstruct methods:

The get methods take no arguments and all return a list of pairs (event_name, event_object) for events matching the type of event implied by the method name. The set methods all take an event name, or list of names, and a new value for those events' parameter, except for setglobalt0 which just takes a new float value for 'globalt0' (mostly for internal use).

A generator's eventstruct can be queried using its 'query' method, with any of the following keys: ['term', 'nonterm', 'active', 'notactive', 'precise', 'notprecise', 'highlevel', 'lowlevel', 'varlinked', 'notvarlinked']. query will return a list of pairs (event_name, event_object) for events matching the query key(s). Use of multiple simultaneous keys will perform logical AND in filtering the results (notice that some of them are mutually exclusive so do not make sense to combine!). Variable-linked events are for advanced use outside of direct integration, for post-processing trajectories with high level events only.

4. Simultaneous events

For continuously-defined dynamical systems it is not generic for multiple simultaneous events to occur. PyDSTool will resolve the correct order of multiple events that are flagged over a single time step, and depending on the generator will either flag an error if two events perfectly coincide or will choose an arbitrary order for them at consecutive time steps.