Skip to main content

SimPN

https://github.com/bpogroup/simpn/actions/workflows/main.yml/badge.svg

SimPN (Simulation with Petri Nets) is a package for discrete event simulation in Python.

SimPN provides a simple syntax that is based on Python functions and variables, making it familiar for people who already know Python. At the same time, it uses the power of and flexibility of Colored Petri Nets (CPN) for simulation. It also provides prototypes for easy modeling of frequently occurring simulation constructs, such as (customer) arrival, processing tasks, queues, choice, parallelism, etc.

Features

  • Easy to use discrete event simulation package in Python.

  • Visualization of simulation models.

  • Prototypes for easily modeling, simulating, and visualizing frequently occurring simulation constructs, including BPMN and Queuing Networks.

  • Different reporting options, including logging to file as an event log for process mining purposes.

Installation

The SimPN package is available on PyPI and can simply be installed with pip.

python -m pip install simpn

There are also executables available, which allow for quick visual simulation of BPMN models. The executables can be downloaded from the releases. You can find more information on how to use these executables in the section Standalone-Limited-Simulation.

Quick Start

You can check out a simple example of a simulation model, e.g., if you like Petri nets:

python examples/presentation/presentation_1.py

If you like BPMN, you can check out this example:

python examples/presentation/presentation_2.py

A Basic Tutorial

To illustrate how SimPN works, let’s consider a simulation model of a cash register at a small shop, which we can initialize as follows. This imports parts from the SimPN library that we use here and further on in the example.

from simpn.simulator import SimProblem, SimToken

shop = SimProblem()

A discrete event simulation is defined by the state of the system that is simulated and the events that can happen in the system.

Simulation State and Variables

In case of our shop, the state of the system consists of customers that are waiting in line at the cash register, resources that are free to help the customer, and resources that are busy helping a customer. Consequently, we can model the state of our simulation, by defining two variables as follows.

customers = shop.add_var("customers")
resources = shop.add_var("resources")

A simulation variable is different from a regular Python variable in two important ways. First, a simulation variable can contain multiple values, while a regular Python variable can only contain one value. Second, values of a simulation variable are available from a specific moment in (simulation) time. More about that later. So, with that in mind, let’s give our variables a value.

resources.put("cassier")
customers.put("c1")
customers.put("c2")
customers.put("c3")

We now gave the resources variable one value, the string cassier, but we gave the customers variable three values. You can probably understand why we did that: we now have one cassier and three customers waiting. This is the initial state of our simulation model.

Simulation Events

Simulation events define what can happen in the system and how the system (state variables) change when they do. We define simulation events as Python functions that take a system state and return a new system state. Remember that the system state is defined in terms of variables, so an event function takes (values of) state variables as input and produces (values of) state variables as output.

def process(customer, resource):
    return [SimToken(resource, delay=0.75)]

shop.add_event([customers, resources], [resources], process)

In our example we introduce a single event that represents a resource processing a waiting customer. First, let’s focus on shop.add_event in the code below. This tells the simulator that our event takes a value from the customers variable and a value from the resources variable as input, produces a value for the resources variable as output, and uses the process function to change the state variables. Describing that in natural language: it takes a customer and a resource and, when it is done, returns a resource.

The process function defines how the event modifies the system state (variables). Taking a value from the customers variable (and calling it customer) and a value from the resources variable (and calling it resource), the function returns the resource again. This return value will be put into the resources variable, as per the shop.add_event definition. However, as you can see, there are several things going on in the return statement.

First, the function does not return a single resource value, but a list of values. This is simply a convention that you have to remember: event functions return a list of values. The reason for this is that we defined the simulation event in shop.add_event as taking a list of values (consisting of one value from customers and one value from resources) as input and as producing a list of values (consisting of one value for resources) as output. Accordingly, we must produce a list of values as output, even if there is only one value.

Second, the function does not return the resource, but returns a SimToken containing the resource. That is because in simulation, values have a time from which they are available. A value with a time is called a token. This represents that the value is only available at, or after, the specified time. In this case, the resource value is made available after a delay of 0.75. You can consider this the time it takes the resource to process the customer. Since it takes 0.75 to process a customer, the resource is only made available again after a delay of 0.75. In the meantime no new process events can happen, because a value from resources, which is needed as input for such an event, is not available.

Putting it all together

Now we have modeled the entire system and we can simulate it. To do that, we call the simulate function on the model. This function takes two parameters. One is the amount of time for which the simulation will be run. The other is the reporter that will be used to report the results of the simulation. In our example we will run the simulation for 10. (Since we only have 3 customers, and each customer takes 0.75 to process, this should be more than enough.) We will use a SimpleReporter from the reporters package to report the result. This reporter simply prints each event that happens to the standard output.

from simpn.reporters import SimpleReporter

shop.simulate(10, SimpleReporter())

As expected, running this code leads to the following output. The event of (starting) processing customer c1 happens at time t=0. It uses value c2 for variable customers and value cassier for variable resources. The event of (starting) processing customer c2 happens at time t=0.75. This is logical, because our definition of the process event that the value cassier is only available in the variable resources again after 0.75. Accordingly, processing of c3 happens at time t=1.5.

process{customers: c1, resources: cassier}@t=0
process{customers: c2, resources: cassier}@t=0.75
process{customers: c3, resources: cassier}@t=1.5

For completeness, the full code of the example is:

from simpn.simulator import SimProblem, SimToken

shop = SimProblem()

resources = shop.add_var("resources")
customers = shop.add_var("customers")

def process(customer, resource):
    return [SimToken(resource, delay=0.75)]

shop.add_event([customers, resources], [resources], process)

resources.put("cassier")
customers.put("c1")
customers.put("c2")
customers.put("c3")

from simpn.reporters import SimpleReporter

shop.simulate(10, SimpleReporter())

Visualizing the Model

To help check whether the model is correct, it is possible to visualize it. To this end, there is a Visualisation class. You can simply create an instance of this class and call the show method to show the model as follows.

from simpn.visualisation import Visualisation

v = Visualisation(shop)
v.show()

The model will now be shown as a Petri net in a separate window. The newly opened window will block further execution of the program until it is closed. You can interact with the model in the newly opened window. Pressing the space bar will advance the simulation by one step. You can also change the layout of the model by dragging its elements around. After the model window is closed, you can save the layout of the model to a file, so that you can open it later. Use the method save_layout to save the model to do so. You can load the layout of the model from the file later, by passing the saved layout as a parameter to the constructor. If the layout file does not exist, the model will be shown with an automatically generated layout.

v = Visualisation(shop, "layout.txt")
v.show()
v.save_layout("layout.txt")

Standalone Limited Simulation

You can use it to open a BPMN model file, such as example.bpmn. These BPMN files can be created using a BPMN modeling tool, such as Signavio. Note that they contain specific annotations to specify simulation properties:

  • Each lane has a number of resources in its name between brackets. For example, a lane named ‘employees (2)’ represents that there are two employees who can perform the tasks in the employees lane.

  • Each start event has a property ‘interarrival_time’, which must be an expression that evaluates to a number. For example, it can be 1, meaning that there is an arrival every 1 time units. It can also be random.expovariate(1), which means that there is an arrival rate that is exponentially distributed with an average of 1.

  • Each task has a property ‘processing_time’, which must be an expression that evaluates to a number. For example, it can be 1, meaning that processing the task takes 1 time unit. It can also be random.uniform(0.5, 1.5), which means that the processing time is uniformly distributed between 0.5 and 1.5.

  • Each outgoing arc of an XOR-split has a percentage on it, representing the probability that this path is followed after a choice. For example, an arc with 25% on it represents that there is a 25% possibility that this path is taken after a choice.

Documentation

For more information, including the API specification, and a far more extensive tutorial, please visit the documentation.

Acknowledgements

UI icons by Flaticon.

Metadata

Release files for simpn 1.10.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for simpn 1.10.0
File Size Uploaded
simpn-1.10.0.tar.gz 2.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for simpn 1.10.0
File Interpreter ABI Platform
simpn-1.10.0-py3-none-any.whl Python 3 none any Details

Total release size: 4.5 MB

Release files / simpn-1.10.0.tar.gz

Download URL simpn-1.10.0.tar.gz
Size 2.6 MB
Tags Source
SHA-256 checksum
How to use checksums
50211f1892be188dcca51f6105533b177275bfb756ad82e826d1d45e7c205b04
BLAKE2b-256 checksum
How to use checksums
42c18ec0d7b1992404399d626fc01ffc59f7a00e3590fef9c3dff3fe328f4261
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.0.1 CPython/3.13.3

Release files / simpn-1.10.0-py3-none-any.whl

Download URL simpn-1.10.0-py3-none-any.whl
Size 1.9 MB
Tags Python 3
SHA-256 checksum
How to use checksums
c342fd2359c88b4782ebdb4e37b14342e0e7c9067438b73ad79359e8dff779b4
BLAKE2b-256 checksum
How to use checksums
b08ffbefdf1b2a1b6806e9ca19438388e7fcecea306d0a129034338eb9fc1deb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.0.1 CPython/3.13.3

Release history Release notifications | RSS feed

This release

1.10.0 This release

2 release files

1.9.8

2 release files

1.9.7

2 release files

1.9.6

2 release files

1.9.5

2 release files

1.9.4

2 release files

1.9.3

2 release files

1.9.2

2 release files

1.9.1

2 release files

1.9.0

2 release files

1.8.0

2 release files

1.7.11

2 release files

1.7.10

2 release files

1.7.9

2 release files

1.7.8

2 release files

1.7.7

2 release files

1.7.6

2 release files

1.7.5

2 release files

1.7.4

2 release files

1.7.3

2 release files

1.7.2

2 release files

1.7.1

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.6

2 release files

1.3.5

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.12

2 release files

1.2.11

2 release files

1.2.9

2 release files

1.2.8

2 release files

1.2.7

2 release files

1.2.6

2 release files

1.2.5

2 release files

1.2.4

2 release files

1.2.3

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.5

2 release files

0.1.3

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page