Simulus API Reference

The API Reference provides detailed descriptions of simulus classes and functions.

simulus.trappable

class simulus.trappable.Trappable(sim)

Bases: object

The base class for all trappables.

This class defines an abstract interface; all subclasses must consider overwrite the methods definded here. These methods are expected to be invoked from the simulator.wait() function.

simulus.trap

class simulus.trap.Trap(sim)

Bases: simulus.trappable.Trappable

A one-time signaling mechanism for inter-process communication.

Trap is one of the two primitive methods in simulus designed for simulation processes to synchronize and communicate with one another. (The other primitive method is using semaphores.)

A trap has three states. It’s “unset” when the trap is first created and and nothing has happended to it. It’s “set” when one or more processes are waiting for the trap to be triggered. It turns to “sprung” when the trap has been triggered, after which there will be no more processes waiting for the trap.

The life of a trap is described below. A trap starts with the “unset” state when it’s created. When a process waits for the trap, the trap goes to “set”, at which state other processes may wait on the trap while the trap remains in the same state. When a process triggers the trap and if the trap is in the “set” state, all processes waiting on the trap will be unblocked and resume execution (it’s guaranteed there is at least one waiting process when the trap is in the “set” state). The trap will then transition into the “sprung” state. When a process triggers the trap which is in the “unset” state, the trap will just transition to the “sprung” state (since there are no processes currently waiting on the trap). If the trap is “sprung”, further waiting on trap will be considered as an no-op; that is, the processes will not be suspended. A trap must not be triggered more than once.

TRAP_UNSET = 0
TRAP_SET = 1
TRAP_SPRUNG = 2
__init__(sim)

A trap can only be created using simulator’s trap() function; it starts from “unset” state and there are no waiting processes.

wait()

A process waits on the trap.

trigger()

Triggering a trap would unblock all waiting processes.

simulus.semaphore

class simulus.semaphore.Semaphore(sim, initval, qdis)

Bases: simulus.trappable.Trappable

A multi-use signaling mechanism for inter-process communication.

A semaphore is one of the two primitive methods in simulus designed for simulation processes to synchronize and communicate with one another. (The other primitive method is using traps.)

A semaphore here implements what is commonly called a “counting semaphore.” Initially, a semaphore can have a nonnegative integer count, which indicates the number of available resources. The processes atomically increment the semaphore count when resources are added or returned to the pool (using the signal() method) and atomically decrement the semaphore count when resources are removed (using the wait() method). When the semaphore count is zero, it means that there are no available resources. In that case, a process trying to decrement the semaphore (to remove a resource) will be blocked until more resources are added back to the pool. Since semaphores need not be incremented and decremented by the same process, they can be used as a signaling mechanism for inter-process communication.

A semaphore is different from a trap. A trap is a one-time signaling mechanism. Multiple processes can wait on a trap. Once a process triggers the trap, all waiting processes will be unblocked. A trap cannot be reused: once a trap is sprung, subsequent waits will not block the processes and it cannot be triggered again. In comparison, a semaphore is a multi-use signaling mechanism. Each time a process waits on a semaphore, the semaphore value will be decremented. If the value becomes negative, the process will be blocked. Each time one signals a semaphore, the semaphore value will be incremented. If there are blocked processes, one of these processes will be unblocked. Processes can use the same semaphore repeatedly.

By default, we use FIFO order (that is, first in first out) to unblock processes if multiple processes are waiting on a semaphore. Other possible queuing disciplines include LIFO (last in first out), SIRO (service in random order), and PRIORITY (depending on the priority of the processes; a lower value means higher priority). One can choose a queuing discipline when the semaphore is created.

__init__(sim, initval, qdis)

A semaphore can only be created using simulator’s semaphore() function; one can set the initial value (must be nonnegative), as well as one of the four queuing disciplines of the semaphore.

wait()

Waiting on a semphore will decrement its value; and if it becomes negative, the process needs to be blocked.

signal()

Signaling a semphore increments its value; and if there are waiting processes, one of them will be unblocked.

trigger()

Signaling a semphore increments its value; and if there are waiting processes, one of them will be unblocked.

simulus.resource

class simulus.resource.Resource(sim, name, capacity, qdis, dc)

Bases: simulus.trappable.Trappable

A resource provides services to processes.

A resource basically models a single-server or multi-server queue. A resource can allow only a limited number of processes to be serviced at any given time. A process arrives and acquires a server at the resource. If there is an available server, the process will gain access to the resource for as long as the service is required. If there isn’t an available server, the process will be put on hold waiting placed in a queue. When another process has finished and released the server, one of the waiting processes will be unblocked and gain access to the resource.

