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():
...