Multi-reloads
In some cases, due to the limited capacity of the vehicle, it is not possible to deliver all the items in one trip. Therefore, the vehicle may require making multiple trips to be able to deliver more items if shift time permits. The reloads
feature allows the vehicle to return to the depot (or any other location) to pick up more items after all the tasks from the previous trip are completed.
The simplest example for this use case would be when we have a vehicle with a specific capacity and jobs with total demand that exceeds the capacity. It is expected that some of the jobs that do not fit the vehicle's capacity will not be assigned to the tour. So, if we don't have an opportunity to increase the vehicle's capacity or add more vehicles, but we still need to complete the tour with the existing vehicle, we should add the reload option to the problem constraint which allows a vehicle to add more items to the tour after previous tasks are completed. The reloads can be done in different locations - in the departure depot, at the delivery or pickup locations, or any other location. The reload option should be added in the fleet
part of the problem with the exact location
of the reload and its duration
as follows:
"reloads":[
{
"location":{
"lat": 52.530976,
"lng": 13.384916
},
"duration":60
},
{
"location":{
"lat": 52.530976,
"lng": 13.384916
},
"duration":60
},
]
Let's consider a simple situation when the vehicle's capacity is smaller than the total jobs demand (capacity - 3, demand - 5), or in other words, the demand exceeds the vehicle's capacity. You can check this very case in this documentation in the Capacitated VRP section on the link. To allow the vehicle from the mentioned example to complete the tour, let's add the reloads
option to the problem. In the problem example below we added the reloads
in the same location where the vehicle started the tour.
Problem
{
"fleet": {
"types": [
{
"id": "e429c9c1e6df",
"profile": "car_bd5be9a38474",
"costs": {
"fixed": 6.0,
"distance": 0.002,
"time": 0.007
},
"shifts": [
{
"start": {
"time": "2022-06-01T08:05:00Z",
"location": {
"lat": 52.530971,
"lng": 13.384915
}
},
"end": {
"time": "2022-06-01T18:05:00Z",
"location": {
"lat": 52.530971,
"lng": 13.384915
}
},
"reloads": [
{
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"duration": 60
}
]
}
],
"capacity": [
3
],
"amount": 1
}
],
"profiles": [
{
"type": "car",
"name": "car_bd5be9a38474"
}
]
},
"plan": {
"jobs": [
{
"id": "job_1",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T14:05:00Z",
"2022-06-01T18:05:00Z"
]
],
"location": {
"lat": 52.44664947511696,
"lng": 13.484988905387555
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_2",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T08:05:00Z",
"2022-06-01T13:05:00Z"
]
],
"location": {
"lat": 52.632489506294235,
"lng": 13.341605471791963
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_3",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T08:05:00Z",
"2022-06-01T13:05:00Z"
]
],
"location": {
"lat": 52.5983459422347,
"lng": 13.392007658723308
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_4",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T08:05:00Z",
"2022-06-01T12:05:00Z"
]
],
"location": {
"lat": 52.577856866026835,
"lng": 13.423782100910442
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_5",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T08:05:00Z",
"2022-06-01T13:05:00Z"
]
],
"location": {
"lat": 52.5983459422347,
"lng": 13.392007658723308
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
}
]
}
}
Solution
From the solution below we can see that after serving 3 jobs and using all its capacity, the vehicle heads to the reload location (starting depot in our case), and after that serves the last job that remains in the tour.
{
"statistic": {
"cost": 273.482,
"distance": 92161,
"duration": 11880,
"times": {
"driving": 9568,
"serving": 1560,
"waiting": 752,
"break": 0
}
},
"tours": [
{
"vehicleId": "e429c9c1e6df_1",
"typeId": "e429c9c1e6df",
"stops": [
{
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"arrival": "2022-06-01T08:05:00Z",
"departure": "2022-06-01T11:20:56Z"
},
"load": [
0
],
"activities": [
{
"jobId": "departure",
"type": "departure"
}
],
"distance": 0
},
{
"location": {
"lat": 52.577856866026835,
"lng": 13.423782100910442
},
"time": {
"arrival": "2022-06-01T11:38:11Z",
"departure": "2022-06-01T11:43:11Z"
},
"load": [
1
],
"activities": [
{
"jobId": "job_4",
"type": "pickup"
}
],
"distance": 16100
},
{
"location": {
"lat": 52.5983459422347,
"lng": 13.392007658723308
},
"time": {
"arrival": "2022-06-01T11:57:43Z",
"departure": "2022-06-01T12:07:43Z"
},
"load": [
3
],
"activities": [
{
"jobId": "job_3",
"type": "pickup",
"location": {
"lat": 52.5983459422347,
"lng": 13.392007658723308
},
"time": {
"start": "2022-06-01T11:57:43Z",
"end": "2022-06-01T12:02:43Z"
}
},
{
"jobId": "job_5",
"type": "pickup",
"location": {
"lat": 52.5983459422347,
"lng": 13.392007658723308
},
"time": {
"start": "2022-06-01T12:02:43Z",
"end": "2022-06-01T12:07:43Z"
}
}
],
"distance": 27415
},
{
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"arrival": "2022-06-01T12:32:18Z",
"departure": "2022-06-01T12:33:18Z"
},
"load": [
0
],
"activities": [
{
"jobId": "reload",
"type": "reload"
}
],
"distance": 36817
},
{
"location": {
"lat": 52.632489506294235,
"lng": 13.341605471791963
},
"time": {
"arrival": "2022-06-01T13:05:00Z",
"departure": "2022-06-01T13:10:00Z"
},
"load": [
1
],
"activities": [
{
"jobId": "job_2",
"type": "pickup"
}
],
"distance": 44396
},
{
"location": {
"lat": 52.44664947511696,
"lng": 13.484988905387556
},
"time": {
"arrival": "2022-06-01T13:52:28Z",
"departure": "2022-06-01T14:10:00Z"
},
"load": [
2
],
"activities": [
{
"jobId": "job_1",
"type": "pickup"
}
],
"distance": 65595
},
{
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"arrival": "2022-06-01T14:38:56Z",
"departure": "2022-06-01T14:38:56Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival"
}
],
"distance": 82587
}
],
"statistic": {
"cost": 273.482,
"distance": 92161,
"duration": 11880,
"times": {
"driving": 9568,
"serving": 1560,
"waiting": 752,
"break": 0
}
},
"shiftIndex": 0
}
]
}
This is how basically the reloads
option works, but in some cases, we should consider other constraints when using the reloads. Below let's build an example problem where we would have more than one reload and additionally the reloads will take place not only in the depot location. Moreover, the reload locations will be operating at specific hours during the day, so we will need to consider the reloads time windows as well.
Tour planning allows adding up to five reloads
per vehicle shift. Each reloads
can have its own location
, duration
, and times
(optional).
In the example below we have again a situation with the demand that exceeds capacity (capacity 5 vs. 7 jobs demand), but also we have the specific time windows for the reloads, that we add to the vehicle shift time constraints. Also, note that we can add any location for the reload but not only the depot. So here in our example, one of the reloads will take place in the vehicle departure location (depot), and another will take place in one of the jobs locations.
Problem
{
"fleet": {
"types": [
{
"id": "e429c9c1e6df",
"profile": "car_bd5be9a38474",
"costs": {
"fixed": 6.0,
"distance": 0.002,
"time": 0.007
},
"shifts": [
{
"start": {
"time": "2022-06-01T08:05:00Z",
"location": {
"lat": 52.530971,
"lng": 13.384915
}
},
"end": {
"time": "2022-06-01T18:05:00Z",
"location": {
"lat": 52.530971,
"lng": 13.384915
}
},
"reloads": [
{
"times": [
[
"2022-06-01T09:05:00Z",
"2022-06-01T13:05:00Z"
]
],
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"duration": 60
},
{
"times": [
[
"2022-06-01T13:05:00Z",
"2022-06-01T18:05:00Z"
]
],
"location": {
"lat": 52.57541509905306,
"lng": 13.409878314747719
},
"duration": 60
}
]
}
],
"capacity": [
5
],
"amount": 1
}
],
"profiles": [
{
"type": "car",
"name": "car_bd5be9a38474"
}
]
},
"plan": {
"jobs": [
{
"id": "job_1",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T09:05:00Z",
"2022-06-01T13:05:00Z"
]
],
"location": {
"lat": 52.44664947511696,
"lng": 13.484988905387555
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_2",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T08:05:00Z",
"2022-06-01T13:05:00Z"
]
],
"location": {
"lat": 52.632489506294235,
"lng": 13.341605471791963
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_3",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T08:05:00Z",
"2022-06-01T13:05:00Z"
]
],
"location": {
"lat": 52.5983459422347,
"lng": 13.392007658723308
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_4",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T08:05:00Z",
"2022-06-01T12:05:00Z"
]
],
"location": {
"lat": 52.577856866026835,
"lng": 13.423782100910442
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_5",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T09:05:00Z",
"2022-06-01T14:05:00Z"
]
],
"location": {
"lat": 52.57541509905306,
"lng": 13.409878314747719
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_6",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T09:05:00Z",
"2022-06-01T14:05:00Z"
]
],
"location": {
"lat": 52.486595664301646,
"lng": 13.423728674699783
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
},
{
"id": "job_7",
"tasks": {
"pickups": [
{
"places": [
{
"times": [
[
"2022-06-01T08:05:00Z",
"2022-06-01T13:05:00Z"
]
],
"location": {
"lat": 52.471783668934606,
"lng": 13.469038314828833
},
"duration": 300
}
],
"demand": [
1
]
}
]
}
}
]
}
}
Solution
From the solution below we can see that the vehicle will serve job_2
, job_3
, job_6
, job_1
, and job_7
, then will make a reload in the depot location within the specified time window, and then serve job_5
and job_4
.
{
"statistic": {
"cost": 260.9809999999999,
"distance": 81441,
"duration": 13157,
"times": {
"driving": 10997,
"serving": 2160,
"waiting": 0,
"break": 0
}
},
"tours": [
{
"vehicleId": "e429c9c1e6df_1",
"typeId": "e429c9c1e6df",
"stops": [
{
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"arrival": "2022-06-01T08:05:00Z",
"departure": "2022-06-01T08:05:00Z"
},
"load": [
0
],
"activities": [
{
"jobId": "departure",
"type": "departure"
}
],
"distance": 0
},
{
"location": {
"lat": 52.632489506294235,
"lng": 13.341605471791963
},
"time": {
"arrival": "2022-06-01T08:36:42Z",
"departure": "2022-06-01T08:41:42Z"
},
"load": [
1
],
"activities": [
{
"jobId": "job_2",
"type": "pickup"
}
],
"distance": 6640
},
{
"location": {
"lat": 52.5983459422347,
"lng": 13.392007658723308
},
"time": {
"arrival": "2022-06-01T09:05:56Z",
"departure": "2022-06-01T09:10:56Z"
},
"load": [
2
],
"activities": [
{
"jobId": "job_3",
"type": "pickup"
}
],
"distance": 8022
},
{
"location": {
"lat": 52.486595664301646,
"lng": 13.423728674699785
},
"time": {
"arrival": "2022-06-01T09:49:34Z",
"departure": "2022-06-01T09:54:34Z"
},
"load": [
3
],
"activities": [
{
"jobId": "job_6",
"type": "pickup"
}
],
"distance": 24199
},
{
"location": {
"lat": 52.44664947511696,
"lng": 13.484988905387556
},
"time": {
"arrival": "2022-06-01T10:10:22Z",
"departure": "2022-06-01T10:15:22Z"
},
"load": [
4
],
"activities": [
{
"jobId": "job_1",
"type": "pickup"
}
],
"distance": 29363
},
{
"location": {
"lat": 52.471783668934606,
"lng": 13.469038314828833
},
"time": {
"arrival": "2022-06-01T10:24:23Z",
"departure": "2022-06-01T10:29:23Z"
},
"load": [
5
],
"activities": [
{
"jobId": "job_7",
"type": "pickup"
}
],
"distance": 37457
},
{
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"arrival": "2022-06-01T10:56:26Z",
"departure": "2022-06-01T10:57:26Z"
},
"load": [
0
],
"activities": [
{
"jobId": "reload",
"type": "reload"
}
],
"distance": 45849
},
{
"location": {
"lat": 52.57541509905306,
"lng": 13.40987831474772
},
"time": {
"arrival": "2022-06-01T11:12:34Z",
"departure": "2022-06-01T11:17:34Z"
},
"load": [
1
],
"activities": [
{
"jobId": "job_5",
"type": "pickup"
}
],
"distance": 61949
},
{
"location": {
"lat": 52.577856866026835,
"lng": 13.423782100910442
},
"time": {
"arrival": "2022-06-01T11:21:24Z",
"departure": "2022-06-01T11:26:24Z"
},
"load": [
2
],
"activities": [
{
"jobId": "job_4",
"type": "pickup"
}
],
"distance": 73264
},
{
"location": {
"lat": 52.530971,
"lng": 13.384915
},
"time": {
"arrival": "2022-06-01T11:44:17Z",
"departure": "2022-06-01T11:44:17Z"
},
"load": [
0
],
"activities": [
{
"jobId": "arrival",
"type": "arrival"
}
],
"distance": 82666
}
],
"statistic": {
"cost": 260.9809999999999,
"distance": 81441,
"duration": 13157,
"times": {
"driving": 10997,
"serving": 2160,
"waiting": 0,
"break": 0
}
},
"shiftIndex": 0
}
]
}