To be able to understand the process view perspective compared to the event view
To be able model discrete-event dynamic systems using the process view
To be able build process view models using the KSL
In the event view, we directly modeled the system state by identifying the events and specifying the state changes.
In the process view, the events will be implied by the action of entities within the system.
An entity is an object of interest that moves through the system. Entities experience processes as they experience the system.
A process can be thought of as a sequence of activities, where an activity is an element of the system that takes an interval of time to complete.
Entities are uniquely identifiable within the system. If there are two entities in the system, they can be distinguished by the values of their attributes.
Entity attributes are properties of the entity class.
Entities carry or retain these attributes and their values as they experience the system. The values of the attributes for particular entity instances might change during the operation of the system.
Not all information in a system is local to the entity types.
Entity class is a base class that encapsulates the ability to experience processes.process() function is a very special function builder that allows for the definition of a co-routine that defines the process for the entity.
Entity class is an inner class for the ProcessModel class, which is a sub-class of ModelElement.ProcessModel class provides management for the entities that experience processes.
ProcessModel class.ResourceWithQ class defines the resource and a queue for the entities to wait.init {
require(numPharmacists > 0) { "The number of pharmacists must be >= 1" }
}
private val pharmacists: ResourceWithQ = ResourceWithQ(this, "Pharmacists", numPharmacists)
private var serviceTime: RandomVariable = RandomVariable(this, rSource = ExponentialRV(0.5, 2))
val serviceRV: RandomSourceCIfc
get() = serviceTime
private var timeBetweenArrivals: RandomVariable = RandomVariable(parent, rSource = ExponentialRV(1.0, 1))
val arrivalRV: RandomSourceCIfc
get() = timeBetweenArrivals
private val wip: TWResponse = TWResponse(this, "${this.name}:NumInSystem")
val numInSystem: TWResponseCIfc
get() = wip
private val timeInSystem: Response = Response(this, "${this.name}:TimeInSystem")
val systemTime: ResponseCIfc
get() = timeInSystem
private val numCustomers: Counter = Counter(this, "${this.name}:NumServed")
val numCustomersServed: CounterCIfc
get() = numCustomersinitialize() method.
initialize() method schedules the first arrival event.arrival() function is the event routine for the arrival event.
seize() function, which attempts to use a unit of the pharmacist resource.
a returned from the seize function is called an allocation.release() function.
ProcessModel to have access to entity and process constructs.ResourceWithQ class to define the resource to be used within the process.Entity to define the Customer entity class and its process routine.activate() function, which is available only within sub-classes of ProcessModelseize(), delay(), and release() functions of the KSLProcess to work with resources via shared mutable state.seize() and delay() functions are suspending functions, which can only be called within a coroutine context.It is critical to understand that
arrival method,Since many customers are active at the same time (in a pseudo-parallelism) they compete for the pharmacist. This causes queueing.
This process view depends on shared state.
In this model, the primary shared state is via the resource.
Half-Width Statistical Summary Report - Confidence Level (95.000)%
Name Count Average Half-Width
------------------------------------------------------------------------------------
NumBusy 30 0.5035 0.0060
# in System 30 1.0060 0.0271
System Time 30 6.0001 0.1441
PharmacyQ:NumInQ 30 0.5025 0.0222
PharmacyQ:TimeInQ 30 2.9961 0.1235
SysTime > 4.0 minutes 30 0.5136 0.0071
Num Served 30 2513.2667 17.6883
-----------------------------------------------------------------------------------------
CreatedState - The entity is placed into this state when it is created. From the created state, the entity can be scheduled to be active.
activate() function of the entity causes the entity to be scheduled to be activated.Scheduled - The scheduled state indicates that the entity is associated with an event that is pending to occur in the event calendar. The entity is scheduled to be active at some future time. The underlying process associated with the entity is suspended.
delay() function will place the entity in the scheduled state.Active - This is the state that indicates that the entity is executing an underlying process. It is executing non-suspending code within its process. It is the active entity. From the active state, the entity can be suspended for a number of reasons, each mapped to various states.
WaitingForResource - This state indicates that the entity’s process is suspended because the entity is waiting for units of a resource.
seize() function can place the entity into this state.WaitingForSignal - This state indicates that the entity’s process is suspended because the entity is waiting on an arbitrary signal to be sent.
BlockedSending - This state indicates that the entity’s process is suspended because the entity is trying to send an item via a shared blocking queue and there is not space for the item in the queue. Blocking queues can be used to communicate between processes.
BlockedReceiving - This state indicates that the entity’s process is suspended because the entity is trying to receive items from a blocking queue and there are no items to receive. This is the other end of the communication channel formed by a blocking queue.
InHoldQueue - This state indicates that the entity’s process is suspended because the entity is an arbitrary queue that holds entities until they are removed and re-activated.
WaitForProcess - An entity may activate another process. This state indicates that the entity’s process is suspended because the entity is waiting for the process to complete before proceeding.
ProcessCoroutine). This class defines the legal states of the coroutine. EntityGenerator is like an EventGenerator but defined to create and activate entities.ResourceWithQ is a shared resource with specified capacity that can be seized and released.HoldQueue is a shared queue that can hold entities until they are removed. The entities that enter the hold queue are suspended.Signal is a class that uses an instance of a HoldQueue to hold entities until they are notified to resume via the index of their rank within the queue.BlockingQueue a blocking queue can be used to assist with communication between two processes.
waitFor(process: KSLProcess) suspending function activates the named process and suspends the current process until the activated process completes.In this example, we model the operation of a STEM Career Fair mixer during a six-hour time period. The purpose of the example is to illustrate the following concepts:
Probabilistic flow of entities
Collecting statistics on observational (tally) and time-persistent data using the KSL responses
Using the seize(), delay(), and release() functions of the KSLProcessBuilder class
Students arrive to a STEM career mixer event according to a Poisson process at a rate of 0.5 student per minute. The students first go to the name tag station, where it takes between 15 and 45 seconds uniformly distributed to write their name and affix the tag to themselves. We assume that there is plenty of space at the tag station, as well as plenty of tags and markers, such that a queue never forms.
After getting a name tag, 50% of the students wander aimlessly around, chatting and laughing with their friends until they get tired of wandering. The time for aimless students to wander around is triangularly distributed with a minimum of 15 minutes, a most likely value of 20 minutes, and a maximum value of 45 minutes. After wandering aimlessly, 90% decide to buckle down and visit companies and the remaining 10% just are too tired or timid and just leave. Those that decide to buckle down visit the MalWart station and then the JHBunt station as described next.
| Entity Type | Activity | Resource Used |
|---|---|---|
| Student (all) | time to affix a name tag ~ UNIF(15, 45) seconds | None |
| Student (wanderer) | Wandering time ~ TRIA(15, 15, 45) minutes | None |
| Student (not timid) | Talk with MalWart ~ EXPO(3) minutes | 1 of 2 MalWart recruiters |
| Student (not timid) | Talk with JHBunt ~ EXPO(6) minutes | 1 of 3 JHBunt recruiters |
class StemFairMixer(parent: ModelElement, name: String? = null) :
ProcessModel(parent, name) {
private val myTBArrivals: RVariableIfc = ExponentialRV(2.0, 1)
private val myNameTagTimeRV = RandomVariable(this,
UniformRV((15.0 / 60.0), (45.0 / 60.0), 2))
private val myWanderingTimeRV = RandomVariable(this,
TriangularRV(15.0, 20.0, 45.0, 3))
private val myTalkWithJHBunt = RandomVariable(this, ExponentialRV(6.0, 4))
private val myTalkWithMalMart = RandomVariable(this, ExponentialRV(3.0, 5))
private val myDecideToWander = RandomVariable(this, BernoulliRV(0.5, 6))
private val myDecideToLeave = RandomVariable(this, BernoulliRV(0.1, 7))
private val myOverallSystemTime = Response(this, "OverallSystemTime")
private val mySystemTimeNW = Response(this, "NonWanderSystemTime")
private val mySystemTimeW = Response(this, "WanderSystemTime")
private val mySystemTimeL = Response(this, "LeaverSystemTime")
private val myNumInSystem = TWResponse(this, "NumInSystem")Define the shared resources for the recruiters.
Define the entity generator for creating and activating the students.
class StemFairMixer(parent: ModelElement, name: String? = null) :
ProcessModel(parent, name) {
.
.
private val myJHBuntRecruiters: ResourceWithQ
= ResourceWithQ(this, capacity = 3, name = "JHBuntR")
private val myMalWartRecruiters: ResourceWithQ
= ResourceWithQ(this, capacity = 2, name = "MalWartR")
private val generator = EntityGenerator(::Student, myTBArrivals,
myTBArrivals) private inner class Student : Entity() {
private val isWanderer = myDecideToWander.value.toBoolean()
private val isLeaver = myDecideToLeave.value.toBoolean()
val stemFairProcess = process(isDefaultProcess = true) {
myNumInSystem.increment()
delay(myNameTagTimeRV)
if (isWanderer) {
delay(myWanderingTimeRV)
if (isLeaver) {
departMixer(this@Student)
return@process
}
}
.
.
}Half-Width Statistical Summary Report - Confidence Level (95.000)%
Name Count Average Half-Width
------------------------------------------------------------------
JHBuntR:BusyUnits 400 2.5219 0.0196
JHBuntR:Util 400 0.8406 0.0065
JHBuntR:Q:NumInQ 400 4.9292 0.3765
JHBuntR:Q:TimeInQ 400 10.5192 0.7647
MalWartR:BusyUnits 400 1.3565 0.0138
MalWartR:Util 400 0.6783 0.0069
MalWartR:Q:NumInQ 400 1.3083 0.1109
MalWartR:Q:TimeInQ 400 2.7696 0.2162
OverallSystemTime 400 34.0890 0.8409
NonWanderSystemTime 400 22.1052 0.8451
WanderSystemTime 400 47.1148 0.8315
LeaverSystemTime 400 27.0520 0.2334
NumInSystem 400 16.7704 0.4829
-----------------------------------------------------------------
TWReponse) and tally response (Response) variables to collect required statistics.RandomVariable to model the time delays and to assign probabilistic student type.delay() suspending function was used to model the name tag activity, the wandering time, and the time spent with the recruiters.seize() and release() functions were used to interact with the recruiter resources.return@ syntax was used to exit the process routine.Student class constructor was supplied to the entity generator via the ::Student syntax.Suppose production orders for tie-dye T-shirts arrive to a production facility according to a Poisson process with a mean rate of 1 per hour.
There are two basic psychedelic designs involving either red or blue dye. For some reason the blue shirts are a little more popular than the red shirts so that when an order arrives about 70% of the time it is for the blue dye designs. In addition, there are two different package sizes for the shirts, 3 and 5 units.
There is a 25% chance that the order will be for a package size of 5 and a 75% chance that the order will be for a package size of 3. Each of the shirts must be individually hand made to the customer’s order design specifications.
The time to produce a shirt (of either color) is uniformly distributed within the range of 15 to 25 minutes.
There are currently two workers who are setup to make either shirt. When an order arrives to the facility, its type (red or blue) is determined and the pack size is determined. Then, the appropriate number of white (un-dyed) shirts are sent to the shirt makers with a note pinned to the shirt indicating the customer order, its basic design, and the pack size for the order.
Meanwhile, the paperwork for the order is processed and a customized packaging letter and box is prepared to hold the order. It takes another worker between 8 to 10 minutes to make the box and print a custom thank you note.
After the packaging is made it waits prior to final inspection for the shirts associated with the order.
After the shirts are combined with the packaging, they are inspected by the packaging worker which is distributed according to a triangular distribution with a minimum of 5 minutes, a most likely value of 10 minutes, and a maximum value of 15 minutes.
Finally, the boxed customer order is sent to shipping.
class TieDyeTShirts(parent: ModelElement, theName: String? = null) :
ProcessModel(parent, theName) {
private val myTBOrders: RVariableIfc = ExponentialRV(60.0)
private val myType: RVariableIfc = DEmpiricalRV(doubleArrayOf(1.0, 2.0),
doubleArrayOf(0.7, 1.0))
private val mySize: RVariableIfc = DEmpiricalRV(doubleArrayOf(3.0, 5.0),
doubleArrayOf(0.75, 1.0))
private val myOrderSize = RandomVariable(this, mySize)
private val myOrderType = RandomVariable(this, myType)
private val myShirtMakingTime = RandomVariable(this, UniformRV(15.0, 25.0))
private val myPaperWorkTime = RandomVariable(this, UniformRV(8.0, 10.0))
private val myPackagingTime = RandomVariable(this, TriangularRV(5.0, 10.0, 15.0))
private val mySystemTime = Response(this, "System Time")
private val myNumInSystem = TWResponse(this, "Num in System")ResourceWithQ to model the shirt makersRequestQ to represent the queue for the orders.
RequestQ is a subclass of the Queue class that is specifically designed to work with seize requests for resources.seize() suspend function allows both the specification of the resource being seized and the queue that will hold the entity if the seize request is not immediately filled. There are two queues involving the use of the packager 1) to hold the in-process orders and 2) the completed orders. private val myShirtMakers: ResourceWithQ = ResourceWithQ(this, capacity = 2,
name = "ShirtMakers_R")
private val myOrderQ : RequestQ = RequestQ(this, name = "OrderQ")
private val myPackager: ResourceWithQ = ResourceWithQ(this, "Packager_R")
private val generator = EntityGenerator(::Order, myTBOrders, myTBOrders)
private val completedShirtQ: BlockingQueue<Shirt> = BlockingQueue(this,
name = "Completed Shirt Q") private inner class Order: Entity() {
val type: Int = myOrderType.value.toInt()
val size: Int = myOrderSize.value.toInt()
var completedShirts : List<Shirt> = emptyList()
val orderMaking : KSLProcess = process("Order Making") {
myNumInSystem.increment()
for(i in 1..size){
val shirt = Shirt(this@Order.id)
activate(shirt.shirtMaking)
}
....
}
}seize() method, which specifies the queue for waiting orders.BlockingQueue holds completed shirts and communicates that they are ready.orderNum is used to identify, for the shirt, which order created it. private inner class Order: Entity() {
...
var a = seize(myPackager, queue = myOrderQ)
delay(myPaperWorkTime)
release(a)
// wait for shirts
completedShirts = waitForItems(completedShirtQ, size, {it.orderNum == this@Order.id})
a = seize(myPackager)
delay(myPackagingTime)
release(a)
myNumInSystem.decrement()
mySystemTime.value = time - this@Order.createTime
}
}Half-Width Statistical Summary Report - Confidence Level (95.000)%
Name Count Average Half-Width
-------------------------------------------------------------------------
ShirtMakers_R:BusyUnits 30 1.0997 0.1407
ShirtMakers_R:Util 30 0.5499 0.0703
ShirtMakers_R:Q:NumInQ 30 1.6911 0.6280
ShirtMakers_R:Q:TimeInQ 30 23.3987 5.8618
Packager_R:BusyUnits 30 0.2968 0.0376
Packager_R:Util 30 0.2968 0.0376
Packager_R:Q:NumInQ 30 0.0424 0.0248
Packager_R:Q:TimeInQ 30 1.0717 0.5204
System Time 30 66.9589 6.3726
Num in System 30 1.1446 0.2502
Completed Shirt Q:RequestQ:NumInQ 30 0.8054 0.1983
Completed Shirt Q:RequestQ:TimeInQ 30 46.1496 5.8560
Completed Shirt Q:ChannelQ:NumInQ 30 0.8047 0.1134
Completed Shirt Q:ChannelQ:TimeInQ 30 14.8315 0.8088
-------------------------------------------------------------------------
ResourceWithQ - models both a resource and a queue to hold waiting entitiesRequestQ - holds entities that are requesting units of a resourceResource - models a set of units that may be used by entitiesBlockingQueue - facilitates communication between two processesEntityGenerator - facilitates creating entities according to a timed patternseize(), delay(), waitForItems() and send() are suspending functionsProcessModel and defining processes via sub-classes of the Entity class.