I created a simple simulation model of an operation that produces items, stores them, and arranges for delivery of these items at defined hours of the day. I noted that the stores do not get completely emptied at those times, however, and I cannot explain why. Please see minimum reproducible example of code below.
import simpy
import numpy
import pandas
import random
import statistics
import pdb
def itemProducer(env):
itemCounter = 1
while True:
yield env.timeout(numpy.random.exponential(1))
yield store.put(f'{itemCounter}')
def itemCollector(env, pickupTimes):
while True:
yield env.timeout(next(pickupTimes)\
- env.now)
i = len(pickupData)
pickupData.loc[i,
'timeBeforePickup'] = env.now
pickupData.loc[i,
'itemsInStoreBefore'] = \
len(store.items)
for item in store.items:
yield store.get()
pickupData.loc[i,
'timeAfterPickup'] = env.now
pickupData.loc[i,
'itemsInStoreAfter'] = len(store.items)
#generating endless stream of pickup times:
collectionTimesPerDay = [9, 12, 15, 17]
def endlessTimesGenerator(collectionTimesPerDay):
t = 0
while True:
for i in collectionTimesPerDay:
yield (t + i)
t += 24
#for data collection:
pickupData = pandas.DataFrame(columns = ['timeBeforePickup',
'itemsInStoreBefore',
'timeAfterPickup',
'itemsInStoreAfter'])
#defining simulation environment, store etc.:
env = simpy.Environment()
store = simpy.Store(env, capacity = 10000)
pickupTimes = endlessTimesGenerator(collectionTimesPerDay)
env.process(itemProducer(env))
env.process(itemCollector(env, pickupTimes))
env.run(until = 72)
The process itemProducer is supposed to produce items at (average) periods of 1 time-unit (say, hours) and add them to a store for later collection. These periods are exponentially distributed, i.e. not exactly 1 each time. The process itemCollector is meant to empty the stores at given times (see list collectionTimesPerDay).
I added a data-frame pickupData for recording of the number of items in store before and after pick-up. I note that the stores are never cleared completely, a behaviour I would like to achieve, however. For instance, at time 9 (timeBeforePickup column) there are 11 items in store before collection. After collection, there are still 5 left in store. How can I achieve that all of the items are cleared by the itemCollector process at the times defined by pickupTimes?
pickupData
timeBeforePickup itemsInStoreBefore timeAfterPickup itemsInStoreAfter
0 9 11 9 5
1 12 10 12 5
2 15 10 15 5
3 17 8 17 4
4 33 22 33 11
5 36 13 36 6
6 39 14 39 7
7 41 8 41 4
8 57 24 57 12
9 60 13 60 6
10 63 6 63 3
11 65 8 65 4
Any help that leads to the solution of this question would be greatly appreciated.
The issue is you are changing the list while iterating over it. Iterators hate changes to a list while they iterate over it.
I simply changed
for item in store.items:
to
while len(store.items) > 0:
Here is the full solution
import simpy
import numpy
import pandas
import random
import statistics
import pdb
def itemProducer(env):
itemCounter = 1
while True:
yield env.timeout(numpy.random.exponential(1))
yield store.put(f'{itemCounter}')
itemCounter += 1
def itemCollector(env, pickupTimes):
while True:
yield env.timeout(next(pickupTimes)\
- env.now)
i = len(pickupData)
pickupData.loc[i,
'timeBeforePickup'] = env.now
pickupData.loc[i,
'itemsInStoreBefore'] = \
len(store.items)
# for item in store.items:
while len(store.items) > 0:
pulled = yield store.get()
pickupData.loc[i,
'timeAfterPickup'] = env.now
pickupData.loc[i,
'itemsInStoreAfter'] = len(store.items)
#generating endless stream of pickup times:
collectionTimesPerDay = [9, 12, 15, 17]
def endlessTimesGenerator(collectionTimesPerDay):
t = 0
while True:
for i in collectionTimesPerDay:
yield (t + i)
t += 24
#for data collection:
pickupData = pandas.DataFrame(columns = ['timeBeforePickup',
'itemsInStoreBefore',
'timeAfterPickup',
'itemsInStoreAfter'])
#defining simulation environment, store etc.:
env = simpy.Environment()
store = simpy.Store(env, capacity = 10000)
pickupTimes = endlessTimesGenerator(collectionTimesPerDay)
env.process(itemProducer(env))
env.process(itemCollector(env, pickupTimes))
env.run(until = 72)
print(pickupData )