8.3 Modeling the Test and Repair System with Movable Resources
The results of the constrained resource analysis in Section 8.1.1 indicated that modeling constrained transfer for the parts in the test and repair system may have a significant effect on the system’s ability to meet the contract requirements. This may be due to having the workers share the roles of tending the machines and transporting the parts. The following example investigates whether or not a set of dedicated workers would make sense for this system. In particular, the number of workers to dedicate to the transport task needs to be determined. Since there is a lot of walking involved, the model needs to be more precise in the physical modeling of the situation. This could also allow different layout configurations to be simulated if the relocation of the stations would make a difference in the efficiency of the system.
To model this situation using movable resources, the distance between the stations and a way to model the velocity of transport are required. Since the workers have a natural variability in the speed of their walking, a model for human walking speed is needed. Based on some time study data, the velocity of a worker walking in the facility has been determined to be distributed according to a triangular distribution with a minimum of 22.86, a mode of 45.72, and a maximum of 52.5, all in meters per minute. Since this distribution will be used in many locations in the model, a random variable should be defined to represent the velocity.
Based on measuring the distance between the stations, the approximate distance between the stations has been determined as given in Table 8.1. Recall that both the loaded and unloaded distances for the movement should be specified. For example, even though no parts are routed from repair to diagnostics, the distance from repair to diagnostics should be given because the worker (transporter) may be at the repair station when something needs to be moved from the diagnostic station. Thus, the worker must walk from the repair station to the diagnostic station (unloaded) in order to pick up the part. Notice also that the distances do not have to be symmetric (i.e. the distance from test 1 to test 2 does not have to be the same as the distance from test 2 to test 1).
Station | Diagnostics | Test 1 | Test 2 | Test 3 | Repair |
---|---|---|---|---|---|
Diagnostics | – | 40 | 70 | 90 | 100 |
Test 1 | 43 | – | 10 | 60 | 80 |
Test 2 | 70 | 15 | – | 65 | 20 |
Test 3 | 90 | 80 | 60 | – | 25 |
Repair | 110 | 85 | 25 | 30 | – |
Starting with the finished model from Example 7.4 of Section 7.1.3 we can make small changes in order to utilize movable resources. The first step is to define the locations and walking speed for the transport workers.
// velocity is in meters/min
private val myWalkingSpeedRV = RandomVariable(this, TriangularRV(22.86, 45.72, 52.5))
private val dm = DistancesModel()
private val diagnosticStation = dm.Location("DiagnosticStation")
private val testStation1 = dm.Location("TestStation1")
private val testStation2 = dm.Location("TestStation2")
private val testStation3 = dm.Location("TestStation3")
private val repairStation = dm.Location("RepairStation")
Since the transport workers need to walk without carrying a part, we need to define the distances to and from the locations as shown in the following code. Notice that the walking speed is used for the default velocity of movement within the distance model.
init {
// distance is in meters
dm.addDistance(diagnosticStation, testStation1, 40.0)
dm.addDistance(diagnosticStation, testStation2, 70.0)
dm.addDistance(diagnosticStation, testStation3, 90.0)
dm.addDistance(diagnosticStation, repairStation, 100.0)
dm.addDistance(testStation1, diagnosticStation, 43.0)
dm.addDistance(testStation1, testStation2, 10.0)
dm.addDistance(testStation1, testStation3, 60.0)
dm.addDistance(testStation1, repairStation, 80.0)
dm.addDistance(testStation2, diagnosticStation, 70.0)
dm.addDistance(testStation2, testStation1, 15.0)
dm.addDistance(testStation2, testStation3, 65.0)
dm.addDistance(testStation2, repairStation, 20.0)
dm.addDistance(testStation3, diagnosticStation, 90.0)
dm.addDistance(testStation3, testStation1, 80.0)
dm.addDistance(testStation3, testStation2, 60.0)
dm.addDistance(testStation3, repairStation, 25.0)
dm.addDistance(repairStation, diagnosticStation, 110.0)
dm.addDistance(repairStation, testStation1, 85.0)
dm.addDistance(repairStation, testStation2, 25.0)
dm.addDistance(repairStation, testStation3, 30.0)
dm.defaultVelocity = myWalkingSpeedRV
spatialModel = dm
}
Now, we can define the transport workers as a pool of movable resources. We will specify that there will be three workers dedicated to the transport task. In this case, the workers will start active at the diagnostics station. For the purposes of this example, we will rely on the default movable resource selection and allocation rules.
private val transportWorkers = MovableResourcePoolWithQ(
this, 3, diagnosticStation, myWalkingSpeedRV, name = "TransportWorkerPool")
Since this situation involves travelling to and from the stations, we need to make a slight change to the test plan definitions given in Example 7.4 of Section 7.1.3. We need to add the destination location to the test plan sequences.
// define steps to represent a plan, include location information
inner class TestPlanStep(
val testMachine: ResourceWithQ,
val processTime: RandomIfc,
val testStation: LocationIfc)
// make all the plans
private val testPlan1 = listOf(
TestPlanStep(myTest2, t11, testStation2), TestPlanStep(myTest3, t12, testStation3),
TestPlanStep(myTest2, t13, testStation2), TestPlanStep(myTest1, t14, testStation1)
)
Notice that the property testStation
has been added to the TestPlanStep
class. This property will hold the destination information for transport from the entity’s current location to its destination. The code illustrates how the information for test plan 1 has been updated. Recall from Example 7.4 that the test machine visitation sequence for test plan 1 is \(2-3-2-1\). This information has been added to the test plan steps for test plan 1. The other test plans are updated in a similar manner. The process description for the test and repair activities becomes straightforward.
private inner class Part : Entity() {
val plan: List<TestPlanStep> = planList.randomElement
val testAndRepairProcess: KSLProcess = process(isDefaultProcess = true) {
currentLocation = diagnosticStation
wip.increment()
timeStamp = time
//every part goes to diagnostics
use(diagnosticWorkers, delayDuration = diagnosticTime)
// get the iterator
val itr = plan.iterator()
// iterate through the plan
while (itr.hasNext()) {
val tp = itr.next()
// goto the location
transportWith(transportWorkers, toLoc = tp.testStation)
// use the tester
use(tp.testMachine, delayDuration = tp.processTime)
}
// visit repair
transportWith(transportWorkers, toLoc = repairStation)
use(repairWorkers, delayDuration = repairTimes[plan]!! )
timeInSystem.value = time - timeStamp
wip.decrement()
}
}
Notice that the property plan
is randomly determined when the part is created. The testAndRepairProcess
has the entity starting at the diagnostic station by setting the part’s currenLocation
property to the diagnostic station location. Then, the part uses the diagnostic workers. Because the test plan has the destination of the next station, the transport is placed at the top of the iteration through the test plan sequence. The transportWith()
function is used to transport the part using the transportWorkers
movable resource pool. Then, the appropriate test machine is used for the appropriate processing time for the test plan step. After visiting all the test machines on its sequence the part then transports to the repair station, where it uses the repair workers.
The following provides the results from running the model under the same settings as in Example 7.4.
Statistical Summary Report
Name | Count | Average | Half-Width |
---|---|---|---|
DiagnosticWorkers:InstantaneousUtil | 10 | 0.75 | 0.005 |
DiagnosticWorkers:NumBusyUnits | 10 | 1.5 | 0.011 |
DiagnosticWorkers:ScheduledUtil | 10 | 0.75 | 0.005 |
DiagnosticWorkers:Q:NumInQ | 10 | 1.928 | 0.107 |
DiagnosticWorkers:Q:TimeInQ | 10 | 38.512 | 2.167 |
DiagnosticWorkers:WIP | 10 | 3.428 | 0.114 |
Test1:InstantaneousUtil | 10 | 0.857 | 0.007 |
Test1:NumBusyUnits | 10 | 0.857 | 0.007 |
Test1:ScheduledUtil | 10 | 0.857 | 0.007 |
Test1:Q:NumInQ | 10 | 3.232 | 0.266 |
Test1:Q:TimeInQ | 10 | 57.414 | 4.459 |
Test1:WIP | 10 | 4.088 | 0.271 |
Test2:InstantaneousUtil | 10 | 0.776 | 0.008 |
Test2:NumBusyUnits | 10 | 0.776 | 0.008 |
Test2:ScheduledUtil | 10 | 0.776 | 0.008 |
Test2:Q:NumInQ | 10 | 1.584 | 0.138 |
Test2:Q:TimeInQ | 10 | 42.121 | 3.445 |
Test2:WIP | 10 | 2.36 | 0.142 |
Test3:InstantaneousUtil | 10 | 0.862 | 0.005 |
Test3:NumBusyUnits | 10 | 0.862 | 0.005 |
Test3:ScheduledUtil | 10 | 0.862 | 0.005 |
Test3:Q:NumInQ | 10 | 2.587 | 0.158 |
Test3:Q:TimeInQ | 10 | 51.666 | 3.03 |
Test3:WIP | 10 | 3.449 | 0.16 |
RepairWorkers:InstantaneousUtil | 10 | 0.868 | 0.005 |
RepairWorkers:NumBusyUnits | 10 | 2.605 | 0.014 |
RepairWorkers:ScheduledUtil | 10 | 0.868 | 0.005 |
RepairWorkers:Q:NumInQ | 10 | 1.294 | 0.081 |
RepairWorkers:Q:TimeInQ | 10 | 25.864 | 1.559 |
RepairWorkers:WIP | 10 | 3.899 | 0.09 |
TransportWorkerPool:R1:InstantaneousUtil | 10 | 0.224 | 0.002 |
TransportWorkerPool:R1:NumBusyUnits | 10 | 0.224 | 0.002 |
TransportWorkerPool:R1:ScheduledUtil | 10 | 0.224 | 0.002 |
TransportWorkerPool:R1:FracTimeMoving | 10 | 0.224 | 0.002 |
TransportWorkerPool:R1:FracTimeTransporting | 10 | 0.147 | 0.001 |
TransportWorkerPool:R1:FracTimeMovingEmpty | 10 | 0.076 | 0.001 |
TransportWorkerPool:R2:InstantaneousUtil | 10 | 0.158 | 0.001 |
TransportWorkerPool:R2:NumBusyUnits | 10 | 0.158 | 0.001 |
TransportWorkerPool:R2:ScheduledUtil | 10 | 0.158 | 0.001 |
TransportWorkerPool:R2:FracTimeMoving | 10 | 0.158 | 0.001 |
TransportWorkerPool:R2:FracTimeTransporting | 10 | 0.104 | 0.001 |
TransportWorkerPool:R2:FracTimeMovingEmpty | 10 | 0.054 | 0 |
TransportWorkerPool:R3:InstantaneousUtil | 10 | 0.085 | 0.001 |
TransportWorkerPool:R3:NumBusyUnits | 10 | 0.085 | 0.001 |
TransportWorkerPool:R3:ScheduledUtil | 10 | 0.085 | 0.001 |
TransportWorkerPool:R3:FracTimeMoving | 10 | 0.085 | 0.001 |
TransportWorkerPool:R3:FracTimeTransporting | 10 | 0.057 | 0.001 |
TransportWorkerPool:R3:FracTimeMovingEmpty | 10 | 0.029 | 0.001 |
TransportWorkerPool:NumBusy | 10 | 0.467 | 0.003 |
TransportWorkerPool:FractionBusy | 10 | 0.156 | 0.001 |
TransportWorkerPool:Q:NumInQ | 10 | 0.001 | 0 |
TransportWorkerPool:Q:TimeInQ | 10 | 0.005 | 0 |
TestAndRepairWithMovableResources:NumInSystem | 10 | 17.692 | 0.363 |
TestAndRepairWithMovableResources:TimeInSystem | 10 | 353.457 | 5.965 |
ProbWithinLimit | 10 | 0.811 | 0.015 |
DiagnosticWorkers:SeizeCount | 10 | 12498.4 | 75.757 |
Test1:SeizeCount | 10 | 14038.7 | 112.996 |
Test2:SeizeCount | 10 | 9377.7 | 97.686 |
Test3:SeizeCount | 10 | 12491 | 74.153 |
RepairWorkers:SeizeCount | 10 | 12485.3 | 73.123 |
TransportWorkerPool:R1:SeizeCount | 10 | 23121.6 | 165.847 |
TransportWorkerPool:R2:SeizeCount | 10 | 16375.3 | 133.58 |
TransportWorkerPool:R3:SeizeCount | 10 | 8905.1 | 137.667 |
As can be seen in the results, the utilization of the three transporters is very low (0.156). Less than three workers are probably needed for the transport task. The reader is asked to explore this issue as an exercise. These results match very closely with the results based on a commercial simulation package discussed in this book..