A process is expected to following the expected sequence of actions to use the resource. The process first calls the acquire() method to gain access to a server; this is potentially a blocking call: the process may be blocked until a server can be assigned to it. Once the call returns, the process has acquired the resource. The process can use the resource for as long as it needs to; this is usually modeled using the sleep() method. Afterwards, the same process is expected to call the release() method to free the resource, so that another waiting process may have a chance to gain access to the resource.

__init__(sim, name, capacity, qdis, dc)

A resource is created using simulator’s resource() function; a resource can have an optional name, a capacity (which must be a postive integer indicating the number of servers at the resource), a queuing discipline, and also an optional data collector for statistics.

acquire()

Acquire a server from the resource.

This method will atomically decrementing a semaphore value (indicating the number of servers). The calling process may be blocked if no more servers are available.

release()

Relinquish the resource acquired previously.

Note that acquire() and release() are expected in pairs, and they should be called by the same process.

num_in_system()

Return the number of processes currently using or waiting to use the resource.

num_in_service()

Return the number of processes currently using the resource. It’s a number between zero and the number of servers (the capacity).

num_in_queue()

Return the number of processes wating to use the resource.

simulus.store

class simulus.store.Store(sim, capacity, initlevel, initobj, name, p_qdis, c_qdis, dc)

Bases: object

A store for synchronizing producer and consumer processes.

A store is a facility either for storing countable objects (such as jobs in a queue, packets in a network router, and io requests at a storage device). It’s similar to a bucket, except that it’s for countable objects.

A store has a maximum capacity, which is a positive quantity specified as an integer. A store can also tell its current storage level, which goes between zero and the maximum capacity.

One or several processes can put objects into the store. They are called producer processes. One or several processes can get objects from the store. They are called consumer processes. The producer process and the consumer process is determined by its performed action on the store. They can be the same process.

A producer process calls the put() method to deposit one or more objects into the store. The put amount shall be specified as an argument (default is one). The current storage level will increase accordingly as a result. However, if a producer process tries to put more objects than the store’s capacity, the producer process will be blocked. The process will remain blocked until the current storage level decreases (by some other processes getting objects from the store) so that there is room for putting all the objects as a result.

Similarly, a consumer process calls the get() method to retrieve one or more objects from the store. The get amount shall be specified as an argument (default is one). The current storage level will decrease accordingly as a result. If a consumer process tries to get more objects than what is avaialble at the store, the consumer process will be blocked. The process will remain blocked until the current storage level goes above the requested amount (by some other processes putting objects into the store).

The store facility can actually be used for storing real (countable) Python objects, if the user calls the put() method and passes in a Python object or a list/tuple of Python objects using the keyworded ‘obj’ argument. In this case, the put amount must match with the number of objects. The stored objects can be retrieved in a first-in-first-out fashion by consumer processes calling the get() method, which specifies the get amount. The same number of Python objects will be returned, either as a list if the get amount is greater than one, or as the object itself if the get amount is one.

__init__(sim, capacity, initlevel, initobj, name, p_qdis, c_qdis, dc)

A store should be created using simulator’s store() function. A store has a capacity (must be positive), an initial level, optional initial jobs, an optional name, a queuing discipline, and a DataCollector instance for statistics collection.

get(amt=1)

Retrieve objects or quantities from the store.

Parameters

amt (int) – the number of objects to be retrieved all at once (default is one)

Returns

This method returns none if no Python objects are stored. Otherwise, if ‘amt’ is one, this method returns the object that was first put into the store; if the ‘amt’ is more than one, this method returns the ‘amt’ number of objects in a list. The objects are stored first in and first out.

put(amt=1, *, obj=None)

Deposit objects or quantities to the store.

Parameters
  • amt (int) – the number of objects to be deposited all at once (default is one)

  • obj (object) – the python object or a list/tuple of python objects to be deposited to the store; this is optional; however, if provided, this has to be a keyworded argument, i.e., user must use the ‘obj’ keyword if providing the object(s) after all

This method does not return a value.

getter(amt=1)

Return a trappable for getting objects or quantities from the store. This function is similar to the get() method, except that it returns a trappable (like traps, semaphores, and resources) on which one can apply conditional wait using the simulator’s wait() function.

putter(amt=1, *, obj=None)

Return a trappable for putting objects or quantities to the store. This function is similar to the put() method, except that it returns a trappable (like traps, semaphores, and resources) on which one can apply conditional wait using the simulator’s wait() function.

getters_in_queue()
putters_in_queue()

simulus.mailbox

class simulus.mailbox.Mailbox(sim, nparts, min_delay, name, dc)

Bases: object

A mailbox for deliverying messages.

