pythonsimpy

store resource of SimPy module (Python) not emptied


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.


Solution

  • 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 )