Clocks

Clocks schedule and execute actions, computations that happen as events at specified times (or under specified conditions).

Virtual Clocks

A Clock is not bound to physical time and executes an event sequence as fast as possible by jumping from event to event.

DiscreteEvents.ClockType
Clock{AC} <: AbstractClock

A virtual clock structure, used for global and thread local clocks.

Fields

  • id::Int: clock ident number 1: master clock, > 1: parallel clock,
  • ac::AC: if id == 1: a Vector{ClockChannel} else: Ref{ActiveClock},
  • state::ClockState: clock state,
  • time::Float64: clock time,
  • unit::FreeUnits: time unit,
  • end_time::Float64: end time for simulation,
  • Δt::Float64: sampling time, timestep between ticks,
  • sc::Schedule: the clock Schedule (events, cond events and sampling),
  • processes::Dict{Any, Prc}: registered Prces,
  • channels::Vector{Channel}: registered (Actor) channels,
  • tn::Float64: next timestep,
  • tev::Float64: next event time,
  • evcount::Int: event counter,
  • scount::Int: sample counter
DiscreteEvents.ClockMethod
Clock(Δt::T=0.01; t0::U=0, unit::FreeUnits=NoUnits) where {T<:Number,U<:Number}

Create a new virtual clock.

Arguments

  • Δt::T=0.01: time increment for sampling. Δt can be set later with sample_time!.
  • t0::U=0: start time for simulation
  • unit::FreeUnits=NoUnits: clock time unit. Units can be set explicitely by setting e.g. unit=minute or implicitly by giving Δt as a time or else setting t0 to a time, e.g. t0=60s.

The created clock has id==1 (master).

You can create clocks easily:

julia> using DiscreteEvents, Unitful, .Threads

julia> import Unitful: s, minute, hr

julia> c = Clock()                  # create a unitless clock (standard)
Clock 1: state=:idle, t=0.0, Δt=0.01, prc:0
  scheduled ev:0, cev:0, sampl:0

julia> c1 = Clock(1s, unit=minute)  # create a clock with unit [minute]
Clock 1: state=:idle, t=0.0minute, Δt=0.01667minute, prc:0
  scheduled ev:0, cev:0, sampl:0

julia> c2 = Clock(1s)               # create a clock with implicit unit [s]
Clock 1: state=:idle, t=0.0s, Δt=1.0s, prc:0
  scheduled ev:0, cev:0, sampl:0

julia> c3 = Clock(t0=60s)           # another clock with implicit unit [s]
Clock 1: state=:idle, t=60.0s, Δt=0.01s, prc:0
  scheduled ev:0, cev:0, sampl:0

julia> c4 = Clock(1s, t0=1hr)       # here Δt's unit [s] takes precedence
Clock 1: state=:idle, t=3600.0s, Δt=1.0s, prc:0
  scheduled ev:0, cev:0, sampl:0

There is a default clock 𝐶 for experimental work:

DiscreteEvents.𝐶Constant
𝐶

𝐶 (𝐶 = \itC+tab) is a default clock, ready to use.

Examples

julia> using DiscreteEvents

julia> resetClock!(𝐶)
"clock reset to t₀=0.0, sampling rate Δt=0.01."

julia> 𝐶  # default clock
Clock 1: state=:idle, t=0.0, Δt=0.01, prc:0
  scheduled ev:0, cev:0, sampl:0

You can query the current clock time:

DiscreteEvents.tauFunction
tau(clk::Clock=𝐶)

Return the current simulation time.

Examples

julia> using DiscreteEvents

julia> resetClock!(𝐶)   # reset the default clock
"clock reset to t₀=0.0, sampling rate Δt=0.01."
julia> tau()            # gives the default clock's time
0.0

Real Time Clocks (Experimental)

Real time clocks are a new feature in v0.3 and thus cannot yet be considered as stable. Please try and report problems.

RTClocks schedule and execute actions on a real (system) time line.

DiscreteEvents.RTClockType
RTClock{E <: ClockEvent} <: AbstractClock

A real time clock checks every given period for scheduled events and executes them. It has a time in seconds since its start or last reset and uses system time for updating.

Real time clocks are controlled over channels. Multiple real time clocks can be setup with arbitrary periods (≥ 1 ms). Real time clocks should not be created directly but rather with CreateRTClock.

Fields

  • Timer::Timer: clock period in seconds, minimum is 0.001 (1 ms)
  • clock::Clock: clock work
  • cmd::Channel{T}: command channel to asynchronous clock
  • back::Channel{T}: back channel from async clock
  • id::Int: arbitrary id number
  • thread::Int: thread the async clock is living in
  • time::Float64: clock time since start in seconds
  • t0::Float64: system time at clock start in seconds
  • T::Float64: clock period in seconds
DiscreteEvents.createRTClockFunction
createRTClock(T::Float64, id::Int, thrd::Int=nthreads(); ch_size::Int=256)

Create, start and return a real time Clock.

The clock takes the current system time and starts to count in seconds with the given period T. Events or sampling functions can then be scheduled to it.