A mailbox is a facility designed specifically for message passing between processes or functions. A mailbox consists of one or more compartments or partitions. A sender can send a message to one of the partitions of the mailbox (which, by default, is partition 0) with a time delay. The message will be delivered to the designated mailbox partition at the expected time. Messages arriving at a mailbox will be stored in the individual partitions until a receiver retrieves them and removes them from the mailbox.

In Python, the concept of messages (or mails in the sense of a mailbox) takes a broader meaning. Basically, they could be any Python objects. And since Python is a dynamically-typed language, one can also use objects of different types as messages.

A mailbox is designed to be used for both process scheduling (in process-oriented simulation) and direct-event scheduling (for event-driven simulation). In the former, a process can send as many messages to a mailbox as it needs to (by repeatedly calling the mailbox’s send() method). The simulation time does not advance since send() does not block. As a matter of fact, one does not need to call send() in a process context at all. In this sense, a mailbox can be considered as a store with an infinite storage capacity. An important difference, however, is that one can send messages to mailboxes and specify a different delay each time.

One or more processes can call the mailbox’s recv() method trying to receive the messages arrived at a mailbox partition. If there are one or more messages already stored at the mailbox partition, the process will retrieve the messages from the partition and return them (in a list) without delay. Here, “retrieve” means the messages indeed will be removed from the mailbox partition. In doing so, subsequent calls to recv() will no longer find the retrieved messages in the mailbox partition. If there are no messages currently in the mailbox partition when calling recv(), the processes will be suspended pending on the arrival of a new message to the designated mailbox partition. Once a message arrives, all waiting processes at the partition will be unblocked and return with the retrieved message.

There are two ways for the recv() method to retrieve the messages. One is to retrieve all messages from the mailbox partition; the other is to retrieve only the first one stored in the mailbox partition. The user can specify which behavior is desirable by setting the ‘isall’ parameter when calling the recv() method. Regardless of the process trying to receive all messages or just one message, if there are multiple processes waiting to receive at a mailbox partition, it is possible a process wakes up upon a message’s arrival and return empty handed (because another process may have taken the message from the mailbox). Simulus do not dictate, when multiple processes are waiting to receive a message, which one will wake up first to retrieve the messages. It’d be arbitrary since simulus handles simultaneous events without model specified ordering.

A mailbox can also be used in the direct-event scheduling context (event-driven approach). In this case, the user can add one or more callback functions to a mailbox partition. A callback function can be any user-defined function. Whenever a message is delivered to a mailbox partition, all callback functions attached to the mailbox partition will be invoked.

Within a callback function, the user has the option to either peek at the mailbox or retrieve (or do nothing of the kind, of course). One can call the peek() method to just look at the messages arrived at a mailbox partition. In this case, a list is returned containing all stored messages in the partition. The messages are not removed from the mailbox. One can also also call the retrieve() method. In this case, the user is given the option to retrieve just one of the messages or all messages. Again, “retrieve” means to remove the message or messages from the mailbox. Like recv(), the user passes in a boolean ‘isall’ argument to indicate which behavior is desirable.

__init__(sim, nparts, min_delay, name, dc)

A mailbox should be created using simulator’s mailbox() function. A mailbox has a number of compartments or partitions, a minimum delay, a name, and DataCollector instance for statistics collection.

send(msg, delay=None, part=0)

Send a message to a mailbox partition.

Parameters
  • msg (object) – a message can be any Python object; but it cannot be None

  • delay (float) – the delay after which the message is expected to be delivered to the mailbox; if it is ignored, the delay will be set to be the min_delay of the mailbox (which is by default zero); if it is set, the delay value must not be smaller than the min_delay of the mailbox

  • part (int) – the partition number of the mailbox to which the message is expected to be delivered; the default is zero

Returns

This method returns the future event scheduled for the message delivery. It’s an opaque object to the user, but the user can use it to cancel or reschedule the event.

recv(part=0, isall=True)

Receive messages from a mailbox partition.

This method must be called within a process context (in a starting function of a process or at a function called directly or indirectly from the starting function). If the mailbox partition is empty when the call is made, the process will be put on hold until a message arrives.

Parameters
  • part (int) – the partition number of the mailbox from which messages are expected to be received

  • isall (boolean) – if True (default), this method will retrieve all messages from the mailbox and return them; if False, this method will only retrieve the first arrived message

Returns

This method returns a list containing all the messages currently stored at the mailbox partition, if ‘isall’ is True (by default). On the other hand, if ‘isall’ is False, this method returns only the first arrived message (not wrapped in a list).

receiver(part=0, isall=True)

Return a trappable for receiving messages from the mailbox. This method is similar to the recv() method, except that it returns a trappable on which one can apply conditional wait using the simulator’s wait() function.

add_callback(func, *args, part=0, **kwargs)

Add a callback function to a mailbox partition.

