2

I have been having problems with the simulation I have built and I am not sure how I can go about solving them. The idea of this simulation is as follows:

There are a total of 10 machines in the system. When the simulation starts, 6 of the machines will begin working, while the other 4 machines will be kept in an inventory as spares.

The requirement is that at any one time in the system, there should be 6 machines working. Any amount of time when there aren't a total of 6 machines working will be counted as downtime.

After some time of working, one of the 6 working machines will fail. When this failure event occurs, we will take 1 machine from the inventory and add it to the group of working machines, so that we can fulfill the requirement of having 6 machines working at any one time.

The failed machine will then be sent to a repair workshop and be repaired after a certain amount of time. When its repair finishes, it will be moved to the inventory where it will join the other machines.

The next time another one of the 6 working machine fails, 1 machine will be taken from the inventory again to replace the failed one. This means that the number of machines inside the inventory will constantly be fluctuating throughout the simulation. Throughout the simulation, I also need to how many machines are there inside the inventory, thus I have added print statements to show me that.

In summary, a machine will go through the following loop: starts working -> fails -> sent to repair workshop -> after repair, placed in inventory -> gets pulled into operation again when another machine fails -> starts working -> fails .. etc.

Another requirement that I have in this simulation is that I need know where Machine 1 to 10 is at any one time. This is so that I can trace the movement of each machine, such as for example, when does machine 7 fail, when does it enter and leave the repair workshop, when does it enter and leave the inventory etc.

After this simulation is built, I will subsequently be varying the initial number of spares and the repair time to study how these factors affect the level of operational availability.

The main problems I am facing:

I am unable to trace each of the 10 machines individually through the entire cycle

I am unable to properly model my spares inventory. If machines 5 - 10 are in operation at the beginning, when one of them fails, the next line of output should tell me that machine 1 has been taken from the inventory (and thus begins operating) to replace the failed one. However, I am unable to get such an output.

Thank you in advance!

I have included my progress so far:

    import simpy
    import random

    RANDOM_SEED = 42
    NUM_SERVERS = 2
    MTBF = 10
    MTTR = 2
    TOTAL_MACHINES = 10
    TOTAL_SPARES = 4
    TOTAL_WORKING = TOTAL_MACHINES - TOTAL_SPARES
    SIM_TIME = 100

    class Working(object):
        def __init__ (self, env, num, repair_workshop, spares_inventory, downtime):
            self.env = env
            self.repair_workshop = repair_workshop
            self.spares_inventory = spares_inventory
            self.downtime = downtime
            self.name = 'Machine %d' % (num + 1)
            print('%s begins working %.2f' % (self.name, self.env.now))
            self.env.process(self.run())

        def run(self):
            yield self.env.timeout(random.expovariate(1.0 / MTBF))
            print('%s stops working %.2f' % (self.name, self.env.now))

            downtime_start = self.env.now
            spare = yield self.spares_inventory.get(1)
            self.downtime.append(self.env.now - downtime_start)

            print('%s taken from inventory at %.2f' % (spare.name, self.env.now))
            print('%d inside inventory' % len(spares_inventory.items))

            with self.repair_workshop.request() as req:
                yield req
                print('%s starts repair %.2f' % (self.name, self.env.now))

                yield self.env.timeout(random.expovariate(1.0 / MTTR))

                yield self.spares_inventory.put(1)
                print('%s finishes repair at %.2f' % (self.name, self.env.now))

            print(' %d inside inventory' % len(spares_inventory.items))

    def main():
        env = simpy.Environment()
        repair_workshop = simpy.Resource(env, capacity = NUM_SERVERS)
        spares_inventory = simpy.Container(env, capacity = TOTAL_MACHINES, init = TOTAL_SPARES)
        downtime = []
        working = [Working(env, i, repair_workshop, spares_inventory, downtime) for i in range(TOTAL_WORKING)]

        env.run(SIM_TIME)

        print('Total downtime for all machines throughout simulation time is %.2f hours' % sum(downtime))
        print('Operational Availability = %.2f percent' % ( (SIM_TIME - sum(downtime)) * 100 / (SIM_TIME)))

    if __name__ == '__main__':
        main()

