python仿真案例(SimPy Examples)

SimPy Examples
官方教程:python离散事件仿真库SimPy官方教程

1.Machine Shop

Covers:

- Interrupts
- Resources: PreemptiveResource

车间(workshop)内有n台独立的机器(machine),不停的有作业(job)到达(足以让机器不会出现空闲),每台机器会周期性的发生故障(break down),修理机器需要一位修理员(repairman)。修理员当然也有其他工作,但是修理机器最重要,机器抢占资源,当修理员结束修理工作后会继续做原先的事,车间继续运作

一台机器有两个进程(process):加工产品(working),机器故障(break_machine),break_machine会中断working

修理员的其它工作也是一个进程(other_job),修理员是一个可抢占资源(PreemptiveResource),容量为1,机器修理优先级为1,其它工作优先级为2(数字越小,优先级越高)

import random
import simpy
random_seed = 42  # 随机种子
pt_mean = 10.0  # 加工时间均值
pt_sigma = 2.0  # 加工时间sigma
MTTF = 300.0  # 故障时间间隔
break_mean = 1 / MTTF  # 指数分布参数
repair_time = 30.0  # 修理时间
job_duration = 30  # 修理员的其它工作所需时间
machine_num = 10  # 机器数量
week = 4  # 模拟周数
sim_time = week * 7 * 24 * 60  # 模拟分钟
random.normalvariate(pt_mean, pt_sigma)
10.737845890176626
random.expovariate(break_mean)
42.57687048011325
def time_per_part():
    """返回每个工序的加工时间"""
    return random.normalvariate(pt_mean, pt_sigma)

def time_to_failure():
    """返回机器下一次故障的时间"""
    return random.expovariate(break_mean)
class Machine(object):
    def __init__(self, env, name, repairman):
        self.env = env  # 仿真环境
        self.name = name  # 机器编号
        self.parts_made = 0  # 已经处理过的工序
        self.broken = False  # 是否故障
        
        """开始执行working和break_machine"""
        self.process = env.process(self.working(repairman))
        env.process(self.break_machine())
                    
    """机器加工"""
    def working(self, repairman):
        while True:  # 从环境开始就在执行working进程
            done_in = time_per_part()  # 随机生成工序加工时间
            while done_in:
                try:
                    start = self.env.now  # 记录工序开始加工的时间
                    yield self.env.timeout(done_in)  # 开始加工
                    done_in = 0  # 加工结束后开启下一个工序
                except simpy.Interrupt:  # 机器故障干扰
                    self.broken = True
                    done_in -= self.env.now - start  # 剩余加工时间
                    with repairman.request(priority=1) as req:  # 申请资源repairman,优先级=1
                        yield req
                        yield self.env.timeout(repair_time)  # 开始修理机器
                    self.broken = False  # 修理结束后标志为False
                self.parts_made += 1  # 记录已经完成的工序数
                     
    """机器故障"""
    def break_machine(self):
        while True:  # 从环境开始就在执行break_machine进程
            yield self.env.timeout(time_to_failure())  # 等待故障时间点
            if not self.broken:  # 在故障时间点机器正在加工
                self.process.interrupt()  # 进程干扰
"""repairman的其它工作, 逻辑同working"""
def other_jobs(env, repairman):
    while True: 
        done_in = job_duration
        while done_in:
            with repairman.request(priority=2) as req:  # other_jobs优先级为2
                yield req
                try:
                    start = env.now
                    yield env.timeout(done_in)
                    done_in = 0
                except simpy.Interrupt:
                    done_in -= env.now - start
print('machine shop simulation')
random.seed(random_seed)

env = simpy.Environment()
repairman = simpy.PreemptiveResource(env, capacity=1)

machines = [Machine(env, 'Machine %d' % i, repairman) for i in range(machine_num)]
env.process(other_jobs(env, repairman))
machine shop simulation

env.run(until=sim_time)  # run等效于自动的next,没有run代码会停留在第一次遇到yield
for ma in machines:
    print(f'{ma.name} made {ma.parts_made} parts')