Parameters
  • func (function) – the callback function, which can be an arbitrary user-defined function

  • args (list) – the positional arguments as a list to be passed to the callback function when it’s invoked upon the message arrival

  • part (int) – the partition number of the mailbox from which messages are expected to be received

  • kwargs (dict) – the keyworded arguments as a dictionary to be passed to the callback function when it’s invoked upon the message arrival

peek(part=0)

Return the messages currently stored in a mailbox partition; the messages are not removed.

Parameters

part (int) – the partition number of the mailbox

Returns

This method returns a list containing all the messages currently stored at the designed partition of the mailbox

retrieve(part=0, isall=True)

Retrieve messages from a mailbox partition.

Parameters
  • part (int) – the partition number of the mailbox from which messages are retrieved; the default is zero

  • isall (boolean) – if True (default), this method will retrieve all messages from the mailbox and return them; if False, this method will only retrieve the first arrived message

Returns

This method returns a list containing all the messages currently stored at the designated mailbox partition, if ‘isall’ is True (by default). If the partition is empty, however, this method returns an empty list. On the other hand, if ‘isall’ is False, this method returns only the first arrived message (not wrapped in a list). If the partition is empty in this case, this method would return None.

simulus.simulator

class simulus.simulator.simulator(name=None, init_time=0)

Bases: object

A simulator instance.

Each simulator instance has an independent timeline (i.e., event list) on which events are scheduled and executed in timestamp order. In parallel simulation, each simulator instance is also known as a logical process (LP).

Simulus supports multiple simulators to run simultaneously. Each simulator can run separately, or can be synchronized and run collectively as part of a parallel discrete-event simulation.

Each simulator can have an optional name, which however must be globally unique. Or, it can remain anonymous. Each simulator maintains an event list and a simulation clock.

__init__(name=None, init_time=0)

Create a simulator.

One can repeatedly create as many simulators as needed. A simulator maintains its own event list (along with all scheduled functions and processes) and keeps track of the simulation time.

Parameters
  • name (string) – a name of the simulator; if ignored, the system will generate a unique name for the simulator; if specified, the name needs to be unique so that the name can be used to retrieve the corresponding simulator instance; the name is also used to determine the seed of the pseudo-random generator attached to the simulator

  • init_time (float) – the optional start time of the simulator; if unspecified, the default is zero

Returns

This function returns the newly created simulator.

sched(func, *args, offset=None, until=None, name=None, repeat_intv=None, **kwargs)

Schedule an event.

An event in simulus is represented as a function invoked in the simulated future.

Parameters
  • func (function) – the event handler, which is a user-defined function

  • args (list) – the positional arguments as a list to be passed to the scheduled function (the event handler) once the function is invoked at the scheduled time

  • offset (float) – relative time from now at which the event is scheduled to happen; if provided, must be a non-negative value

  • until (float) – the absolute time at which the event is scheduled to happen; if provided, it must not be earlier than the current time; note that either ‘offset’ or ‘until’ can be used, but not both; if both are ignored, it’s assumed to be the current time

  • name (string) – an optional name for the event

  • repeat_intv (float) – if provided, the event will be repeated with the given time interval; the interval must be a strictly postive value

  • kwargs (dict) – the keyworded arguments as a dictionary to be passed to the scheduled function (the event handler), once the function is invoked at the scheduled time

Returns

This method returns a direct scheduling event (which is an opaque object to the user), with which the user can cancel the event, or reschedule the event, or apply conditional wait on the event if needed

cancel(o)

Cancel a scheduled event or kill a process.

This method takes one argument, which is the return value from sched() or process(). In either case, it’s an opaque object to the user, which can be either an event or process. If it’s an event, when cancelled, the previously scheduled function will no longer be invoked at the expected time. Note that the method has no effect if the event that has already happened. If the argument is a process, it’s the same as to kill the process using the kill() method.

resched(e, offset=None, until=None)

Reschedule an event.

One can change the time of a scheduled event using this method. When rescheduled, the previously scheduled function will be invoked at the new designated time. If the event already happens, this method would have no effect.

This method takes at least one argument, which is the return value from sched(). Additionally, one can either provide an ‘offset’ time from now or an absolute time ‘until’, but not both. If both ‘offset’ and ‘until’ are ignored, the rescheduled event is for the current time. The time should never be earlier than the current time.

This method returns the same event upon having successfully rescheduled the event. Otherwise, it returns None.

process(proc, *args, offset=None, until=None, name=None, prio=0, prio_args=None, **kwargs)

Create a process and schedule its execution.

A process is a separate thread of control. During its execution, a process can sleep for some time, or wait for certain conditions to become true (on a trap or semaphore or others). In any case, the process can be suspended and the simulation time may advance until it resumes execution.

