pythonsimpyevent-simulation

How to end a process in Simpy simulation


I am a newbie to SimPy and am trying to get the following done. Simulate processes, where each has a different time delay. Once the first process is done, i am interrupting other processes, which i catch. But once in the catch block all these processes resume again and the environment clock continues till all of them are done. I want the simulation to stop right after the first processes is done and then update remaining time on other processes and basically stop everything so the env.now time is at when the first process is done.

Sample code

class Main:
   env = simpy.Environment()
   res1 = Resource(env, list(), 10)
   res2 = Resource(env, list(), 15)
   res3 = Resource(env, list(), 20)
   #Add other resources to each
   res1.appendOtherResource(res2)
   res1.appendOtherResource(res3)
   res2.appendOtherResource(res1)
   res2.appendOtherResource(res3)
   res3.appendOtherResource(res2)
   res3.appendOtherResource(res1)

   Resources = list()
   Resources.append(res1)
   Resources.append(res2)
   Resources.append(res3)
   env.process(scheduleResources(Resources, env))
   env.run()
   

def scheduleResources(Resources, env)
    for resource in Resources:
        env.process(resource.start())

Class Resource has a start and run method with the following: Also assume each resource has a processing time when it is instantiated: Each resource also has a reference to other resources, not showing code for all that to keep it short.

class Resource:
    def __init__(self, env, otherResources, timeToProcess):
        self.resource_process = None
        self.otherResources = otherResources
        self.env = env
        self.timeToProcess = timeToProcess



def appendOtherResource(self, otherResource):
     self.otherResources.append(otherResource)

def start(self):
     yield (self.env.timeout(0))
     self.resource_process = self.env.process(self.run())

 def run(self):
    try:
        self.env.timeout(self.timeToProcess)
        self.timeToProcess = 0
        for res in self.otherResources:
            if res.resource_process != None:
                 if res.resource_process.is_alive:
                        res.resource_process.interrupt() 
     except simpy.Interrupt as interrupt:
        self.timeToProcess = self.timeToProcess - self.env.now
        #How do i ensure that the process that is interrupted is killed after this step and does not continue 

What happens currently is that the shortest resource process is done. Interrupt is called on all other resources, and the timeToProcess is updated correctly. But after this the environment runs to complete all resource processes and the env.now changes beyond the first stop that was intended.

I tried env.exit() but with no success. Is there any way in simpy to stop simulation immediately, no matter what processes are scheduled.

Regards


Solution

  • when you call env.run() the simulation runs until all events complete. The issues is when you interrupt you resources process, your resource processes are interrupted but not the timeout events in your processes. so your resource processes have all completed or been interrupted, but the env.run is still waiting for all the timeouts to complete before it ends the simulation.

    env.run() has a until parameter. this parameter can be a number or a event. when it is a number that is the time unit the sim will end on. When it is a event, the simulation goes until the event fires.

    I wrapped you resource processes in a env.all_of event. This event fires will all the events in its list have fired. and passed this to env.run

    env.run(until=env.all_of(resource_processes))
    

    I also made some other minor changes to get you code to run and to better trace events

    Also not that you can resume the simulation by calling env.run() a second time

    here is the code

    import simpy
    
    def scheduleResources(Resources, env):
    
        # start the resource process and return a list of hte processes
        process_list = []
        for resource in Resources:
            process_list.append(resource.start())
    
        return(process_list)
    
    class Resource:
        def __init__(self, env, id, otherResources, timeToProcess):
            self.id = id
            self.resource_process = None
            self.otherResources = otherResources
            self.env = env
            self.timeToProcess = timeToProcess
    
        def appendOtherResource(self, otherResource):
            self.otherResources.append(otherResource)
    
        def start(self):
            #yield (self.env.timeout(0))
            self.resource_process = self.env.process(self.run())
            return self.resource_process
    
        def run(self):
            try:
                yield self.env.timeout(self.timeToProcess)
                self.timeToProcess = 0
                for res in self.otherResources:
                    if res.resource_process != None:
                        if res.resource_process.is_alive:
                                res.resource_process.interrupt() 
                print (self.id, "finished")
            except simpy.Interrupt as interrupt:
                print(self.id, "interupted")
                self.timeToProcess = self.timeToProcess - self.env.now
    
    def main():
        env = simpy.Environment()
        res1 = Resource(env, 1, list(), 10)
        res2 = Resource(env, 2, list(), 15)
        res3 = Resource(env, 3, list(), 20)
        #Add other resources to each
        res1.appendOtherResource(res2)
        res1.appendOtherResource(res3)
        res2.appendOtherResource(res1)
        res2.appendOtherResource(res3)
        res3.appendOtherResource(res2)
        res3.appendOtherResource(res1)
    
        Resources = list()
        Resources.append(res1)
        Resources.append(res2)
        Resources.append(res3)
    
        resource_processes = scheduleResources(Resources, env)
    
        # stop when all the resources process ends
        env.run(until=env.all_of(resource_processes))
        #env.run()
        print('end time', env.now)
    
    main()