Machine 0 made 3369 parts
Machine 1 made 3386 parts
Machine 2 made 3358 parts
Machine 3 made 3444 parts
Machine 4 made 3482 parts
Machine 5 made 3367 parts
Machine 6 made 3384 parts
Machine 7 made 3314 parts
Machine 8 made 3415 parts
Machine 9 made 3391 parts

2.Bank Renege

Covers:

- Resources: Resource
- Condition events

一个银行柜台,顾客随机到达,每位顾客都有等待时间最大值,如果指定时间内等到柜台,则占用柜台服务一段时间,如果指定时间内没有等到柜台,则离去,通过results = yield req | env.timeout(patience)实现多条件。

import random
import simpy

min_patience = 1
max_patience = 3
SEED = 42
new_customers = 5
interval = 10.0


# 顾客行为
def customer(env, name, counter, time_in_bank):
    arrive = env.now  # 顾客到达
    print(f'{name} arrive at {arrive}')
    with counter.request() as req:  # 请求counter
        patience = random.uniform(min_patience, max_patience)  # 顾客最多等待时间
        results = yield req | env.timeout(patience)  # counter空闲或者等待超时
        wait = env.now - arrive
        if req in results:  # counter空闲为顾客服务
            print(f'{name} wait {wait} at {env.now}')
            tib = random.expovariate(1.0 / time_in_bank)
            yield env.timeout(tib)
            print(f'{name} finish at {env.now}')
        else:  # 等待超时顾客离去
            print(f'{name} RENEGED after wait {wait} at {env.now}')


# 随机生成顾客
def source(env, number, interval, counter):
    for i in range(number):
        c = customer(env, i, counter, time_in_bank=12.0)
        env.process(c)
        t = random.expovariate(1.0 / interval)
        yield env.timeout(t)


print('Bank renege')
random.seed(SEED)
env = simpy.Environment()
counter = simpy.Resource(env, capacity=1)
env.process(source(env, new_customers, interval, counter))
env.run()
Bank renege
0 arrive at 0
0 wait 0 at 0
0 finish at 3.859488768899587
1 arrive at 10.200602872748009
1 wait 0.0 at 10.200602872748009
2 arrive at 12.726464729449145
2 RENEGED after wait 1.173877665258832 at 13.900342394707977
1 finish at 23.750678978331123
3 arrive at 34.99934572496295
3 wait 0.0 at 34.99934572496295
3 finish at 37.95994607648084
4 arrive at 40.47980731892786
4 wait 0.0 at 40.47980731892786
4 finish at 43.14010734889202

输出results有以下两种情况,Request()或者Timeout()

<ConditionValue {<Request() object at 0x7fc0c06473d0>: None}>
<ConditionValue {<Timeout(1.1738776652588323) object at 0x7fc0c06474c0>: None}>

3.Carwash

Covers:

- Waiting for other processes
- Resources: Resource

Carwash的洗车机器数量有限,汽车随机到达,到达后申请资源machine,获取资源后开始wash直到结束后释释放资源离开Carwash。通过set_up生成汽车,初始4辆,后续随机时间间隔到达

import simpy
import random

RANDOM_SEED = 42
NUM_MACHINES = 2
WASHTIME = 5
T_INTER = 7
SIM_TIME = 20


class CarWash(object):
    def __init__(self, env, num_machine, wash_time):
        self.env = env
        self.machine = simpy.Resource(env, capacity=num_machine)
        self.wash_time = wash_time

    def wash(self, car):
        print(f'{car} start washing at {self.env.now}')
        yield self.env.timeout(self.wash_time)
        print(f'{car} finish washing at {self.env.now}')


def car(env, name, cw):
    print(f'{name} arrive at {env.now}')
    with cw.machine.request() as req:
        yield req
        yield env.process(cw.wash(name))
    print(f'{name} leave at {env.now}')