This method creates a process and schedule for the process to run (from a starting function) in the simulated future (including now).

Parameters
  • proc (function) – the starting function of the process, which can be an arbitrary user-defined function.

  • args (list) – the positional arguments as a list to be passed to the starting function when the process begins at the scheduled time

  • offset (float) – relative time from now at which the process is expected to start running; if provided, it must be a non-negative value (zero is OK)

  • until (float) – the absolute time at which the process is expected to start running; if provided, it must not be earlier than the current time; note that either ‘offset’ or ‘until’ can be used, but not both; if both are ignored, it is assumed to be the current time

  • name (string) – an optional name for the process

  • prio – the priority of the process (which is default to be zero). A priority can be any numerical value: a lower value means higher priority. The argument here can also be a function, which will be invoked when needed by the system to prioritize waiting processes (e.g., when qdis is set to be QDIS.PRIORITY for a resource or facility). If it is indeed a fucntion, the function may take a list of arguments (provided by prio_args) and should return a numerical value

  • prio_args – the user-defined arguments to the function specifed by prio; if provided, the arguments must be placed inside a list

  • kwargs (dict) – the keyworded arguments as a dictionary to be passed to the starting function when the process begins at the scheduled time

Returns

This method returns the process being created (it’s an opaque object to the user); the user can use it to check whether the process is terminated, to join the process (i.e., to wait for its termination), or even to explicitly kill the process.

cur_process()

Return the current running process, or None if we are not in a process context.

terminated(p)

Check whether the given process has terminated.

kill(p=None)

Kill a process.

The process to be killed should be provided as the only argument. If it’s ignored, it’s assumed to be the current process, which means the process is trying to kill itself. In this case, this method must be called within a process context (not in an event handler or in the main function).

get_priority(p=None)

Get the priority of a process.

A process should be provided as the only argument. If it’s ignored, it’s assumed to be the current process.

set_priority(prio, prio_args=None, p=None)

Set the priority of a process.

Parameters
  • prio – the new priority of the process. A priority can be any numerical value: a lower value means higher priority. The argument here can be a function, which is invoked when needed by the system to prioritize waiting processes (e.g., when qdis is set to be QDIS.PRIORITY for a resource or facility). If it is indeed a fucntion, the function may take a list of arguments (provided by prio_args) and should return a numerical value

  • prio_args – the user-defined arguments to the function specifed by prio; if provided, the arguments must be placed inside a list

  • p – the process to change priority; if ignored, it’s assumed to be the current process

sleep(offset=None, until=None)

A process blocks for a certain time duration.

This method must be called within a process context (not in an event handler or in the main function). The process will be put on hold for the given period of time. It will resume execution after the time period has passed.

Note that sleep cannot be interrupted, although the process can be killed by another process when it’s asleep. For interruptable sleep, use the wait() method.

Parameters
  • offset (float) – relative time from now until which the process will be put on hold; if provided, it must be a non-negative value

  • until (float) – the absolute time at which the process is expected to resume execution; if provided, it must not be earlier than the current time; either ‘offset’ or ‘until’ must be provided, but not both

This method does not return a value. When it returns, the process has already resumed execution.

trap()

Create and return a trap for inter-process communication.

semaphore(initval=0, qdis=0)

Create a semaphore for inter-process communication.

Parameters
  • initval (int) – the initial value of the semaphore; the value must be non-negative; the default is zero

  • qdis (int) – the queuing discipline for the waiting processes, which can be selected from QDIS.FIFO (first in first out), QDIS.LIFO (last in first out), QDIS.SIRO (service in random order), or QDIS.PRIORITY (based on process priority); if ignored, the default is QDIS.FIFO

Returns

This method returns a newly created semaphore.

resource(capacity=1, qdis=0, name=None, collect=None)

Create and return a resource.

Parameters
  • capacity (int) – the capacity of the resource; the value must be a positive integer; the default is one

  • qdis (int) – the queuing discipline for the waiting processes, which can be selected from QDIS.FIFO (first in first out), QDIS.LIFO (last in first out), QDIS.SIRO (service in random order), or QDIS.PRIORITY (based on process priority); if ignored, the default is QDIS.FIFO

  • name (string) – the optional name of the resource

  • collect (DataCollector) – the optional collector for statistics

Returns

This method returns the newly created resource.

The DataCollector, if provided, accepts the following values:
  • arrivals: timemarks (time of job arrivals)

  • services: timemarks (time of jobs entering services)

  • reneges: timemarks (time of jobs reneging from queue)

  • departs: timemarks (time of jobs departing from system)

  • inter_arrivals: dataseries (job inter-arrival time)

  • queue_times: dataseries (time of jobs in queue before servicing)

  • renege_times: dataseries (time of jobs in queue before reneging)

  • service_times: dataseries (time of jobs in service)

  • system_times: dataseries (time of jobs in system)

  • in_systems: timeseries (number of jobs in system)

  • in_services: timeseries (number of jobs in service)

  • in_queues: timeseries (number of jobs in queue)

