Learning Objectives

  • To develop a deeper understanding of effective modeling with processes and resources.

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(isDefaultProcess = true) {
            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 Blocking

Imagine that at the first station there is a chair for the customer to sit in while receiving service from the first worker. Any customers that arrive while a customer is receiving service at the first station must wait for the server to be free (i.e. the chair to be available). We assume that there is (at least conceptually) an infinite amount of space for the waiting customers at the first station. Now, at the second station, there are two chairs. The customer arriving to the second station will sit in the first chair when receiving service from the server. The second chair is provided for one waiting customer and there is no space for any other customers to wait at the second station. Thus, a customer finishing service at the first station cannot move into (use) the second station if there is a customer waiting at the second station. If a customer at the first station cannot move into the waiting line (2nd chair) at the second station, then the customer is considered blocked. What does this customer do? Well, they are selfish and do not give up their current chair until they can get a chair at the second station. Thus, waiting at the second station may cause waiting to occur at the first station.

Modeling Space as a Resource

Consider the activity diagram for this situation. Notice how the arrows for the seize and release of the resources overlap.

KSL Implementation

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

Resource Pools

  • A resource pool is a generalization of the concept of a resource that permits individual instances of the Resource class to be combined together into a larger pool of units.

  • Resource pools facilitate the sharing of instances of resources across processes.

  • The resources within a pool are not necessarily homogeneous.

  • The important concepts involved in using resource pools are:

    • how to select resources to satisfy a request, and
    • how to allocate a request for units across the pool of resources.

Resource Selection Becomes Important

/**
 * Provides for a method to select resources from a list such that
 * the returned list may contain resources that can fill the amount needed
 */
fun interface ResourceSelectionRuleIfc {
    /**
     * @param amountNeeded the amount needed from resources
     * @param list of resources to consider selecting from
     * @return the selected list of resources. It may be empty
     */
    fun selectResources(amountNeeded: Int, list: List<Resource>): List<Resource>
}
  • Two implementations of the ResourceSelectionRuleIfc interface.
    • FirstFullyAvailableResource selects the first resource from a supplied list that can fully meet the request.
    • ResourceSelectionRule selects a list of resources that (in total) have enough available units to fully meet the request.

Allocation Rules Become Important

/**
 *  Function to determine how to allocate requirement for units across
 *  a list of resources that have sufficient available units to meet
 *  the amount needed.
 */
fun interface AllocationRuleIfc {

    /** The method assumes that the provided list of resources has
     *  enough units available to satisfy the needs of the request.
     *
     * @param amountNeeded the amount needed from resources
     * @param resourceList list of resources to be allocated from
     * @return the amount to allocate from each resource as a map
     */
    fun makeAllocations(amountNeeded: Int, resourceList: List<Resource>): Map<Resource, Int>
}
  • The KSL provides a default instance of the AllocationRuleIfc interface called AllocateInOrderListedRule This rule takes in a list of resources that in total has enough units available and allocates from each listed resource (in the order listed) until the entire amount requested is filled.
  • The rules RandomAllocationRule and ResourceAllocationRule provide for random selection and for selection according to a comparator function, respectively.

Resource Pool Example

The first pool will have 3 resources (john, paul, and george) and the second pool will have 2 resources (ringo and george). One of the resources (george) is shared (in common) between the two pools. The following code creates the four resources, adds them to lists, and then supplies the lists to instances of the ResourcePoolWithQ class.

class ResourcePoolExample(parent: ModelElement) : ProcessModel(parent, null) {

    private val john = Resource(this, name = "John")
    private val paul = Resource(this, name = "Paul")
    private val george = Resource(this, name = "George")
    private val ringo = Resource(this, name = "Ringo")
    private val list1 = listOf(john, paul, george)
    private val list2 = listOf(ringo, george)
    private val pool1: ResourcePoolWithQ = ResourcePoolWithQ(this, list1, name = "pool1")
    private val pool2: ResourcePoolWithQ = ResourcePoolWithQ(this, list2, name = "pool2")
    init {
        val rule = LeastUtilizedResourceAllocationRule()
        pool1.initialDefaultResourceAllocationRule = rule
        pool2.initialDefaultResourceAllocationRule= rule
    }

Using a Resource Pool

The two pools are shared between two processes using straightforward (seize-delay-release) logic.

    private inner class Customer: Entity() {
        val usePool1: KSLProcess = process("Pool 1 Process") {
            wip1.increment()
            timeStamp = time
            val a  = seize(pool1, 1)
            delay(st)
            release(a)
            tip1.value = time - timeStamp
            wip1.decrement()
        }

        val usePool2: KSLProcess = process("Pool 2 Process") {
            wip2.increment()
            timeStamp = time
            val a  = seize(pool2, 1)
            delay(st)
            release(a)
            tip2.value = time - timeStamp
            wip2.decrement()
        }
    }

Test and Repair Example

Consider a test and repair shop for computer parts (e.g. circuit boards, hard drives, etc.) The system consists of an initial diagnostic station through which all newly arriving parts must be processed. Currently, newly arriving parts arrive according to a Poisson arrival process with a mean rate of 3 per hour. The diagnostic station consists of 2 diagnostic machines that are fed the arriving parts from a single queue. Data indicates that the diagnostic time is quite variable and follows an exponential distribution with a mean of 30 minutes. Based on the results of the diagnostics, a testing plan is formulated for the parts. There are currently three testing stations 1. 2, 3 which consist of one machine each. The testing plan consists of an ordered sequence of testing stations that must be visited by the part prior to proceeding to a repair station. Because the diagnosis often involves similar problems, there are common sequences that occur for the parts.

Test Plan % of Parts Sequence
1 25% 2,3,2,1
2 12.5% 3,1
3 37.5% 1,3,1
4 25% 2,3

Test Plan Testing Distributions

  • Data on the testing times indicate that the distribution is well modeled with a lognormal distribution with mean, \(\ \mu\), and standard deviation, \(\sigma\) in minutes.
  • Data indicates that the repair time can be characterized by a triangular distribution with the minimum, mode, and maximum.
Test Plan Testing Time Parameters Repair Time Parameters
1 (20,4.1), (12,4.2), (18,4.3), (16,4.0) (30,60,80)
2 (12,4), (15,4) (45,55,70)
3 (18,4.2), (14,4.4), (12,4.3) (30,40,60)
4 (24,4), (30,4) (35,65,75)

Test and Repair System

The arrangement of the stations and the flow of the parts following Plan 2 in the test and repair shop.

Activity Diagram for Test and Repair System

Implementing the Process

    private inner class Part : Entity() {
        val testAndRepairProcess: KSLProcess = process(isDefaultProcess = true) {
            wip.increment()
            timeStamp = time
            //every part goes to diagnostics
            use(myDiagnostics, delayDuration = diagnosticTime)
            delay(moveTime)
            // determine the test plan
            val plan: List<TestPlanStep> = planList.randomElement
            // get the iterator
            val itr = plan.iterator()
            // iterate through the plan
            while (itr.hasNext()) {
                val tp = itr.next()
                use(tp.resource, delayDuration = tp.processTime)
                if (tp.resource != myRepair) {
                    delay(moveTime)
                }
            }
            timeInSystem.value = time - timeStamp
            wip.decrement()
        }

Setting Up the Test Plans

    inner class TestPlanStep(val resource: ResourceWithQ, val processTime: RandomIfc)

    // make all the plans
    private val testPlan1 = listOf(
        TestPlanStep(myTest2, t11), TestPlanStep(myTest3, t21),
        TestPlanStep(myTest2, t31), TestPlanStep(myTest1, t41), TestPlanStep(myRepair, r1)
    )
    private val testPlan2 = listOf(
        TestPlanStep(myTest3, t12),
        TestPlanStep(myTest1, t22), TestPlanStep(myRepair, r2)
    )
    private val testPlan3 = listOf(
        TestPlanStep(myTest1, t13), TestPlanStep(myTest3, t23),
        TestPlanStep(myTest1, t33), TestPlanStep(myRepair, r3)
    )
    private val testPlan4 = listOf(
        TestPlanStep(myTest2, t14),
        TestPlanStep(myTest3, t24), TestPlanStep(myRepair, r4)
    )

Using an Empirical List

We can use a random list (REmpiricalList) to model this situation.

    // set up the sequences and the random selection of the plan
    private val sequences = listOf(testPlan1, testPlan2, testPlan3, testPlan4)
    private val planCDf = doubleArrayOf(0.25, 0.375, 0.7, 1.0)
    private val planList = REmpiricalList<List<TestPlanStep>>(this, sequences, planCDf)

Summary of New Concepts

  • Resources can be used to model space

  • ResourcePool: A class that represents a set of resources from which a resource can be selected and seized.

  • ResourcePoolWithQ: A class that represents a set of resource from which a resource can be selected and seized with a specified queue to hold entities that are not immediately allocated a resource.

  • ResourceSelectionRuleIfc: An interface that defines how a resource can be selected for allocation from a resource pool.

  • Data structures such as lists can be used to hold information about the process

⌂ Index