Title Modelling Requests to a Courier Service
DateSubmitted 9 Aug 2009
CaseStudyType TeachingCaseStudy
OperationsResearchTopics SimulationModelling
ApplicationAreas Logistics
ProblemDescription A courier company offers two services: an Inner City delivery service that delivers packages within the Inner City area in under an hour; a Metropolitan delivery service that delivers packages within the Metropolitan area in less than 4 hours.

An Inner City courier leaves the distribution centre if EITHER there are 10 deliveries to make OR it has been 15 minutes since the "oldest" delivery request arrived, i.e., the first request that arrived after the last delivery run departed. A Metropolitan courier leaves the distribution centre if EITHER there are 30 deliveries to make OR it has been 30 mins since the oldest delivery arrived. The courier company works for 8 hours a day and starts each day with no deliveries to make.

The courier company has collected data on the time between requests for both Inner City and Metropolitan deliveries as well as the time to make Inner City and Metropolitan deliveries. They want to know how many deliveries their Inner City and Metropolitan couriers make each day and also how long they are on the road during a day.

ProblemFormulation To simplify the modelling we will abstract 4 sections of the problem:

  1. Requests for Inner City deliveries;
  2. Requests for Metropolitan deliveries;
  3. Delivery runs for Inner City deliveries;
  4. Delivery runs for Metropolitan deliveries.

We start by assuming we can model these accurately using random variables, so we can concentrate on the remaining logic in the system. Figure 1 shows a flow diagram for the Inner City deliveries (note that a flowchart for the Metropolitan deliveries would be similar).

Figure 1 Flow Diagram for Inner City Deliveries


The way to implement this flow digram will differ depending on the simulation modelling tools used.

ComputationalModel To model the flow digram from Figure 1 we first abstract the delivery requests and request runs as Arena submodels. The submodel for requests only generates requests, so a single submodel with only an exit point is needed. The submodel for a delivery run has the load of deliveries as input and outputs the load of deliveries after the delivery run finishes, so it has a single entry and exit point. The following flash tutorial shows how to add the appropriate submodels in Arena:

Table 1 Submodel Inputs

| Name | =Requests for Inner City Delivery=| | Number of Entry Points | 0 | | Number of Exit Points | 1 |

| Name | =Requests for Metropolitan Delivery=| | Number of Entry Points | 0 | | Number of Exit Points | 1 |

| Name | =Inner City Delivery Run=| | Number of Entry Points | 1 | | Number of Exit Points | 1 |

| Name | =Metropolitan Delivery Run=| | Number of Entry Points | 1 | | Number of Exit Points | 1 |

Table 1 shows the input value for the submodels.

Next, we want queues that store delivery entities until a delivery run occurs. We define a boolean variable Loading Inner City that is 1 when an inner city courier may be loaded for delivery and 0 otherwise. We then add a Hold module that stores deliveries until Loading Inner City is 1. Remember that Hold modules that Scan for Condition only scan for the condition when a calendar event occurs. The next flash tutorial shows how to add a Hold module and a Variable to implement this queue for Inner City deliveries. Table 2 also summarises the input for the Variable and Hold modules.

Table 2 Inputs for the Variable and Hold modules | Name | LoadingInnerCity | | Initial Values | 0 |

| Name | Wait for Inner City Delivery | | *Type*| Scan for Condition | | Condition | LoadingInnerCity == 1 |

Next, we want to create logical entities to take care of triggering delivery runs if the oldest delivery request has waited long enough. First, we create an Expression that acts like a constant and stores the maximum time a delivery will wait before a courier run must deliver it. Table 3 shows the input for this Expression.

Table 3 Expression Input

| Name | InnerCityMaxTime | | Expression Values | 15 |

Note that the main difference between Variables and Expressions is that Variables can change during a simulation. The value of an Expression may change, but the actual Expression will not. Note also that we could have simply "hard coded" the 15 into our model, but by using Expressions we can easily change this time later if the courier operation changes.

Now, we add a Variable that holds the time the oldest undelivered inner city delivery request arrived. Table 4 shows the input for this Variable.

Table 4 Variable Input

| Name | InnerCityOldestUndelivered |

Each time a delivery request comes in we use a Decide module to check if there are any other requests waiting. If there are no other deliveries waiting we note down its arrival time in InnerCityOldestUndelivered using an Assign module. The following flash tutorial shows how to add the Decide and Assign modules:

Next we create a timing entity using a Separate module. The duplicate entity then waits for the maximum time before a delivery run must deliver the oldest request (i.e., InnerCityMaxTime) using a Delay module and then leaves the system. This may seem like a waste of simulation modules, but the departure of this duplicate entity from the model creates a calendar event which will cause all Hold modules using Scan for Condition to check their conditions. The original request joins the queue of requests waiting to be delivered (it will be the first one in the queue of course). The following flash tutorial shows how to create the timing entity for the Inner City deliveries:

