I do not use recursion very often in simulation. Most of my processes will have a queue and if a entity needs the same process, I just put it back into the queue and let the queue processor process it multiple times.
The example is pealing layers of a onion one layer at a time
here it is with recursion
"""
demonstrates how recurssion can be used in simpy
by simulating the pealing of a onion
programmer: Michael R. Gibbs
"""
import simpy
import random
class Onion():
"""
Simple onion object
id: unique oning id
layers: number of layers to peal
"""
# class var for generating ids
id = 0
def __init__(self):
"""
initialize onion with unique id and random number of layers
"""
Onion.id += 1
self.id = Onion.id
self.layers = random.randint(5,9)
def get_onions(env, res):
"""
sim startup
generate a bunch of onions to be pealed and
start pealing them
"""
for i in range(5):
onion = Onion()
env.process(peal_onion_layer(env,res,onion,0))
# note the lack of of a yeild so all onions get processed in parallel
def peal_onion_layer(env,res,onion,pealed):
"""
gets a pealer resource and peals one onion layer
and release pealer.
will need to get the pealer resource again if another layer
needs to be pealed
"""
with res.request() as req: # Generate a request event
yield req # Wait for access
yield env.timeout(1) # peal
# release resource
pealed += 1
print(f"{env.now} onion: {onion.id} pealed layer {pealed}, {onion.layers - pealed} layers to go")
if onion.layers <= pealed:
#finish, stop recursion
print(f"{env.now} onion: {onion.id} is pealed, {pealed} layers")
else:
# still have layers to peal, recurse
yield env.process(peal_onion_layer(env,res,onion,pealed))
# do any post recurse acions here
print(f"{env.now} onion: {onion.id} exiting layer {pealed}")
# start up recursion
env = simpy.Environment()
res = simpy.Resource(env, capacity=2)
get_onions(env,res)
env.run()
Here is the same sim without recursion
"""
demonstrates how queue can be used instead of recursion
by simulating the pealing of a onion
programmer: Michael R. Gibbs
"""
import simpy
import random
class Onion():
"""
Simple onion object
id: unique oning id
layers: number of layers to peal
"""
# class var for generating ids
id = 0
def __init__(self):
"""
initialize onion with unique id and random number of layers
"""
Onion.id += 1
self.id = Onion.id
self.layers = random.randint(5,9)
def get_onions(env, res):
"""
sim startup
generate a bunch of onions to be pealed and
start pealing them
"""
for i in range(5):
onion = Onion()
env.process(peal_onion_layer(env,res,onion,0))
# note the lack of of a yeild so all onions get processed in parallel
def peal_onion_layer(env,res,onion,pealed):
"""
gets a pealer resource and peals one onion layer
and release pealer.
will need to get the pealer resource again if another layer
needs to be pealed
"""
with res.request() as req: # Generate a request event
yield req # Wait for access
yield env.timeout(1) # peal
# release resource
pealed += 1
print(f"{env.now} onion: {onion.id} pealed layer {pealed}, {onion.layers - pealed} layers to go")
if onion.layers <= pealed:
#finish, stop recursion
print(f"{env.now} onion: {onion.id} is pealed, {pealed} layers")
else:
# still have layers to peal, recurse
env.process(peal_onion_layer(env,res,onion,pealed))
# start up recursion
env = simpy.Environment()
res = simpy.Resource(env, capacity=2)
get_onions(env,res)
env.run()