Salabim: a discrete-event simulation library for Python

3 minute read

Published:

At one point in my research career, I was creating discrete-event simulation programs from scratch. I had even taught others how to do so as part of my teaching assistant duties at the City University of Hong Kong. Eventually, I discovered SimPy, which saved me the hassle of writing my own event loops, and I started teaching that as well – my GitHub contains a set of examples for this purpose. However, monitoring queues and resources in SimPy requires a bit of monkey-patching. Trying to find an existing solution, I turned to an alternate Python discrete-event simulation library called salabim.

The Environment class

In both SimPy and salabim, the Environment class is the core class of any discrete-event simulation. Simulation models are generally created by inheriting the Environment class, adding fields to hold Resource instances, simulation parameters, and the system state. Most class constructors in the SimPy library accept an env parameter for accessing the shared simulation environment.

In salabim, there are several hacks in the code. Firstly, a global variable is set to the last created environment, which becomes the default environment in constructors that expect one as an argument. Secondly, salabim environments are automatically monkey-patched so that calling a constructor as env.X() instead of salabim.X() automatically sets the new object’s environment to env.

As an example:

import salabim as sim

model = sim.Environment()

# THE FOLLOWING TWO LINES ARE EQUIVALENT

res = model.Resource()
# res = sim.Resource(env=model)

class MyComponent(sim.Component):
    def process(self):
        print(f'{self.name()} arrived at {self.env.now()}')
        self.hold(5.5)
        print(f'{self.name()} left at {self.env.now()}')

# THE FOLLOWING TWO LINES ARE EQUIVALENT
# force_at = True forces an arrival at the current time

model.ComponentGenerator(MyComponent, iat=1, force_at=True, till=10)
# model.ComponentGenerator(MyComponent, env=model, iat=1, force_at=True, till=10)

model.run()

Time units and simulation epoch

In Simpy, time is pure numeric (integer or float), and the time units are up to the program user to interpret. On the other hand, salabim environments contain an optional time_unit field and datetime0 field, allowing simulation timestamps to correspond to actual time values. Conversion methods are also provided to convert different time units to that used internally by the simulation environment.

Additional features of salabim

Salabim contains built-in animation capabilities, whereas SimPy does not. Salabim also contains a built-in Monitor class, making it easy to track resource and queue statistics. For example, the Monitor object for a resource’s queue length (the number of “components” waiting for the resource) is my_resource.requesters().length. In addition to polling the monitor’s current value, one can obtain summary statistics or plot the monitor’s value over time.

One complaint

One complaint I have about salabim is poor coding style: for a start, the entire library’s code is contained in a single file. Additionally, salabim does not seem to follow best practices for Python packaging.

One simulation library I found that contains built-in monitors and a sensible submodule hierarchy is kalasim. Unfortunately, using kalasim would require me to learn both Kotlin and the koin dependency injection library, which it is heavily integrated with and which works quite differently from SimPy and salabim’s preferred method of supplying a env parameter to most simulation methods. Another library, simmer (written in R and C++), has built-in monitoring and plotting functionalities, and introduces the concept of a trajectory which resembles the flowchart-based model design process often used in graphical (and almost always commercial) simulation software such as Arena. However, defining custom behaviour in simmer is considerably more difficult than in SimPy or salabim. In particular, I was unable to specify entity cloning/batching/separating behaviour in the manner I wanted, based on the corresponding building blocks in Arena. Thus so far, salabim, messy code and all, is the open-source discrete-event simulation library that best fits my needs.