Skip to main content

Vehicle Capacity and Node Demand

In Vehicle Routing Problems (VRP), vehicle capacity and node demand are fundamental constraints that define the problem's boundaries. The optimizer's goal is to create the most efficient routes while ensuring that the vehicle's capacity is not exceeded at any point along its route.

SWAT's optimization APIs support multi-dimensional capacity, allowing you to model real-world constraints beyond simple weight or volume.

  • Vehicle Capacity: This is defined on the vehicle object as a dictionary of key-value pairs. Each key represents a dimension of capacity (e.g., units, special_handling), and the value is the maximum amount that vehicle can carry.

  • Node Demand: This is defined on the node object, also as a dictionary. For a Pickup and Delivery (PDP) model, demand is expressed as:

    • Positive values at pickup nodes, which increase the vehicle's load.
    • Negative values at dropoff nodes, which decrease the vehicle's load.

For the optimizer to consider assigning a booking to a vehicle, all demand keys on the nodes must have a corresponding capacity key on the vehicle.

How Capacity Matching Works in PDP

The optimizer checks for two conditions before and during route construction:

  1. Dimension Matching: Does the vehicle have capacity for every type of demand the booking requires? If a booking's nodes require a special_handling demand, but the vehicle does not have this capacity defined, that booking can never be assigned to that vehicle.

  2. Capacity Limits: Will the vehicle's capacity be exceeded at any point? The optimizer tracks the vehicle's load dynamically. After each pickup, the load increases, and it must not exceed the vehicle's capacity. After each dropoff, the load decreases.

This allows for sophisticated fleet management where, for example, only refrigerated trucks can be assigned to orders requiring cold storage.

Example

Here is a simplified PDP example demonstrating these concepts.

  • The Vehicle: Has a capacity for units (20), but no capacity for special_handling.
  • Booking A: Has a demand of 15 units.
  • Booking B: Has a demand of 10 units. The optimizer must choose between serving Booking A or Booking B, as serving both would exceed the vehicle's capacity (15 + 10 > 20).
  • Booking C: Has a demand for special_handling. Because the vehicle does not have this capacity, this booking will be rejected.
{
"current_time": "2025-11-21T08:00:00Z",
"engine_settings": {
"calculation_parameters": {
"calculations_mode": "sync",
"scheduling_mode": "prebook"
},
"model_parameters": {
"optimize_quantity": "total_time"
},
"routing_engine": {
"routing_engine_name": "osrme",
"url": "http://mapbox-osrm-proxy",
"road_network": "van"
}
},
"nodes": [
{
"uid": "depot",
"node_type": "depot",
"lat": 1.3521,
"lon": 103.8198,
"open_time_ts": "2025-11-21T08:00:00Z",
"close_time_ts": "2025-11-21T20:00:00Z",
"close_time_ts_dynamic": "2025-11-21T20:00:00Z",
"service_time": 0,
"demand": {}
},
{
"uid": "pickup_A",
"booking_uid": "booking_A",
"node_type": "pickup",
"lat": 1.38,
"lon": 103.85,
"open_time_ts": "2025-11-21T08:00:00Z",
"close_time_ts": "2025-11-21T20:00:00Z",
"close_time_ts_dynamic": "2025-11-21T20:00:00Z",
"service_time": 300,
"demand": { "units": 15 },
"penalty": 100000
},
{
"uid": "dropoff_A",
"booking_uid": "booking_A",
"node_type": "dropoff",
"lat": 1.306,
"lon": 103.832,
"open_time_ts": "2025-11-21T08:00:00Z",
"close_time_ts": "2025-11-21T20:00:00Z",
"close_time_ts_dynamic": "2025-11-21T20:00:00Z",
"service_time": 300,
"demand": { "units": -15 },
"penalty": 100000
},
{
"uid": "pickup_B",
"booking_uid": "booking_B",
"node_type": "pickup",
"lat": 1.29,
"lon": 103.85,
"open_time_ts": "2025-11-21T08:00:00Z",
"close_time_ts": "2025-11-21T20:00:00Z",
"close_time_ts_dynamic": "2025-11-21T20:00:00Z",
"service_time": 300,
"demand": { "units": 10 },
"penalty": 100000
},
{
"uid": "dropoff_B",
"booking_uid": "booking_B",
"node_type": "dropoff",
"lat": 1.283,
"lon": 103.858,
"open_time_ts": "2025-11-21T08:00:00Z",
"close_time_ts": "2025-11-21T20:00:00Z",
"close_time_ts_dynamic": "2025-11-21T20:00:00Z",
"service_time": 300,
"demand": { "units": -10 },
"penalty": 100000
},
{
"uid": "pickup_C",
"booking_uid": "booking_C",
"node_type": "pickup",
"lat": 1.35,
"lon": 103.93,
"open_time_ts": "2025-11-21T08:00:00Z",
"close_time_ts": "2025-11-21T20:00:00Z",
"close_time_ts_dynamic": "2025-11-21T20:00:00Z",
"service_time": 300,
"demand": { "units": 5, "special_handling": 1 },
"penalty": 100000
},
{
"uid": "dropoff_C",
"booking_uid": "booking_C",
"node_type": "dropoff",
"lat": 1.34,
"lon": 103.92,
"open_time_ts": "2025-11-21T08:00:00Z",
"close_time_ts": "2025-11-21T20:00:00Z",
"close_time_ts_dynamic": "2025-11-21T20:00:00Z",
"service_time": 300,
"demand": { "units": -5, "special_handling": -1 },
"penalty": 100000
}
],
"vehicles": [
{
"agent_id": "vehicle_1",
"start_time": "2025-11-21T08:00:00Z",
"end_time": "2025-11-21T20:00:00Z",
"capacity": { "units": 20 },
"partial_route": ["depot"]
}
]
}

Playground

You can experiment with this PDP scenario in the playground below.

  • Observe that Booking C is rejected because of the mismatched special_handling demand.
  • Observe that the optimizer chooses to serve Booking A (15 units) and rejects Booking B (10 units), because serving both would exceed the vehicle's capacity of 20 units.
  • Try adding "special_handling": 1 to the vehicle's capacity. Booking C should now be served.
  • Try increasing the vehicle's capacity for units to 25 or more. The optimizer should now be able to serve both Booking A and Booking B.
Loading...