TimeInSystem
Mean 0.893513
CI_Half_Width 0.028886
8 Extended Radiology Clinic
In this lab we will extend the simulation developed in the previous lab to include a priority value for patients, use a priority order for scanning, and make the scanners require half an hour of maintenance every 8 hours. The maintenance should only occur when the machine is free, if it is busy when the 8 hours are up the maintenance should take place the next time it is free. We assume that there are 5 levels of priority (1, 2, 3, 4, 5) and more important patients (lower value of priority e.g. 1 is more important than 2) are always seen before any patients of lower priority. In addition, priority 1 and 2 patients are urgent so they do not need to check in, they go directly to queueing for a scan. We want to use the simulation to determine the 90th percentile of time that patients spend in the clinic, between arriving and leaving. In addition we want to compare the time that the different priority levels spend in the clinic.
Once again, since the aim of the lab is to learn Jaamsim, the conceptual model is not given here, instead it is available separately in Chapter 14. To have your lab signed off you need to show that you have built the simulation model, can run it, and the output matches the provided results.
8.1 Experiments
In this lab we will perform one experiment with 50 replications each 1 week long. We will use the same distributions for the interarrival time, check in time, and scan duration for appointment patients as in the previous lab. The proportion of each type of patient in each priority group is given in Table 8.1:
| Priority | Proportion |
|---|---|
| 1 | 0.05 |
| 2 | 0.2 |
| 3 | 0.15 |
| 4 | 0.4 |
| 5 | 0.2 |
8.2 Jaamsim Model
To model the priorities, priority order, and maintenance we need to add some components to the model from the previous lab, so create a new folder called RC2 and copy your .cfg file (and the .png files so that the graphics work) from the previous lab folder into this folder and rename it to radiology_lab_extended.cfg. First we need to add a priority attribute to the Patient entity. We can do this under the Options tab on the PatientEntity using the AttributeDefinitionList. Table Table 8.2 shows how to create the priority attribute and make the default value 0.
| Object | Keyword | Value |
|---|---|---|
| PatientEntity | AttributeDefinitionList | { priority 0 } |
Next we need a distribution to model the probabilities of the priorities. We use a DiscreteDistribution object as this allows us to define a list of values and the probability of each value occuring. Create a DiscreteDistribution object called PriorityDistribution with the values shown in Table 8.3.
| Object | Keyword | Value |
|---|---|---|
| PriorityDistribution | UnitType | DimensionlessUnit |
| RandomSeed | 4 | |
| ValueList | 1 2 3 4 5 | |
| ProbabilityList | 0.05 0.2 0.15 0.4 0.2 |
So far we have created the priority attribute and distribution, but we need to assign values from the distribution to the patient entities. With the HCCM objects we can assign attributes in the same object that create the arrival.
| Object | Keyword | Value |
|---|---|---|
| PatientArrival | AssignmentList | { ‘this.CurrentParticipants(1).priority |
| = [PriorityDistribution].Value’ } |
Now that we have the priority attribute we can use it change the path of the patients. We can use a Branch object (under Process Flow palette) to send the patients to different places based on the priorty attribute: those with priority 1 and 2 should go straight to the scan queue, while those with priorities 3, 4, and 5 go to wait for check in.
| Object | Keyword | Value |
|---|---|---|
| PriorityBranch | NextComponentList | WaitForScan WaitForCheckIn |
| Choice | ‘this.obj.priority \(\leq\) 2 ? 1 : 2’ |
We also need to update the routing from the Arrival object so that the patients go from the Arrive to the Branch.
| Object | Keyword | Value |
|---|---|---|
| PatientArrival | NextAEJObject | PriorityBranch |
Now we need to add the new Maintenance activity, and RequireMaintenance event. We need an additional event as well as the activity because we do not want to interrupt a scan with maintenance, if the machine is currently in use when the 8 hour time is reached. This means we cannot simply schedule another maintenance activity 8 hours after the last one was scheduled, as the machine might be in use at this time. Instead, we schedule an event (called a logic event in Jaamsim) in 8 hours time, the event then triggers some logic which checks to see if the machine is free and can start maintenance, and if not changes an attribute so that it will start maintenance the next time it is free. We therefore also need to add an attribute on the CTMachineEntity called NeedMaintenance, which defaults to 0 and we will set to 1 when it has been 8 hours since the last maintenance.
For the maintenance activity create a process activity with a duration of 30 minutes with the CTMachineEntity as the only participant and the next activity is WaitForTaskCTMachine. Also use the start assignment list to set the value of the NeedMaintenance attribute to 0.
Then create a logic event called RequireMaintenance and for now just use the assignment list to set the NeedMaintenance attribute to 1. Also set the participant.
Now we will add the new logic before connecting it with new triggers. Follow the same instructions as in the previous lab to create a new class called RadiologyExtendedControlUnit in the labs package, and copy the java code from the RadiologyControlUnit to the new RadiologyExtendedControlUnit. Add the relevant lines to the sim_custom.inc file so that it is available in the HCCM palette. Then replace the RadiologyControlUnit with a RadiologyExtendedControlUnit, and replace the references to the RadiologyControlUnit with RadiologyExtendedControlUnit in the trigger objects: StartWaitCheckIn, StartWaitScan, StartWaitTaskReceptionist, and StartWaitTaskCTMachine.
In the new class we need to first update the OnStartWaitForTaskCTMachine, to include the logic for having maintenance and prioritising patients, and add two new ones for the logic triggered when a CTMachine arrives, and when the RequireMaintenance event occurs. Note that we don’t need to update the OnStartWaitForScan logic as this will only start a scan if the patient is the only one waiting, so the priority does not matter.
First update the OnStartWaitTaskCTMachine code as follows, note that on line 4 a comparator is created to compare patients by their priority attribute. This is then used on line 14 to sort the patients by priority. Also line 7 used the getNumAttribute function to access the value of the NeedMaintenance attribute of the CT Machine. You will need to fill in the parts labelled A, B, and C. In A the CT Machine should transition to the maintenance activity as it needs maintenance and has just become free. In B we want to save the priority of the highest priority patient that is waiting. In C we want to get the priority of the current patient in the loop, to see if it is the same as the highest priority waiting.
public void OnStartWaitForTaskCTMachine(List<ActiveEntity> ents, double simTime) {
ArrayList<ActiveEntity> waitPats = this.getEntitiesInActivity("PatientEntity", "WaitForScan", simTime);
AttributeCompare priorityComp = new AttributeCompare("priority");
ActivityStartCompare actStartComp = this.new ActivityStartCompare();
ActiveEntity ct = ents.get(0);
int reqMaintenance = (int) getNumAttribute(ct, "NeedMaintenance", simTime, -1);
if (reqMaintenance == 1) {
// A //
}
else if (waitPats.size() > 0) {
Collections.sort(waitPats, priorityComp);
int highestPriority = // B //
ArrayList<ActiveEntity> priorityPatients = new ArrayList<ActiveEntity>();
for (ActiveEntity wP : waitPats) {
int patPri = // C //
if (patPri == highestPriority) {
priorityPatients.add(wP);
}
}
Collections.sort(priorityPatients, actStartComp);
ActiveEntity patient = priorityPatients.get(0);
transitionTo("Scan", patient, ct);
}
}Then create two new methods called OnCTArrival and OnRequireMaintenance:
public void OnCTArrival(List<ActiveEntity> ents, double simTime) {
double maintenanceTimeGap = 8 * 60 * 60;
LogicEvent le = (LogicEvent) getSubmodelEntity("RequireMaintenance");
le.scheduleEvent(ents, maintenanceTimeGap);
}
public void OnRequireMaintenance(List<ActiveEntity> ents, double simTime) {
double maintenanceTimeGap = 8 * 60 * 60;
LogicEvent le = (LogicEvent) getSubmodelEntity("RequireMaintenance");
le.scheduleEvent(ents, simTime + maintenanceTimeGap);
ActiveEntity ct = ents.get(0);
if (ct.getCurrentActivity(simTime).equals("WaitForTaskCTMachine")) {
transitionTo("Maintenance", ct);
}
}To get the new logic used in the simulation we need to add two new triggers: StartCTArrival, and StartRequireMaintenance. Set the control unit for both the triggers to be the RadiologyExtendedControlUnit, and make the control policies the respective methods in the java code. Then update the TriggerList and TriggerChoice on both the CTMachineArrival and RequireMaintenance to refer to these triggers.
In the previous lab we looked at the mean time that patients spend in the clinic, and we were able to output this by first using a Statistics object to calculate it and then setting it in the Simulation object’s RunOutputList. In this instance we are interested in the 90th percentile of time that patients spend in the clinic. Unfortunately the Statistics object does not provide the 90th percentile as an output. Therefore we need to capture each of the individual times that each patient spends in the clinic and calculate the 90th percentile ourselves. We can do this using an EntityLogger object from the Process Flow palette; create one and place it between the TimeInSystem object and the PatientExit, and name it PatientLogger. We then need to update the routing so that patients go through the PatientLogger object before leaving, and tell the PatientLogger object which values to record as shown in Table 8.7, note that TotalTime is an output on the entity which stores the total time that the entity has been in the simulation for:
| Object | Keyword | Value |
|---|---|---|
| TimeInSystem | NextComponent | PatientLogger |
| PatientLogger | DataSource | { [Simulation].ReplicationNumber } |
| { ‘this.obj.TotalTime / 1[h]’ } | ||
| NextComponent | PatientLeave |
Now if you save and run your simulation you should be able to see the CT Machines performing maintenance every 8 hours.
The simulation object should be configured correctly from the previous lab so we don’t need to update it. Now if you save and run your simulation a file should be created called radiology_lab_extended.dat.
With the model complete and the results recorded we can use Python to analyse them. First download the Python analysis file provided on Canvas under the Lab 6: Extended Radiology Clinic Assignment. Then, change the name of the .log file to match yours, and make sure it is in the same directory as the Python file, then run the Python file. The following table should be printed:
8.3 Task
By also saving the priority of the patients in the patient logger, construct 95% confidence intervals for the 90th percentile of the time spent in the clinic for each priority group. You should get the following output:
Mean CI_Half_Width
Priority
1.0 0.481123 0.011888
2.0 0.501677 0.006659
3.0 0.649709 0.013620
4.0 0.886900 0.023870
5.0 1.755913 0.128359
Once your simulation is working and you are getting the correct results, you can get your lab signed off.