store(capacity=1, initlevel=0, initobj=None, p_qdis=0, c_qdis=0, name=None, collect=None)

Create and return a store.

Parameters
  • capacity (int, float) – the capacity of the store; the value must be positive; the default is one

  • initlevel (int, float) – the initial storage level; the value must be non-negative and it cannot be larger than the capacity; the default is zero

  • initobj (object or list/tuple) – initial objects to be deposited in the store (if real objects are going to be used for the store operation), in which case the number of objects must match with the initlevel

  • p_qdis (int) – the queuing discipline for the waiting producer processes (putters), which can be selected from QDIS.FIFO (first in first out), QDIS.LIFO (last in first out), QDIS.SIRO (service in random order), or QDIS.PRIORITY (based on process priority); if ignored, the default is QDIS.FIFO

  • c_qdis (int) – the queuing discipline for the waiting consumer processes (getter); if ignored, the default is QDIS.FIFO

  • name (string) – the optional name of the store

  • collect (DataCollector) – the optional data collector for statistics

Returns

This method returns the newly created store.

The DataCollector, if provided, accepts the following values:
  • puts: timeseries (time and amount of put requests)

  • put_times: dataseries (waiting time to put items)

  • put_queues: timeseries (number of processes waiting to put items)

  • gets: timeseries (time and amount of get requests)

  • get_times: dataseries (waiting time to get items)

  • get_queues: timeseries (number of processes waiting to get items)

  • levels: timeseries (storage levels)

bucket(capacity=1, initlevel=0, p_qdis=0, c_qdis=0, name=None, collect=None)

Create and return a bucket.

Parameters
  • capacity (float) – the capacity of the bucket; the value must be positive; the default is one

  • initlevel (float) – the initial storage level; the value must be non-negative and it cannot be larger than the capacity; the default is zero

  • p_qdis (int) – the queuing discipline for the waiting producer processes (putters), which can be selected from QDIS.FIFO (first in first out), QDIS.LIFO (last in first out), QDIS.SIRO (service in random order), or QDIS.PRIORITY (based on process priority); if ignored, the default is QDIS.FIFO

  • c_qdis (int) – the queuing discipline for the waiting consumer processes (getter); if ignored, the default is QDIS.FIFO

  • name (string) – the optional name of the bucket

  • collect (DataCollector) – the optional data collector for statistics

Returns

This method returns the newly created bucket.

The DataCollector, if provided, accepts the following values:
  • puts: timeseries (time and amount of put requests)

  • put_times: dataseries (waiting time to put items)

  • put_queues: timeseries (number of processes waiting to put items)

  • gets: timeseries (time and amount of get requests)

  • get_times: dataseries (waiting time to get items)

  • get_queues: timeseries (number of processes waiting to get items)

  • levels: timeseries (storage levels)

mailbox(name=None, min_delay=0, nparts=1, collect=None)

Create and return a mailbox.

Parameters
  • name (string) – an optional name of the mailbox

  • min_delay (float) – the minimum delay for messages to be transported through the mailbox

  • nparts (int) – the number of compartments/partitions; the value must be a positive integer; the default is one

  • collect (DataCollector) – the optional collector for statistics; when provided, if the number of partitions is greater than one, it should be a list or tuple of DataCollectors, one for each partition; if there’s only one partition, it should be the DataCollector itself (as opposed to be wrapped in a list)

Returns

This method returns the newly created mailbox.

The DataCollector, if provided, accepts the following values:
  • arrivals: timemarks (time of message arrivals)

  • retrievals: timemarks (time of message retrieval requests)

  • messages: timeseries (number of messages in mailbox)

wait(traps, offset=None, until=None, method=<built-in function all>)

Conditional wait on one or more trappables for some time.

This method must be called within a process context (not in an event handler or in the main function). The process will be put on hold waiting for one or a list of trappables and for a given amount of time if specified. The process will resume execution after the given condition is met.

Parameters
  • traps (trappable, list, tuple) – either a trappable (an event, a process, a trap, a semaphore, or one of the resources and facilities), or a list/tuple of trappables

  • offset (float) – relative time from now until which the process will wait at the latest; if provided, it must be a non-negative value

  • until (float) – the absolute time at which the process is expected to resume execution at the latest; if provided, it must not be earlier than the current time; either ‘offset’ or ‘until’ can be provided, but not both; if both ‘offset’ and ‘until’ are ignored, there will be no time limit on the wait

  • method (function) – can be either ‘all’ (the default) or ‘any’; if ‘all’, all trappables must be triggered before this process can resume execution (or timed out); if ‘any’, one of the trappables must be triggered before this process can resume execution (or timed out); this parameter would have no effect if only one trappable (whether it’s standalone or as part of the list) is provided as the first argument

