Suppose a service facility consists of two stations in series (tandem), each with its own FIFO queue. Each station consists of a queue and a single server. A customer completing service at station 1 proceeds to station 2, while a customer completing service at station 2 leaves the facility. Assume that the inter-arrival times of customers to station 1 are IID exponential random variables with a mean of 1 minute. Service times of customers at station 1 are exponential random variables with a mean of 0.7 minute, and at station 2 are exponential random variables with mean 0.9 minute. Develop an model for this system. Run the simulation for exactly 20000 minutes and estimate for each station the expected average delay in queue for the customer, the expected time-average number of customers in queue, and the expected utilization. In addition, estimate the average number of customers in the system and the average time spent in the system.
Consider again the tandem queueing situation. In this situation, parts will enter the system at a loading dock that is 60 feet from the station staffed by the first worker. For simplicity, let’s assume that there are always plenty of workers available to move the part from the loading dock to the first station. After the part completes processing at station 1, it is moved to station 2. Again, assume that there are plenty of workers available to move the part from station 1 to station 2. The distance from station 1 to station 2 is 30 feet. After completing processing at station 2, the part is moved to a storage area. We can assume that the walking speed of the workers that move the parts is a triangular distributed random variable with a minimum of 88, a mode of 176, and a maximum of 264, all in feet per minute.
To model the time that it takes to move, we need to translate the distance traveled into time.
If we know the distance to walk, we can determine the time via the following relationship, where \(v\) is the speed (velocity), \(d\) is the distance, and \(t\) is the time.
\[v = \frac{d}{t}\]
\[t = \frac{d}{v}\]
ModelElement is something that can be in the model, to model locations we can define a SpatialModel and elements that are able to know their locations within space.DistancesModel.
Entity class has attributes such as currentLocation which work with spatial models.Location class within the DistancesModel to define locationsaddDistance() function to specify the distance between two locationsspatialModel property to specify the spatial model used by the ProcessModelclass TandemQueueWithUnConstrainedMovement(
parent: ModelElement,
name: String? = null
) : ProcessModel(parent, name) {
// velocity is in feet/min
private val myWalkingSpeedRV = TriangularRV(88.0, 176.0, 264.0)
private val dm = DistancesModel()
private val enter = dm.Location("Enter")
private val station1 = dm.Location("Station1")
private val station2 = dm.Location("Station2")
private val exit = dm.Location("Exit")
init {
// distance is in feet
dm.addDistance(enter, station1, 60.0, symmetric = true)
dm.addDistance(station1, station2, 30.0, symmetric = true)
dm.addDistance(station2, exit, 60.0, symmetric = true)
dm.defaultVelocity = myWalkingSpeedRV
spatialModel = dm
}DistancesModel spatial model, there is no concept of coordinates.currentLocationmoveTo() function is a special case of the more general move() function
move(fromLoc: LocationIfc, toLoc: LocationIfc, velocity: Double = entity.velocity.value, movePriority: Int = PRIORITY, suspensionName: String? = null)moveTo() uses the entity’s current location as the origin of the move. private inner class Customer : Entity() {
val tandemQProcess: KSLProcess = process(isDefaultProcess = true) {
currentLocation = enter
wip.increment()
timeStamp = time
moveTo(station1)
use(worker1, delayDuration = st1)
moveTo(station2)
use(worker2, delayDuration = st2)
moveTo(exit)
timeInSystem.value = time - timeStamp
wip.decrement()
}
}Name Count Average Half-Width
--------------------------------------------------------------------------
worker1:InstantaneousUtil 30 0.6982 0.0023
worker1:NumBusyUnits 30 0.6982 0.0023
worker1:ScheduledUtil 30 0.6982 0.0023
worker1:WIP 30 2.2966 0.0388
worker1:Q:NumInQ 30 1.5983 0.0371
worker1:Q:TimeInQ 30 1.6001 0.0356
worker2:InstantaneousUtil 30 0.8984 0.0037
worker2:NumBusyUnits 30 0.8984 0.0037
worker2:ScheduledUtil 30 0.8984 0.0037
worker2:WIP 30 8.5321 0.4317
worker2:Q:NumInQ 30 7.6337 0.4289
worker2:Q:TimeInQ 30 7.6378 0.4190
TandemQModel:NumInSystem 30 11.7191 0.4414
TandemQModel:TimeInSystem 30 11.7288 0.4250
worker1:SeizeCount 30 14981.8667 40.6211
worker2:SeizeCount 30 14979.4667 41.3926
-------------------------------------------------------------------------
enter location.class TandemQueueWithConstrainedMovement(parent: ModelElement,name: String? = null
) : ProcessModel(parent, name) {
// velocity is in feet/min
private val myWalkingSpeedRV = TriangularRV(88.0, 176.0, 264.0)
private val dm = DistancesModel()
private val enter = dm.Location("Enter")
private val station1 = dm.Location("Station1")
private val station2 = dm.Location("Station2")
private val exit = dm.Location("Exit")
init {
// distance is in feet
dm.addDistance(enter, station1, 60.0, symmetric = true)
dm.addDistance(station1, station2, 30.0, symmetric = true)
dm.addDistance(station2, exit, 60.0, symmetric = true)
dm.addDistance(station2, enter, 90.0, symmetric = true)
dm.addDistance(exit, station1, 90.0, symmetric = true)
dm.addDistance(exit, enter, 150.0, symmetric = true)
dm.defaultVelocity = myWalkingSpeedRV
spatialModel = dm
}MovableResource is a resource that can move within a spatial model.
private val mover1: MovableResourceWithQ = MovableResourceWithQ(this, enter, myWalkingSpeedRV, "Mover1")
private val mover2: MovableResourceWithQ = MovableResourceWithQ(this, enter, myWalkingSpeedRV, "Mover2")
private val mover3: MovableResourceWithQ = MovableResourceWithQ(this, enter, myWalkingSpeedRV, "Mover3")
private val worker1: ResourceWithQ = ResourceWithQ(this, "worker1")
private val worker2: ResourceWithQ = ResourceWithQ(this, "worker2")MovableResource Classseize the movable resource, then move it to the current location, then moveWith the movable resource to the destination, and finally release it. private inner class Customer : Entity() {
val tandemQProcess: KSLProcess = process(isDefaultProcess = true) {
currentLocation = enter
wip.increment()
timeStamp = time
val a1 = seize(mover1)
move(mover1, toLoc = enter)
moveWith(mover1, toLoc = station1)
release(a1)
use(worker1, delayDuration = st1)
val a2 = seize(mover2)
move(mover2, toLoc = station1)
moveWith(mover2, toLoc = station2)
release(a2)
use(worker2, delayDuration = st2)
val a3 = seize(mover3)
move(mover3, toLoc = station2)
moveWith(mover3, toLoc = exit)
release(a3)
timeInSystem.value = time - timeStamp
wip.decrement()
}
}seize-move-moveWith-release is so common we can instead use the transportWith() function.
use function. private inner class Customer : Entity() {
val tandemQProcess: KSLProcess = process(isDefaultProcess = true) {
currentLocation = enter
wip.increment()
timeStamp = time
transportWith(mover1, station1)
use(worker1, delayDuration = st1)
transportWith(mover2, station2)
use(worker2, delayDuration = st2)
transportWith(mover3, exit)
timeInSystem.value = time - timeStamp
wip.decrement()
}
}transportWith() implemented? suspend fun transportWith(movableResourceWithQ: MovableResourceWithQ,
toLoc: LocationIfc, emptyVelocity: Double = movableResourceWithQ.velocity.value,
transportVelocity: Double = movableResourceWithQ.velocity.value,
loadingDelay: GetValueIfc = ConstantRV.ZERO, unLoadingDelay: GetValueIfc = ConstantRV.ZERO,
requestPriority: Int = PRIORITY, emptyMovePriority: Int = PRIORITY,
loadingPriority: Int = PRIORITY, transportPriority: Int = PRIORITY,
unLoadingPriority: Int = PRIORITY
) {
val a = seize(movableResourceWithQ, seizePriority = requestPriority)
move(movableResourceWithQ, entity.currentLocation, emptyVelocity, emptyMovePriority)
if (loadingDelay != ConstantRV.ZERO) {
delay(loadingDelay, loadingPriority)
}
moveWith(movableResourceWithQ, toLoc, transportVelocity, transportPriority)
if (unLoadingDelay != ConstantRV.ZERO) {
delay(unLoadingDelay, unLoadingPriority)
}
if (movableResourceWithQ.homeBase != null){
move(movableResourceWithQ, movableResourceWithQ.homeBase!!, emptyVelocity, emptyMovePriority)
}
release(a)
} private val mover1 = MovableResource(this, enter, myWalkingSpeedRV, "Mover1")
private val mover2 = MovableResource(this, enter, myWalkingSpeedRV, "Mover2")
private val mover3 = MovableResource(this, enter, myWalkingSpeedRV, "Mover3")
private val moverList = listOf(mover1, mover2, mover3)
private val movers = MovableResourcePoolWithQ(this, moverList, myWalkingSpeedRV, name = "Movers")
private val myLoadingTime = RandomVariable(this, UniformRV(0.5, 0.8))
val loadingTimeRV: RandomSourceCIfc
get() = myLoadingTime
private val myUnLoadingTime = RandomVariable(this, UniformRV(0.25, 0.5))
val unloadingTimeRV: RandomSourceCIfc
get() = myUnLoadingTimeseize-move-delay(forLoading)-moveWith-delay(forUnloading)-releasetransportWith makes this easy. private inner class Customer : Entity() {
val tandemQProcess: KSLProcess = process(isDefaultProcess = true) {
currentLocation = enter
wip.increment()
timeStamp = time
transportWith(movers, station1, loadingDelay = myLoadingTime, unLoadingDelay = myUnLoadingTime)
use(worker1, delayDuration = st1)
transportWith(movers, station2, loadingDelay = myLoadingTime, unLoadingDelay = myUnLoadingTime)
use(worker2, delayDuration = st2)
transportWith(movers, exit, loadingDelay = myLoadingTime, unLoadingDelay = myUnLoadingTime)
timeInSystem.value = time - timeStamp
wip.decrement()
}
}DistancesModel is a SpatialModel that provides named locations and the distances associated with movement between them.Entity class provides basic tracking of the location of the entity when using movement constructs.move() moves a spatial element to a particular location from another locationmoveWith() used with movable resources to move both the resource and the entity to the locationtransportWith() used to move a movable resource to the entity’s location and then move both the entity and the resource to the specified destination.