Salabim documentation

Introduction

Salabim is a package for discrete event simulation in Python. It follows the methodology of process description as originally demonstrated in Simula and later in Prosim, Must and Tomas.
It is also quite similar to SimPy 2.

The package comprises discrete event simulation, queue handling, resources and statistical sampling. On top of that real time animation is built in.

The package comes with a number of sample models.

Requirements

Salabim runs on

  • CPython
  • PyPy platform
  • Pythonista (iOS)

The package runs under Python 2.7 or 3.x.

The following packages are required:

Platform Base functionality Animation Video production
CPython
PIL opencv, numpy
PyPy
PIL N/A
Pythonista
N/A

Several CPython packages, like WinPython support PIL out of the box. If not, install with: pip install Pillow

For, video production, installation of opencv and numpy may be required (via pip install opencv-python and pip install numpy).

Running models under PyPy is highly recommended for production runs, where run time is important. We have have found 6 to 7 times faster execution compared to CPython. However, for development, nothing can beat CPython or Pythonista.

Installation

The preferred way to install salabim is from PyPI with:

pip install salabim

or to upgrade to a new version:

pip install salabim --upgrade

You can find the package along with some support files and sample models on www.github.com/salabim/salabim. From there you can directly download as a zip file and next extract all files.

For Pythonista, the easiest way to install is:

  • Tap ‘Open in…’.
  • Tap ‘Run Pythonista Script’.
  • Pick this script and tap the run button
  • Import file
  • Possibly after short delay, there will be a salabim-master.zip file in the root directory
  • Tap this zip file and Extract files
  • All files are now in a directory called salabim-master
  • Optionally rename this directory to salabim

Salabim itself is provided as one Python script, called salabim.py. You may place that file in any directory where your models reside.

If you want salabim to be available from other directories, without copying the salabim.py script, run the supplied install.py file. In doing so, you will create (or update) a salabim directory in the site-package directory, which will contain a copy of the salabim package.

Python

Python is a widely used high-level programming language for general-purpose programming, created by Guido van Rossum and first released in 1991. An interpreted language, Python has a design philosophy that emphasizes code readability (notably using whitespace indentation to delimit code blocks rather than curly brackets or keywords), and a syntax that allows programmers to express concepts in fewer lines of code than might be used in languages such as C++ or Java. The language provides constructs intended to enable writing clear programs on both a small and large scale.

A good way to start learning about Python is https://www.python.org/about/gettingstarted/

Salabim modelling

A simple model

Let’s start with a very simple model, to demonstrate the basic structure, process interaction, component definition and output:

# Example - basic.py
import salabim as sim


class Car(sim.Component):
    def process(self):
        while True:
            yield self.hold(1)


env = sim.Environment(trace=True)
Car()
env.run(till=5)

In basic steps:

We always start by importing salabim:

import salabim as sim

Now we can refer to all salabim classes and function with sim.. For convenience, some functions or classes can be imported with, for instance:

from salabim import now, main, Component

It is also possible to import all methods, classes and globals by:

from salabim import *

, but we do not recommend that method.

The main body of every salabim model should contain (usually) starts with:

env = Environment(parameters)

For each (active) component we define a class as in:

class Car(sim.Component):

The class inherits from sim.Component.

Although it is possible to define other processes within a class, the standard way is to define a generator function called process in the class. A generator is a function with at least one yield statement. These are used in salabim context as a signal to give control to the sequence mechanism.

In this example,

yield self.hold(1)

gives control,to the sequence mechanism and comes back after 1 time unit. The self. part means that it is this component to be held for some time. We will see later other uses of yield like passivate, request, wait and standby.

In the main body an instance of a car is created by Car(). As there is a generator function called process in Car, this process description will be activated (by default at time now). It is possible to start a process later, but this is by far the most common way to start a process.

With

env.run(till=5)

we start the simulation and get back control after 5 time units. A component called main is defined under the hood to get access to the main process.

When we run this program, we get the following output:

0.000 main                 current
                           car create
                           car activate                         scheduled for      0.000 @process
                           main run                             scheduled for      5.000
0.000 car                  current
                           car hold                             scheduled for      1.000
1.000 car                  current
                           car hold                             scheduled for      2.000
2.000 car                  current
                           car hold                             scheduled for      3.000
3.000 car                  current
                           car hold                             scheduled for      4.000
4.000 car                  current
                           car hold                             scheduled for      5.000
5.000 main                 current

A bank example

Now let’s move to a more realistic model. Here customers are arriving in a bank, where there is one clerk. This clerk handles the customers in first in first out (fifo) order. We see the following processes:

  • The customer generator that creates the customers, with an inter arrival time of uniform(5,15)
  • The customers
  • The clerk, which serves the customers in a constant time of 30 (overloaded and non steady state system)

And we need one queue for the customers to wait for service.

The model code is:

# Example - bank, 1 clerk.py
import salabim as sim


class CustomerGenerator(sim.Component):
    def process(self):
        while True:
            Customer()
            yield self.hold(sim.Uniform(5, 15).sample())


class Customer(sim.Component):
    def process(self):
        self.enter(waitingline)
        if clerk.ispassive():
            clerk.activate()
        yield self.passivate()


class Clerk(sim.Component):
    def process(self):
        while True:
            while len(waitingline) == 0:
                yield self.passivate()
            self.customer = waitingline.pop()
            yield self.hold(30)
            self.customer.activate()


env = sim.Environment(trace=True)
CustomerGenerator()
clerk = Clerk()
waitingline = sim.Queue('waitingline')

env.run(till=50)
print()
waitingline.print_statistics()

Let’s look at some details:

yield self.hold(sim.Uniform(5, 15).sample())

will do the statistical sampling and wait for that time till the next customer is created.

With:

self.enter(waitingline)

the customer places itself at the tail of the waiting line.

Then, the customer check whether the clerk is idle, and if so, reactivates him immediately.:

while clerk.ispassive():
    clerk.reactivate()

And then the customer is going to wait for a reactivation (by the clerk) and then leaves the queue:

yield self.passivate()
self.leave(waitingline)

In the main section of the program, we create the CustomerGenerator, the Clerk and a queue called waitingline. After the simulation is finished, the statistics of the queue are presented with

waitingline.print_statistics()

The output looks like:

     0.000 main                 current
                                customergenerator create
                                customergenerator activate           scheduled for      0.000 @process
                                clerk create
                                clerk activate                       scheduled for      0.000 @process
                                waitingline create
                                main run                             scheduled for     50.000
     0.000 customergenerator    current
                                customer create
                                customer activate                    scheduled for      0.000 @process
                                customergenerator hold               scheduled for     14.631
     0.000 clerk                current
                                clerk passivate
     0.000 customer             current
                                customer                             enter waitingline
                                clerk activate                       scheduled for      0.000
                                customer passivate
     0.000 clerk                current
                                customer                             leave waitingline
                                clerk hold                           scheduled for     30.000
    14.631 customergenerator    current
                                customer rename to customer.0
                                customer.1 create
                                customer.1 activate                  scheduled for     14.631 @process
                                customergenerator hold               scheduled for     21.989
    14.631 customer.1           current
                                customer.1                           enter waitingline
                                customer.1 passivate
    21.989 customergenerator    current
                                customer.2 create
                                customer.2 activate                  scheduled for     21.989 @process
                                customergenerator hold               scheduled for     32.804
    21.989 customer.2           current
                                customer.2                           enter waitingline
                                customer.2 passivate
    30.000 clerk                current
                                customer.0 activate                  scheduled for     30.000
                                customer.1                           leave waitingline
                                clerk hold                           scheduled for     60.000
    30.000 customer.0           current
    30.000 customer.0           ended
    32.804 customergenerator    current
                                customer.3 create
                                customer.3 activate                  scheduled for     32.804 @process
                                customergenerator hold               scheduled for     40.071
    32.804 customer.3           current
                                customer.3                           enter waitingline
                                customer.3 passivate
    40.071 customergenerator    current
                                customer.4 create
                                customer.4 activate                  scheduled for     40.071 @process
                                customergenerator hold               scheduled for     54.737
    40.071 customer.4           current
                                customer.4                           enter waitingline
                                customer.4 passivate
    50.000 main                 current

Statistics of waitingline at        50
                                                                     all    excl.zero         zero
-------------------------------------------- -------------- ------------ ------------ ------------
Length of waitingline                        duration             50           35.369       14.631
                                             mean                  1.410        1.993
                                             std.deviation         1.107        0.754

                                             minimum               0            1
                                             median                2            2
                                             90% percentile        3            3
                                             95% percentile        3            3
                                             maximum               3            3

Length of stay in waitingline                entries               2            1            1
                                             mean                  7.684       15.369
                                             std.deviation         7.684        0

                                             minimum               0           15.369
                                             median               15.369       15.369
                                             90% percentile       15.369       15.369
                                             95% percentile       15.369       15.369
                                             maximum              15.369       15.369

Now, let’s add more clerks. Here we have chosen to put the three clerks in a Queue, although in this case it could have been also a Python list:

clerks = sim.Queue('clerks')
for i in range(3):
    Clerk().enter(clerks)

And, to restart a clerk:

for clerk in clerks:
    if clerk.ispassive():
       clerk.reactivate()
       break  # reactivate only one clerk

The complete source of a three clerk post office:

# Example - bank, 3 clerks.py
import salabim as sim


class CustomerGenerator(sim.Component):
    def process(self):
        while True:
            Customer()
            yield self.hold(sim.Uniform(5, 15).sample())


class Customer(sim.Component):
    def process(self):
        self.enter(waitingline)
        for clerk in clerks:
            if clerk.ispassive():
                clerk.activate()
                break  # activate only one clerk
        yield self.passivate()


class Clerk(sim.Component):
    def process(self):
        while True:
            while len(waitingline) == 0:
                yield self.passivate()
            self.customer = waitingline.pop()
            yield self.hold(30)
            self.customer.activate()


env = sim.Environment(trace=False)
CustomerGenerator()
clerks = sim.Queue('clerks')
for i in range(3):
    Clerk().enter(clerks)
waitingline = sim.Queue('waitingline')

env.run(till=50000)
waitingline.length.print_histogram(30, 0, 1)
print()
waitingline.print_info()
waitingline.print_statistics()

waitingline.length.print_histogram(30, 0, 1)
print()
waitingline.length_of_stay.print_histogram(30, 0, 10)

waitingline.length_of_stay.print_statistics()
waitingline.length.print_statistics()

A bank office example with resources

The salabim package contains another useful concept for modelling: resources. Resources have a limited capacity and can be claimed by components and released later.

In the model of the bank with the same functionality as the above example, the clerks are defined as a resource with capacity 3.

The model code is:

# Example - bank, 3 clerks (resources).py
import salabim as sim


class CustomerGenerator(sim.Component):
    def process(self):
        while True:
            Customer()
            yield self.hold(sim.Uniform(5, 15).sample())


class Customer(sim.Component):
    def process(self):
        yield self.request(clerks)
        yield self.hold(30)
        self.release()


env = sim.Environment(trace=False)
CustomerGenerator()
clerks = sim.Resource('clerk', capacity=3)

env.run(till=50000)

clerks.requesters().length.print_histogram(30, 0, 1)
print()
clerks.requesters().length_of_stay.print_histogram(30, 0, 10)

clerks.print_statistics()
clerks.print_info()

Let’s look at some details.

clerks = sim.Resource('clerks', capacity=3)

This defines a resource with a capacity of 3.

And then, a customer, just tries to claim one unit (=clerk) from the resource with:

yield self.request(clerks)

Here, we use the default of 1 unit. If the resource is not available, the customer just waits for it to become available (in order of arrival, here).

In contrast with the previous example, the customer now holds itself for 10 minutes.

And after these 10 minutes, the customer releases the resource with:

self.release()

The effect is that salabim then tries to honour the next pending request, if any.

The statistics are maintained in a system queue, called clerk.requesters().

The output is very similar to the earlier example. The statistics are exactly the same.

The bank office example with balking and reneging

Now, we assume that clients are not going to the queue when there are more than 5 clients waiting (balking). On top of that, if a client is waiting longer than 50, he will leave as well (reneging).

The model code is:

# Example - bank, 3 clerks, reneging.py
import salabim as sim


class CustomerGenerator(sim.Component):
    def process(self):
        while True:
            Customer()
            yield self.hold(sim.Uniform(5, 15).sample())


class Customer(sim.Component):
    def process(self):
        if len(waitingline) >= 5:
            env.number_balked += 1
            env.print_trace('', '', 'balked')
            yield self.cancel()
        self.enter(waitingline)
        for clerk in clerks:
            if clerk.ispassive():
                clerk.activate()
                break  # activate only one clerk
        yield self.hold(50)  # if not serviced within this time, renege
        if self in waitingline:
            self.leave(waitingline)
            env.number_reneged += 1
            env.print_trace('', '', 'reneged')
        else:
            yield self.passivate()  # wait for service to be completed


class Clerk(sim.Component):
    def process(self):
        while True:
            while len(waitingline) == 0:
                yield self.passivate()
            self.customer = waitingline.pop()
            self.customer.activate()
            yield self.hold(30)
            self.customer.activate()


env = sim.Environment(trace=False)
CustomerGenerator()
env.number_balked = 0
env.number_reneged = 0
clerks = sim.Queue('clerks')
for i in range(3):
    Clerk().enter(clerks)

waitingline = sim.Queue('waitingline')
waitingline.length.monitor(False)
env.run(duration=1500)
waitingline.length.monitor(True)
env.run(duration=1500)
waitingline.length.print_histogram(30, 0, 1)
print()
waitingline.length_of_stay.print_histogram(30, 0, 10)
print('number reneged', env.number_reneged)
print('number balked', env.number_balked)

Let’s look at some details.

yield self.cancel()

This makes the current component (a customer) a data component (and be subject to carbage collection), if the queue length is 5 or more.

The reneging is implemented by a hold of 50. If a clerk can service the customers, it will take the customer out of the waitingline and ativate it at that moment. The customer just has to check whether he is still in the waiting line. If so, he has been serviced in time and thus will renege.:

yield self.hold(50)
if self in waitingline:
    self.leave(waitingline)
    env.number_reneged += 1
else:
     self.passivate()

All the clerk has to do when starting servicing a client is to get the next customer in line out of the queue (as before) and activate this customer (at time now). The effect is that the hold of the customer will end.:

self.customer = waitingline.pop()
self.customer.activate()

The bank office example with balking and reneging (resources)

Now we show how the balking and reneging is implemented with resources.