Returns

The return value of this method is a tuple that consists of two elements: the first element is to indicate which of the trappables have been triggered or not; the second element of tuple is to indicate whether timeout happens.

If the first argument when calling this method is only one trappable (not in a list or tuple), the first element of the returned tuple will be simply a scalar value, True or False, depending on whether the trappable has been triggered or not.

If the first argument when calling this method is a list or a tuple of trappables (even if the list or the tuple has only one element), the first element of the returned tuple will be a list of booleans, each of which indicates whether the corresponding trappable has been triggered or not.

run(offset=None, until=None)

Run simulation and process events.

This method processes the events in timestamp order and advances the simulation time accordingly.

Parameters
  • offset (float) – relative time from now until which the simulator should advance its simulation time; if provided, it must be a non-negative value

  • until (float) – the absolute time until which the simulator should advance its simulation time; if provided, it must not be earlier than the current time

The user can specify either ‘offset’ or ‘until’, but not both; if both ‘offset’ and ‘until’ are ignored, the simulator will run as long as there are events on the event list. Be careful, in this case, the simulator may run forever for some models as there could always be events scheduled in the future.

The simulator will process all events in timestamp order. When the method returns, the simulation time will advance to the designated time, if either ‘offset’ or ‘until’ is specified. All events with timestamps smaller than the designated time will be processed. If neither ‘offset’ nor ‘until’ is provided, the simulator will advance to the time of the last processed event.

step()

Process only one event.

This method processes the next event and advances the simulation time to the time of the next event. If no event is available on the event list, this method does nothing.

peek()

Return the time of the next scheduled event, or infinity if no future events are available.

rng()

Return the pseudo-random number generator attached to this simulator. It’s a random.Random instance (Mersenne twister).

sync()

Return the synchronized group to which this simulator belongs to, or None if the simulator does not belong to any group.

show_calendar()

Print the list of all future events currently on the event list. This is an expensive operation and should be used responsively, possibly just for debugging purposes.

show_runtime_report(prefix='')

Print a report on the simulator’s runtime performance.

Parameters

prefix (str) – all print-out lines will be prefixed by this string (the default is empty); this would help if one wants to find the report in a large amount of output

simulus.sync

class simulus.sync.sync(sims, enable_smp=False, enable_spmd=False, lookahead=inf, smp_ways=None)

Bases: object

A synchronized group of simulators whose simulation clocks will advance synchronously.

__init__(sims, enable_smp=False, enable_spmd=False, lookahead=inf, smp_ways=None)

Create a synchronized group of multiple simulators.

Bring all simulators in the group to synchrony; that is, the simulation clocks of all the simulators in the group, from now on, will be advanced synchronously in a coordinated fashion.

Parameters
  • sims (list or tuple) – a list of local simulators; the simulators are identified either by their names or as direct references to instances

  • enable_smp (bool) – enable SMP (Symmetric Multi-Processing) mode, in which case each local simulator will run as a separate process, and communication between the simulators will be facilitated through inter-process communication (IPC) mechanisms; the default is False, in which case all local simulators will run sequentially within the same process

  • enable_spmd (bool) – enable SPMD (Single Program Multiple Data) mode, in which case multiple simulus instances, potentially on distributed memory machines, will run in parallel, where communication between the simulus instances will be facilitated through the Message Passing Interface (MPI); the default is False, in which case the local simulus instance will run standalone with all simulators running either sequentially as one process (when enable_smp is False), or in parallel as separate processes (when enable_smp is True)

  • lookahead (float) – the maximum difference in simulation time between the simulators in the group; the default is infinity; the final lookahead for parallel simulation will be determined by the min delays of the named mailboxes of the simulators

  • smp_ways (int) – the maximum number of processes to be created for shared-memory multiprocessing. This parameter is only used when SMP is enabled.

Returns

This function creates, initializes, and returns a synchronized group. The simulators will first advance their simulation clock (asynchronously) to the maximum simulation time among all simulators (including both local simulators and remote ones, if enable_spmd is True). When the function returns, the listed simulators are bound to the synchronized group. That is, the simulation clocks of the simulators will be advanced synchronously from now on: all simulators will process events (including all messages sent between the simulators) in the proper timestamp order. (This is also known as the local causality constraint in the parallel discrete-event simulation literature.)

run(offset=None, until=None, show_runtime_report=False)

Process events of all simulators in the synchronized group each in timestamp order and advances the simulation time of all simulators synchronously.