def set_up(env, num_machine, wash_time, inter):
    car_wash = CarWash(env, num_machine, wash_time)
    count = 0
    for i in range(4):
        env.process(car(env, i, car_wash))
        count = i

    while True:
        yield env.timeout(random.randint(inter - 2, inter + 2))
        count += 1
        env.process(car(env, count, car_wash))


print('Carwash')
random.seed(RANDOM_SEED)
env = simpy.Environment()
env.process(set_up(env, NUM_MACHINES, WASHTIME, T_INTER))
env.run(until=SIM_TIME)
Carwash
0 arrive at 0
1 arrive at 0
2 arrive at 0
3 arrive at 0
0 start washing at 0
1 start washing at 0
4 arrive at 5
0 finish washing at 5
1 finish washing at 5
0 leave at 5
1 leave at 5
2 start washing at 5
3 start washing at 5
5 arrive at 10
2 finish washing at 10
3 finish washing at 10
2 leave at 10
3 leave at 10
4 start washing at 10
5 start washing at 10
4 finish washing at 15
5 finish washing at 15
4 leave at 15
5 leave at 15
6 arrive at 17
6 start washing at 17

4.Movie Renege

Covers:

 - Resources: Resource
 - Condition events
 - Shared events
import collections
import random
import simpy

RANDOM_SEED = 42
TICKETS = 50
SIM_TIME = 120


def moviegoer(env, theater, num_tickets, movie):
    with theater.counter.request() as req:
        results = yield req | theater.sold_out[movie]  # 申请counter资源或者票卖完
        if req not in results:  # 如果票卖完
            theater.num_renegers[movie] += 1
        if theater.available[movie] < num_tickets:  # 如果票不够,顾客考虑0.5秒后离去
            yield env.timeout(0.5)
            return
        theater.available[movie] -= num_tickets  # 获取counter,计算余票
        if theater.available[movie] < 2:  # 触犯sold_out时间
            theater.sold_out[movie].succeed()
            theater.when_sold_out[movie] = env.now
            theater.available[movie] = 0
        yield env.timeout(1)


def customer_arrivals(env, theater):
    while True:
        yield env.timeout(random.expovariate(1 / 0.5))  # 顾客到达时间间隔
        movie = random.choice(theater.movies)  # 随机选择电影
        num_tickets = random.randint(1, 6)  # 随机生成所需电影票
        if theater.available[movie]:  # 如果还有剩余的票
            env.process(moviegoer(env, theater, num_tickets, movie))  # 开启买票进程


Theater = collections.namedtuple('Theater', 'counter, movies, available, sold_out, when_sold_out, num_renegers')
print('Movie renege')
random.seed(RANDOM_SEED)
env = simpy.Environment()
counter = simpy.Resource(env, capacity=1)
movies = ['Python Unchained', 'Kill Process', 'Pulp Implementation']
available = {movie: TICKETS for movie in movies}
sold_out = {movie: env.event() for movie in movies}
when_sold_out = {movie: None for movie in movies}
num_renegers = {movie: 0 for movie in movies}
theater = Theater(counter, movies, available, sold_out, when_sold_out, num_renegers)

env.process(customer_arrivals(env, theater))
env.run(until=SIM_TIME)

for movie in movies:
    if theater.sold_out[movie]:
        print(f'{movie} sold out at {theater.when_sold_out[movie]}')
        print(f'{theater.num_renegers[movie]} people leave')
Movie renege
Python Unchained sold out at 38.0100301436374
16 people leave
Kill Process sold out at 43.0100301436374
5 people leave
Pulp Implementation sold out at 28.0100301436374
5 people leave

5.Gas Station Refueling

Covers:

- Resources: Resource
- Resources: Container
- Waiting for other processes
import itertools
import simpy
import random
car_tank = [5, 25]
car_tank_max = 50
speed = 2
transport = 300
threshold = 10
inter = [30, 300]
sim_time = 1000
RANDOM_SEED = 42


