
별도의 의존성 없이 설치가 가능합니다.
$ pip install simpy
Environment는 SimPy 시뮬레이션의 핵심 컨트롤러 역할을 합니다. 시간의 흐름을 관리하고 이벤트를 처리하는 중심 객체로, 시뮬레이션의 전체 상태를 추적합니다. 모든 이벤트와 프로세스는 Environment 객체 내에서 생성되고 실행되며, env.run()을 호출하여 시뮬레이션이 진행됩니다.
기능: 이벤트 생성, 시뮬레이션 시간 업데이트
사용 예:
import simpy
env = simpy.Environment()
SimPy의 Event는 특정 시점에 발생하는 사건으로, 비동기적으로 실행되는 작업입니다. 모든 SimPy의 이벤트는 yield 구문을 통해 시뮬레이션 내에서 대기 상태를 나타내며, 이벤트가 발생하면 이후의 작업이 진행됩니다. 프로세스 간 통신을 지원하여 여러 객체가 동시에 이벤트를 기다릴 수 있게 합니다.
기능: 상태 변화, 대기 및 동기화 제공
사용 예:
def event_example(env):
print("이벤트 대기 시작", env.now)
yield env.timeout(5)
print("이벤트 완료", env.now)
env.process(event_example(env))
Process는 SimPy에서 동작하는 비동기적 작업의 단위입니다. 각 프로세스는 실행되는 동안 다양한 이벤트와 상호작용하며, yield 키워드를 사용해 이벤트가 완료될 때까지 기다렸다가 다음 작업으로 넘어갑니다. 서로 다른 프로세스가 동시에 동작하거나 특정 조건에서만 동작하도록 구성할 수 있어 복잡한 시스템 시뮬레이션이 가능합니다.
기능: 프로세스 간의 대기 및 실행 순서 제어
사용 예:
def process1(env):
print("Process1 시작", env.now)
yield env.timeout(2)
print("Process1 종료", env.now)
def process2(env):
print("Process2 시작", env.now)
yield env.timeout(3)
print("Process2 종료", env.now)
env.process(process1(env))
env.process(process2(env))
SimPy의 Resource는 시스템 내에서 제한된 자원을 모델링하기 위해 사용됩니다. 예를 들어, 주차 공간, 기계, 충전소 등의 한정된 자원을 나타낼 수 있습니다. 프로세스는 자원을 요청하고, 사용 후에는 자원을 반납하는 방식으로 동작합니다. Resource는 여러 프로세스가 하나의 자원을 공유할 때 발생할 수 있는 경쟁 상황을 시뮬레이션하기에 유용합니다.
charging_station = simpy.Resource(env, capacity=2) # 충전소 용량은 2SimPy의 환경에서는 가상의 시뮬레이션 시간이 현실 시간과 다르게 빠르게 흐릅니다. 하지만 실제 시간과 일치시키고자 할 때는 Real-time 모드를 사용할 수 있습니다. 이는 주로 실시간 시스템의 테스트에 유용하며, realtime 매개변수 설정으로 실제 시간에 맞추어 시뮬레이션 속도를 조정합니다.
env = simpy.RealtimeEnvironment(factor=1.0) # 실시간 속도와 동기화SimPy 시뮬레이션에서는 상태나 자원의 변화를 모니터링할 수 있습니다. 주로 자원의 사용 상태, 이벤트 발생 시점, 프로세스의 진행 상태 등을 추적하며 시스템이 어떻게 동작하는지 분석하는 데 사용됩니다. 변수나 로그를 통해 자원 대기 시간, 사용률, 이벤트 발생 시간 등을 기록하면 시뮬레이션 결과를 검토하고 최적화하는 데 도움이 됩니다.
def monitor_example(env, resource):
while True:
print(f'자원 사용률: {resource.count}/{resource.capacity} at {env.now}')
yield env.timeout(1) # 매 1단위 시간마다 기록SimPy의 timeout은 지정된 시간이 흐른 후에 프로세스를 재개할 수 있게 해주는 기능으로, 프로세스나 이벤트가 정해진 시간에 발생하도록 예약하는 방식입니다. 이 기능은 시스템의 구성 요소가 특정 시간 간격으로 활동하는 상황을 모델링할 때 매우 유용합니다.
def time_scheduling_example(env):
print("5초 뒤에 이벤트 발생 예정")
yield env.timeout(5) # 5초 대기
print("이벤트 발생", env.now)이처럼 SimPy의 다양한 구성 요소들은 복잡한 시스템의 동작을 현실감 있게 표현할 수 있게 해주고, 서로 밀접하게 연결되어 시뮬레이션을 더욱 풍부하게 만듭니다.
SimPy에서는 OOP 개념을 이용해 다양한 시스템의 구성 요소를 클래스로 정의하고, 객체로 만들 수 있어요.
예를 들어, 각각의 차량을 하나의 객체로 만들고, 충전소 역시 객체로 만들어 관리할 수 있습니다. 이렇게 하면 여러 대의 차량이나 여러 개의 충전소를 동시에 관리하는 상황을 쉽게 모델링할 수 있죠.
클래스 (Class)와 객체 (Object)
Car라는 클래스를 만들고 이를 통해 여러 대의 차량 객체를 생성하면, 각각의 차량이 서로 다른 주차 시간이나 주행 시간을 가지도록 설정할 수 있어요.class Car:
def __init__(self, env, name, station, 주행시간, 충전시간):
self.env = env
self.name = name
self.station = station
self.주행시간 = 주행시간
self.충전시간 = 충전시간
env.process(self.run()) # 시뮬레이션에 이 차량 추가
def run(self):
yield self.env.timeout(self.주행시간)
print(f'{self.name} {self.env.now} 분에 도착')
with self.station.request() as req:
yield req
print(f'{self.name} {self.env.now} 분에 충전 시작')
yield self.env.timeout(self.충전시간)
print(f'{self.name} {self.env.now} 분에 충전 완료')
이 Car 클래스는 각각의 차량이 언제 충전을 시작하고 끝내는지를 표현하는 독립적인 객체로 모델링해줍니다.
상속 (Inheritance)
캡슐화 (Encapsulation)
다형성 (Polymorphism)
ElectricCar라는 클래스를 Car 클래스에서 상속받아 전기차 전용 특성을 추가할 수도 있고, Car 클래스의 주행 시간, 충전 시간 등은 해당 객체 내부에 캡슐화되어 관리됩니다. 여러 종류의 차량(Car, ElectricCar, HybridCar 등)이 모두 run() 메서드를 가질 수 있고, run()이 각 객체에서 다른 방식으로 동작할 수 있는 것처럼 객체 묶음 만들기!가 편하겠죠!이런 OOP 개념이 중요한 이유는 복잡한 시스템을 설계하고 시뮬레이션할 때 구성 요소 간의 관계와 특성을 명확히 표현할 수 있기 때문이에요. 코드의 구조화가 잘되어야 다른 사람도 쉽게 이해하고, 필요할 때 시스템에 새로운 요소를 쉽게 추가할 수 있죠.
주요 요소 설명
주유소는 두 개의 연료 펌프(Resource)를 가지고 있고, 각 펌프는 같은 연료 탱크(Container)에서 연료를 가져옵니다.
차량은 무작위 시간 간격으로 도착하여 연료 펌프를 요청하고, 충전을 완료하면 떠납니다.
주유소의 연료 관리는 연료 수준을 정기적으로 확인하고, 일정 수준 이하로 떨어지면 트럭이 연료를 보충하도록 호출합니다.
실행 흐름
시뮬레이션이 시작되면 car_generator가 차량을 생성합니다.
차량이 주유소에 도착하면 연료 펌프를 요청하고, 연료를 충전한 후 떠납니다.
gas_station_control이 주기적으로 연료 탱크의 수준을 확인하며, 연료가 부족해지면 tank_truck을 호출하여 주유소의 연료를 보충합니다.
import itertools
import random
import simpy
# 시뮬레이션 설정 상수
RANDOM_SEED = 42 # 랜덤 시드 값 (재현 가능성 확보)
STATION_TANK_SIZE = 200 # 주유소 연료 탱크 용량 (리터)
THRESHOLD = 25 # 주유소 탱크의 최소 연료 수준 (%)
CAR_TANK_SIZE = 50 # 자동차 최대 연료 탱크 크기 (리터)
CAR_TANK_LEVEL = [5, 25] # 자동차의 초기 연료 수준 범위 (리터)
REFUELING_SPEED = 2 # 자동차 주유 속도 (리터/초)
TANK_TRUCK_TIME = 300 # 탱크 트럭 도착 시간 (초)
T_INTER = [30, 300] # 차량 도착 간격 (최소/최대, 초)
SIM_TIME = 1000 # 전체 시뮬레이션 시간 (초)
# 차량 프로세스 정의: 차량이 주유소에 도착하여 연료를 요청하고 주유하는 과정
def car(name, env, gas_station, station_tank):
"""자동차가 주유소에 도착하여 주유를 요청하고 충전하는 함수.
주유 펌프를 요청한 후, 주유소의 연료를 채워가며, 연료가 부족할 경우 대기."""
# 차량의 초기 연료 수준 설정
car_tank_level = random.randint(*CAR_TANK_LEVEL)
print(f'{env.now:6.1f} s: {name} arrived at gas station') # 차량 도착 출력
# 주유 펌프를 요청
with gas_station.request() as req:
yield req # 주유 펌프가 이용 가능해질 때까지 대기
# 차량의 최대 연료 수준까지 필요한 연료 계산
fuel_required = CAR_TANK_SIZE - car_tank_level
# 주유소 연료 탱크에서 필요한 연료만큼 가져오기
yield station_tank.get(fuel_required)
# 주유 시간이 걸리는 만큼 대기
yield env.timeout(fuel_required / REFUELING_SPEED)
print(f'{env.now:6.1f} s: {name} refueled with {fuel_required:.1f}L')
# 주유소 연료 수준을 모니터링하는 제어 프로세스 정의
def gas_station_control(env, station_tank):
"""주기적으로 주유소 연료 수준을 확인하고,
연료 수준이 특정 임계치 아래로 떨어지면 탱크 트럭을 호출하는 함수."""
while True:
# 연료 수준이 임계치 아래일 경우 탱크 트럭 호출
if station_tank.level / station_tank.capacity * 100 < THRESHOLD:
print(f'{env.now:6.1f} s: Calling tank truck')
# 트럭 도착 및 주유소 연료 충전 완료까지 대기
yield env.process(tank_truck(env, station_tank))
# 일정 시간 대기 후 연료 수준 재확인
yield env.timeout(10)
# 주유소에 도착하여 연료를 충전하는 트럭 프로세스 정의
def tank_truck(env, station_tank):
"""지정된 시간 후 주유소에 도착하여 연료를 충전하는 탱크 트럭."""
# 트럭의 이동 시간만큼 대기
yield env.timeout(TANK_TRUCK_TIME)
# 주유소 탱크의 부족한 연료량 계산
amount = station_tank.capacity - station_tank.level
# 주유소 탱크에 부족한 연료만큼 추가
station_tank.put(amount)
print(f'{env.now:6.1f} s: Tank truck arrived and refueled station with {amount:.1f}L')
# 주유소에 차량이 도착하는 생성기 프로세스 정의
def car_generator(env, gas_station, station_tank):
"""랜덤 간격으로 새로운 차량이 주유를 위해 도착하는 과정."""
for i in itertools.count():
# 다음 차량 생성까지 랜덤 간격으로 대기
yield env.timeout(random.randint(*T_INTER))
# 새로운 차량의 주유 프로세스 시작
env.process(car(f'Car {i}', env, gas_station, station_tank))
# 시뮬레이션 설정 및 실행
print('Gas Station refueling simulation')
random.seed(RANDOM_SEED)
# 환경과 공유 자원 초기화
env = simpy.Environment() # 시뮬레이션 환경 생성
gas_station = simpy.Resource(env, 2) # 주유 펌프 (2대) 설정
station_tank = simpy.Container(env, STATION_TANK_SIZE, init=STATION_TANK_SIZE) # 주유소 연료 탱크
# 제어 및 차량 생성기 프로세스 시작
env.process(gas_station_control(env, station_tank))
env.process(car_generator(env, gas_station, station_tank))
# 정의된 시간 동안 시뮬레이션 실행
env.run(until=SIM_TIME)
- 출력 예시
차량들이 주유소에 도착하여 연료를 충전하는 과정이 시간 순서대로 기록됩니다.
특정 시간에 연료가 부족해지면 주유소가 트럭을 호출하고, 트럭이 도착하여 연료를 보충한 후 차량들이 다시 연료를 충전할 수 있게 됩니다.
simpy 공식문서
[Simulation] SimPy 패키지로 스타벅스 예제 만들기 (1)
SimPy 주차장 시뮬레이션 결과 시각화
Simpy gitlab