The simulator

At the heart of Turberfield is a simulator. It keeps the world ticking along. It looks for messages which are due for attention and dispatches them to where they are to be handled.

The main job of the simulator is to run tasks. These tasks are supplied by you.

Here’s how you would set up and run a simulation:

# The asyncio module is standard in Python 3.4 and above
loop = asyncio.get_event_loop()

# Create a generator for the simulation 'tick-stream'
ticks = Simulation.ticks(0, 10, 1)

# More about the JobQueue later...
jq = JobQueue(loop=loop)

# Simulation will run only a single task; 'agent', defined below.
tasks = [Simulation.task(loop, agent, jq)]

# Initialise the simulation object
sim = Simulation(tasks, jq)

# Run the simulation until it finishes or times out.
loop.run_until_complete(asyncio.wait_for(
    sim.dispatch(ticks), loop=loop, timeout=1))

What is a task? It’s a function or method which listens for the passage of time and which can ask for things to happen in the future. Here’s what the task agent might look like:

@asyncio.coroutine
def agent(q, jq, priority):
    tick = None
    while type(tick) is not Stop:
        tick = yield from q.get()
        # Do stuff...
        msg = "For the attention of..."
        yield from jq.put(tick.t + 1, priority, msg)
    else:
        return tick

Agent is a coroutine. To work with the Turberfield simulator, it must have these three positional parameters; q, jq, and priority:

q
Your task gets its ticks from this object.
jq
This is a JobQueue which your task uses to post messages.
priority
This is assigned to your task by the Turberfield simulator as a unique integer, thereby eliminating contention with other coroutines.

Job Queue

class turberfield.dynamics.jobqueue.JobQueue(loop)[source]

You create a JobQueue just as you would a standard Queue from the asyncio module.

put(t, priority, msg)[source]

Place a message on the queue, to be processed at time t with priority priority.

Ticks

class turberfield.dynamics.types.Tick

A named tuple of (‘t’, ‘priority’) to specify the time and priority order of a message.

class turberfield.dynamics.types.Stop

A special case of Tick which terminates a time sequence.

Messages

You can send any objects you like to the job queue. To define how they are processed when their time arrives, you should register a handler according to their type.

turberfield.dynamics.simulation.message_handler(msg, *args, **kwargs)[source]

The simulation calls this function, passing to it every message which pops up in the job queue.

This function has been wrapped with the functools.singledispatch decorator.

So, to register your own supplied function as a handler for a particular class of message, make a call like the following:

message_handler.register(type(msg), custom_handler)

Simulation

class turberfield.dynamics.simulation.Simulation(tasks, jq=None, buf=None)[source]

The simulator in Turberfield is a shareable object. You need to define it once in your main module:

sim = Simulation(tasks, jq, buf)

Elsewhere, you can grab a readable copy like this:

sim = Simulation(None)
tasks
A list of task objects. Each should be the result of a call to Simulation.task.
jq (optional)
A JobQueue object. The simulation will get its messages here.
buf (optional)
A container which will record jobs as they are processed by the simulation. This can be useful for debugging purposes. If you omit this argument, the Simulation object will create its own (small) buffer.
dispatch(ticks)[source]

The coroutine which drives the simulation loop.

ticks
An iterable of Ticks which ends with a Stop.
static task(loop, fn, jq, priority=1, *args, **kwargs)[source]

Create a asyncio.Task for fn (which must be a coroutine). The returned object is decorated with attributes necessary for use with a Simulation object. jq should be a JobQueue object common to all the tasks in your simulation.

static ticks(start, end, interval)[source]

Returns a generator of Ticks which will end with a Stop.