Table 5 Module inputs

| Name | Any Inner City Deliveries Waiting? | | Type | 2-way by Condition | | If | Expression | | Value | NQ(Wait for Inner City Delivery.Queue) > 0 |

| Name | Set Inner City Oldest Undelivered | | *Assignments (secondary dialog via Add button)*|| || Type | Variable | || Variable Name | InnerCityOldestUndelivered | || New Value | =TNOW=|

| Name | Create Inner City Timer | | Type | Duplicate Original | | # of Duplicates | 1 |

| Name | Wait Inner City Max Time | | Delay Time | InnerCityMaxTime | | Units | Minutes |

| Name | Dispose Inner City Timer |

Table 5 shows the inputs for the modules added.

Next, we create logical entities to trigger a delivery if:

  1. The amount of time since the oldest delivery arrived has reached the maximum time;
  2. The number of deliveries waiting has reached the maximum load.
The arrival of a new delivery creates a calendar event, as does the disposal of the timing entity created just before. Hold modules will scan the simulation state each calendar event, so we can use one of these to trigger a delivery if either the maximum number of requests have been received or the maximum time for a request to wait has expired. The following flash tutorial shows how to create the logical trigger entity and hold it until one of the criteria for loading has occurred:

Once a delivery run is needed, we simply "free" the requests being stored by setting the LoadingInnerCity boolean variable to 1 (i.e., true). We then hold the logical entity until the deliveries have been loaded before returning it to the first Hold module to wait for the next required delivery run. The following flash tutorial shows how to start loading and then wait until the entire delivery has been loaded before looping back to wait for the next load:

Note the use of a Create module to create a single logical entity at the start of the simulation. Table 6 shows the inputs to the 4 modules added.

Table 6 Inputs for trigger modules

| Name | Create Inner City Logical Entity | | Entity Type | InnerCityLogical | | Max Arrivals | 1 | | First Creation | 0.0 |

| Name | Wait for Inner City Trigger | | *Type*| Scan for Condition | | Condition | ( NQ(Wait for Inner City Delivery.Queue) >= InnerCityMaxLoad ) ||
( TNOW - InnerCityOldestUndelivered >= InnerCityMaxTime )

| Name | Start Inner City Loading | | *Assignments (secondary dialog via Add button)*|| || Type | Variable | || Variable Name | LoadingInnerCity | || New Value | =1=|

| Name | Wait for Inner City Loading | | *Type*| Scan for Condition | | Condition | NQ(Wait for Inner City Delivery.Queue) == 0 |

Now that we can trigger the loading of a delivery run, we want to combine all the deliveries into a single load, finish loading and make a delivery run. Once the run is finished we want to separate the deliveries and let them leave the system. First, we create a variable NumberInnerCityLoad to record the number of requests in the load. This is set at the same time loading starts, in the Start Inner City Loading Assign module. Next, we send requests from Wait for Inner City Delivery to a Batch module to be joined into a load. The size of the load for this particular delivery is recorded in an Assign module and the loading is finished (i.e., LoadingInnerCity is set to 0 - false). The following flash tutorial shows how to create the new variable for tracking the number of deliveries in the load, join the deliveries together into a single load and then single loading is complete:

The load is then delivered and split into the original requests (using a Separate module) to keep the count of requests in and out of the system accurate before the requests are disposed of. The following flash tutorial shows how the requests are extracted from the batched load and removed from the simulation:

Table 7 shows the inputs to the modules extended/added.

Adding deliveries to Courier Model

Table 7 Inputs for delivery modules

| Name | Start Inner City Loading | | *Assignments (secondary dialog via Add button)*|| || Type | Variable | || Variable Name | NumberInnerCityLoad | || New Value | NQ(Wait for Inner City Delivery.Queue) |

| Name | Load Inner City Deliveries | | *Batch Size*| NumberInnerCityLoad |

| Name | Inner City Delivery Loaded | | *Assignments (secondary dialog via Add button)*|| || Type | Variable | || Variable Name | LoadingInnerCity | || New Value | =0=|

| Name | Inner City Delivery Finished | | *Type*| Split Existing Batch |

| Name | Dispose Inner City Delivery |

Now the logic of the Inner City part of the model is complete except for the arrivals and delivery runs.

Next you will have to repeat the previous steps to build the Metropolitan part of the model. You can cut-and-paste modules in the flowchart view, but you will need to be careful to rename everything correctly.

