如何在 SimPy 中拆分相关事件
How to split up dependent events in SimPy
我想实现一个离散事件维护调度模拟,其中一些维护活动必须在另一个维护活动发生时发生。
例如,如果墙壁每 5 年重新粉刷一次,干衬里每 14 年更换一次,那么每当更换干衬里并重新启动时钟时,都必须重新粉刷墙壁。
yr 5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 14: paint walls
yr 19: paint walls
...
我不确定我是否应该将每个 activity 实现为一个引用依赖进程的进程,或者 "wall maintenance" 是否应该是一个内部处理逻辑的进程,或者其他一些
我的代码将每个 activity 作为一个进程,从属进程存储为一个属性,但我觉得我可能错过了执行此操作的正确方法,因为我看到事件发生了两次同年
这是我最后采用的方法:
import simpy
from simpy.events import Interrupt
class Construction(object):
def __init__(self, name, components):
self.name = name
self.components = components
self.link_components()
def link_components(self):
"""Link each component to the next outermost component
"""
for i, component in enumerate(self.components):
try:
component.dependent = self.components[i+1]
except IndexError:
component.dependent = None
class Component(object):
def __init__(self, env, name, lifespan):
"""Represents a component used in a construction.
"""
self.env = env
self.name = name
self.lifespan = lifespan
self.action = env.process(self.run())
def run(self):
while True:
try:
yield self.env.timeout(self.lifespan)
self.replace()
except Interrupt: # don't replace
pass
def replace(self):
print "yr %d: replace %s" % (env.now, self.name)
if self.dependent:
self.dependent.action.interrupt() # stop the dependent process
self.dependent.replace() # replace the dependent component
env = simpy.Environment()
components = [Component(env, 'structure', 60),
Component(env, 'dry-lining', 14),
Component(env, 'paint', 5)]
wall = Construction('wall', components)
env.run(until=65)
您应该始终从一个非常简单(且错误)的实现开始,以便更好地理解您的用例并感受一切是如何工作的,例如:
import simpy
def paint_walls(env, interval):
while True:
yield env.timeout(interval)
print('yr %2d: paint walls' % env.now)
def replace_dry_lining(env, interval):
while True:
yield env.timeout(interval)
print('yr %d: replace dry-lining' % env.now)
env = simpy.Environment()
env.process(paint_walls(env, interval=5))
env.process(replace_dry_lining(env, interval=14))
env.run(until=20)
输出:
yr 5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 15: paint walls
然后您可以开始extending/modifying您的模拟。以下是如何为您的问题建模的两种可能性:
变体 A
我们继续使用两个独立的进程,但需要一种方法在它们之间交换事件 "dry-lining replaced",这样我们也可以刷墙:
import simpy
class Maintenance:
PAINT_WALL_INTERVAL = 5
REPLACE_DRY_LINING_INTERVAL= 14
def __init__(self, env):
self.env = env
self.dry_lining_replaced = env.event()
self.p_paint_walls = env.process(self.paint_walls())
self.p_replace_dry_lining = env.process(self.replace_dry_lining())
def paint_walls(self):
timeout = self.PAINT_WALL_INTERVAL
while True:
yield self.env.timeout(timeout) | self.dry_lining_replaced
print('yr %2d: paint walls' % self.env.now)
def replace_dry_lining(self):
timeout = self.REPLACE_DRY_LINING_INTERVAL
while True:
yield self.env.timeout(timeout)
print('yr %2d: replace dry-lining' % self.env.now)
self.dry_lining_replaced.succeed()
self.dry_lining_replaced = self.env.event()
env = simpy.Environment()
m = Maintenance(env)
env.run(until=20)
输出:
yr 5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 14: paint walls
yr 19: paint walls
变体 B
我们也可以只用一个等待 "paint walls" 或 "replace dry-lining" 事件的进程对其进行建模:
import simpy
def maintenance(env):
PAINT_WALL_INTERVAL = 5
REPLACE_DRY_LINING_INTERVAL = 14
paint_wall = env.timeout(PAINT_WALL_INTERVAL)
replace_dry_lining = env.timeout(REPLACE_DRY_LINING_INTERVAL)
while True:
results = yield paint_wall | replace_dry_lining
do_paint = paint_wall in results
do_replace = replace_dry_lining in results
if do_replace:
print('yr %2d: replace dry-lining' % env.now)
replace_dry_lining = env.timeout(REPLACE_DRY_LINING_INTERVAL)
if do_paint or do_replace:
print('yr %2d: paint walls' % env.now)
paint_wall = env.timeout(PAINT_WALL_INTERVAL)
env = simpy.Environment()
env.process(maintenance(env))
env.run(until=20)
输出:
yr 5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 14: paint walls
yr 19: paint walls
我想实现一个离散事件维护调度模拟,其中一些维护活动必须在另一个维护活动发生时发生。
例如,如果墙壁每 5 年重新粉刷一次,干衬里每 14 年更换一次,那么每当更换干衬里并重新启动时钟时,都必须重新粉刷墙壁。
yr 5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 14: paint walls
yr 19: paint walls
...
我不确定我是否应该将每个 activity 实现为一个引用依赖进程的进程,或者 "wall maintenance" 是否应该是一个内部处理逻辑的进程,或者其他一些
我的代码将每个 activity 作为一个进程,从属进程存储为一个属性,但我觉得我可能错过了执行此操作的正确方法,因为我看到事件发生了两次同年
这是我最后采用的方法:
import simpy
from simpy.events import Interrupt
class Construction(object):
def __init__(self, name, components):
self.name = name
self.components = components
self.link_components()
def link_components(self):
"""Link each component to the next outermost component
"""
for i, component in enumerate(self.components):
try:
component.dependent = self.components[i+1]
except IndexError:
component.dependent = None
class Component(object):
def __init__(self, env, name, lifespan):
"""Represents a component used in a construction.
"""
self.env = env
self.name = name
self.lifespan = lifespan
self.action = env.process(self.run())
def run(self):
while True:
try:
yield self.env.timeout(self.lifespan)
self.replace()
except Interrupt: # don't replace
pass
def replace(self):
print "yr %d: replace %s" % (env.now, self.name)
if self.dependent:
self.dependent.action.interrupt() # stop the dependent process
self.dependent.replace() # replace the dependent component
env = simpy.Environment()
components = [Component(env, 'structure', 60),
Component(env, 'dry-lining', 14),
Component(env, 'paint', 5)]
wall = Construction('wall', components)
env.run(until=65)
您应该始终从一个非常简单(且错误)的实现开始,以便更好地理解您的用例并感受一切是如何工作的,例如:
import simpy
def paint_walls(env, interval):
while True:
yield env.timeout(interval)
print('yr %2d: paint walls' % env.now)
def replace_dry_lining(env, interval):
while True:
yield env.timeout(interval)
print('yr %d: replace dry-lining' % env.now)
env = simpy.Environment()
env.process(paint_walls(env, interval=5))
env.process(replace_dry_lining(env, interval=14))
env.run(until=20)
输出:
yr 5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 15: paint walls
然后您可以开始extending/modifying您的模拟。以下是如何为您的问题建模的两种可能性:
变体 A
我们继续使用两个独立的进程,但需要一种方法在它们之间交换事件 "dry-lining replaced",这样我们也可以刷墙:
import simpy
class Maintenance:
PAINT_WALL_INTERVAL = 5
REPLACE_DRY_LINING_INTERVAL= 14
def __init__(self, env):
self.env = env
self.dry_lining_replaced = env.event()
self.p_paint_walls = env.process(self.paint_walls())
self.p_replace_dry_lining = env.process(self.replace_dry_lining())
def paint_walls(self):
timeout = self.PAINT_WALL_INTERVAL
while True:
yield self.env.timeout(timeout) | self.dry_lining_replaced
print('yr %2d: paint walls' % self.env.now)
def replace_dry_lining(self):
timeout = self.REPLACE_DRY_LINING_INTERVAL
while True:
yield self.env.timeout(timeout)
print('yr %2d: replace dry-lining' % self.env.now)
self.dry_lining_replaced.succeed()
self.dry_lining_replaced = self.env.event()
env = simpy.Environment()
m = Maintenance(env)
env.run(until=20)
输出:
yr 5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 14: paint walls
yr 19: paint walls
变体 B
我们也可以只用一个等待 "paint walls" 或 "replace dry-lining" 事件的进程对其进行建模:
import simpy
def maintenance(env):
PAINT_WALL_INTERVAL = 5
REPLACE_DRY_LINING_INTERVAL = 14
paint_wall = env.timeout(PAINT_WALL_INTERVAL)
replace_dry_lining = env.timeout(REPLACE_DRY_LINING_INTERVAL)
while True:
results = yield paint_wall | replace_dry_lining
do_paint = paint_wall in results
do_replace = replace_dry_lining in results
if do_replace:
print('yr %2d: replace dry-lining' % env.now)
replace_dry_lining = env.timeout(REPLACE_DRY_LINING_INTERVAL)
if do_paint or do_replace:
print('yr %2d: paint walls' % env.now)
paint_wall = env.timeout(PAINT_WALL_INTERVAL)
env = simpy.Environment()
env.process(maintenance(env))
env.run(until=20)
输出:
yr 5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 14: paint walls
yr 19: paint walls