I am new to simulation and am going through the simpy documentation. I kind of get the gist of it but cannot really seem to grasp how to translate the production line that I want to simulate.
I am trying to simulate a production line with m number of machines and m-1 number of buffers.
The machines basically work the same:
- Processes units at speed s per state
- random distribution of states and time in said state
- Is there starvation/blockage from the buffers?
The buffers all work the same:
- Receives units from previous machine
- Loses units from next machine
- capacity(t) = capacity(t-1) + (input previous machine) – (output next machine)
- Capacity(t) <= Max capacity
Now I get that the first steps of simulation are baby steps so I need to make a simple model first. What I am looking to create then using simpy is a 2 machine and 1 buffer system with fixed processing speeds, failure rate and maintenance speeds:
- An unlimited capacity of units is delivered to machine 1.
- Machine 1 processes at speed s, fails after f minutes and is repaired after r minutes. If buffer is full then Machine 1 stops.
- Buffer gets filled by Machine 1 and emptied by Machine 2. There is a max capacity.
- Machine 2 processes at min(speed s, capacity buffer) fails after f minutes and is repaired after r minutes. If buffer is empty then Machine 2 stops.
- Unlimited buffer capacity after Machine 2.
EDIT: The answer I received from @Michael works very well, I tried playing around with failures and maintenance. The machine seems to fail and repaired, but keeps failing at multiples of the time to failure (which I need to fix). The code I am using is as follows:
# Machine 1 speed_1 = 2 # Avg. processing time of Machine 1 in minutes # speed_1_stdev = 0.6 # St. dev. of processing time of Machine 1 MTTF_1 = 10 # Mean time to failure Machine 1 # fail_1 = 1/MTTF_1 # Parameter for exp. distribution repair_1 = 3 # Time it takes to repair Machine 1 # Machine 2 speed_2 = 3 # Processing time of Machine 2 in minutes # speed_2_stdev = 0.6 # St. dev. of processing time of Machine 2 MTTF_2 = 7 # Mean time to failure Machine 1 # fail_2 = 1/MTTF_2 # Parameter for exp. distribution repair_2 = 4 # Time it takes to repair Machine 2 # Simulation time time = 120 # Sim time in minutes #--------------------------------------------------------------------- # Class setup for a Machine class Machine(object): """ A machine produces units at a fixed processing speed, takes units from a store before and puts units into a store after. Machine has a *name*, a processing speed *speed*, a preceeding buffer *in_q*, and a proceeding buffer *out_q*. Next steps: - Machine produces units at distributed processing speeds. - A machine fails at fixed intervals and is repaired at a fixed time. - Failure and repair times are distributed. """ def __init__(self, env, name, in_q, out_q, speed, mttf, repair): self.env = env self.name = name self.in_q = in_q self.out_q = out_q self.speed = speed self.mttf = mttf self.repair = repair self.broken = False # Start the producing process self.process = env.process(self.produce()) # Start the failure process env.process(self.fail_machine()) def produce(self): """ Produce parts as long as the simulation runs. """ while True: part = yield self.in_q.get() try: # If want to see time {self.env.now:.2f} print(f'{self.name} has got a part') yield env.timeout(self.speed) if len(self.out_q.items) < self.out_q.capacity: print(f'{self.name} finish a part next buffer has {len(self.out_q.items)} and capacity of {self.out_q.capacity}') else: print(f'{self.env.now:.2f} {self.name} output buffer full!!!') yield self.out_q.put(part) print(f'{self.name} pushed part to next buffer') except simpy.Interrupt: self.broken = True yield self.env.timeout(self.repair) print(f'{self.env.now:.2f} {self.name} is in fixed') self.broken = False def fail_machine(self): """ The machine is prone to break down every now and then. """ while True: yield self.env.timeout(self.mttf) print(f'{self.env.now:.2f} {self.name} is in failure.') if not self.broken: # Machine only fails if currently working. self.process.interrupt(self.mttf) #--------------------------------------------------------------------- # Generating the arrival of parts in the entry buffer to be used by machine 1 def gen_arrivals(env, entry_buffer): """ Start the process for each part by putting the part in the starting buffer """ while True: yield env.timeout(random.uniform(0,0.001)) # print(f'{env.now:.2f} part has arrived') part = object() # Too lazy to make a real part class, also isn't necessary yield entry_buffer.put(part) #--------------------------------------------------------------------- # Create environment and start the setup process env = simpy.Environment() bufferStart = simpy.Store(env) # Buffer with unlimited capacity buffer1 = simpy.Store(env, capacity = 8) # Buffer between machines with limited capacity bufferEnd = simpy.Store(env) # Last buffer with unlimited capacity # The machines __init__ starts the machine process so no env.process() is needed here machine_1 = Machine(env, 'Machine 1', bufferStart, buffer1, speed_1, MTTF_1, repair_1) machine_2 = Machine(env, 'Machine 2', buffer1, bufferEnd, speed_2, MTTF_2, repair_2) env.process(gen_arrivals(env, bufferStart)) # Execute env.run(until = time)
Advertisement
Answer
Got bored of my work, so I made some small changes to fix your code. I think it even works. One final note, more then one machine can use the same buffer. so a pool of machines can process out of the same one buffer. This is what I do instead of using a simpy.resurce for a resource pool
import simpy import random speed_1 = 3 # Avg. processing time of Machine 1 in minutes speed_2 = 4 # Processing time of Machine 2 in minutes time = 120 # Sim time in minutes # Class setup for Machine 1 # This needs to be done as there are varying processing speeds class Machine(object): """ A machine produces units at a fixed processing speed, takes units from a store before and puts units into a store after. Machine has a *name* Next steps: - Machine produces units at distributed processing speeds. - A machine fails at fixed intervals and is repaired at a fixed time. - Failure and repair times are distributed. """ def __init__(self, env, name, in_q, out_q, speed): self.env = env self.name = name self.in_q = in_q self.out_q = out_q self.speed = speed # Start the producing process self.process = env.process(self.produce()) def produce(self): """ Produce parts as long as the simulation runs. """ while True: part = yield self.in_q.get() print(f'{self.env.now:.2f} Machine {self.name} has got a part') yield env.timeout(self.speed) print(f'{self.env.now:.2f} Machine {self.name} finish a part next q has {len(self.out_q.items)} and capacit of {self.out_q.capacity}') yield self.out_q.put(part) print(f'{self.env.now:.2f} Machine {self.name} pushed part to next buffer') def gen_arrivals(env, entry_buffer): """ start the process for each part by putting the part in the starting buffer """ while True: yield env.timeout(random.uniform(1,4)) print(f'{env.now:.2f} part has arrived') part = object() # too lazy to make a real part class yield entry_buffer.put(part) # Create environment and start the setup process env = simpy.Environment() bufferStart = simpy.Store(env) # Buffer with unlimited capacity buffer1 = simpy.Store(env, capacity = 6) # Buffer between machines with limited capacity bufferEnd = simpy.Store(env) # Last buffer with unlimited capacity # the machines __init__ starts the machine process so no env.process() is needed here machine_1 = Machine(env, 'Machine 1', bufferStart, buffer1, speed_1) machine_2 = Machine(env, 'Machine 2', buffer1, bufferEnd, speed_2) #machine_3 = Machine(env, 'Machine 3', buffer1, bufferEnd, speed_2) env.process(gen_arrivals(env, bufferStart)) # Execute env.run(until = time)