This guide describes loadDemands
and loadLimits
, and how they relate to each
other.
As mentioned in Pickup and Delivery Time Window Constraints, the
OptimizeToursRequest
message (REST, gRPC) contains a number of
properties that specify constraints on the problem being optimized. Several
OptimizeToursRequest
properties represent load constraints.
Vehicles and shipments have physical properties that must be considered when planning a route.
- Vehicles: The
loadLimits
property specifics the maximum load the vehicle can handle. See theVehicle
message's (REST, gRPC) documentation. - Shipments: The
loadDemands
property specifies how much load a given shipment consumes. See theShipment
message's (REST, gRPC) documentation.
Together, these two constraints make it possible for the optimizer to appropriately assign shipments to vehicles in a manner that best matches your fleet capacity and shipment demands.
This rest of this document discusses loadLimits
and loadDemands
in detail.
Load demands and limits: types
You express each load demand and limit constraint in terms of a type.
You can provide your own set of load types, like the following examples:
- weight
- volume
- linear measurements
- names of items or equipment being transported
This guide uses weightKg
as an example type.
Both Shipment.loadDemands
and Vehicle.loadLimits
use the Protocol Buffers
map
type, with string
keys that represent the types of load.
Shipment.loadDemands
values use the Load
message (REST, gRPC).
The Load
message has a single amount
property representing how much capacity
is required to complete the shipment in the specified type.
Vehicle.loadLimits
values use the LoadLimit
message (REST,
gRPC). The LoadLimit
message has several properties, with maxLoad
representing the vehicle's maximum load capacity in the specified type.
A shipment's loadDemands
consumes its assigned vehicle's loadLimits
only if
the two have matching load type keys. For example, a shipment with
loadDemands
of:
"loadDemands": {
"weightKg": {
"amount": 50
}
}
requires 50 load units in the weightKg
type for the shipment to be
completed. A vehicle with loadLimits
of:
"loadLimits": {
"weightKg": {
"maxLoad": 100
}
}
may be able to complete the shipment, as the vehicle's maxLoad
in the
weightKg
type is greater than or equal to the shipment's loadDemands
in
the weightKg
type. However, a vehicle with loadLimits
of:
"loadLimits": {
"equipmentRackStorage": {
"maxLoad": 10
}
}
implicitly has unlimited weightKg
capacity due to the absence of a
weightKg
load limit, so the vehicle is not constrained by the shipment's
weight demand.
Load transfer between shipments and vehicles
As shipments are picked up and delivered by vehicles, the shipment's
loadDemand
transfers between the shipment and the vehicle. You can see the
vehicle's loads in the OptimizeToursResponse
message's (REST,
gRPC)routes.transitions
entry for a given vehicle. The sequence is as
follows:
- The required load capacity is defined for the shipment as a
loadDemand
. - The shipment is picked up by its assigned vehicle and the vehicle's
vehicleLoads
increases by the amount of the shipment'sloadDemand
. This transfer is represented by positivevisits.loadDemands
in the response message. - The vehicle delivers the shipment and the vehicle's
vehicleLoads
decreases by the amount of the delivered shipment'sloadDemand
. This transfer is represented by negativevisits.loadDemands
in the response message.
A vehicle's vehicleLoads
cannot exceed its specified loadLimits
at any point
on its route.
A complete example with load demands and limits
See an example request with load demands and limits
{ "populatePolylines": false, "populateTransitionPolylines": false, "model": { "globalStartTime": "2023-01-13T16:00:00Z", "globalEndTime": "2023-01-14T16:00:00Z", "shipments": [ { "deliveries": [ { "arrivalLocation": { "latitude": 37.789456, "longitude": -122.390192 }, "duration": "250s" } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 100.0, "loadDemands": { "weightKg": { "amount": 50 } } }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.789116, "longitude": -122.395080 }, "duration": "250s" } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 15.0, "loadDemands": { "weightKg": { "amount": 10 } } }, { "deliveries": [ { "arrivalLocation": { "latitude": 37.795242, "longitude": -122.399347 }, "duration": "250s" } ], "pickups": [ { "arrivalLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "duration": "150s" } ], "penaltyCost": 50.0, "loadDemands": { "weightKg": { "amount": 80 } } } ], "vehicles": [ { "endLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "startLocation": { "latitude": 37.794465, "longitude": -122.394839 }, "costPerHour": 40.0, "costPerKilometer": 10.0, "loadLimits": { "weightKg": { "maxLoad": 100 } } } ] } }
The example request contains several load-related parameters:
shipments[0]
has a load demand of 50weightKg
.shipments[1]
has a load demand of 10weightKg
.shipments[2]
has a load demand of 80weightKg
.vehicles[0]
has a load limit of 100weightKg
.
See a response to the request with load demands and limits
{ "routes": [ { "vehicleStartTime": "2023-01-13T16:00:00Z", "vehicleEndTime": "2023-01-13T16:43:27Z", "visits": [ { "isPickup": true, "startTime": "2023-01-13T16:00:00Z", "detour": "0s", "loadDemands": { "weightKg": { "amount": "50" } } }, { "shipmentIndex": 1, "isPickup": true, "startTime": "2023-01-13T16:02:30Z", "detour": "150s", "loadDemands": { "weightKg": { "amount": "10" } } }, { "startTime": "2023-01-13T16:08:55Z", "detour": "150s", "loadDemands": { "weightKg": { "amount": "-50" } } }, { "shipmentIndex": 1, "startTime": "2023-01-13T16:16:37Z", "detour": "343s", "loadDemands": { "weightKg": { "amount": "-10" } } }, { "shipmentIndex": 2, "isPickup": true, "startTime": "2023-01-13T16:27:07Z", "detour": "1627s", "loadDemands": { "weightKg": { "amount": "80" } } }, { "shipmentIndex": 2, "startTime": "2023-01-13T16:36:26Z", "detour": "0s", "loadDemands": { "weightKg": { "amount": "-80" } } } ], "transitions": [ { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T16:00:00Z", "vehicleLoads": { "weightKg": {} } }, { "travelDuration": "0s", "waitDuration": "0s", "totalDuration": "0s", "startTime": "2023-01-13T16:02:30Z", "vehicleLoads": { "weightKg": { "amount": "50" } } }, { "travelDuration": "235s", "travelDistanceMeters": 795, "waitDuration": "0s", "totalDuration": "235s", "startTime": "2023-01-13T16:05:00Z", "vehicleLoads": { "weightKg": { "amount": "60" } } }, { "travelDuration": "212s", "travelDistanceMeters": 791, "waitDuration": "0s", "totalDuration": "212s", "startTime": "2023-01-13T16:13:05Z", "vehicleLoads": { "weightKg": { "amount": "10" } } }, { "travelDuration": "380s", "travelDistanceMeters": 1190, "waitDuration": "0s", "totalDuration": "380s", "startTime": "2023-01-13T16:20:47Z", "vehicleLoads": { "weightKg": {} } }, { "travelDuration": "409s", "travelDistanceMeters": 1371, "waitDuration": "0s", "totalDuration": "409s", "startTime": "2023-01-13T16:29:37Z", "vehicleLoads": { "weightKg": { "amount": "80" } } }, { "travelDuration": "171s", "travelDistanceMeters": 665, "waitDuration": "0s", "totalDuration": "171s", "startTime": "2023-01-13T16:40:36Z", "vehicleLoads": { "weightKg": {} } } ], "metrics": { "performedShipmentCount": 3, "travelDuration": "1407s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2607s", "travelDistanceMeters": 4812, "maxLoads": { "weightKg": { "amount": "80" } } }, "routeCosts": { "model.vehicles.cost_per_kilometer": 48.12, "model.vehicles.cost_per_hour": 28.966666666666665 }, "routeTotalCost": 77.086666666666659 } ], "metrics": { "aggregatedRouteMetrics": { "performedShipmentCount": 3, "travelDuration": "1407s", "waitDuration": "0s", "delayDuration": "0s", "breakDuration": "0s", "visitDuration": "1200s", "totalDuration": "2607s", "travelDistanceMeters": 4812, "maxLoads": { "weightKg": { "amount": "80" } } }, "usedVehicleCount": 1, "earliestVehicleStartTime": "2023-01-13T16:00:00Z", "latestVehicleEndTime": "2023-01-13T16:43:27Z", "totalCost": 77.086666666666659, "costs": { "model.vehicles.cost_per_hour": 28.966666666666665, "model.vehicles.cost_per_kilometer": 48.12 } } }
The added load constraints affect the order of visits
:
shipment[0]
is picked upshipment[1]
is picked upshipment[0]
is deliveredshipment[1]
is deliveredshipment[2]
is picked upshipment[2]
is delivered
This order reflects that three shipments cannot be completed by the vehicle at
the same time because their total loadDemands
exceed the vehicle's
loadLimits
.
Each visits
entry includes the change in vehicle load resulting from the
completion of the Visit
. Positive load values represent shipment loading while
negative values represent shipment unloading.
Each transitions
entry includes the total vehicle load during the
Transition
. transitions[2]
, for example, has a weightKg
load of 60,
representing the combined loads of shipment[0]
and shipment[1]
.
Metrics objects routes[0].metrics
and metrics.aggregatedRouteMetrics
include
a maxLoads
property. The value for type weightKg
is 80, representing
the portion of the vehicle's route that transported shipments[2]
to its
delivery location.
Soft load limit constraints
As with time windows described in Pickup and Delivery Time Window
Constraints, load limit constraints have hard and soft variants. The
LoadLimit
message's maxLoad
property expresses a hard constraint: the
vehicle must never carry load exceeding the maxLoad
value in the specified
type. Properties softMaxLoad
and costPerUnitAboveSoftMax
express a soft
constraint, with every unit exceeding softMaxLoad
incurring a
costPerUnitAboveSoftMax
cost.
Soft load limit constraints have several uses, such as:
- balancing shipments across more vehicles than the minimum number necessary when it is cost effective to do so
- expressing driver preference for the number of items they can comfortably pickup and deliver on a given route
- loading vehicles below their maximum physical capacity to limit wear and reduce maintenance costs
Hard and soft load limit constraints can be used together. For example, a hard load limit might express the maximum weight of cargo a vehicle can safely carry or the maximum number of items that will fit in a vehicle at one time, while a soft load limit might be the maximum weight or number of items that would tax the driver's ability to fit everything in the vehicle.