pythonsimpy

Why does the output change after putting code in a function in a simpy process?


In a simpy process I handle a simpy interrupt. I do this in two places and so I want to put the handling code in a function. Before putting it in a function all functions well, after putting it in a function it yields different results.

Below the written out code

def offer(env, simpy_car, simpy_tankcar, route, timestep):
    action = None
    t_prev = -1
    for lat in route:
        try:
            # fuel consumption during timestep unequal to one hour
            # it is assumed that each simpy timestep equals one hour
            t = env.now
            fuel = simpy_car.power * (t - t_prev) * 3600 / (E_OIL * EFFICIENCY)
            t_prev = t
            yield env.process(simpy_car.consume(env, timestep, fuel, lat))
        except sp.Interrupt as e:
            action = e.cause
        while True:
            try:
                if action == 'weather':
                    yield env.process(simpy_car.refuel(env, simpy_tankcar))
                elif action == 'tanking':
                    yield env.process(simpy_car.tanking(env, simpy_tankcar))
                    simpy_car.status = 'DRIVING'
                elif action == 'refuelling':
                    simpy_tankcar.status = 'DRIVING'
                    simpy_car.status = 'DRIVING'
                action = None
                break
            except sp.Interrupt as e:
                action = e.cause

        try:
            yield env.process(simpy_car.check_fuel(env, simpy_tankcar))
        except sp.Interrupt as e:
            action = e.cause
        while True:
            try:
                if action == 'weather':
                    yield env.process(simpy_car.refuel(env, simpy_tankcar))
                elif action == 'tanking':
                    yield env.process(simpy_car.tanking(env, simpy_tankcar))
                    simpy_car.status = 'DRIVING'
                elif action == 'refuelling':
                    simpy_tankcar.status = 'DRIVING'
                    simpy_car.status = 'DRIVING'
                action = None
                break
            except sp.Interrupt as e:
                action = e.cause

    yield from ()

Below the code with the function and function calls

def process_interrupt(action, simpy_car, simpy_tankcar):
    """
    State machine
    """
    while True:
        try:
            if action == 'weather':
                yield env.process(simpy_car.refuel(env, simpy_tankcar))
            elif action == 'tanking':
                yield env.process(simpy_car.tanking(env, simpy_tankcar))
                simpy_car.status = 'DRIVING'
            elif action == 'refuelling':
                simpy_tankcar.status = 'DRIVING'
                simpy_car.status = 'DRIVING'
            action = None
            break
        except sp.Interrupt as e:
            action = e.cause
    return action



def offer(env, simpy_car, simpy_tankcar, route, timestep):
    action = None
    t_prev = -1
    for lat in route:
        try:
            t = env.now
            fuel = simpy_car.power * (t - t_prev) * 3600 / (E_OIL * EFFICIENCY)
            t_prev = t
            yield env.process(simpy_car.consume(env, timestep, fuel, lat))
        except sp.Interrupt as e:
            action = e.cause
        action = process_interrupt(action, simpy_car, simpy_tankcar)

        try:
            yield env.process(simpy_car.check_fuel(env, simpy_tankcar))
        except sp.Interrupt as e:
            action = e.cause
        action = process_interrupt(action, simpy_car, simpy_tankcar)

    yield from ()

I expect both versions to yield the same results, but they don't. What am I doing wrong? Why does the function version yield different results?


Solution

  • Both the main function (offer) and the new function (process_interrupt) are python generators. So in fact you want to divide a generator into two generators. There is a special syntax for delegating to a subgenerator (PEP 380):

    yield from <expr>
    

    When

    action = process_interrupt(action, simpy_car, simpy_tankcar)
    

    is replaced by

    action = yield from process_interrupt(action, simpy_car, simpy_tankcar) 
    

    everything works fine.