Resource States#
In the tutorial for the first simulation, there was this example of a resource state:
class CheckoutLane(UP.Actor):
customer_queue = UP.ResourceState[SIM.Store]()
with UP.EnvironmentContext() as env:
lane = CheckoutLane(
name="FirstLane",
customer_queue={
"kind": UP.SelfMonitoringStore,
"capacity":10,
}
)
The obvious question is, why? The following works just fine:
import upstage_des.api as UP
import simpy as SIM
class CheckoutLane(UP.Actor):
customer_queue = UP.State[SIM.Store]()
with UP.EnvironmentContext() as env:
queue_store = UP.SelfMonitoringStore(env, capacity=10)
lane = CheckoutLane(
name="FirstLane",
customer_queue=queue_store,
)
def proc():
yield lane.customer_queue.put("thing")
env.process(proc())
env.run()
print(lane.customer_queue.items)
>>> ["thing"]
There are several reasons for doing this:
To make cloning an Actor for rehearsal more aware and intelligent about stores and containers as states
Simplifies actor instantiation. Instead of having to build a store on your own for each actor, the states accept simpler data types and handle the environment for you.
Better default behavior instead of needing a partial or a lambda function in the factory.
Default expectations, such as being frozen.
State
does not understanding recording of entries or items/counts in stores or containers.
In practice, the second and last reasons are the most compelling in our experience.
Default Resource Arguments#
You can pair setting a default resource with default keyword arguments for its instantiation. This helps
if you want to have default capacities or initial counts. Use the default_kwargs
input to ResourceState
to accomplish this. Default arguments are overriden by whatever is passed at actor instantiation.
class CheckoutLane(UP.Actor):
customer_queue = UP.ResourceState[SIM.Store]()
magazines = UP.ResourceState[SIM.Container](
default = SIM.Container,
default_kwargs={"capacity": 50, "init": 25},
)
with UP.EnvironmentContext() as env:
lane = CheckoutLane(
name="FirstLane",
customer_queue={
"kind": UP.SelfMonitoringStore,
"capacity":10,
}
)
assert lane.magazines.level == 25
lane2 = CheckoutLane(
name="SecondLane",
customer_queue={
"kind": UP.SelfMonitoringStore,
"capacity":10,
},
magazines = {"init": 10},
)
assert lane2.magazines.level == 10
Instantiation Arguments#
The input an Actor needs to receive for a ResourceState is a dictionary of:
‘kind’: The class of the store or container, which is optional if you provided a default.
‘capacity’: A numeric capacity of the store or container.
‘init’: For containers only, an optional starting amount.
key:value pairs for any other input expected as a keyword argument by the store or container class.
If you want to pre-load a store with items, it’s recommended to run a process to yield them. That way you get all the recording you want, if you have it enabled.
The alternative is to do the trick of the_actor.some_store.items.extend(list_of_your_items)
, but that won’t get the recording
to work. You could, in a pinch, run the_actor.some_store._record("hand-record")
.