Clocks

Brian is a clock-based simulator: operations are done synchronously at each tick of a clock.

Many Brian objects store a clock object, passed in the initialiser with the optional keyword clock. For example, to simulate a neuron group with time step dt=1 ms:

myclock=Clock(dt=1*ms)
group=NeuronGroup(100,model='dx/dt=1*mV/ms : volt',clock=myclock)

If no clock is specified, the program uses the global default clock. When Brian is initially imported, this is the object defaultclock, and it has a default time step of 0.1 ms. In a simple script, you can override this by writing (for example):

defaultclock.dt = 1*ms

You may wish to use multiple clocks in your program. In this case, for each object which requires one, you have to pass a copy of its Clock object. The network run function automatically handles objects with different clocks, updating them all at the appropriate time according to their time steps (value of dt).

Multiple clocks can be useful, for example, for defining a simulation that runs with a very small dt, but with some computationally expensive operation running at a lower frequency. In the following example, the model is simulated with dt=0.01 ms and the variable x is recorded every ms:

simulation_clock=Clock(dt=0.01*ms)
record_clock=Clock(dt=1*ms)
group=NeuronGroup(100,model='dx/dt=-x/tau : volt',clock=simulation_clock)
M=StateMonitor(group,'x',record='True',clock=record_clock)

The current time of a clock is stored in the attribute t (simulation_clock.t) and the timestep is stored in the attribute dt.

When using multiple clocks, it can be important to specify the order in which they evaluated, which you can using the order keyword of the Clock object, e.g.:

clock_first = Clock(dt=1*ms, order=0)
clock_second = Clock(dt=5*ms, order=1)

Every 5ms, these two clocks will coincide, and the order attribute means that clock_first will always be evaluated before clock_second.

Other clocks

The default clock uses an underlying integer representation. This behaviour was changed in Brian 1.3 from earlier versions which used a float representation. To recover the earlier behaviour if it is important, you can use FloatClock or NaiveClock.

You may want to have events that happen at regular times, but still want to use the default clock for all other objects, in which case you can use the EventClock for a network_operation() and it will not create any clock ambiguities, e.g.:

from brian import *

...

G = NeuronGroup(N, eqs, ...)

...

@network_operation(clock=EventClock(dt=1*second))
def do_something():
   ...