Learning Objectives

  • To develop an understanding of the modeling of movement within a DEDS

Tandem Queueing System

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.

Process View: Tandem Queue System

    private inner class Customer : Entity(){
        val tandemQProcess : KSLProcess = process {
            wip.increment()
            timeStamp = time
            seize(worker1)
            delay(st1)
            release(worker1)
            seize(worker2)
            delay(st2)
            release(worker2)
            timeInSystem.value = time - timeStamp
            wip.decrement()
        }

Tandem Queue with Movement

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.

Modeling Movement Time

  • 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}\]

  • Thus, we can randomly generate the value of \(v\), and use the relationship to determine the time taken.

\[t = \frac{d}{v}\]

Modeling Distances

  • The KSL has a construct to model elements that have physical locations.
  • While a 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.
  • The most basic spatial model is a DistancesModel.
    • A distance model defines locations.
    • A distance model defines the distance between locations.
    • A spatial model can have a default movement velocity or speed.
  • The Entity class has attributes such as currentLocation which work with spatial models.

DistancesModel

Defining a DistancesModel

  • Use the Location class within the DistancesModel to define locations
  • Use the addDistance() function to specify the distance between two locations
  • Use the spatialModel property to specify the spatial model used by the ProcessModel
class 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
    }

Remarks About Distances and Locations

  • A location is just a named spatial element.
    • For the DistancesModel spatial model, there is no concept of coordinates.
    • For other spatial models, there can be coordinates or positions.
  • In general travel between two locations should be conceptualized as in both directions.
  • The distance between two locations does not have to be symmetric. Why?
dm.addDistance(fromLocation = enter, toLocation = station1, distance = 60.0, symmetric = false)
dm.addDistance(fromLocation = station1, toLocation = enter, distance = 75.0, symmetric = false)

KSL Implementation

  • Once distances have been defined the tandem queue system movement can be easily handled within the process description.
  • Notice the setting of the entity’s currentLocation
  • The moveTo() 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()
        }
    }

Tandem Queue with Unconstrained Movement Results

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 
-------------------------------------------------------------------------

Movable Resources

  • In the previous example, we assumed that there was an unlimited number of workers available to move the parts.
  • This results in only a simple delay time to move from one location to another.
  • Now, let’s make the more realistic assumption that in order to move a finite number of workers are available such that when a part needs to move between the stations one of the workers dedicated to the transport task must be available to move the part; otherwise the part must wait until a transport worker is available.
  • For simplicity, we are going to assume that their are 3 transport workers, with one dedicated to moving parts to station 1, another dedicated to moving parts from station 1 to station 2, and the third worker dedicated to moving parts from the third workstation to storage.
  • We are also going to assume that the transport worker stays at the drop off location until requested for its next movement.

Updating the Distances

  • Because the transport workers stay at the drop off location, we may need to consider two-way travel.
  • Also, we will need to consider where the transport workers are located at the start of the simulation.
    • We are going to assume that the transport workers all start at the enter location.
    • This requires an update to the distances. Why?
    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
      }

Defining Mobile Resources

  • A MovableResource is a resource that can move within a spatial model.
    • They are just like resources but they can have a velocity and maintain their location.
      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")
  • The usage of a movable resource must account for its movement to the pick up location. This would account for an empty move.

The MovableResource Class

Modeling with Movable Resources

  • We seize 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()
        }
    }

Modeling With Movable Resources Version 2

  • The pattern of seize-move-moveWith-release is so common we can instead use the transportWith() function.
    • This is similar to the 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()
          }
      }

How is 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)
    }

Modeling With Movable Resources Version 3

  • Movable resources can also be placed in resource pools and shared.
  • Also, it is easy to add loading and unloading time.
    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() = myUnLoadingTime

Modeling With Movable Resources Version 4

  • The loading and unloading time could be modeled with simple delays.
    • seize-move-delay(forLoading)-moveWith-delay(forUnloading)-release
    • transportWith 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()
        }
    }

Summary of New Concepts

  • Modeling entity movement can be achieved with a simple delay.
  • If distance and velocity are available, then we can compute the time of the delay.
  • The KSL DistancesModel is a SpatialModel that provides named locations and the distances associated with movement between them.
  • The Entity class provides basic tracking of the location of the entity when using movement constructs.
  • Movable resources are resources that can move within a spatial model.
    • To and from distance become important.
  • New suspend functions include:
    • move() moves a spatial element to a particular location from another location
    • moveWith() used with movable resources to move both the resource and the entity to the location
    • transportWith() used to move a movable resource to the entity’s location and then move both the entity and the resource to the specified destination.
      • Loading and unloading times can be easily incorporated into the process.
⌂ Index