Salabim: a discrete-event simulation library for Python
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.