The model code is:

# Example - bank, 3 clerks, reneging (resources).py
import salabim as sim


class CustomerGenerator(sim.Component):
    def process(self):
        while True:
            Customer()
            yield self.hold(sim.Uniform(5, 15).sample())


class Customer(sim.Component):
    def process(self):
        if len(clerks.requesters()) >= 5:
            env.number_balked += 1
            env.print_trace('', '', 'balked')
            yield self.cancel()
        yield self.request(clerks, fail_delay=50)
        if self.failed():
            env.number_reneged += 1
            env.print_trace('', '', 'reneged')
        else:
            yield self.hold(30)
            self.release()


env = sim.Environment(trace=False)
CustomerGenerator()
env.number_balked = 0
env.number_reneged = 0
clerks = sim.Resource('clerk', 3)

env.run(till=50000)

clerks.requesters().length.print_histogram(30, 0, 1)
print()
clerks.requesters().length_of_stay.print_histogram(30, 0, 10)
print('number reneged', env.number_reneged)
print('number balked', env.number_balked)

As you can see, the balking part is exactly the same as in the example without resources.

For the renenging, all we have to do is add a fail_delay):

yield self.request(clerks, fail_delay=50)

If the request is not honoured within 50 time units, the process contines after that request statement. And then, we just check whether the request has failed:

if self.failed():
    env.number_reneged += 1

This example shows clearly the advantage of the resource solution over the passivate/activate method.

A bank office example with states

The salabim package contains yet another useful concept for modelling: states. In this case, we define a state called worktodo.

The model code is:

# Example - bank, 3 clerks (state).py
import salabim as sim


class CustomerGenerator(sim.Component):
    def process(self):
        while True:
            Customer()
            yield self.hold(sim.Uniform(5, 15).sample())


class Customer(sim.Component):
    def process(self):
        self.enter(waitingline)
        worktodo.trigger(max=1)
        yield self.passivate()


class Clerk(sim.Component):
    def process(self):
        while True:
            if len(waitingline) == 0:
                yield self.wait(worktodo)
            self.customer = waitingline.pop()
            yield self.hold(30)
            self.customer.activate()


env = sim.Environment(trace=False)
CustomerGenerator()
for i in range(3):
    Clerk()
waitingline = sim.Queue('waitingline')
worktodo = sim.State('worktodo')

env.run(till=50000)
waitingline.length.print_histogram(30, 0, 1)
print()
waitingline.length_of_stay.print_histogram(30, 0, 10)

Let’s look at some details.

worktodo = sim.State('worktodo')

This defines a state with an initial value False.

In the code of the customer, the customer tries to trigger one clerk with:

worktodo.trigger(max=1)

The effect is that if there are clerks waiting for worktodo, the first clerk’s wait is honoured and that clerk continues its process after:

yield self.wait(worktodo)

Note that the clerk is only going to wait for worktodo after completion of a job if there are no customers waiting.

Component

General

Components are the key element of salabim simulations.

Components can be either data or active. An active component has one or more process descriptions and is activated at some point of time. You can make a data component active with activate. And an active component can become data either with a cancel or by reaching the end of its process method.

It is easy to create a data component by:

box = sim.Component()

Data components may be placed in a queue. You can’t activate this component as such as there is no associated process method.

In order to make an active component it is necessary to first define a class:

class Ship(sim.Component):

And then there has to be at least one generator method, normally called process:

class Ship(sim.Component):
    def process(self):
        ....
        yield ...
        ....

The process has to have at least one yield statement!

Creation and activation can be combined by making a new instance of the class:

ship1 = Ship()
ship2 = Ship()
ship3 = Ship()

This causes three Ships to be created and to start them at Sim.process(). The ships will automatically get the name ship...........1, etc., unless a name is given explicitly. If only one ship is created it will get the name ship.

If no process method is found for Ship, the ship will be a data component. In that case, it becomes active by means of an activate statement:

class Crane(sim.Component):
    def unload(self):
        ....
        yield ...
        ....

crane1 = Crane()
crane1.activate(process='unload')

crane2 = Crane(process='unload')

Effectively the effect of the creation and start of crane1 and crane2 is the same.

Although not very common, it is possible to activate a component at a certain time or with a specified delay:

ship1.activate(at=100)
ship2.activate(delay(50))

At time of creation it is sometimes useful to be able to set attributes, prepare for actions, etc. This is possible in salabim by defining an __init__ and/or a setup method:

If the __init__ method is used, it is required to call the Component __init__ method from within the overridden method:

class Ship(sim.Component):
    def __init__(self,length):
        sim.Component.__init__(self)
        self.length = length

ship = Ship(250)

In most cases, the setup method is preferred however. This method is called after ALL initialization code of Component is executed.

class Ship(sim.Component):
    def setup(self,length):
        self.length = length

ship = Ship(250)

Only in very specific cases, __init__ will be necessary.

Note that the setup code can be used for data components as well.

Process interaction

It is important to realize that a component may be in one of the following states:

  • data
  • current
  • scheduled
  • passive
  • requesting
  • waiting
  • standby

The scheme below shows how components can go from state to state.

from/to data current scheduled passive requesting waiting standby
data   activate[1] activate        
current process end yield cancel   yield hold yield activate yield passivate yield request yield wait yield standby
scheduled cancel next event hold activate passivate request wait standby
passive cancel activate[1] activate hold[2]   request wait standby
requesting cancel claim honor time out activate[3] passivate request activate[4] wait standby
waiting cancel wait honor timeout acivate[5] passivate wait wait activate[6] standby
standby cancel next event activate passivate request wait  

[1] via scheduled
[2] not recommended
[3] with keep_request=False (default)
[4] with keep_request=True. This allows to set a new time out [5] with keep_wait=False (default)
[6] with keep_wait=True. This allows to set a new time out

Creation of a component

Although it is possible to create a component directly with x=sim.Component(), but then it’s virtually impossible to make that component into a non data component, because there’s no process method. So, nearly always we define a class based on sim.Component:

def Car(sim.Component):
    def process(self):
        ...

If we then say car=Car(), a component is created and it activated from process. This process has to be a generator function, so needs to contain at least one yield statemement.

The result is that car is put on the future event list (for time now) and when it’s its turn, the component becomes current.

It is also possible to set a time at which the component (car) becomes active, like car=Car(at=10).

And instead of starting at process, the component may be initialized to start at another generation function, like car=Car(process=’wash’).

And, finally, if there is a process method, you can disable the automatic activation (i.e. make it a data component) , by specifying process=None.

If there is no process method, and process= is not given, the component becomes a data component.

activate

Activate is the way to turn a data component into a live component. If you do not specify a process, the generator function process is assumed. So you can say:

car0 = Car(process=None)  # data component
car0.activate()  # activate @ process if exists, otherwise error
car1 = Car(process=None)  # data component
car1.activate(process='wash')  # activate @ wash
  • If the component to be activated is current, always use yield self.activate. The effect is that the component becomes scheduled, thus this is essentially equivalent to the preferred hold method.
  • If the component to be activated is passive, the component will be activated at the specified time.
  • If the component to be activated is scheduled, the component will get a new scheduled time.
  • If the component to be activated is requesting, the request will be terminated, the attribute failed set and the component will become scheduled. If keep_request=True is specified, only the fail_at will be updated and the component will stay requesting.
  • If the component to be activated is waiting, the wait will be terminated, the attribute failed set and the component will become scheduled. If keep_wait=True is specified, only the fail_at will be updated and the component will stay waiting.
  • If the component to be activated is standby, the component will get a new scheduled time and become scheduled.

hold

Hold is the way to make a, usually current, component scheduled.

  • If the component to be held is current, the component becomes scheduled for the specified time. Always use yield self.hold() is this case.
  • If the component to be held is passive, the component becomes scheduled for the specified time.
  • If the component to be held is scheduled, the component will be rescheduled for the specified time, thus essentially the same as activate.
  • If the component to be held is standby, the component becomes scheduled for the specified time.
  • If the component to be activated is requesting, the request will be terminated, the attribute failed set and the component will become scheduled. It is recommended to use the more versatile activate method.
  • If the component to be activated is waiting, the wait will be terminated, the attribute failed set and the component will become scheduled. It is recommended to use the more versatile activate method.

passivate

Passivate is the way to make a, usually current, component passive This is actually the same as scheduling for time=inf.

  • If the component to be passivated is current, the component becomes passive. Always use yield seld.passivate() is this case.
  • If the component to be passivated is passive, the component remains passive.
  • If the component to be passivated is scheduled, the component will be passivated.
  • If the component to be held is standby, the component will be passivated.
  • If the component to be activated is requesting, the request will be terminated, the attribute failed set and the component will become pass It is recommended to use the more versatile activate method.
  • If the component to be activated is waiting, the wait will be terminated, the attribute failed set and the component will become scheduled. It is recommended to use the more versatile activate method.

cancel

Cancel has the effect that the component becomes a data component.

  • If the component to be cancelled is current, use always yield self.cancel().
  • If the component to be cancelled is requesting, the request will be terminated, the attribute failed set and the component will become a data component.
  • If the component to be cancelled is waiting, the wait will be terminated, the attribute failed set and the component will become a data component.

standby

Standby is a

request

wait

Queue

Salabim has a class Queue for queue handling of component. The advantage over the standard list and deque are:

  • double linked, reulting in easy and efficient insertion at any place
  • data collection
  • priority sorting

Salabim uses queues internally for resource and states as well.

Definition of a queue is simple:

waitingline=sim.Queue('waitingline')

There is a set of methods for components to enter and leave a queue:

Component Queue Description
c.enter(q) q.add(c) enter at the tail
c.enter_to_head(q) q.add_at_head(c) enter at the head
c.enter_in_front(q, c1) q.add_in_front_of(c, c1) enter in front of another component
c.enter_behind(q, c1) q.add_behind(c, c1)` enter behind of another component
c.enter_sorted(q, p) q.add_sorted(c, p) enter according to priority
c.leave(q) q.remove(c) leave

The head a queue can be retrieved with q.head(). If a queue is empty, None will be returned.
The tail a queue can be retrieved with q.tail(). If a queue is empty, None will be returned.
To remove the head of a queue, use pop(q). The component at the head will be returned (None if queue is empty). The successor of a component in queue can be retrieved with c.successor(q) or q.successor(c)
The predecessor of a component in queue can be retrieved with c.predecessor(q) or q.predecessor(c)

Queue is a standard ABC class, which means that the following methods are supported: len(q) to retrieve the length of a queue, alternatively via the timestamped monitor with )q.length() c in q to check whether a component is in a queue for c in q: to traverse a queue (it is even possible to remove and add components in the for body. for c in reverse(q) to traverse a queue from tail to head

Salabim keeps track of the enter time in a queue: c.enter_time(q)

Unless disabled explicitly, the length of the queue and length of stay of components is monitored in q.length and q.length_of_stay. It is possible to obtain a number of statistics on these monitors (cf. Monitor).

With q.print_statistics() the key statistics of these two monitors are printed.

E.g.:

-------------------------------------------- -------------- ------------ ------------ ------------
Length of waitingline                        duration          50000        48499.381     1500.619
                                             mean                  8.427        8.687
                                             std.deviation         4.852        4.691

                                             minimum               0            1
                                             median                9           10
                                             90% percentile       14           14
                                             95% percentile       16           16
                                             maximum              21           21

Length of stay in waitingline                entries            4995         4933           62
                                             mean                 84.345       85.405
                                             std.deviation        48.309       47.672

                                             minimum               0            0.006
                                             median               94.843       95.411
                                             90% percentile      142.751      142.975
                                             95% percentile      157.467      157.611
                                             maximum             202.153      202.153

With q.print_info() a summary of the contents of a queue can be printed.

E.g.

Queue 0x20e116153c8
  name=waitingline
  component(s):
    customer.4995        enter_time 49978.472 priority=0
    customer.4996        enter_time 49991.298 priority=0

Resource

Resources are a powerful way of process interaction.

A resource has always a capacity (which can be zero). This capacity will be specified at time of creation, but may change over time. There are two of types resources: * standard resources, where each claim is associated with a component (the claimer). It is not necessary that the claimed quantities are integer. * anonymous resources, where only the claimed quantity is registered. This is most useful for dealing with levels, lengths, etc.

Resources are defined like:

clerks = Resource('clerks', capacity=3)

And then a component can request a clerk:

yield self.request(clerks)  # request 1 from clerks

It is also possible to request for more resources at once:

yield self.request(clerks,(assistance,2))  # request 1 from clerks AND 2 from assistance

Resources have a queue requesters containing all components try to claim from the resource. And a queue claimers containing all components claiming from the resource (not for anonymous resources.

It is possible to release a quantity from a resource with c.release(), e.g.

self.release(r)  # releases all claimed quantity from r
self.release((r,2))  # release quantity 2 from r

Alternatively, it is possible to release from a resource directly, e.g.

r.release()  # releases the total quantity from all claiming components
r.release(10)  # releases 10 from the resource; only valid for anonymous resources

After a release, for all requesting components will be checked whether their claim can be honored.

Resources have a number of monitors and timestamped monitors:

  • claimers().length
  • claimers().length_of_stay
  • requesters().length
  • requesters().length_of_stay
  • claimed_quantity
  • available_quantity
  • capacity

By default, all monitors are enabled.

With r.print_statistics() the key statistics of these all monitors are printed.

E.g.:

Statistics of clerk at     50000.000
                                                                     all    excl.zero         zero
-------------------------------------------- -------------- ------------ ------------ ------------
Length of requesters of clerk                duration          50000        48499.381     1500.619
                                             mean                  8.427        8.687
                                             std.deviation         4.852        4.691

                                             minimum               0            1
                                             median                9           10
                                             90% percentile       14           14
                                             95% percentile       16           16
                                             maximum              21           21

Length of stay in requesters of clerk        entries            4995         4933           62
                                             mean                 84.345       85.405
                                             std.deviation        48.309       47.672

                                             minimum               0            0.006
                                             median               94.843       95.411
                                             90% percentile      142.751      142.975
                                             95% percentile      157.467      157.611
                                             maximum             202.153      202.153

Length of claimers of clerk                  duration          50000        50000            0
                                             mean                  2.996        2.996
                                             std.deviation         0.068        0.068

                                             minimum               1            1
                                             median                3            3
                                             90% percentile        3            3
                                             95% percentile        3            3
                                             maximum               3            3

Length of stay in claimers of clerk          entries            4992         4992            0
                                             mean                 30           30
                                             std.deviation         0.000        0.000

                                             minimum              30.000       30.000
                                             median               30           30
                                             90% percentile       30           30
                                             95% percentile       30           30
                                             maximum              30.000       30.000

Capacity of clerk                            duration          50000        50000            0
                                             mean                  3            3
                                             std.deviation         0            0

                                             minimum               3            3
                                             median                3            3
                                             90% percentile        3            3
                                             95% percentile        3            3
                                             maximum               3            3

Available quantity of clerk                  duration          50000          187.145    49812.855
                                             mean                  0.004        1.078
                                             std.deviation         0.068        0.268

                                             minimum               0            1
                                             median                0            1
                                             90% percentile        0            1
                                             95% percentile        0            2
                                             maximum               2            2

Claimed quantity of clerk                    duration          50000        50000            0
                                             mean                  2.996        2.996
                                             std.deviation         0.068        0.068

                                             minimum               1            1
                                             median                3            3
                                             90% percentile        3            3
                                             95% percentile        3            3
                                             maximum               3            3

With r.print_info() a summary of the contents of a queue can be printed.

E.g.

Resource 0x112e8f0b8
  name=clerk
  capacity=3
  requesting component(s):
    customer.4995        quantity=1
    customer.4996        quantity=1
  claimed_quantity=3
  claimed by:
    customer.4992        quantity=1
    customer.4993        quantity=1
    customer.4994        quantity=1

The capacity may be changed with r.set_capacity(x). Note that this may lead to requesting components to be honoured.

Querying of the capacity, claimed quantity and available quantity can be done via the timestamped monitors: r.capacity(), r.claimed_quantity() and r.available_quantity()

State

Although states may be used to monitor values over time, they are mostly used for the Component’s wait method.

Definition is simple, like dooropen=sim.State('dooropen'). The default initial value is False, meaning the door is closed.

Now we can say dooropen.set() to open the door.

If we just want at most one person to enter, we say dooropen.trigger(max=1).

We can obtain the current value by just calling the state, like in:

print('door is ',('open' if dooropen() else 'closed'))

Alternatively, we can get the current value with the get method:

print('door is ',('open' if dooropen.get() else 'closed'))

The state is automatically monitored in the state.value timestamped monitor.

States can be used also for non values other than bool type. E.g.

light=sim.State('light', value='red')
...
light.state.set('green')

Or define a int/float state:

level=sim.State('level', value=0)
...
level.set(level()+10)

The state value will be monitored and therefore, statistical information and histograms will be available.

Process interaction with wait()

A component can wait for a state to get a certain value. In its most simple form:

yield self.wait(dooropen)

Once the dooropen state is True, the component will continue.

As with request() it is possible to set a timeout with fail_at or fail_delay:

yield self.wait(dooropen, fail_delay=10)
if self.failed:
    print('impatient ...')

In the above example we tested for a state to be True.

There are three ways to test for a value:

Scalar testing

It is possible (and even more common) to test for a certain value:

yield self.wait((light, 'green'))

Or more states at once:

yield self.wait((light, 'green'), night)  # honoured as soon as light is green OR it's night
yield self.wait((light, 'green'), (light, 'yellow'))  # honoured as soon is light is green OR yellow

It is also possible to wait for all conditions to be satisfied, by adding all=True:

yield self.wait((light,'green'), enginerunning, all=True)  # honoured as soon as light is green and engine is running

Evaluation testing

Here, we use a string containing an expression that can evaluate to True or False. This is done by specifying at least one $ in the test-string. This $ will be replaced at run time by state.value(), where state is the state under test. Here are some examples:

yield self.wait((light, '$ in ("green","yellow")'))
    # if at run time light.value() is 'green', test for eval(state.value() in ("green,"yellow")) ==> True
yield self.wait((level, '$ < 30'))
    # if at run time level.value() is 50, test for eval(state.value() < 30) ==> False

During the evaluation, self refers to the component under test and state to the state under test. E.g.:

self.limit = 30
yield self.wait((level, 'self.limit >= $'))
    # if at run time level.value() is 50, test for eval(self.limit >= state.value()) ==> False

Function testing

This is a more complicated but also more versatile way of specifying the honour-condition. In that case, a function is required to specify the condition. The function needs to accept a three arguments:

  • x state.value()
  • component component under test
  • state under test

E.g.:

yield self.wait((light, lambda x, _, _: x in ('green', 'yellow'))
    # x is light.value()
yield self.wait((level, lambda x, _, _: x < 30))
    # x is light.value()

And, of course, it is possible to define a function:

def levelreached(x):
    value, component, _ = x
    return value < component.limit

...

self.limit = 30
yield self.wait((level, levelreached))

Combination of testing methods

It is possible to mix scalar, evaluation and function testing. And it’s also possible to specify all=True in any case.

Monitor and MonitorTimestamp

Monitors and timestamped monitors are a way to collect data from the simulation. They are automatically collected for resources, queues and states. On top of that the user can define its own (timestamped) monitors.

Monitor

The Monitor class collects values which do not have a direct relation with the current time, e.g. the processing time of a part.

We define the monitor with processingtime=sim.Monitor('processingtime') and then collect values by processingtime.tally(env.now()-start)

There is whole set of statistical data available:

  • number_of_entries
  • number_of_entries_zero
  • mean
  • std
  • minimum
  • median
  • maximum
  • percentile
  • bin_count (number of entries between to given values)
  • histogram (numpy definition)

For all these statistics, it is possible to exclude zero entries, e.g. m.mean(ex0=True) returns the mean, excluding zero entries.

Besides, it is possible to get all collected values as a list with _x or as a numpy array as x(). Also, here exclude zero entries is an option.

Note that apart from _x, all the x-values are converted to a numeric value, if possible. If not, 0 will be assumed.

With the monitor method, the monitor can be enbled or disabled. Note that a tally is just ignored when the monitor is disabled.

Also, the current status (enabled/disabled) can be retrieved.

proctime.monitor(True)  # enable monitoring
proctime.monitor(False)  # disable monitoring
if proctime.monitor():
    print('proctime is enabled')

Calling m.reset() will clear all tallied values.

The statistics of a monitor can be printed with print_statistics(). E.g: waitingline.length_of_stay.print_statistics():

Statistics of Length of stay in waitingline at     50000
                        all    excl.zero         zero
-------------- ------------ ------------ ------------
entries            4995         4933           62
mean                 84.345       85.405
std.deviation        48.309       47.672

minimum               0            0.006
median               94.843       95.411
90% percentile      142.751      142.975
95% percentile      157.467      157.611
maximum             202.153      202.153

And, a histogram can be printed with print_histogram(). E.g. waitingline.length_of_stay.print_histogram(30, 0, 10):

Histogram of Length of stay in waitingline
                        all    excl.zero         zero
-------------- ------------ ------------ ------------
entries            4995         4933           62
mean                 84.345       85.405
std.deviation        48.309       47.672

minimum               0            0.006
median               94.843       95.411
90% percentile      142.751      142.975
95% percentile      157.467      157.611
maximum             202.153      202.153

           <=       entries     %  cum%
        0            62       1.2   1.2 |
       10           169       3.4   4.6 ** |
       20           284       5.7  10.3 ****    |
       30           424       8.5  18.8 ******         |
       40           372       7.4  26.2 *****               |
       50           296       5.9  32.2 ****                     |
       60           231       4.6  36.8 ***                          |
       70           192       3.8  40.6 ***                             |
       80           188       3.8  44.4 ***                                |
       90           136       2.7  47.1 **                                   |
      100           352       7.0  54.2 *****                                      |
      110           491       9.8  64.0 *******                                            |
      120           414       8.3  72.3 ******                                                   |
      130           467       9.3  81.6 *******                                                          |
      140           351       7.0  88.7 *****                                                                 |
      150           224       4.5  93.2 ***                                                                       |
      160           127       2.5  95.7 **                                                                          |
      170            67       1.3  97.0 *                                                                            |
      180            59       1.2  98.2                                                                               |
      190            61       1.2  99.4                                                                                |
      200            24       0.5  99.9                                                                                |
      210             4       0.1 100                                                                                   |
      220             0       0   100                                                                                   |
      230             0       0   100                                                                                   |
      240             0       0   100                                                                                   |
      250             0       0   100                                                                                   |
      260             0       0   100                                                                                   |
      270             0       0   100                                                                                   |
      280             0       0   100                                                                                   |
      290             0       0   100                                                                                   |
      300             0       0   100                                                                                   |
          inf         0       0   100

MonitorTimestamp

The MonitorTimestamp class collects value along with the current (simulation) time. e.g. the number of parts a machine is working on.

Making your own monitorstamp is considerably more complicated than Monitors as it involves defining a getter function. For applications, we therefore recommend to create a state and use the value as a timestamped monitor.

There is whole set of statistical data available, which are all weighted with the duration:

  • duration
  • duration_zero (time that the value was zero)
  • mean
  • std
  • minimum
  • median
  • maximum
  • percentile
  • bin_count (number of entries between to given values)
  • histogram (numpy definition)

For all these statistics, it is possible to exclude zero entries, e.g. m.mean(ex0=True) returns the mean, excluding zero entries.

Besides, it is possible to get all collected values as a list with _x and to get the associated timestamps with _t.

But, more useful is xduration(), which return a tuple of two numpy arrays: x-values and associated durations.
The method xt() returns a tuple of two numpy arrays: x-values and associated timestamps.
The method tx() returns a tuple of two numpy arrays: timestamps and associated x-values.

When monitoring is disabled, a nan value will be tallied. All statistics will ignore the periods from nan to a non-nan value. This also holds for the xduration() method, but NOT for xt() and tx(). Thus, the x-arrays of xduration() are not necessarily the same as the x-arrays in xt() and tx(). This is the reason why there’s no x() or t() method.
It is easy to get just the x-array with xduration[0].

Note that apart from _x, all the x-values are converted to a numeric value, if possible. If not, 0 will be assumed.

With the monitor method, the timestamped monitor can be enbled or disabled.

Also, the current status (enabled/disabled) can be retrieved.

level.monitor(True)  # enable monitoring
level.monitor(False)  # disable monitoring
if level.monitor():
    print('level is enabled')

Calling m.reset() will clear all tallied values and timestamps.

The statistics of a timestamped monitor can be printed with print_statistics(). E.g: waitingline.length.print_statistics():

Statistics of Length of waitingline at     50000
                        all    excl.zero         zero
-------------- ------------ ------------ ------------
duration          50000        48499.381     1500.619
mean                  8.427        8.687
std.deviation         4.852        4.691

minimum               0            1
median                9           10
90% percentile       14           14
95% percentile       16           16
maximum              21           21

And, a histogram can be printed with print_histogram(). E.g.:

waitingline.length.print_histogram(30, 0, 1)
Histogram of Length of waitingline
                        all    excl.zero         zero
-------------- ------------ ------------ ------------
duration          50000        48499.381     1500.619
mean                  8.427        8.687
std.deviation         4.852        4.691

minimum               0            1
median                9           10
90% percentile       14           14
95% percentile       16           16
maximum              21           21

           <=      duration     %  cum%
        0          1500.619   3.0   3.0 **|
        1          2111.284   4.2   7.2 ***  |
        2          3528.851   7.1  14.3 *****      |
        3          4319.406   8.6  22.9 ******            |
        4          3354.732   6.7  29.6 *****                  |
        5          2445.603   4.9  34.5 ***                        |
        6          2090.759   4.2  38.7 ***                           |
        7          2046.126   4.1  42.8 ***                               |
        8          1486.956   3.0  45.8 **                                  |
        9          2328.863   4.7  50.4 ***                                     |
       10          4337.502   8.7  59.1 ******                                         |
       11          4546.145   9.1  68.2 *******                                               |
       12          4484.405   9.0  77.2 *******                                                      |
       13          4134.094   8.3  85.4 ******                                                              |
       14          2813.860   5.6  91.1 ****                                                                    |
       15          1714.894   3.4  94.5 **                                                                         |
       16           992.690   2.0  96.5 *                                                                            |
       17           541.546   1.1  97.6                                                                               |
       18           625.048   1.3  98.8 *                                                                              |
       19           502.291   1.0  99.8                                                                                |
       20            86.168   0.2 100.0                                                                                |
       21             8.162   0.0 100                                                                                   |
       22             0       0   100                                                                                   |
       23             0       0   100                                                                                   |
       24             0       0   100                                                                                   |
       25             0       0   100                                                                                   |
       26             0       0   100                                                                                   |
       27             0       0   100                                                                                   |
       28             0       0   100                                                                                   |
       29             0       0   100                                                                                   |
       30             0       0   100                                                                                   |
          inf         0       0   100

Distributions

Salabim can be used with the standard random module, but it is easier to use the salabim distributions.

Internally, salabim uses the random module. There is always a seed associated with each distribution, which is normally random.random.

When a new environment is created, the random seed 1234567 will be set by default. However, it is possible to override this behaviour with the random_seed parameter:

  • any hashable value, to set another seed
  • null string (‘’): no reseeding
  • None: true random, non reproducible (based on current time)

As a distribution is an instance of a class, it can be used in assignment, parameters, etc. E.g.:

interarrivaltime = sim.Uniform(10,15)

And then, to wait for a time sampled from this distribution:

yield self.hold(interarrivaltime.sample())

All distributions support the following methods:

  • mean()
  • sample()

Salabim provides the following distribution classes:

Constant

No sampling is required for this distribution, as it always returns the same value. E.g.

processingtime = sim.Constant(10)

Uniform

Uniform distribution between lowerbound and upperbound. E.g.

processingtime = sim.Uniform(5, 15)

Triangular

Triangular distribution between lowerbound and upperbound with a given median. E.g.:

processingtime = sim.Triangular(5, 15, 8)

Exponential

Exponential distribution with a given mean. E.g.

interarrivaltime = sim.Exponential(10)  # on an average every 10 time units

Normal

Normal distribution with a given mean and standard deviation. E.g.

processingtime = sim.Normal(10, 2)

Note that this might result in negative values, which might not correct if it is a duration. In that case, sample like:

yield self.hold(max(0,processingtime.sample())

Cdf

Cumulative distribution function, specified as a list x[i],p[i] values, where p[i] is the cumulative probability that xn<=pn. E.g.:

processingtime = sim.Cdf(5, 0, 10, 50, 15, 90, 30, 95, 60, 100)

This means that 0% is <5, 50% is < 10, 90% is < 15, 95% is < 30 and 100% is <60.

Note

It is required that p[0] is 0 and that p[i]<=p[i+1] and that x[i]<=x[i+1].

It is not required that the last p[] is 100, as all p[]’s are automatically scaled. This means that the two distributions below are identical to the first example:

processingtime = sim.Cdf(5, 0.00, 10, 0.50, 15, 0.90, 30, 0.95, 60, 1.00)
processingtime = sim.Cdf(5,    0, 10,   10, 15,   18, 30,   19, 60,   20)

Pdf

Probability density function, specified as:

  1. list or tuple of x[i], p[i] where p[i] is the probability (density)
  2. list or tuple of x[i] followed by a list or tuple p[i]
  3. list or tuple of x[i] followed by a scalar (value not important)

Note

It is required that the sum of p[i]’s is greater than 0.

E.g.:

processingtime = sim.Pdf(5, 10, 10, 50, 15, 40)

This means that 10% is 5, 50% is 10 and 40% is 40.

It is not required that the sum of the p[i]’s is 100, as all p[]’s are automatically scaled. This means that the two distributions below are identical to the first example:

processingtime = sim.Pdf(5, 0.10, 10, 0.50, 15, 0.40)
processingtime = sim.Pdf(5,    2, 10,   10, 15,    8)

And the same with the second form:

processingtime = sim.Pdf((5, 10, 15), (10, 50, 40))

If all x[i]’s have the same probability, the third form is very useful:

dice = sim.Pdf((1,2,3,4,5,6),1)
dice = sim.Pdf(range(1,7),1)  # same as above

x[i] may be of any type, so it possible to use:

color = sim.Pdf('Green', 45, 'Yellow', 10, 'Red', 45)
cartype = sim.Pdf(ordertypes,1)

If the x-value is a salabim distribution, not the distribution but a sample of that distribution is returned when sampling:

processingtime = sim.Pdf(sim.Uniform(5, 10), 50, sim.Uniform(10, 15), 40, sim.Uniform(15, 20), 10)
proctime=processingtime.sample()

Here proctime will have a probability of 50% being between 5 and 10, 40% between 10 and 15 and 10% between 15 and 20.

Distribution

A very special distribution is the Distribution class. Here, a string will contain the specification of the distribution. This is particularly useful when the distributions are specified in an external file. E.g.

with open('experiment1.txt', 'r') as f:
    interarrivaltime = sim.Distribution(read(f))
    processingtime = sim.Distribution(read(f))

With a file experiment.txt:

Uniform(10,15)
Triangular(1,5,2)

Animation

Animation is a very powerful tool to debug, test and demonstrate simulations. Salabim’s animation engine also allows some user input.

Salabim animations are always synchronized with the simulation clock and run in real time. One time unit in the simulation can correspond to any period in real time, e.g.

  • 1 time unit in simulation time –> 1 second real time (speed = 1) (default)
  • 1 time unit in simulation time –> 4 seconds real time (speed = 0.25)
  • 4 time units in simulation time –> 1 second real time (speed = 4)

In order to run an animated simulation at least one call to animation_parameters had to be issued. And then upon the next run statement, the animation will start.

The animation uses a coordinate system that -by default- is in screen pixels. The lower left corner is (0,0). But, the user can change both the coordinate of the lower left corner (translation) as well as set the x-coordinate of the lower right hand corner (scaling). Note that x- and y-scaling are always the same.
Furthermore, it is possible to specify the colour of the background with animation_parameters.

The animation is started with a run statement.

There is actually just one animation object class: Animate. This class can be used to show:

  • line (if line0 is specified)
  • rectangle (if rectangle0 is specified)
  • polygon (if polygon0 is specified)
  • circle (if circle0 is specified)
  • text (if text is specified)
  • image (if image is specicified)

Note that only one type is allowed per instance of Animate.

Nearly all attributes of an Animate object are interpolated between time t0 and t1. If t0 is not specified, now() is assumed. If t1 is not specified inf is assumed, which means that the attribute will be the ‘0’ attribute.

E.g.:

Animate(x0=100,y0=100,rectangle0==(-10,-10,10,10)) will show a square around (100,100) for ever
Animate(x0=100,y0=100,x1=200,y1=0,rectangle0=(-10,-10,10,10)) will still show the same square around (100,100) as t1 is not specified
Animate(t1=env.now()+10,x0=100,y0=100,x1=200,y1=0,rectangle0=(-10,-10,10,10)) will show a square moving from (100,100) to (200,0) in 10 units of time.

It also possible to let the rectangle change shape over time:

Animate(t1=env.now(),x0=100,y0=100,x1=200,y1=0,rectangle0=(-10,-10,10,10),rectangle1=(-20,-20,20,20)) will show a moving and growing rectangle.

By default, the animation object will not change anymore after t1, but will remain visible. Alternatively, if keep=False is specified, the object will disappear at time t1.

Also, colors, fontsizes, angles can be changed in a linear way over time.

E.g.:

Animate(t1=env.now()+10,text='Test',textcolor0='red',textcolor1='blue',angle0=0,angle1=360) will show a rotating text changing from red to blue in 10 units of time.

The animation object can be updated with the update method. Here, once again, all the attributes can be specified to change over time. Note that the defaults for the ‘0’ values are the actual values at t=now().

Thus,

an=Animate(t0=0,t1=10,x0=0,x1=100,y0=0,circle0=(10,),circle1=(20,)) will show a horizontally moving, growing circle.

Now, at time t=5, we issue an.update(t1=10,y1=50,circle1=(10,)) Then x0 will be set 50 (halfway 0 an 100) and cicle0 to (15,) (halfway 10 and 20). Thus the circle will shrink to its original size and move vertically from (50,0) to (50,50). This concept is very useful for moving objects whose position and orientation are controlled by the simulation.

Here we explain how an attribute changes during time. We use x as an example. Normally, x=x0 at t=t0 and x=x1 at t>=t1. between t=t0 and t=t1, x is linearly interpolated. An application can however override the x method. The prefered way is to subclass the Animate class:

# Demo animate 1
import salabim as sim


class AnimateMovingText(sim.Animate):
    def __init__(self):
        sim.Animate.__init__(self, text='', x0=100, x1=1000, y0=100, t1=env.now() + 10)

    def x(self, t):
        return sim.interpolate(sim.interpolate(t, self.t0, self.t1, 0, 1)**2, 0, 1, self.x0, self.x1)

    def y(self, t):
        return int(t) * 50

    def text(self, t):
        return '{:0.1f}'.format(t)


env = sim.Environment()

env.animation_parameters()

AnimateMovingText()

env.run()


This code will show the current simulation time moving from left to right, uniformly accelerated. And the text will be shown a bit higher up, every second. It is not necessary to use t0, t1, x0, x1, but is a convenient way of setting attributes.

The following methods may be overridden:

method circle image line polygon rectangle text
anchor  
     
angle
circle
         
fillcolor
   
 
fontsize          
image  
       
layer
line    
     
linecolor
 
 
linewidth
 
 
offsetx
offsety
polygon      
   
rectangle        
 
text          
textcolor          
visible
width  
       
x
y

Dashboard animation

Here we present an example model where the simulation code is completely separated from the animation code. This makes communication and debugging and switching off animation much easier.

The example below generates 15 persons starting at time 0, 1, … . These persons enter a queue called q and stay there 15 time units.

The animation dashboard shows the first 10 persons in the queue q, along with the length of that q.

# Demo animate 2.py
import salabim as sim


class AnimateWaitSquare(sim.Animate):
    def __init__(self, i):
        self.i = i
        sim.Animate.__init__(self,
            rectangle0=(-10, -10, 10, 10), x0=300 - 30 * i, y0=100, fillcolor0='red', linewidth0=0)

    def visible(self, t):
        return q[self.i] is not None


class AnimateWaitText(sim.Animate):
    def __init__(self, i):
        self.i = i
        sim.Animate.__init__(self, text='', x0=300 - 30 * i, y0=100, textcolor0='white')

    def text(self, t):
        component_i = q[self.i]

        if component_i is None:
            return ''
        else:
            return component_i.name()


def do_animation():
    env.animation_parameters()
    for i in range(10):
        AnimateWaitSquare(i)
        AnimateWaitText(i)
    show_length = sim.Animate(text='', x0=330, y0=100, textcolor0='black', anchor='w')
    show_length.text = lambda t: 'Length= ' + str(len(q))


class Person(sim.Component):
    def process(self):
        self.enter(q)
        yield self.hold(15)
        self.leave(q)


env = sim.Environment(trace=True)

q = sim.Queue('q')
for i in range(15):
    Person(name='{:02d}'.format(i), at=i)

do_animation()

env.run()

All animation initialization is in do_animation, where first 10 rectangle and text Animate objects are created. These are classes that are inherited from sim.Animate.

The AnimateWaitSquare defines a red rectangle at a specific position in the sim.Animate.__init__() call. Note that normally these squares should be displayed. But, here we have overridden the visible method. If there is no i-th component in the q, the square will be made invisible. Otherwise, it is visible.

The AnimateWaitText is more or less defined in a similar way. It defines a text in white at a specific position. Only the text method is overridden and will return the name of the i-th component in the queue, if any. Otherwise the null string will be returned.

The length of the queue q could be defined also by subclassing sim.Animate, but here we just make a direct instance of Animate with the null string as the text to be displayed. And then we immediately override the text method with a lambda function. Note that in this case, self is not available!

Video production

An animation can be recorded as a .mp4 video by sprecifying video=filename in the call to animation_parameters. The effect is that 30 time per second (scaled animation time) a frame is written. In this case, the animation does not run synchronized with the wall clock anymore. Depending on the complexity of the animation, th simulation might run faster of slower than real time. Other than with an ordinary animation, frames are never skipped.

Once control is given back to main, the .mp4 file is closed.

Contributing to salabim

If you would like to contribute to salabim, please send a pull request to GitHb.

Reference

salabim discrete event simulation

The MIT License (MIT)

Copyright (c) 2017 Ruud van der Ham, ruud@salabim.org

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

see www.salabim.org for more information, the manual and updates.

class salabim.Animate(parent=<object object>, layer=0, keep=True, visible=True, screen_coordinates=False, t0=<object object>, x0=0, y0=0, offsetx0=0, offsety0=0, circle0=<object object>, line0=<object object>, polygon0=<object object>, rectangle0=<object object>, image=<object object>, text=<object object>, font='', anchor='center', linewidth0=1, fillcolor0='black', linecolor0='black', textcolor0='black', angle0=0, fontsize0=20, width0=<object object>, t1=<object object>, x1=<object object>, y1=<object object>, offsetx1=<object object>, offsety1=<object object>, circle1=<object object>, line1=<object object>, polygon1=<object object>, rectangle1=<object object>, linewidth1=<object object>, fillcolor1=<object object>, linecolor1=<object object>, textcolor1=<object object>, angle1=<object object>, fontsize1=<object object>, width1=<object object>, env=<object object>)

defines an animation object

Parameters:
  • parent (Component) – component where this animation object belongs to (default None)
    if given, the animation object will be removed automatically upon termination of the parent
  • layer (int) – layer value
    lower layer values are on top of higher layer values (default 0)
  • keep (bool) – keep
    if False, animation object is hidden after t1, shown otherwise (default True)
  • visible (bool) – visible
    if False, animation object is not shown, shown otherwise (default True)
  • screen_coordinates (bool) – use screen_coordinates
    normally, the scale parameters are use for positioning and scaling objects.
    if True, screen_coordinates will be used instead.
  • t0 (float) – time of start of the animation (default: now)
  • x0 (float) – x-coordinate of the origin (default 0) at time t0
  • y0 (float) – y-coordinate of the origin (default 0) at time t0
  • offsetx0 (float) – offsets the x-coordinate of the object (default 0) at time t0
  • offsety0 (float) – offsets the y-coordinate of the object (default 0) at time t0
  • circle0 (tuple) – the circle at time t0 specified as a tuple (radius,)
  • line0 (tuple) – the line(s) at time t0 (xa,ya,xb,yb,xc,yc, …)
  • polygon0 (tuple) – the polygon at time t0 (xa,ya,xb,yb,xc,yc, …)
    the last point will be auto connected to the start
  • rectangle0 (tuple) – the rectangle at time t0
    (xlowerleft,ylowerlef,xupperright,yupperright)
  • image (str or PIL image) – the image to be displayed
    This may be either a filename or a PIL image
  • text (str) – the text to be displayed
  • font (str or list/tuple) – font to be used for texts
    Either a string or a list/tuple of fontnames. If not found, uses calibri or arial
  • anchor (str) – anchor position
    specifies where to put images or texts relative to the anchor point
    possible values are (default: center)
    : nw    n    ne
    w   center  e
    sw    s    se
  • linewidth0 (float) – linewidth of the contour at time t0 (default 0 = no contour)
  • fillcolor0 (colorspec) – color of interior at time t0 (default black)
  • linecolor0 (colorspec) – color of the contour at time t0 (default black)
  • textcolor0 (colorspec) – color of the text at time 0 (default black)
  • angle0 (float) – angle of the polygon at time t0 (in degrees) (default 0)
  • fontsize0 (float) – fontsize of text at time t0 (default: 20)
  • width0 (float) – width of the image to be displayed at time t0 (default: no scaling)
  • t1 (float) – time of end of the animation (default: inf)
    if keep=True, the animation will continue (frozen) after t1
  • x1 (float) – x-coordinate of the origin (default x0) at time t1
  • y1 (float) – y-coordinate of the origin (default y0) at time t1
  • offsetx1 (float) – offsets the x-coordinate of the object (default offsetx0) at time t1
  • offsety1 (float) – offsets the y-coordinate of the object (default offsety0) at time t1
  • circl10 (tuple) – the circle at time t1 specified as a tuple (radius,)
  • line1 (tuple) – the line(s) at time t1 (xa,ya,xb,yb,xc,yc, …) (default: line0)
    should have the same length as line0
  • polygon1 (tuple) – the polygon at time t1 (xa,ya,xb,yb,xc,yc, …) (default: polygon0)
    should have the same length as polygon0
  • rectangle1 (tuple) – the rectangle at time t1 (default: rectangle0)
    (xlowerleft,ylowerlef,xupperright,yupperright)
  • linewidth1 (float) – linewidth of the contour at time t1 (default linewidth0)
  • fillcolor1 (colorspec) – color of interior at time t1 (default fillcolor0)
  • linecolor1 (colorspec) – color of the contour at time t1 (default linecolor0)
  • textcolor1 (colorspec) – color of text at time t1 (default textcolor0)
  • angle1 (float) – angle of the polygon at time t1 (in degrees) (default angle0)
  • fontsize1 (float) – fontsize of text at time t1 (default: fontsize0)
  • width1 (float) – width of the image to be displayed at time t1 (default: width0)

Note

one (and only one) of the following parameters is required:
  • circle0
  • image
  • line0
  • polygon0
  • rectangle0
  • text
colors may be specified as a
  • valid colorname
  • hexname
  • tuple (R,G,B) or (R,G,B,A)

colornames may contain an additional alpha, like red#7f hexnames may be either 3 of 4 bytes long (RGB or RGBA) both colornames and hexnames may be given as a tuple with an additional alpha between 0 and 255, e.g. (255,0,255,128), (‘red’,127)`` or ('#ff00ff',128)

Permitted parameters

parameter circle image line polygon rectangle text
parent
layer
keep
scree_coordinates
t0,t1
x0,x1
y0,y1
offsetx0,offsetx1
offsety0,offsety1
circle0,circle1
         
image  
       
line0,line1    
     
polygon0,polygon1      
   
rectangle0,rectangle1        
 
text          
font            
anchor  
     
linewidth0,linewidth1
 
 
fillcolor0,fillcolor1
   
 
linecolor0,linecolor1
 
 
textcolor0,textcolor1.          
angle0,angle1  
font          
fontsize0,fontsize1          
width0,width1  
       
image(t=<object object>)

returns image and a serial number at time t use the function spec_to_image to change the image here
if there’s a change in the image, a new serial number should be returned if there’s no change, do not update the serial number

remove()

removes the animation object from the animation queue, so effectively ending this animation.

Note

The animation object might be still updated, if required

update(layer=<object object>, keep=<object object>, visible=<object object>, t0=<object object>, x0=<object object>, y0=<object object>, offsetx0=<object object>, offsety0=<object object>, circle0=<object object>, line0=<object object>, polygon0=<object object>, rectangle0=<object object>, image=<object object>, text=<object object>, font=<object object>, anchor=<object object>, linewidth0=<object object>, fillcolor0=<object object>, linecolor0=<object object>, textcolor0=<object object>, angle0=<object object>, fontsize0=<object object>, width0=<object object>, t1=<object object>, x1=<object object>, y1=<object object>, offsetx1=<object object>, offsety1=<object object>, circle1=<object object>, line1=<object object>, polygon1=<object object>, rectangle1=<object object>, linewidth1=<object object>, fillcolor1=<object object>, linecolor1=<object object>, textcolor1=<object object>, angle1=<object object>, fontsize1=<object object>, width1=<object object>)

updates an animation object

Parameters:
  • layer (int) – layer value
    lower layer values are on top of higher layer values (default see below)
  • keep (bool) – keep
    if False, animation object is hidden after t1, shown otherwise (default see below)
  • visible (bool) – visible
    if False, animation object is not shown, shown otherwise (default see below)
  • t0 (float) – time of start of the animation (default: now)
  • x0 (float) – x-coordinate of the origin (default see below) at time t0
  • y0 (float) – y-coordinate of the origin (default see below) at time t0
  • offsetx0 (float) – offsets the x-coordinate of the object (default see below) at time t0
  • offsety0 (float) – offsets the y-coordinate of the object (default see below) at time t0
  • circle0 (tuple) – the circle at time t0 specified as a tuple (radius,) (default see below)
  • line0 (tuple) – the line(s) at time t0 (xa,ya,xb,yb,xc,yc, …) (default see below)
  • polygon0 (tuple) – the polygon at time t0 (xa,ya,xb,yb,xc,yc, …)
    the last point will be auto connected to the start (default see below)
  • rectangle0 (tuple) – the rectangle at time t0
    (xlowerleft,ylowerlef,xupperright,yupperright) (default see below)
  • image (str or PIL image) – the image to be displayed
    This may be either a filename or a PIL image (default see below)
  • text (str) – the text to be displayed (default see below)
  • font (str or list/tuple) – font to be used for texts
    Either a string or a list/tuple of fontnames. (default see below) If not found, uses calibri or arial
  • anchor (str) – anchor position
    specifies where to put images or texts relative to the anchor point (default see below)
    possible values are (default: center)
    : nw    n    ne
    w   center  e
    sw    s    se
  • linewidth0 (float) – linewidth of the contour at time t0 (default see below)
  • fillcolor0 (colorspec) – color of interior/text at time t0 (default see below)
  • linecolor0 (colorspec) – color of the contour at time t0 (default see below)
  • angle0 (float) – angle of the polygon at time t0 (in degrees) (default see below)
  • fontsize0 (float) – fontsize of text at time t0 (default see below)
  • width0 (float) – width of the image to be displayed at time t0 (default see below)
  • t1 (float) – time of end of the animation (default: inf)
    if keep=True, the animation will continue (frozen) after t1
  • x1 (float) – x-coordinate of the origin (default x0) at time t1
  • y1 (float) – y-coordinate of the origin (default y0) at time t1
  • offsetx1 (float) – offsets the x-coordinate of the object (default offsetx0) at time t1
  • offsety1 (float) – offsets the y-coordinate of the object (default offsety0) at time t1
  • circle1 (tuple) – the circle at time t1 specified as a tuple (radius,)
  • line1 (tuple) – the line(s) at time t1 (xa,ya,xb,yb,xc,yc, …) (default: line0)
    should have the same length as line0
  • polygon1 (tuple) – the polygon at time t1 (xa,ya,xb,yb,xc,yc, …) (default: polygon0)
    should have the same length as polygon0
  • rectangle1 (tuple) – the rectangle at time t1 (default: rectangle0)
    (xlowerleft,ylowerlef,xupperright,yupperright)
  • linewidth1 (float) – linewidth of the contour at time t1 (default linewidth0)
  • fillcolor1 (colorspec) – color of interior/text at time t1 (default fillcolor0)
  • linecolor1 (colorspec) – color of the contour at time t1 (default linecolor0)
  • angle1 (float) – angle of the polygon at time t1 (in degrees) (default angle0)
  • fontsize1 (float) – fontsize of text at time t1 (default: fontsize0)
  • width1 (float) – width of the image to be displayed at time t1 (default: width0)

Note

The type of the animation cannot be changed with this method.
The default value of most of the parameters is the current value (at time now)

x(t=<object object>)

x-position of an animate object. May be overridden.

Parameters:t (float) – current time
Returns:x – default behaviour: linear interpolation between self.x0 and self.x1
Return type:float
class salabim.AnimateButton(x=0, y=0, width=80, height=30, linewidth=0, fillcolor='40%gray', linecolor='black', color='white', text='', font='', fontsize=15, action=None, env=<object object>)

defines a button

Parameters:
  • x (int) – x-coordinate of centre of the button in screen coordinates (default 0)
  • y (int) – y-coordinate of centre of the button in screen coordinates (default 0)
  • width (int) – width of button in screen coordinates (default 80)
  • height (int) – height of button in screen coordinates (default 30)
  • linewidth (int) – width of contour in screen coordinates (default 0=no contour)
  • fillcolor (colorspec) – color of the interior (default 40%gray)
  • linecolor (colorspec) – color of contour (default black)
  • color (colorspec) – color of the text (default white)
  • text (str or function) – text of the button (default null string)
    if text is an argumentless function, this will be called each time; the button is shown/updated
  • font (str) – font of the text (default Helvetica)
  • fontsize (int) – fontsize of the text (default 15)
  • action (function) – action to take when button is pressed
    executed when the button is pressed (default None) the function should have no arguments

Note

On CPython/PyPy platforms, the tkinter functionality is used. On Pythonista, this is emulated by salabim

remove()

removes the button object.
the ui object is removed from the ui queue, so effectively ending this ui

class salabim.AnimateSlider(layer=0, x=0, y=0, width=100, height=20, vmin=0, vmax=10, v=<object object>, resolution=1, linecolor='black', labelcolor='black', label='', font='', fontsize=12, action=None, env=<object object>)

defines a slider

Parameters:
  • x (int) – x-coordinate of centre of the slider in screen coordinates (default 0)
  • y (int) – y-coordinate of centre of the slider in screen coordinates (default 0)
  • vmin (float) – minimum value of the slider (default 0)
  • vmax (float) – maximum value of the slider (default 0)
  • v (float) – initial value of the slider (default 0)
    should be between vmin and vmax
  • resolution (float) – step size of value (default 1)
  • width (float) – width of slider in screen coordinates (default 100)
  • height (float) – height of slider in screen coordinates (default 20)
  • linewidth (float) – width of contour in screen coordinate (default 0 = no contour)
  • fillcolor (colorspec) – color of the interior (default 40%gray)
  • linecolor (colorspec) – color of contour (default black)
  • labelcolor (colorspec) – color of the label (default black)
  • label (str) – label if the slider (default null string)
    if label is an argumentless function, this function will be used to display as label, otherwise the label plus the current value of the slider will be shown
  • font (str) – font of the text (default Helvetica)
  • fontsize (int) – fontsize of the text (default 12)
  • action (function) – function executed when the slider value is changed (default None)
    the function should one arguments, being the new value
    if None (default), no action

Note

The current value of the slider is the v attibute of the slider.
On CPython/PyPy platforms, the tkinter functionality is used.
On Pythonista, this is emulated by salabim

remove()

removes the slider object
The ui object is removed from the ui queue, so effectively ending this ui

v(value=<object object>)

value

Parameters:value (float) – new value
if omitted, no change
Returns:Current value of the slider
Return type:float
class salabim.Cdf(spec, randomstream=<object object>)

Cumulative distribution function

Cdf(spec,seed)

Parameters:
  • spec (list or tuple) –

    list with x-values and corresponding cumulative density (x1,c1,x2,c2, …xn,cn)
    Requirements:

    x1<=x2<= …<=xn
    c1<=c2<=cn
    c1=0
    cn>0
    all cumulative densities are auto scaled according to cn, so no need to set cn to 1 or 100.
  • randomstream (randomstream) – if omitted, random will be used
    if used as random.Random(12299) it defines a new stream with the specified seed
mean()
Returns:mean of the distribution
Return type:float
sample()
Returns:sample
Return type:float
class salabim.Component(name=<object object>, at=<object object>, delay=0, urgent=False, process=<object object>, suppress_trace=False, mode=None, env=<object object>, *args, **kwargs)

Component object

A salabim component is used as a data component (primarily for queueing) or as a component with a process
Usually, a component will be defined as a subclass of Component.

Parameters:
  • name (str) – name of the component.
    if the name ends with a period (.), auto serializing will be applied
    if omitted, the name will be derived from the class it is defined in (lowercased)
  • at (float) – schedule time
    if omitted, now is used
  • delay (float) – schedule with a delay
    if omitted, no delay
  • urgent (bool) – urgency indicator
    if False (default), the component will be scheduled behind all other components scheduled for the same time
    if True, the component will be scheduled in front of all components scheduled for the same time
  • process (str) – name of process to be started.
    if omitted, it will try to start self.process()
    if None, no process will be started even if self.process() exists, i.e. become a data component.
    note that the function must be a generator, i.e. contains at least one yield.
  • suppress_trace (bool) – suppress_trace indicator
    if True, this component will be excluded from the trace
    If False (default), the component will be traced
    Can be queried or set later with the suppress_trace method.
  • mode (str preferred) – mode
    will be used in trace and can be used in animations
    if omitted, the mode will be None.
    also mode_time will be set to now.
  • env (Environment) – environment where the component is defined
    if omitted, _default_env will be used
activate(at=<object object>, delay=0, urgent=False, process=<object object>, keep_request=False, keep_wait=False, mode=<object object>)

activate component

Parameters:
  • at (float) – schedule time
    if omitted, now is used
    inf is allowed
  • delay (float) – schedule with a delay
    if omitted, no delay
  • urgent (bool) – urgency indicator
    if False (default), the component will be scheduled behind all other components scheduled for the same time
    if True, the component will be scheduled in front of all components scheduled for the same time
  • process (str) – name of process to be started.
    if omitted, process will not be changed
    if the component is a data component, the generator function process will be used as the default process.
    note that the function must be a generator, i.e. contains at least one yield.
  • keep_request (bool) – this affects only components that are requesting.
    if True, the requests will be kept and thus the status will remain requesting
    if False (the default), the request(s) will be canceled and the status will become scheduled
  • keep_wait (bool) – this affects only components that are waiting.
    if True, the waits will be kept and thus the status will remain waiting
    if False (the default), the wait(s) will be canceled and the status will become scheduled
  • mode (str preferred) – mode
    will be used in trace and can be used in animations
    if nothing specified, the mode will be unchanged.
    also mode_time will be set to now, if mode is set.

Note

if to be applied for the current component, use yield self.activate().
if both at and delay are specified, the component becomes current at the sum of the two values.

base_name()
Returns:base name of the component (the name used at init or name)
Return type:str
cancel(mode=<object object>)

cancel component (makes the component data)

Parameters:mode (str preferred) – mode
will be used in trace and can be used in animations
if nothing specified, the mode will be unchanged.
also mode_time will be set to now, if mode is set.

Note

if to be used for the current component, use yield self.cancel(...).

claimed_quantity(resource)
Parameters:resource (Resoure) – resource to be queried
Returns:the claimed quantity from a resource – if the resource is not claimed, 0 will be returned
Return type:float or int
claimed_resources()
Returns:list of claimed resources
Return type:list
creation_time()
Returns:time the component was created
Return type:float
enter(q)

enters a queue at the tail

Parameters:q (Queue) – queue to enter

Note

the priority will be set to the priority of the tail component of the queue, if any or 0 if queue is empty

enter_at_head(q)

enters a queue at the head

Parameters:q (Queue) – queue to enter

Note

the priority will be set to the priority of the head component of the queue, if any or 0 if queue is empty

enter_behind(q, poscomponent)

enters a queue behind a component

Parameters:
  • q (Queue) – queue to enter
  • poscomponent (Component) – component to be entered behind

Note

the priority will be set to the priority of poscomponent

enter_in_front_of(q, poscomponent)

enters a queue in front of a component

Parameters:
  • q (Queue) – queue to enter
  • poscomponent (Component) – component to be entered in front of

Note

the priority will be set to the priority of poscomponent

enter_sorted(q, priority)

enters a queue, according to the priority

Parameters:
  • q (Queue) – queue to enter
  • priority (float) – priority in the queue

Note

The component is placed just before the first component with a priority > given priority

enter_time(q)
Parameters:q (Queue) – queue where component belongs to
Returns:time the component entered the queue
Return type:float
failed()
Returns:
  • True, if the latest request/wait has failed (either by timeout or external) (bool)
  • False, otherwise
hold(duration=<object object>, till=<object object>, urgent=False, mode=<object object>)

hold the component

Parameters:
  • duration (float) – specifies the duration
    if omitted, 0 is used
    inf is allowed
  • till (float) – specifies at what time the component will become current
    if omitted, now is used
    inf is allowed
  • urgent (bool) – urgency indicator
    if False (default), the component will be scheduled behind all other components scheduled for the same time
    if True, the component will be scheduled in front of all components scheduled for the same time
  • mode (str preferred) – mode
    will be used in trace and can be used in animations
    if nothing specified, the mode will be unchanged.
    also mode_time will be set to now, if mode is set.

Note

if to be used for the current component, use yield self.hold(...).

if both duration and till are specified, the component will become current at the sum of these two.

index_in_queue(q)
Parameters:q (Queue) – queue to be queried
Returns:index of component in q – if component belongs to q
-1 if component does not belong to q
Return type:int
iscurrent()
Returns:True if status is current, False otherwise
Return type:bool

Note

Be sure to always include the parentheses, otherwise the result will be always True!

isdata()
Returns:True if status is data, False otherwise
Return type:bool

Note

Be sure to always include the parentheses, otherwise the result will be always True!

ispassive()
Returns:True if status is passive, False otherwise
Return type:bool

Note

Be sure to always include the parentheses, otherwise the result will be always True!

isrequesting()
Returns:True if status is requesting, False otherwise
Return type:bool

Note

Be sure to always include the parentheses, otherwise the result will be always True!

isscheduled()
Returns:True if status is scheduled, False otherwise
Return type:bool

Note

Be sure to always include the parentheses, otherwise the result will be always True!

isstandby()
Returns:True if status is standby, False otherwise
Return type:bool

Note

Be sure to always include the parentheses, otherwise the result will be always True

iswaiting()
Returns:True if status is waiting, False otherwise
Return type:bool

Note

Be sure to always include the parentheses, otherwise the result will be always True!

leave(q)

leave queue

Parameters:q (Queue) – queue to leave

Note

statistics are updated accordingly

mode(value=<object object>)
Parameters:value (any, str recommended) – new mode
if omitted, no change
mode_time will be set if a new mode is specified
Returns:mode of the component – the mode is useful for tracing and animations.
Usually the mode will be set in a call to passivate, hold, activate, request or standby.
Return type:any, usually str
mode_time()
Returns:time the component got it’s latest mode – For a new component this is the time the component was created.
this function is particularly useful for animations.
Return type:float
name(txt=<object object>)
Parameters:txt (str) – name of the component
if txt ends with a period, the name will be serialized
if omittted, no change
Returns:Name of the component
Return type:str
passivate(mode=<object object>)

passivate the component

mode : str preferred
mode
will be used in trace and can be used in animations
if nothing is specified, the mode will be unchanged.
also mode_time will be set to now, if mode is set.

Note

if to be used for the current component (nearly always the case), use yield self.passivate(...).

predecessor(q)
Parameters:
  • q (Queue) – queue where the component belongs to
  • Returns (Component) – predecessor of the component in the queue if component is not at the head.
    returns None if component is at the head.
priority(q, priority=<object object>)

gets/sets the priority of a component in a queue

Parameters:
  • q (Queue) – queue where the component belongs to
  • priority (float) – priority in queue
    if omitted, no change
Returns:

the priority of the component in the queue

Return type:

float

Note

if you change the priority, the order of the queue may change

release(*args)

release a quantity from a resource or resources

Parameters:args (sequence of items, where each items can be) –
  • a resources, where quantity=current claimed quantity
  • a tuple/list containing a resource and the quantity to be released

Note

It is not possible to release from an anonymous resource, this way. Use Resource.release() in that case.

Example

yield self.request(r1,(r2,2),(r3,3,100))
–> requests 1 from r1, 2 from r2 and 3 from r3 with priority 100

c1.release
–> releases 1 from r1, 2 from r2 and 3 from r3

yield self.request(r1,(r2,2),(r3,3,100))
c1.release((r2,1))
–> releases 1 from r2

yield self.request(r1,(r2,2),(r3,3,100))
c1.release((r2,1),r3)
–> releases 2 from r2,and 3 from r3

request(*args, **kwargs)

request from a resource or resources

Parameters:
  • args (sequence) –
    • sequence of resources, where quantity=1, priority=tail of requesters queue)
    • sequence of tuples/lists containing
      resource, a quantity and optionally a priority. if the priority is not specified, this request for the resources be added to the tail of the requesters queue
  • fail_at (float) – time out
    if the request is not honored before fail_at, the request will be cancelled and the parameter failed will be set.
    if not specified, the request will not time out.
  • fail_delay (float) – time out
    if the request is not honored before now+fail_delay, the request will be cancelled and the parameter failed will be set.
    if not specified, the request will not time out.
  • mode (str preferred) – mode
    will be used in trace and can be used in animations
    if nothing specified, the mode will be unchanged.
    also mode_time will be set to now, if mode is set.

Note

Not allowed for data components or main.

If to be used for the current component (which will be nearly always the case), use yield self.request(...).

If the same resource is specified more that once, the quantities are summed

The requested quantity may exceed the current capacity of a resource

The parameter failed will be reset by a calling request or wait

Example

yield self.request(r1)
–> requests 1 from r1
yield self.request(r1,r2)
–> requests 1 from r1 and 1 from r2
yield self.request(r1,(r2,2),(r3,3,100))
–> requests 1 from r1, 2 from r2 and 3 from r3 with priority 100
yield self.request((r1,1),(r2,2))
–> requests 1 from r1, 2 from r2

requested_quantity(resource)
Parameters:resource (Resoure) – resource to be queried
Returns:the requested (not yet honored) quantity from a resource – if there is no request for the resource, 0 will be returned
Return type:float or int
requested_resources()
Returns:list of requested resources
Return type:list
running_process()
Returns:name of the running process – if data component, None
Return type:str
scheduled_time()
Returns:time the component scheduled for, if it is scheduled – returns inf otherwise
Return type:float
sequence_number()
Returns:sequence_number of the component – (the sequence number at init or name)
normally this will be the integer value of a serialized name, but also non serialized names (without a dot at the end) will be numbered)
Return type:int
setup(*args, **kwargs)

called immediately after initialization of a component.

by default this is a dummy method, but it can be overridden.

Example

class Car(sim.Component):
def setup(self, color):
self.color = color
def process(self):

redcar=Car(color=’red’)
bluecar=Car(color=’blue’)

standby(mode=<object object>)

puts the component in standby mode

Parameters:mode (str preferred) – mode
will be used in trace and can be used in animations
if nothing specified, the mode will be unchanged.
also mode_time will be set to now, if mode is set.

Note

Not allowed for data components or main.

if to be used for the current component (which will be nearly always the case), use yield self.standby(...).

status()

returns the status of a component

possible values are
  • data
  • passive
  • scheduled
  • requesting
  • current
  • standby
successor(q)
Parameters:q (Queue) – queue where the component belongs to
Returns:the successor of the component in the queue – if component is not at the tail.
returns None if component is at the tail.
Return type:Component
suppress_trace(value=<object object>)
Parameters:value (bool) – new suppress_trace value
if omitted, no change
Returns:suppress_status – components with the suppress_status of False, will be ignored in the trace
Return type:bool
wait(*args, **kwargs)

wait for any or all of the given state values are met

Parameters:
  • args (sequence of items, where each item can be) –
    • a state, where value=True, priority=tail of waiters queue)
    • a tuple/list containing
      state, a value and optionally a priority.
      if the priority is not specified, this component will be added to the tail of the waiters queue
  • fail_at (float) – time out
    if the wait is not honored before fail_at, the wait will be cancelled and the parameter failed will be set.
    if not specified, the wait will not time out.
  • fail_delay (float) – time out
    if the wait is not honored before now+fail_delay, the request will be cancelled and the parameter failed will be set.
    if not specified, the wait will not time out.
  • all (bool) – if False (default), continue, if any of the given state/values is met
    if True, continue if all of the given state/values are met
  • mode (str preferred) – mode
    will be used in trace and can be used in animations
    if nothing specified, the mode will be unchanged.
    also mode_time will be set to now, if mode is set.

Note

Not allowed for data components or main.

If to be used for the current component (which will be nearly always the case), use yield self.wait(...).

It is allowed to wait for more than one value of a state
the parameter failed will be reset by a calling wait

If you want to check for all components to meet a value (and clause), use Component.wait(…, all=True)

The value may be specified in three different ways:

  • constant, that value is just compared to state.value()
    yield self.wait((light,’red’))
  • an expression, containg one or more $-signs the $ is replaced by state.value(), each time the condition is tested.
    self refers to the component under test, state refers to the state under test.
    yield self.wait((light,’$ in (“red”,”yellow”)’))
    yield self.wait((level,’$<30’))
  • a function. In that case the parameter should function that should accept three arguments: the value, the component under test and the state under test.
    usually the function will be a lambda function, but that’s not a requirement.
    yield self.wait((light,lambda t, comp, state: t in (‘red’,’yellow’)))
    yield self.wait((level,lambda t, comp, state: t < 30))

Example

yield self.wait(s1)
–> waits for s1.value()==True
yield self.wait(s1,s2)
–> waits for s1.value()==True or s2.value==True
yield self.wait((s1,False,100),(s2,’on’),s3)
–> waits for s1.value()==False or s2.value==’on’ or s3.value()==True s1 is at the tail of waiters, because of the set priority yield self.wait(s1,s2,all=True)
–> waits for s1.value()==True and s2.value==True

class salabim.Constant(value, randomstream=<object object>)

constant distribution

Constant(value,randomstream)

Parameters:
  • value (float) – value to be returned in sample
  • randomstream (randomstream) – randomstream to be used
    if omitted, random will be used
    if used as random.Random(12299) it assigns a new stream with the specified seed
    Note that this is only for compatibility with other distributions
mean()

returns the mean of the distribution

sample()

returns sample (is always the specified constant)

class salabim.Distribution(spec, randomstream=<object object>)

Generate a distribution from a string

Distribution(spec,randomstream)

Parameters:
  • spec (str) –
    • string containing a valid salabim distribution, where only the first letters are relevant and casing is not important
    • string containing one float (c1), resulting in Constant(c1)
    • string containing two floats seperated by a comma (c1,c2), resulting in a Uniform(c1,c2)
    • string containing three floats, separated by commas (c1,c2,c3), resulting in a Triangular(c1,c2,c3)
  • randomstream (randomstream) – if omitted, random will be used
    if used as random.Random(12299) it assigns a new stream with the specified seed

Note

The randomstream in the specifying string is ignored.
It is possible to use expressions in the specification, as long these are valid within the context of the salabim module, which usually implies a global variable of the salabim package.

Examples

Uniform(13) ==> Uniform(13)
Uni(12,15) ==> Uniform(12,15)
UNIF(12,15) ==> Uniform(12,15)
N(12,3) ==> Normal(12,3)
Tri(10,20). ==> Triangular(10,20,15)
10. ==> Constant(10)
12,15 ==> Uniform(12,15)
(12,15) ==> Uniform(12,15)
Exp(a) ==> Exponential(100), provided sim.a=100

mean()
Returns:mean of the distribution
Return type:float
sample()
Returns:sample
Return type:float
class salabim.Environment(trace=False, random_seed=<object object>, name=<object object>, is_default_env=True)

environment object

Parameters:
  • trace (bool) – defines whether to trace or not
    if omitted, False
  • random_seed (hashable object, usually int) – the seed for random, equivalent to random.seed()
    if None, a purely random value (based on the current time) will be used (not reproducable)
    if the null string (‘’), no action on random is taken
    if omitted, 1234567 will be used.
  • name (str) – name of the environment
    if the name ends with a period (.), auto serializing will be applied
    if omitted, the name environment. (serialized)
  • is_default_env (bool) – if True, this environment becomes the default environment
    if False, no change
    if omitted, this environment becomes the default environment

Note

The trace may be switched on/off later with trace
The seed may be later set with random_seed()
Initially, the random stream will be seeded with the value 1234567. If required to be purely, not not reproducable, values, use random_seed=None.

an_system_buttons()

function to initialize the system animation buttons
called by run(), if animation is True.
may be overridden to change the standard behaviour.

an_system_clocktext()

function to initialize the system clocktext
called by run(), if animation is True.
may be overridden to change the standard behaviour.

an_system_modelname()

function to show the modelname
called by run(), if animation is True.
may be overridden to change the standard behaviour.

animation_parameters(animate=<object object>, speed=<object object>, width=<object object>, height=<object object>, x0=<object object>, y0=0, x1=<object object>, background_color=<object object>, fps=<object object>, modelname=<object object>, use_toplevel=<object object>, show_fps=<object object>, show_speed=<object object>, show_time=<object object>, video=<object object>)

set animation parameters

Parameters:
  • animate (bool) – animate indicator
    if omitted, True, i.e. animation on
    Installation of PIL is required for animation.
  • speed (float) – speed
    specifies how much faster or slower than real time the animation will run. e.g. if 2, 2 simulation time units will be displayed per second.
  • width (int) – width of the animation in screen coordinates
    if omitted, no change. At init of the environment, the width will be set to 1024 for CPython and the current screen width for Pythonista.
  • height (int) – height of the animation in screen coordinates
    if omitted, no change. At init of the environment, the height will be set to 768 for CPython and the current screen height for Pythonista.
  • x0 (float) – user x-coordinate of the lower left corner
    if omitted, no change. At init of the environment, x0 will be set to 0.
  • y0 (float) – user y_coordinate of the lower left corner
    if omitted, no change. At init of the environment, y0 will be set to 0.
  • x1 (float) – user x-coordinate of the lower right corner
    if omitted, no change. At init of the environment, x1 will be set to 1024 for CPython and the current screen width for Pythonista.
  • background_color (colorspec) – color of the background
    if omitted, no change. At init of the environment, this will be set to white.
  • fps (float) – number of frames per second
  • modelname (str) – name of model to be shown in upper left corner, along with text ‘a salabim model’
    if omitted, no change. At init of the environment, this will be set to the null string, which implies suppression of this feature.
  • use_toplevel (bool) – if salabim animation is used in parallel with other modules using tkinter, it might be necessary to initialize the root with tkinter.TopLevel(). In that case, set this parameter to True.
    if False (default), the root will be initialized with tkinter.Tk()
  • show_fps (bool) – if True, show the number of frames per second (default)
    if False, do not show the number of frames per second
  • show_speed (bool) – if True, show the animation speed (default)
    if False, do not show the animation speed
  • show_time (bool) – if True, show the time (default)
    if False, do not show the time
  • video (str) – if video is not omitted, a mp4 format video with the name video will be created.
    The video has to have a .mp4 etension
    This requires installation of opencv (cv2) and numpy.

Note

The y-coordinate of the upper right corner is determined automatically in such a way that the x and scaling are the same.

Note that changing the parameters x0, x1, y0, width, height, background_color, modelname, use_toplevelmand video, animate has no effect on the current animation. So to avoid confusion, do not use change these parameters when an animation is running.
On the other hand, changing speed, show_fps, show_time, show_speed and fps can be useful in a running animation.

base_name()

returns the base name of the environment (the name used at init or name)

current_component()
Returns:the current_component
Return type:Component
main()
Returns:the main component
Return type:Component
name(txt=<object object>)
Parameters:txt (str) – name of the environment
if txt ends with a period, the name will be serialized
if omittted, no change
Returns:Name of the environment
Return type:str
now()
Returns:the current simulation time
Return type:float
peek()

returns the time of the next component to become current
if there are no more events, peek will return inf
for advance use with animation / GUI event loops

print_trace(s1='', s2='', s3='', s4='')

prints a trace line

Parameters:
  • s1 (str) – part 1 (usually formatted now), padded to 10 characters
  • s2 (str) – part 2 (usually formatted now), padded to 20 characters
  • s3 (str) – part 3 (usually formatted now), padded to 35 characters
  • s4 (str) – part 4 (usually formatted now)

Note

if the current component’s suppress_trace is True, nothing is printed

run(duration=<object object>, till=<object object>)

start execution of the simulation

Parameters:
  • duration (float) – schedule with a delay of duration
    if 0, now is used
  • till (float) – schedule time
    if omitted, inf is assumed

Note

only issue run() from the main level

sequence_number()

returns the sequence_number of the environment (the sequence number at init or name)
normally this will be the integer value of a serialized name, but also non serialized names (without a dot at the end) will be numbered)

step()

executes the next step of the future event list

for advanced use with animation / GUI loops

trace(value=<object object>)

trace status

Parameters:value (bool) – new trace status
if omitted, no change
Returns:trace status
Return type:bool

Note

If you want to test the status, always include parentheses, like

if env.trace():
class salabim.Exponential(mean, randomstream=<object object>)

exponential distribution

Exponential(mean,randomstream)

Parameters:
  • mean (float) – mean of the distribtion
    must be >0
  • randomstream (randomstream) – randomstream to be used
    if omitted, random will be used
    if used as random.Random(12299) it assigns a new stream with the specified seed
mean()

returns the mean of the distribution

sample()

returns sample

class salabim.Monitor(name=<object object>, monitor=True, type='any', env=<object object>)

Monitor object

Parameters:
  • name (str) – name of the monitor
    if the name ends with a period (.), auto serializing will be applied
    if omitted, the name monitor. (serialized)
  • monitor (bool) – if True (default), monitoring will be on.
    if False, monitoring is disabled
    it is possible to control monitoring later, with the monitor method
  • type (str) –
    specifies how tallied values are to be stored
    • ’any’ (default) stores values in a list. This allows
      non numeric values. In calculations the values are forced to a numeric value (0 if not possible)
    • ’bool’ (True, False) Actually integer >= 0 <= 255 1 byte
    • ’int8’ integer >= -128 <= 127 1 byte
    • ’uint8’ integer >= 0 <= 255 1 byte
    • ’int16’ integer >= -32768 <= 32767 2 bytes
    • ’uint16’ integer >= 0 <= 65535 2 bytes
    • ’int32’ integer >= -2147483648<= 2147483647 4 bytes
    • ’uint32’ integer >= 0 <= 4294967295 4 bytes
    • ’int64’ integer >= -9223372036854775808 <= 9223372036854775807 8 bytes
    • ’uint64’ integer >= 0 <= 18446744073709551615 8 bytes
    • ’float’ float 8 bytes
  • env (Environment) – environment where the monitor is defined
    if omitted, default_env will be used
bin_count(lowerbound, upperbound, ex0=False)

count of the number of tallied values in range (lowerbound,upperbound]

Parameters:
  • lowerbound (float) – non inclusive lowerbound
  • upperbound (float) – inclusive upperbound
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:

number of values >lowerbound and <=upperbound

Return type:

int

maximum(ex0=False)

maximum of tallied values

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:maximum
Return type:float
mean(ex0=False)

mean of tallied values

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:mean
Return type:float
median(ex0=False)

median of tallied values weighted wioth their durations

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:median
Return type:float
minimum(ex0=False)

minimum of tallied values

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:minimum
Return type:float
monitor(value=<object object>)

enables/disabled monitor

Parameters:value (bool) – if True, monitoring will be on.
if False, monitoring is disabled
if omitted, no change
Returns:True, if monitoring enabled. False, if not
Return type:bool
name(txt=<object object>)
Parameters:txt (str) – name of the monitor
if txt ends with a period, the name will be serialized
if omittted, no change
Returns:Name of the monitor
Return type:str
number_of_entries(ex0=False)

count of the number of entries

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:number of entries
Return type:int
number_of_entries_zero()

count of the number of zero entries

Returns:number of zero entries
Return type:int
percentile(q, ex0=False)

q-th percentile of tallied values

Parameters:
  • q (float) – percentage of the distribution
    must be between 0 and 100
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:

q-th percentile – 0 returns the minimum, 50 the median and 100 the maximum

Return type:

float

print_histogram(number_of_bins=30, lowerbound=0, bin_width=1, ex0=False)

print monitor statistics and histogram

Parameters:
  • number_of_bins (int) – number of bins
    if 0, also the header of the histogram will be surpressed
  • lowerbound (float) – first bin
  • bin_width (float) – width of the bins
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
print_statistics(show_header=True, show_legend=True, do_indent=False)

print monitor statistics

Parameters:
  • show_header (bool) – primarily for internal use
  • show_legend (bool) – primarily for internal use
  • do_indent (bool) – primarily for internal use
reset(monitor=<object object>)

resets monitor

Parameters:monitor (bool) – if True, monitoring will be on.
if False, monitoring is disabled if omitted, no change of monitoring state
std(ex0=False)

standard deviation of tallied values

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:standard deviation
Return type:float
tally(x)
Parameters:x (any, preferably int, float or translatable into int or float) – value to be tallied
x(ex0=False, force_numeric=True)

array/list of tallied values

Parameters:
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
  • convert_to_numeric (bool) – if True (default), convert non numeric tallied values numeric if possible, otherwise assume 0
    if False, do not interpret x-values, return as list if type is list
Returns:

all tallied values

Return type:

array/list

class salabim.MonitorTimestamp(name, getter, monitor=True, type='any', env=<object object>)

monitortimestamp object

Parameters:
  • name (str) – name to be used at print_histogram
  • getter (function) – this function must return the current value
    usually this will be a method of an object
  • monitor (bool) – if True (default), monitoring will be on.
    if False, monitoring is disabled
    it is possible to control monitoring later, with the monitor method
  • type (str) –

    specifies how tallied values are to be stored Using a int, uint of float type results in less memory usage and better performance. Note that the getter should never return the number not to use as this is used to indicate ‘off’

    • ’any’ (default) stores values in a list. This allows for
      non numeric values. In calculations the values are forced to a numeric value (0 if not possible) do not use -inf
    • ’bool’ bool (False, True). Actually integer >= 0 <= 254 1 byte do not use 255
    • ’int8’ integer >= -127 <= 127 1 byte do not use -128
    • ’uint8’ integer >= 0 <= 254 1 byte do not use 255
    • ’int16’ integer >= -32767 <= 32767 2 bytes do not use -32768
    • ’uint16’ integer >= 0 <= 65534 2 bytes do not use 65535
    • ’int32’ integer >= -2147483647 <= 2147483647 4 bytes do not use -2147483648
    • ’uint32’ integer >= 0 <= 4294967294 4 bytes do not use 4294967295
    • ’int64’ integer >= -9223372036854775807 <= 9223372036854775807 8 bytes do not use -9223372036854775808
    • ’uint64’ integer >= 0 <= 18446744073709551614 8 bytes do not use 18446744073709551615
    • ’float’ float 8 bytes do not use -inf
  • env (Environment) – environment where the monitor is defined
    if omitted, default_env will be used

Note

A MonitorTimestamp collects both the value and the time. All statistics are based on the durations as weights.

Example

Tallied at time 0: 10 (xnow in definition of monitortimestamp)
Tallied at time 50: 11
Tallied at time 70: 12
Tallied at time 80: 10
Now = 100

This results in:
x= 10 duration 50
x= 11 duration 20
x= 12 duration 10
x= 10 duration 20

And thus a mean of (10*50+11*20+12*10+10*20)/(50+20+10+20)

bin_count(lowerbound, upperbound, ex0=False)

count of the number of tallied values, weighted with the duration in range (lowerbound,upperbound]

Parameters:
  • lowerbound (float) – non inclusive lowerbound
  • upperbound (float) – inclusive upperbound
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:

number of values >lowerbound and <=upperbound

Return type:

int

duration(ex0=False)

total duration

Parameters:ex0 (bool) – if False (default), include samples with value 0. if True, exclude zero samples.
Returns:
Return type:total duration
duration_zero()

total duration of samples with value 0

Returns:
Return type:total duration of zero samples
maximum(ex0=False)

maximum of tallied values

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:maximum
Return type:float
mean(ex0=False)

mean of tallied values, weighted with their durations

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:mean
Return type:float
median(ex0=False)

median of tallied values weighted with their durations

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:median
Return type:float
minimum(ex0=False)

minimum of tallied values

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:minimum
Return type:float
monitor(value=<object object>)

enables/disabled timestamped monitor

Parameters:value (bool) – if True, monitoring will be on.
if False, monitoring is disabled
if omitted, no change
Returns:True, if monitoring enabled. False, if not
Return type:bool
name(txt=<object object>)
Parameters:txt (str) – name of the monitor
if txt ends with a period, the name will be serialized
if omittted, no change
Returns:Name of the monitor
Return type:str
percentile(q, ex0=False)

q-th percentile of tallied values, weighted with their durations

Parameters:
  • q (float) – percentage of the distribution
    must be between 0 and 100
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:

q-th percentile – 0 returns the minimum, 50 the median and 100 the maximum

Return type:

float

print_histogram(number_of_bins=30, lowerbound=0, bin_width=1, ex0=False)

print timestamped monitor statistics and histogram

Parameters:
  • number_of_bins (int) – number of bins
    if 0, also the header of the histogram will be surpressed
  • lowerbound (float) – first bin
  • bin_width (float) – width of the bins
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
print_statistics(show_header=True, show_legend=True, do_indent=False)

print timestamped monitor statistics

Parameters:
  • show_header (bool) – primarily for internal use
  • show_legend (bool) – primarily for internal use
  • do_indent (bool) – primarily for internal use
reset(monitor=<object object>)

resets timestamped monitor

Parameters:monitor (bool) – if True (default), monitoring will be on.
if False, monitoring is disabled
if omitted, the monitor state remains unchanged
std(ex0=False)

standard deviation of tallied values, weighted with their durations

Parameters:ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
Returns:standard deviation
Return type:float
tally()

tally the current value, if monitor is on

tx(ex0=False, exoff=False, force_numeric=False)

tuple of array with timestamps and array/list with x-values

Parameters:
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
  • exoff (bool) – if False (default), include self.off. if True, exclude self.off’s
  • force_numeric (bool) – if True (default), convert non numeric tallied values numeric if possible, otherwise assume 0
    if False, do not interpret x-values, return as list if type is list
Returns:

array with timestamps and array/list with x-values

Return type:

tuple

Note

The value self.off is stored when monitoring is turned off

xduration(ex0=False, force_numeric=True)

tuple of array with x-values and array with durations

Parameters:
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
  • force_numeric (bool) – if True (default), convert non numeric tallied values numeric if possible, otherwise assume 0
    if False, do not interpret x-values, return as list if type is list
Returns:

array/list with x-values and array with durations

Return type:

tuple

xt(ex0=False, exoff=False, force_numeric=True)

tuple of array/list with x-values and array with timestamp

Parameters:
  • ex0 (bool) – if False (default), include zeroes. if True, exclude zeroes
  • exoff (bool) – if False (default), include self.off. if True, exclude self.off’s
  • force_numeric (bool) – if True (default), convert non numeric tallied values numeric if possible, otherwise assume 0
    if False, do not interpret x-values, return as list if type is list
Returns:

array/list with x-values and array with timestamps

Return type:

tuple

Note

The value self.off is stored when monitoring is turned off

class salabim.Normal(mean, standard_deviation=<object object>, randomstream=<object object>)

normal distribution

Normal(mean,standard_deviation,randomstream)

Parameters:
  • mean (float) – mean of the distribution
  • standard_deviation (float) – standard deviation of the distribution
    if omitted, 0 is used, thus effectively a contant distributiin
    must be >=0
  • randomstream (randomstream) – randomstream to be used
    if omitted, random will be used
    if used as random.Random(12299) it assigns a new stream with the specified seed
mean()

returns the mean of the distribution

sample()

returns sample

class salabim.Pdf(spec, probabilities=<object object>, randomstream=<object object>)

Probability distribution function

Pdf(spec,probabilities,seed)

Parameters:
  • spec (list or tuple) –

    either

    • if no probabilities specified
      : list with x-values and corresponding probability (x0, p0, x1, p1, …xn,pn)
    • if probabilities is specified
      : list with x-values
  • probabilities (list, tuple or float) – if omitted, spec contains the probabilities
    the list (p0, p1, …pn) contains the probabilities of the corresponding x-values from spec.
    alternatively, if a float is given (e.g. 1), all x-values have equal probability. The value is not important.
  • randomstream (randomstream) – if omitted, random will be used
    if used as random.Random(12299) it assigns a new stream with the specified seed

Note

p0+p1=…+pn>0
all densities are auto scaled according to the sum of p0 to pn, so no need to have p0 to pn add up to 1 or 100.
The x-values may be any type.
If it is a salabim distribution, not the distribution, but a sample will be returned when calling sample.

mean()
Returns:mean of the distribution – if the mean can’t be calculated (if not all x-values are scalars or distributions), nan will be returned.
Return type:float
sample()
Returns:sample
Return type:any (usually float)
class salabim.Queue(name=<object object>, monitor=True, env=<object object>, _isinternal=False)

Queue object

Parameters:
  • name (str) – name of the queue
    if the name ends with a period (.), auto serializing will be applied
    if omitted, the name queue. (serialized)
  • monitor (bool) – if True (default) , both length and length_of_stay are monitored
    if False, monitoring is disabled.
  • env (Environment) – environment where the queue is defined
    if omitted, default_env will be used
  • _isinternal (bool) – for internal use only
add(component)

adds a component to the tail of a queue

Parameters:component (Component) – component to be added to the tail of the queue
may not be member of the queue yet

Note

the priority will be set to the priority of the tail of the queue, if any or 0 if queue is empty

add_at_head(component)

adds a component to the head of a queue

Parameters:component (Component) – component to be added to the head of the queue
may not be member of the queue yet

Note

the priority will be set to the priority of the head of the queue, if any or 0 if queue is empty

add_behind(component, poscomponent)

adds a component to a queue, just behind a component

Parameters:
  • component (Component) – component to be added to the queue
    may not be member of the queue yet
  • poscomponent (Component) – component behind which component will be inserted
    must be member of the queue

Note

the priority of component will be set to the priority of poscomponent

add_in_front_of(component, poscomponent)

adds a component to a queue, just in front of a component

Parameters:
  • component (Component) – component to be added to the queue
    may not be member of the queue yet
  • poscomponent (Component) – component in front of which component will be inserted
    must be member of the queue

Note

the priority of component will be set to the priority of poscomponent

add_sorted(component, priority)

adds a component to a queue, according to the priority

Parameters:
  • component (Component) – component to be added to the queue
    may not be member of the queue yet
  • priority (float) – priority of the component

Note

component will be placed just after the last component with a priority <= priority

base_name()
Returns:base name of the queue (the name used at init or name)
Return type:str
clear()

empties a queue

removes all components from a queue

component_with_name(txt)

returns a component in the queue according to its name

Parameters:txt (str) – name of component to be retrieved
Returns:the first component in the queue with name txt – returns None if not found
Return type:Component
copy(name)

returns a copy of two queues

Parameters:
  • name (str) – name of the new queue
  • resulting queue will contain all elements of self n (the) –
  • priority will be copied from original queue. (The) –
  • the order will be maintained. (Also,) –
difference(q, name)

returns the difference of two queues

Parameters:
  • q (Queue) – queue to be ‘subtracted’ from self
  • name (str) – name of the new queue
  • resulting queue will contain all elements of self that are not (the) –
  • q n (in) –
  • priority will be copied from the original queue. (the) –
  • the order will be maintained. (Also,) –
head()
Returns:the head component of the queue, if any. None otherwise
Return type:Component

Note

q[0] is a more Pythonic way to access the head of the queue

index(component)

get the index of a component in the queue

Parameters:component (Component) – component to be queried
does not need to be in the queue
Returns:index of component in the queue – 0 denotes the head,
returns -1 if component is not in the queue
Return type:int
intersect(q, name)

returns the intersect of two queues

Parameters:
  • q (Queue) – queue to be intersected with self
  • name (str) – name of the new queue
  • resulting queue will contain all elements that (the) –
  • in self and q n (are) –
  • priority will be set to 0 for all components in the (the) –
  • queue n (resulting) –
  • order of the resulting queue is as follows n (the) –
  • the same order as in self. (in) –
monitor(value)

enables/disables monitoring of length_of_stay and length

Parameters:value (bool) – if True, monitoring will be on.
if False, monitoring is disabled

Note

it is possible to individually control monitoring with length_of_stay.monitor() and length.monitor()

move(name)

makes a copy of a queue and empties the original

Parameters:
  • name (str) – name of the new queue
  • resulting queue will contain all elements of self, (the) –
  • the proper priority n (with) –
  • will be emptied (self) –
name(txt=<object object>)
Parameters:txt (str) – name of the queue
if txt ends with a period, the name will be serialized
if omittted, no change
Returns:Name of the queue
Return type:str
pop()

removes the head component, if any.

Returns:The head component – None if the queue is empty
Return type:Component
predecessor(component)

predecessor in queue

Parameters:component (Component) – component whose predecessor to return
must be member of the queue
Returns:predecessor of component, if any – None otherwise.
Return type:Component
print_statistics()

prints a summary of statistics of a queue

remove(component)

removes component from the queue

Parameters:component (Component) – component to be removed
must be member of the queue
reset_monitors(monitor=<object object>)

resets queue monitor length_of_stay and time stamped monitor length

Parameters:monitor (bool) – if True, monitoring will be on.
if False, monitoring is disabled
if omitted, no change of monitoring state

Note

it is possible to reset individual monitoring with length_of_stay.reset() and length.reset()

sequence_number()
Returns:sequence_number of the queue – (the sequence number at init or name)
normally this will be the integer value of a serialized name, but also non serialized names (without a dot at the end) will be numbered)
Return type:int
successor(component)

successor in queue

Parameters:component (Component) – component whose successor to return
must be member of the queue
Returns:successor of component, if any – None otherwise
Return type:Component
tail()
Returns:the tail component of the queue, if any. None otherwise
Return type:Component

Note

q[-1] is a more Pythonic way to access the tail of the queue

union(q, name)
Parameters:
  • q (Queue) – queue to be unioned with self
  • name (str) – name of the new queue
Returns:

queue containing all elements of self and q

Return type:

Queue

Note

the priority will be set to 0 for all components in the resulting queue
the order of the resulting queue is as follows
: first all components of self, in that order, followed by all components in q that are not in self, in that order.

class salabim.Resource(name=<object object>, capacity=1, anonymous=False, monitor=True, env=<object object>)
Parameters:
  • name (str) – name of the resource
    if the name ends with a period (.), auto serializing will be applied
  • omitted, the name resource. will be used (if) –
  • capacity (float) – capacity of the resouce
    if omitted, 1
  • anonymous (bool) – anonymous specifier
    if True, claims are not related to any component. This is useful if the resource is actually just a level.
    if False, claims belong to a component.
  • monitor (bool) – if True (default) , the requesters queue, the claimers queue, the capacity, the available_quantity and the claimed_quantity are monitored
    if False, monitoring is disabled.
  • env (Environment) – environment to be used
    if omitted, _default_env is used
base_name()
Returns:base name of the resource (the name used at init or name)
Return type:str
claimers()
Returns:queue with all components claiming from the resource – will be an empty queue for an anonymous resource
Return type:Queue
monitor(value)

enables/disables the resource monitors and timestamped monitors

Parameters:value (bool) – if True, monitoring is enabled
if False, monitoring is disabled

Note

it is possible to individually control monitoring with claimers().monitor() and requesters().monitor(), capacity.monitor(), available_quantity.monitor) or claimed_quantity.monitor()

name(txt=<object object>)
Parameters:txt (str) – name of the resource
if txt ends with a period, the name will be serialized
if omittted, no change
Returns:Name of the resource
Return type:str
print_statistics()

prints a summary of statistics of a resource

release(quantity=<object object>)

releases all claims or a specified quantity

Parameters:quantity (float) – quantity to be released
if not specified, the resource will be emptied completely
for non-anonymous resources, all components claiming from this resource will be released.

Note

quantity may not be specified for a non-anomymous resoure

requesters()
Returns:queue containing all components with not yet honored requests
Return type:Queue
reset_monitors(monitor=<object object>)

resets the resource monitors and timestamped monitors

Parameters:monitor (bool) – if True, monitoring will be on.
if False, monitoring is disabled
if omitted, no change of monitoring state

Note

it is possible to reset individual monitoring with claimers().reset_monitors(), requesters().reset_monitors, capacity.reset(), available_quantity.reset() or claimed_quantity.reset()

sequence_number()
Returns:sequence_number of the resource – (the sequence number at init or name)
normally this will be the integer value of a serialized name, but also non serialized names (without a dot at the end) will be numbered)
Return type:int
set_capacity(cap)
Parameters:cap (float or int) – capacity of the resource
this may lead to honoring one or more requests.
if omitted, no change
class salabim.State(name=<object object>, value=False, type='any', monitor=True, env=<object object>)
Parameters:
  • name (str) – name of the state
    if the name ends with a period (.), auto serializing will be applied
    if omitted, the name state. will be used
  • value (any, preferably printable) – initial value of the state
    if omitted, False
  • monitor (bool) – if True (default) , the waiters queue and the value are monitored
    if False, monitoring is disabled.
  • type (str) –

    specifies how the state values are monitored. Using a int, uint of float type results in less memory usage and better performance. Note that you should avoid the number not to use as this is used to indicate ‘off’

    • ’any’ (default) stores values in a list. This allows for non numeric values. In calculations the values are forced to a numeric value (0 if not possible) do not use -inf
    • ’bool’ bool (False, True). Actually integer >= 0 <= 254 1 byte do not use 255
    • ’int8’ integer >= -127 <= 127 1 byte do not use -128
    • ’uint8’ integer >= 0 <= 254 1 byte do not use 255
    • ’int16’ integer >= -32767 <= 32767 2 bytes do not use -32768
    • ’uint16’ integer >= 0 <= 65534 2 bytes do not use 65535
    • ’int32’ integer >= -2147483647 <= 2147483647 4 bytes do not use -2147483648
    • ’uint32’ integer >= 0 <= 4294967294 4 bytes do not use 4294967295
    • ’int64’ integer >= -9223372036854775807 <= 9223372036854775807 8 bytes do not use -9223372036854775808
    • ’uint64’ integer >= 0 <= 18446744073709551614 8 bytes do not use 18446744073709551615
    • ’float’ float 8 bytes do not use -inf
  • env (Environment) – environment to be used
    if omitted, _default_env is used
base_name()
Returns:base name of the state (the name used at init or name)
Return type:str
get()

get value of the state

Returns:value of the state
Return type:any
monitor(value=<object object>)

enables/disables the state monitors and timestamped monitors

Parameters:value (bool) – if True, monitoring will be on.
if False, monitoring is disabled
if not specified, no change

Note

it is possible to individually control requesters().monitor(),
value.monitor()
name(txt=<object object>)
Parameters:txt (str) – name of the state
if txt ends with a period, the name will be serialized
if omittted, no change
Returns:Name of the state
Return type:str
print_statistics()

prints a summary of statistics of the state

reset(value=False)

reset the value of the state

Parameters:value (any (preferably printable)) – if omitted, False
if there is a change, the waiters queue will be checked to see whether there are waiting components to be honored

Note

This method is identical to set, except the default value is False.

reset_monitors(monitor=<object object>)

resets the timestamped monitor for the state’s value and the monitors of the requesters queue

Parameters:monitor (bool) – if True, monitoring will be on.
if False, monitoring is disabled
if omitted, no change of monitoring state
sequence_number()
Returns:sequence_number of the state – (the sequence number at init or name)
normally this will be the integer value of a serialized name, but also non serialized names (without a dot at the end) will be numbered)
Return type:int
set(value=True)

set the value of the state

Parameters:value (any (preferably printable)) – if omitted, True
if there is a change, the waiters queue will be checked to see whether there are waiting components to be honored

Note

This method is identical to reset, except the default value is True.

trigger(value=True, value_after=<object object>, max=inf)

triggers the value of the state

Parameters:
  • value (any (preferably printable)) – if omitted, True
  • value_after (any (preferably printable)) – after the trigger, this will be the new value.
    if omitted, return to the the before the trigger.
  • max (int) – maximum number of components to be honored for the trigger value
    default: inf

Note

The value of the state will be set to value, then at most max waiting components for this state will be honored and next the value will be set to value_after and again checked for possible honors.

waiters()
Returns:queue containing all components waiting for this state
Return type:Queue
class salabim.Triangular(low, high=<object object>, mode=<object object>, randomstream=<object object>)

triangular distribution

Triangular(low,high,mode,seed)

Parameters:
  • low (float) – lowerbound of the distribution
  • high (float) – upperbound of the distribution
    if omitted, low will be used, thus effectively a constant distribution
    high must be >= low
  • mode (float) – mode of the distribution
    if omitted, the average of low and high will be used, thus a symmetric triangular distribution
    mode must be between low and high
  • randomstream (randomstream) – randomstream to be used
    if omitted, random will be used
    if used as random.Random(12299) it assigns a new stream with the specified seed
mean()

returns the mean of the distribution

sample()

returns sample

class salabim.Uniform(lowerbound, upperbound=<object object>, randomstream=<object object>)

uniform distribution

Uniform(lowerbound,upperboud,seed)

Parameters:
  • lowerbound (float) – lowerbound of the distribution
  • upperbound (float) – upperbound of the distribution
    if omitted, lowerbound will be used
    must be >= lowerbound
  • randomstream (randomstream) – randomstream to be used
    if omitted, random will be used
    if used as random.Random(12299) it assigns a new stream with the specified seed
mean()

returns the mean of the distribution

sample()

returns sample

salabim.colorinterpolate(t, t0, t1, v0, v1)

does linear interpolation of colorspecs

Parameters:
  • t (float) – value to be interpolated from
  • t0 (float) – f(t0)=v0
  • t1 (float) – f(t1)=v1
  • v0 (colorspec) – f(t0)=v0
  • v1 (colorspec) – f(t1)=v1
Returns:

f(t)

Return type:

float

Note

Note that no extrapolation is done, i.e f(t)=v0 for t<t0 and f(t)=v1 for t>t1.
This function is heavily used during animation.

salabim.default_env()
Returns:default environment
Return type:Environment
salabim.interpolate(t, t0, t1, v0, v1)

does linear interpolation

Parameters:
  • t (float) – value to be interpolated from
  • t0 (float) – f(t0)=v0
  • t1 (float) – f(t1)=v1
  • v0 (float, list or tuple) – f(t0)=v0
  • v1 (float, list or tuple) – f(t1)=v1
    if list or tuple, len(v0) should equal len(v1)
Returns:

f(t)

Return type:

float or tuple

Note

Note that no extrapolation is done, i.e f(t)=v0 for t<t0 and f(t)=v1 for t>t1.
This function is used during animation.

salabim.random_seed(seed, randomstream=<object object>)
Parameters:
  • seed (hashable object, usually int) – the seed for random, equivalent to random.seed()
    if None, a purely random value (based on the current time) will be used (not reproducable)
  • randomstream (randomstream) – randomstream to be used
    if omitted, random will be used
    if used as random.Random(12299) it assigns a new stream with the specified seed
salabim.show_colornames()

show (print) all available color names and their value.

salabim.show_fonts()

show (print) all available fonts on this machine

Indices and tables