Parameters
  • offset (float) – relative time from now until which each of the simulators should advance its simulation time; if provided, it must be a non-negative value

  • until (float) – the absolute time until which each of the simulators should advance its simulation time; if provided, it must not be earlier than the current time

The user can specify either ‘offset’ or ‘until’, but not both; if both ‘offset’ and ‘until’ are ignored, the simulator will run as long as there are events on the event lists of the simulators. Be careful, in this case, the simulation may run forever as for some models there may always be future events.

Each simulator will process their events in timestamp order. Synchronization is provided so that messages sent between the simulators may not produce causality errors. When this method returns, the simulation time of the simulators will advance to the designated time, if either ‘offset’ or ‘until’ has been specified. All events with timestamps smaller than the designated time will be processed. If neither ‘offset’ nor ‘until’ is provided, the simulators will advance to the time of the last processed event among all simulators.

If SPMD is enabled, at most one simulus instance (at rank 0) is allowed to specify the time (using ‘offset’ or ‘until’). All the other simulators must not specify the time.

send(sim, mbox_name, msg, delay=None, part=0)

Send a messsage from a simulator to a named mailbox.

Parameters
  • sim (simulator) – the simulator from which the message will be sent

  • name (str) – the name of the mailbox to which the message is expected to be delivered

  • msg (object) – a message can be any Python object; however, a message needs to be pickle-able as it may be transferred between different simulators located on separate processes (with different Python interpreter) or even on different machines; a message also cannot be None

  • delay (float) – the delay with which the message is expected to be delivered to the mailbox; if it is ignored, the delay will be set to be the min_delay of the mailbox; if it is set, the delay value must not be smaller than the min_delay of the mailbox

  • part (int) – the partition number of the mailbox to which the message will be delivered; the default is zero

Returns

This method returns nothing (as opposed to the mailbox send() method); once sent, it’s sent, as it cannot be cancelled or rescheduled.

classmethod comm_rank()

Return the process rank of this simulus instance.

classmethod comm_size()

Return the total number processes.

show_runtime_report(show_partition=True, prefix='')

Print a report on the runtime performance of running the synchronized group.

Parameters
  • show_partition (bool) – if it’s True (the default), the print-out report also contains the processor assignment of the simulators

  • prefix (str) – all print-out lines will be prefixed by this string (the default is empty); this would help if one wants to find the report in a large amount of output

simulus.utils

class simulus.utils.QDIS

Bases: object

Queuing disciplines used by semaphores and resources.

FIFO = 0
LIFO = 1
SIRO = 2
PRIORITY = 3
class simulus.utils.WelfordStats

Bases: object

Welford’s one-pass algorithm to get simple statistics (including the mean and variance) from a series of data.

push(x)

Add data to the series.

min()
max()
mean()
stdev()
var()
class simulus.utils.TimeMarks(keep_data=False)

Bases: object

A series of (increasing) time instances.

__len__()

Return the number of collected samples.

push(t)
data()

Return all samples if keep_data has been set when the timemarks was initialized; otherwise, return None.

rate(t=None)

Return the arrival rate, which is the averge number of sameples up to the given time. If t is ignored, it’s up to the time of the last entry.

class simulus.utils.DataSeries(keep_data=False)

Bases: object

A series of numbers.

__len__()

Return the number of collected samples.

push(d)
data()

Return all samples if keep_data has been set when the dataseries has been initialized; otherwise, return None.

mean()

Return the sample mean.

stdev()

Return the sample standard deviation.

var()

Return the sample variance.

min()

Return the minimum of all samples.

max()

Return the maximum of all samples.

class simulus.utils.TimeSeries(keep_data=False)

Bases: object

A series of time-value pairs.

__len__()

Return the number of collected samples.

push(d)
data()

Return all samples if keep_data has been set when the timeseries was initialized; otherwise, return None.

rate(t=None)

Return the arrival rate, which is the averge number of samples up to the given time. If t is ignored, it’s up to the time of the last entry.

mean()

Return the sample mean.

stdev()

Return the sample standard deviation.

var()

Return the sample variance.

min()

Return the minimum of all samples.

max()

Return the maximum of all samples.

avg_over_time(t=None)

Return the average value over time. If t is ignored, it’s the average up to the time of the last entry.

class simulus.utils.DataCollector(**kwargs)

Bases: object

Statistics collection for resources, stores, buckets, and mailboxes.

__init__(**kwargs)

Initialize the data collector. kwargs is the keyworded arguments; it’s a dictionary containing all attributes allowed to be collected at the corresponding resource or facility.

report(t=None)

Print out the collected statistics nicely. If t is provided, it’s expected to the the simulation end time; if t is ignored, the statistics are up to the time of the last sample.