With Stefan's help, I have modified my script:

    class Working(object):

        def __init__ (self, env, num, repair_workshop, spares_inventory, downtime, machine):
            self.env = env
            self.repair_workshop = repair_workshop
            self.spares_inventory = spares_inventory
            self.downtime = downtime
            self.machine = machine
            self.name = ('Machine %d' % (num + 1))
            print('%s begins working %.2f' % (self.name, self.env.now))
            self.env.process(self.run())

        def run(self):
            yield self.env.timeout(random.expovariate(1.0 / MTBF))
            print('%s stops working %.2f' % (self.name, self.env.now))

            downtime_start = self.env.now
            spare = yield self.spares_inventory.get(1)
            self.downtime.append(self.env.now - downtime_start)

            print('%s taken from inventory at %.2f' % (spare.name, self.env.now))
            print('%d inside inventory' % len(spares_inventory.items))

            with self.repair_workshop.request() as req:
                yield req
                print('%s starts repair %.2f' % (self.name, self.env.now))

                yield self.env.timeout(random.expovariate(1.0 / MTTR))

                yield self.spares_inventory.put(1)
                print('%s finishes repair at %.2f' % (self.name, self.env.now))

            print(' %d inside inventory' % len(spares_inventory.items))

    def main():
        env = simpy.Environment()
        repair_workshop = simpy.Resource(env, capacity = NUM_SERVERS)
        downtime = []

        machines = [object() for i in range(TOTAL_MACHINES)]
        working, spares = machines[:TOTAL_WORKING], machines[TOTAL_WORKING:]
        spares_inventory = simpy.Store(env, capacity = TOTAL_MACHINES)
        spares_inventory.items = spares
        working = [Working(env, i, repair_workshop, spares_inventory, downtime, machine) for i, machine in enumerate(working)]

        env.run(SIM_TIME)

        print('Total downtime for all machines throughout simulation time is %.2f hours' % sum(downtime))
        print('Operational Availability = %.2f percent' % ( (SIM_TIME - sum(downtime)) * 100 / (SIM_TIME)))

    if __name__ == '__main__':
        main()

This is the traceback I have received:

    Traceback (most recent call last):
    File "/Users/Scripts/8oct1.py", line 70, in <module> main()
    File "/Users/Scripts/8oct1.py", line 64, in main env.run(SIM_TIME)
    File "/Library/Python/2.7/site-packages/simpy/core.py", line 120, in run self.step()
    File "/Library/Python/2.7/site-packages/simpy/core.py", line 213, in step raise event._value
    TypeError: __init__() takes exactly 2 arguments (3 given)
Craig
  • 43
  • 4

1 Answers1

2

You could use a Store instead of a Container. With Store, you can use distinguishable objects for your machine which will allow you to trace their way through the simulation.

For example,

machines = [object() for i in range(TOTAL_MACHINES)]
working, spares = machines[:TOTAL_WORKING], machines[TOTAL_WORKING:]
spares_inventory = Store(env, capacity=TOTAL_MACHINES)
spares_inventory.items = spares
working = [Working(env, i, machine) for i, machine in enumerate(working)]

Of course, instead of object you also use (named) tuples, plain ints or any other object that is best suited to represent a machine.

Stefan Scherfke
  • 3,012
  • 1
  • 19
  • 28
  • hi Stefan, I've implemented the above-mentioned changes to main() in my code, but I still seem to be getting the following error: TypeError: __init__() takes exactly 2 arguments (3 given); Could you kindly help? Thanks! – Craig Oct 10 '14 at 10:39
  • The simulation runs smoothly for a few lines initially but this error appears right after the print statement 'Engine x stops working'. – Craig Oct 10 '14 at 10:45
  • Could you please provide a complete stack trace? – Stefan Scherfke Oct 11 '14 at 10:59
  • I have added in the traceback as well as the way I have modified my code with your help, thanks! – Craig Oct 12 '14 at 15:27
  • The traceback you posted missed the most important things. :-) However, I was able to reproduce your problem. The original exception shows you the actual error. Its "self.spares_inventory.get(1)". Remove the argument "1" and it works. – Stefan Scherfke Oct 12 '14 at 20:02
  • And it needs to be "spares_inventory.put(spare)" not "spares_inventory.put(1)", because you are using Store now. – Stefan Scherfke Oct 12 '14 at 20:05