Arguments

  • T::Float64: period (clock resolution) in seconds, T ≥ 0.001
  • id::Int: clock identification number other than 0:nthreads()
  • thrd::Int=nthreads(): thread, the clock task should run in
  • ch_size::Int=256: clock communication channel size

You can work with real time clocks easily:

julia> rtc = createRTClock(0.01, 99)     # create a real time clock
RTClock 99 on thread 1: state=:idle, t=0.2489 s, T=0.01 s, prc:0
   scheduled ev:0, cev:0, sampl:0

julia> sleep(1)

julia> tau(rtc)                          # query its time after a sleep
1.2774972207844257

julia> a = [1]                           # create a mutable variable
1-element Vector{Int64}:
 1

julia> f(x) = x[1] += 1                  # an incrementing function
f (generic function with 1 method)

julia> event!(rtc, fun(f, a), every, 1)  # increment now and then every second
DiscreteEvents.Register{DiscreteEvents.DiscreteEvent{DiscreteEvents.var"#9#10"{Nothing, typeof(Main.var"ex-clocks".f), Tuple{Vector{Int64}}}, Float64}}(DiscreteEvents.DiscreteEvent{DiscreteEvents.var"#9#10"{Nothing, typeof(Main.var"ex-clocks".f), Tuple{Vector{Int64}}}, Float64}(DiscreteEvents.var"#9#10"{Nothing, typeof(Main.var"ex-clocks".f), Tuple{Vector{Int64}}}(nothing, Main.var"ex-clocks".f, ([1],)), 1.4505343697965145, 1.0, 9223372036854775807))

julia> sleep(3)                          # sleep 3 seconds

julia> a[1]                              # query a
1

julia> stopRTClock(rtc)                  # stop the clock
DiscreteEvents.Stop()

Clock Operation

Virtual clocks can be run, stopped or stepped through and thereby used to simulate chains of events.

DiscreteEvents.run!Function
run!(clk::Clock, duration::N) where {N<:Number}

Run a simulation for a given duration.

DiscreteEvents.incr!Function
incr!(clk::Clock)

Take one simulation step, execute the next tick or event.

DiscreteEvents.resetClock!Function
resetClock!(clk::Clock, Δt::T=0.01; t0::U=0; <keyword arguments>) where {T<:Number, U<:Number}

Reset a clock.

Arguments

  • clk::Clock
  • Δt::T=0.01: sample rate
  • t0::Float64=0 or t0::Time: start time
  • hard::Bool=true: time is reset, all scheduled events and sampling are deleted. If hard=false, then only time is reset, event and sampling times are adjusted accordingly.
  • unit=NoUnits: the Time unit for the clock after reset. If a Δt::Time is given, its Time unit goes into the clock Time unit. If only t0::Time is given, its Time unit goes into the clock time unit.

Examples

julia> using DiscreteEvents, Unitful

julia> import Unitful: s

julia> c = Clock(1s, t0=60s)
Clock 1: state=:idle, t=60.0s, Δt=1.0s, prc:0
  scheduled ev:0, cev:0, sampl:0

julia> resetClock!(c)
"clock reset to t₀=0.0, sampling rate Δt=0.01."

julia> c
Clock 1: state=:idle, t=0.0, Δt=0.01, prc:0
  scheduled ev:0, cev:0, sampl:0
resetClock!(rtc::RTClock)

Reset a real time clock. Set its time to zero and delete all scheduled and sampling events.

DiscreteEvents.sync!Function
sync!(clk::Clock, to::Clock=𝐶)

Force a synchronization of two clocks. Change all registered times of clk accordingly. Convert or force clk.unit to to.unit.

Time Units

You can set time units of a virtual clock:

DiscreteEvents.setUnit!Function
setUnit!(clk::Clock, new::FreeUnits)

set a clock to a new time unit in Unitful. If necessary convert current clock times to the new unit.

Arguments

  • clk::Clock
  • new::FreeUnits: new is one of ms, s, minute or hr or another Unitful Time unit.

Examples

julia> using DiscreteEvents, Unitful

julia> import Unitful: Time, s, minute, hr

julia> c = Clock(t0=60)     # setup a new clock with t0=60
Clock 1: state=:idle, t=60.0, Δt=0.01, prc:0
  scheduled ev:0, cev:0, sampl:0

julia> tau(c)               # current time is 60.0 NoUnits
60.0

julia> setUnit!(c, s)       # set clock unit to Unitful.s
60.0 s

julia> tau(c)               # current time is now 60.0 s
60.0 s

julia> setUnit!(c, minute)  # set clock unit to Unitful.minute
1.0 minute

julia> tau(c)               # current time is now 1.0 minute
1.0 minute

julia> isa(tau(c), Time)
true

julia> uconvert(s, tau(c))  # ... which can be converted to other time units
60.0 s

julia> tau(c).val           # it has a value of 1.0
1.0

julia> c.time               # internal clock time is set to 1.0 (a Float64)
1.0

julia> c.unit               # internal clock unit is set to Unitful.minute
minute
Note

This is not yet implemented for parallel clocks!