Your complete model should look like that in Figure 2.

Figure 2 Courier Model with complete Inner City and Metropolitan logic


Initial Distributions

Once you have completed both the Inner City and Metropolitan parts of the model, you will need to add some distributions to "drive" your simulation. To start off with we will try some simple distributions. Use exponential interarrival times with mean 10 minutes and 20 minutes for Inner City and Metropolitan delivery requests (make sure to use EXPO(10) and EXPO(20) for the first arrival times too). We will use triangular distributions with min, mode and max of 10, 15, 20 and 25, 45, 90 for Inner CIty and Metropolitan delivery runs respectively. If you are unsure how to add and edit Create and Process modules, you should complete the Output Buffering case study before continuing. Add the appropriate Create and Process modules to your submodels. Connect them to the appropriate entry enter_icon.png and exit exit_icon.png points. Make sure you create different entities for each type of delivery and different resources for each type of delivery run. Finally, make sure you have an infinite number of couriers by editing the capacity of your Resources (click on the Resources module in the Basic Process template and look for Capacity..

Now run your model for 50 replications. Each replication should run for 8 hours (i.e., 1 day of the courier operation) and the Base Time Units are minutes.


You should notice that your model appears to "stall". If you look carefully you will see the logic entity that triggers Inner City delivery runs in an infinite loop. This happens because our hold condition is not quite correct. We rely on a request arriving and resetting the oldest delivery time. However, this does not happen before the truck is loaded, so the initial trigger hold does not work. We need to wait for a delivery after we load the truck. By adding an extra condition to the timing trigger, we can fix the problem. Figure 3 shows this extra condition, that the oldest delivery still waiting has been there for too long AND that there are deliveries in the queue.

Figure 3 Extra condition for timing trigger


| Name | Wait for Inner City Trigger | | *Type*| Scan for Condition | | Condition | ( NQ(Wait for Inner City Delivery.Queue) >= InnerCityMaxLoad ) ||
( ( NQ(Wait for Inner City Delivery.Queue) > 0 ) && ( TNOW - InnerCityOldestUndeliveredl >= InnerCityMaxTime ) )


Figure 4 Initial output from the Courier Model


After debugging and re-running you model you should get the output shown in Figure 4, such that the number of Inner City couriers busy on average is within [0.5424, 0.5824] with 95% confidence. Or that the average number times a courier makes a delivery in a day is within [17.85, 19.11] with 95% confidence. However, we are just using some very crude estimates of input data to verify our model.

You can also use animation to check your model, such as animating queues and times to make sure the maximum load and maximum time restrictions are enforced. You can visualise the utilisation of the couriers and the number of deliveries in the last load taken by a courier. The following flash tutorial shows how to add the animation to your model:

Conclusions The simulation model developed in this case study is only a starting point for experimentation. The logic of the system being modelled has been correctly implemented, but the input is based on rough estimates and more work needs to be done to get valid inputs and compare the simulation model for a range of different input options.

This work is done in another case study, Input and Output for a Courier Service Model.



Topic attachments
I Attachment History ActionSorted ascending Size Date Who Comment
Unknown file formatflv CourierServiceModel_Animate.flv r1 manage 8625.7 K 2010-09-14 - 15:35 TWikiAdminUser  
Unknown file formatflv CourierServiceModel_Decide.flv r1 manage 3178.2 K 2010-09-14 - 12:16 TWikiAdminUser  
Unknown file formatflv CourierServiceModel_Dispose.flv r2 r1 manage 1726.5 K 2010-09-14 - 13:02 TWikiAdminUser  
Unknown file formatflv CourierServiceModel_Hold.flv r1 manage 2037.2 K 2010-09-14 - 12:12 TWikiAdminUser  
Unknown file formatflv CourierServiceModel_Load.flv r2 r1 manage 3390.3 K 2010-09-14 - 13:07 TWikiAdminUser  
Unknown file formatflv CourierServiceModel_Loop.flv r1 manage 3181.7 K 2010-09-14 - 12:31 TWikiAdminUser  
Unknown file formatflv CourierServiceModel_Submodels.flv r1 manage 1889.9 K 2010-09-14 - 12:01 TWikiAdminUser  
Unknown file formatflv CourierServiceModel_Timer.flv r1 manage 3346.2 K 2010-09-14 - 12:45 TWikiAdminUser  
Unknown file formatflv CourierServiceModel_Trigger.flv r1 manage 5342.6 K 2010-09-14 - 13:14 TWikiAdminUser  

This topic: OpsRes > SubmitCaseStudy > CourierServiceModel
Topic revision: r22 - 2010-09-14 - MichaelOSullivan
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2023 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback