Hi Sebastian,
As Michael says, SimPy is a great choice for modeling production processes, especially when you want to simulate discrete-events like processing times, machine operations, and transport delays. It allows you to build complex systems by modeling them as processes that interact with each other.
For logging events, a simple approach would be to use dictionaries to record timestamps and states during the simulation. After collecting the data, you can use libraries like Pandas and Seaborn to visualise and analyse the results.
Below is some sample code that demonstrates how to:
• Build a simple production simulation.
• Log events using dictionaries.
• Visualise the production timeline using Seaborn.
import simpy
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# Initialise the data logging dictionary
log_data = {
'Product': [],
'Process': [],
'Start_Time': [],
'End_Time': []
}
# Define the production processes
def production_process(env, name, machines, log_data):
"""Simulates the production process of a single product."""
# Process 1: Heating
with machines['Heater'].request() as request:
yield request
start_time = env.now
yield env.timeout(2) # Heating time
end_time = env.now
# Log the event
log_data['Product'].append(name)
log_data['Process'].append('Heating')
log_data['Start_Time'].append(start_time)
log_data['End_Time'].append(end_time)
# Process 2: Processing
with machines['Processor'].request() as request:
yield request
start_time = env.now
yield env.timeout(3) # Processing time
end_time = env.now
# Log the event
log_data['Product'].append(name)
log_data['Process'].append('Processing')
log_data['Start_Time'].append(start_time)
log_data['End_Time'].append(end_time)
# Process 3: Cooling
with machines['Cooler'].request() as request:
yield request
start_time = env.now
yield env.timeout(1) # Cooling time
end_time = env.now
# Log the event
log_data['Product'].append(name)
log_data['Process'].append('Cooling')
log_data['Start_Time'].append(start_time)
log_data['End_Time'].append(end_time)
def product_generator(env, machines, log_data, weekly_plan):
"""Generates products based on the weekly production plan."""
for i, product in enumerate(weekly_plan):
yield env.timeout(product['arrival_time'])
env.process(production_process(env, f'Product_{i+1}', machines, log_data))
# Set up the simulation environment
env = simpy.Environment()
# Define the machines as resources
machines = {
'Heater': simpy.Resource(env, capacity=1),
'Processor': simpy.Resource(env, capacity=1),
'Cooler': simpy.Resource(env, capacity=1)
}
# Example weekly production plan
weekly_plan = [
{'arrival_time': 0},
{'arrival_time': 1},
{'arrival_time': 2},
{'arrival_time': 3},
{'arrival_time': 4},
]
# Start the product generator
env.process(product_generator(env, machines, log_data, weekly_plan))
# Run the simulation
env.run()
# Convert log data into a DataFrame
df = pd.DataFrame(log_data)
# Print the DataFrame
print(df)
# Visualise the production timeline
plt.figure(figsize=(12, 6))
sns.set_style("whitegrid")
# Create a color palette for the processes
processes = df['Process'].unique()
palette = sns.color_palette("tab10", len(processes))
color_dict = dict(zip(processes, palette))
# Plot the Gantt chart
for product_name, product in df.groupby('Product'):
for _, row in product.iterrows():
plt.barh(
y=row['Product'],
width=row['End_Time'] - row['Start_Time'],
left=row['Start_Time'],
edgecolor='black',
color=color_dict[row['Process']],
label=row['Process'] if row['Product'] == 'Product_1' else ""
)
# Remove duplicate labels in the legend
handles, labels = plt.gca().get_legend_handles_labels()
by_label = dict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys(), title='Process')
plt.xlabel('Time')
plt.ylabel('Product')
plt.title('Production Timeline')
plt.show()

Here's a little explanation of what's going on...
Simulation Setup:
- We define three resources: Heater, Processor, and Cooler, each representing a machine in the production line.
- The production_process function simulates the steps each product goes through, requesting access to the necessary machines and logging the start and end times.
- The product_generator function introduces products into the system based on the weekly_plan, which specifies the arrival times of each product.
Logging Events:
- Events are logged into the log_data dictionary whenever a process starts and ends.
- This dictionary is later converted into a Pandas DataFrame for easier manipulation and visualization.
Visualisation:
- We use Seaborn and Matplotlib to create a Gantt chart that visualises the timeline of each product through the different processes.
- Each bar in the chart represents the time a product spends in a particular process.
I hope this helps!
Best,
Harry Munro
Founder, TeachEm