I m trying to formulate an inventory simulation with Simpy (a Python library for discrete event simulation) using an Object Oriented approach.
The simulation follow these steps:
- Initialization : A warehouse with unlimoted capacity and initial inventory on hand.
- Serve Customers : Customers arrive each inter arrival time and ask for a quantity of items demand.
- If we have enough inventory on hand, the customer is served
- If the demand is greater than the inventory on hand, all the items are sold
- Check Inventory for replenishment : If the inventory on hand drop at a certain level reorder point, we place an order to reach target inventory. We wait the lead time to fulfill the warehouse.
The problem
It s my first object oriented approach to simulation and I cannont see why the simulation doesn't run properly. Actually, I don't get any output and the script keeps running without displaying anaything.
import simpy
import numpy as np
class warehouse(object):
# initialize warehouse object
def __init__(self, env, initial_inv, reorder_point, target_inv, lead_time):
self.env = env
self.on_hand_inv = initial_inv
self.reorder_point = reorder_point
self.target_inv = target_inv
self.lead_time = lead_time
# start processes
self.env.process(self.chek_inventory_and_order())
self.env.process(self.serve_customer())
# process to serve Customer
def serve_customer(self):
while True:
interarrival = np.random.exponential(1./5)
yield self.env.timeout(interarrival)
demand = np.random.randint(1,5)
if demand < self.on_hand_inv:
self.on_hand_inv -= demand
print("{:.2f}, sold {}".format(self.env.now, demand))
else:
print("{:.2f}, sold {} (out of stock)".format(self.env.now, self.on_hand_inv))
self.on_hand_inv = 0
# process to place order
def chek_inventory_and_order(self):
while True:
if self.on_hand_inv <= self.reorder_point:
order_qty = self.target_inv - self.on_hand_inv
print("{:.2f}, place order of {}".format(self.env.now, order_qty))
yield self.env.timeout(self.lead_time)
self.on_hand_inv = order_qty
order_qty = 0
print("{:.2f}, order recieved. {} in inventory".format(self.env.now, self.on_hand_inv))
def run_simulation(print_=False):
np.random.seed(0)
env = simpy.Environment()
s = warehouse(env, 40, 5, 40, 2.)
env.run(until=10.)
CodePudding user response:
In your method chek_inventory_and_order
you have all of the logic, including the yield
which would get you out of the method, contained under an if
statement which is False
given the initialization. Since that’s all happening within a while True
, you’ve created an infinite loop. Looks to me like that’s why it keeps running forever with no output.
CodePudding user response:
Since you haven't responded to the request for clarification about whether SimPy is a requirement, and the question isn't tagged simpy
, here's a working implementation in an alternative modeling framework based on the Event Graph modeling paradigm published by Lee Schruben back in 1983. In the intervening years the name has morphed to Event Relationship Graphs.
The code below is implemented using SimpleKit, a freely available (MIT License) discrete event scheduling engine written in Python. The linked GitHub repository contains simplekit.py, the model engine; several demo models; and SimulationBasics.pdf, which started life as a general tutorial for the 2007 Winter Simulation Conference but has been extended to include details about SimpleKit. Head straight to section 5 if you want to skip over the background and leap straight to event graphs and SimpleKit.
Without further ado:
from simplekit import SimpleKit
from numpy.random import default_rng
import sys
class Inventory(SimpleKit):
"""Implementation of an inventory model using SimpleKit."""
def __init__(self, initial_inv, reorder_point, target_inv, lead_time,
max_order_qty = 5, arrival_rate = 5., halt_time = 100, seed = 0):
"""Construct an instance of the Inventory system."""
SimpleKit.__init__(self)
self.on_hand_inv = initial_inv
self.reorder_point = reorder_point
self.target_inv = target_inv
self.lead_time = lead_time
self.max_order_qty = max_order_qty 1
self.halt_time = halt_time
self.backordered = False
self.mean_interarrival_time = 1. / arrival_rate
self.rng = default_rng(seed)
def init(self):
"""Initialize all state variables, schedule first customer arrival and model halt time."""
self.schedule(self.customer_arrival, self.rng.exponential(self.mean_interarrival_time))
self.schedule(self.halt, self.halt_time)
def customer_arrival(self):
"""Generate customer demand, schedule next arrival, schedule backorder if low inventory."""
demand = self.rng.integers(1, self.max_order_qty) # upper limit is exclusive
if demand < self.on_hand_inv:
self.on_hand_inv -= demand
print("{:.2f}, sold {}".format(self.model_time, demand))
else:
print("{:.2f}, sold {} (out of stock)".format(self.model_time, self.on_hand_inv))
self.on_hand_inv = 0
self.schedule(self.customer_arrival, self.rng.exponential(self.mean_interarrival_time))
if self.on_hand_inv <= self.reorder_point and not self.backordered:
self.schedule(self.generate_backorder, 0)
def generate_backorder(self):
"""Determine backorder quantity, schedule its arrival based on lead_time."""
order_qty = self.target_inv - self.on_hand_inv
self.backordered = True
print("{:.2f}, place order of {}".format(self.model_time, order_qty))
self.schedule(self.backorder_arrives, self.lead_time, order_qty)
def backorder_arrives(self, amount):
"""Update inventory based on backorder amount."""
self.on_hand_inv = amount
self.backordered = False
print("{:.2f}, order recieved. {} in inventory".format(self.model_time, self.on_hand_inv))
if __name__ == '__main__':
# Instantiate and run a copy of the Inventory model.
Inventory(40, 5, 40, 2., seed = 12345).run()
Place your model and simplekit.py in the same directory/folder, and you should be ready to go. Running this with the particular seed choice produces:
0.04, sold 4
0.97, sold 2
1.06, sold 5
1.32, sold 2
1.61, sold 2
1.99, sold 1
2.30, sold 4
2.89, sold 2
3.11, sold 1
3.44, sold 1
3.55, sold 3
3.71, sold 4
3.73, sold 4
3.73, place order of 35
3.77, sold 1
3.97, sold 3
4.05, sold 1 (out of stock)
4.21, sold 0 (out of stock)
4.28, sold 0 (out of stock)
4.34, sold 0 (out of stock)
4.37, sold 0 (out of stock)
4.71, sold 0 (out of stock)
5.35, sold 0 (out of stock)
5.61, sold 0 (out of stock)
5.73, order recieved. 35 in inventory
.
.
.
and a whole lot more.