def car(env, name, gas_station, fuel_pump):
    car_level = random.randint(*car_tank)  # 汽车当前油量
    print(f'{name} arrive at gas station at {env.now}')
    with gas_station.request() as req:
        start = env.now
        yield req  # 申请加油机
        car_demand = car_tank_max - car_level  # 汽车需要油量
        yield fuel_pump.get(car_demand)  # 从容器中取出
        yield env.timeout(car_demand / speed)  # 加油时间
        print(f'{name} finish refueling at {env.now} with time {env.now - start}')


def gas_station_control(env, fuel_pump):
    while True:
        if fuel_pump.level / fuel_pump.capacity * 100 < threshold:  # 如果油箱油量小于阈值
            print(f'call tank truck at {env.now}')
            yield env.process(tank_truck(env, fuel_pump))  # 呼叫tank truck给油箱加油
        yield env.timeout(10)  # 每10分钟检查一次


def tank_truck(env, fuel_pump):
    yield env.timeout(transport)  # truck运输时间
    amount = fuel_pump.capacity - fuel_pump.level  # 当前油箱油量
    yield fuel_pump.put(amount)  # 给油箱加油

def car_generate(env, gas_station, fuel_pump):
    for i in itertools.count():
        yield env.timeout(random.randint(*inter))  # 固定时间间隔生成汽车
        env.process(car(env, i, gas_station, fuel_pump))

print('Gas Station refuelling')
random.seed(RANDOM_SEED)
env = simpy.Environment()
fuel_pump = simpy.Container(env, capacity=200, init=200)
gas_station = simpy.Resource(env, capacity=2)
env.process(car_generate(env, gas_station, fuel_pump))
env.process(gas_station_control(env, fuel_pump))
env.run(until=sim_time)
Gas Station refuelling
0 arrive at gas station at 87
0 finish refueling at 105.5 with time 18.5
1 arrive at gas station at 129
1 finish refueling at 148.0 with time 19.0
2 arrive at gas station at 284
2 finish refueling at 305.0 with time 21.0
3 arrive at gas station at 385
3 finish refueling at 398.5 with time 13.5
4 arrive at gas station at 459
call tank truck at 460
4 finish refueling at 481.0 with time 22.0
5 arrive at gas station at 705
6 arrive at gas station at 750
6 finish refueling at 779.0 with time 29.0
5 finish refueling at 781.5 with time 76.5
7 arrive at gas station at 891
7 finish refueling at 904.0 with time 13.0

6.Event Latency

Covers:

- Resources: Store
import simpy


class Cable(object):
    def __init__(self, env, delay):
        self.env = env
        self.delay = delay
        self.store = simpy.Store(env)

    def put(self, value):
        yield self.env.timeout(self.delay)
        self.store.put(value)

    def get(self):
        return self.store.get()


def sender(env, cable):
    while True:
        yield env.timeout(5)
        env.process(cable.put(f'Sender sent this at {env.now}'))


def receiver(env, cable):
    while True:
        ms = yield cable.get()
        print(f'Received this at {env.now} while {ms}')


print('Event Latency')
env = simpy.Environment()
cable = Cable(env, 10)
env.process(sender(env, cable))
env.process(receiver(env, cable))
env.run(100)
Event Latency
Received this at 15 while Sender sent this at 5
Received this at 20 while Sender sent this at 10
Received this at 25 while Sender sent this at 15
Received this at 30 while Sender sent this at 20
Received this at 35 while Sender sent this at 25
Received this at 40 while Sender sent this at 30
Received this at 45 while Sender sent this at 35
Received this at 50 while Sender sent this at 40
Received this at 55 while Sender sent this at 45
Received this at 60 while Sender sent this at 50
Received this at 65 while Sender sent this at 55
Received this at 70 while Sender sent this at 60
Received this at 75 while Sender sent this at 65
Received this at 80 while Sender sent this at 70
Received this at 85 while Sender sent this at 75
Received this at 90 while Sender sent this at 80
Received this at 95 while Sender sent this at 85

你可能感兴趣的:(DES,python,仿真,simpy,shop)