capacities of resources and stops, and the constraints built on them. Each section states one rule, with a link to the API reference where the exact behavior is written. The last section shows how to check the loads in a solution.
These snippets use local times where they have times. The plan you put them in needs "tz". See Dates and time zones.
Capacities of vehicles and stops
A heterogeneous fleet is a fleet whose vehicles don’t carry the same amounts. You model it with thecapacities of each resource. capacities is a map. You choose its keys, such as weight, volume or nbPackages, and the engine tracks each key on its own.
- A ceiling on the running load. The
capacitiesof a resource is a ceiling. The load of the vehicle can’t go over it at any point of the tour. The ceiling applies to all the orders of the resource together. In the sandbox, a van withweight: 100and two deliveries of 60 kg served only one of them. - A pickup adds, a delivery deducts. A
pickupstop adds itscapacitiesto the load. Adeliverystop deducts them. A delivery whose order has no pickup is loaded at departure, so it counts infilledCapacitiesAtBeginfrom the start of the tour. - A key the vehicle doesn’t declare has no limit. If a stop carries
volumeand the resource declares onlyweight, nobody checksvolumeon that resource. - A stop that fits no vehicle is left out. The engine lists it in
unaffectedStopIds. A delivery of 120 kg with only a 100 kg van is an example. In the sandbox, the solution gave no other message. - Vehicles can differ. Each resource has its own ceilings. With two vans of 100 kg and 300 kg, a delivery of 150 kg goes to the big van. In the sandbox, the engine put a delivery of 40 kg on the big van too, and left the small van unused.
overlappingCapacitiesByStopTag is a different field. It limits how many vehicles are at the same time at stops that share a tag. It doesn’t limit a load, and the API reference calls it a soft limit.
See Resource.capacities and overlappingCapacitiesByStopTag for the rule.
Compartments set up before departure
A multi-compartment vehicle has a space that you can arrange in several ways before it leaves the depot. For example, a reefer van can be set up all chilled, or half frozen and half chilled. Two business rules sound alike here, so ask which one is yours:- One setup for the whole day. The driver chooses the setup at the depot. It can’t change on the road. Use
atLeastOneConstraint, as this section shows. - Never both on board at once. The two kinds of goods can share the van over the day, as long as they are never on board together. Use
atLeastOneValidCapacity, which the engine checks at every stop. See Cold chain last mile for an example.
capacities constraint with a resourceTag. Then combine the candidates with atLeastOneConstraint:
- One setup holds for the whole tour. The engine checks it against the highest load of each key over the day, even when the two peaks happen at different times. In the sandbox, a plan asked a van to deliver 4 units of frozen goods first. Then the van had to pick up 8 units of chilled goods and deliver them. The two would never be on board together, yet the engine dropped the frozen stop. Without the constraint, it planned all three stops.
- A stop that fits no setup is dropped. With the setups above, a van with 2 units of frozen goods and 7 of chilled goods drops the chilled stop. The engine gives no message.
- A resource without the tag isn’t bound. The constraint ignores a van that doesn’t have
reefer-vanin itstags. It gives no message either. - The solution doesn’t name the setup. You work it out from
maxFilledCapacities: take the first setup whose ceilings are all at or above the peak of each key. A tour with a peak of 4 frozen and 5 chilled fails the first setup, becausechilledVolume10 is enough butfrozenVolume0 isn’t. It fits the second setup, 5 and 5. When several setups fit, any of them works. See A check in code.
AdditionalConstraintAtLeastOneConstraint for the rule.
Pickups after deliveries
A crew delivers bulky items and also collects old ones. The load area works last in, first out, so an old item collected early blocks the items still to be delivered. The rule is: collect only once every delivery is done, or once no more than 300 kg is left on board.atLeastOneValidCapacity expresses it. It lists capacity thresholds. At every stop, at least one listed capacity must be at or under its threshold. This is not the same rule as atLeastOneConstraint in the previous section. The engine checks atLeastOneValidCapacity after each stop, so it can express an order.
To build the rule, add two counters next to weight:
- Every delivery stop carries
nbDeliveries: 1. - Every pickup stop carries
nbPickups: 1. - The resource declares both counters, with ceilings that are higher than any tour needs, such as 30 and 10. The ceiling of
nbDeliveriesalso caps the number of deliveries in one tour, so set it at or above the most deliveries a vehicle can make. The same holds fornbPickupsand the pickups.
nbDeliveries equal to its number of deliveries and nbPickups at 0. Then the constraint lists the thresholds:
nbDeliveries at or under 0). Or no pickup has happened yet (nbPickups at or under 0). Or the load is at or under 300 kg (weight). A pickup raises nbPickups to 1 for good. From then on, the vehicle must have finished its deliveries or stay at 300 kg or less.
In the sandbox, a plan with three deliveries of 200 kg and a pickup of 60 kg shows the rule at work. Without the rule, the engine did the pickup first, with 660 kg on board. With the rule, it moved the pickup after the last delivery. Checking the solution shows this plan.
To forbid every pickup while a delivery remains, whatever the weight, drop the weight threshold: { "nbDeliveries": 0, "nbPickups": 0 }.
See AdditionalConstraintAtLeastOneValidCapacity for the rule.
Tolerated overload
By default, a load over the ceiling makes a stop impossible to plan, so the engine drops it. Your business may prefer an occasional overload to a dropped stop. Overflow is the word for that case. To allow it, set thecapacities ceiling of the resource at the absolute physical or legal limit. Then add a costsByCapacity entry that starts charging at the load you normally plan for:
tourCost of 50. A tour of 950 kg had a tourCost of 2,600, which is 100 × 1 plus 50 × 50. At 1,050 kg, the engine dropped one stop.
Keep overcostFloor below the ceiling. A floor at or above the ceiling is never reached, so its overcostCoeff never applies.
The cost only counts through the minimizeCosts objective. See Objectives. It is computed on the total handled at the stops of the tour. See Piecewise-linear cost functions.
See CostFloorsAndCoeffs for the rule.
Counting visits with a capacity
A capacity doesn’t have to measure anything physical. Declare a synthetic capacity on the resource, then give each relevant stop a quantity of 1. The ceiling then caps the number of such visits in one tour. For example, a tour can make at most 3 visits to a restricted zone:weight: 100 and dangerousGoodsWeight: 30, and two stops carry 40 kg with 20 kg of dangerous goods each. The weight fits, but the dangerous goods don’t, so the engine dropped one stop.
Soft version
A soft cap uses a cost instead of a capacity. Tag the relevant stops withrestrictedZone. Then add a costsByStopTag entry that charges past a free allowance. A tour can then make a fourth restricted visit, at a price, instead of being blocked:
restrictedZone stops in the tour. The first three cost nothing, and each one after that adds 500. Five restricted stops add 1,000 to the cost of the tour. This penalty is part of tourCost, so subtract it if you report that figure as money. Add the entry to the cost of every resource that may serve the zone. A resource without it pays nothing for restricted visits.
The cost only counts through minimizeCosts. With the default objectives, the engine weighs it after maximizeMandatoryStops and minimizeDelay. See the default list. So the engine accepts a fourth visit before it leaves a mandatory stop unplanned or arrives late against a preferredTimeWindows.
Choose the coefficient against the other costs of the fleet, because the penalty only competes with them. Suppose that sending a second van costs less than 500 in total. The engine then sends that van rather than make a fourth visit.
If the zone has a real legal limit, keep the hard cap as well. This hard cap holds only on tours of deliveries alone or of pickups alone. Set its ceiling above costFloor, for example a ceiling of 5 with a floor of 3. Each restricted stop then carries both the restrictedZone tag and the restrictedZoneVisits capacity.
See Capacities and costsByStopTag for the rule.
Last in, first out
LIFO means last in, first out. A vehicle can only unload what it loaded last, like a car carrier loaded nose to tail on one deck.removalStrategy: "lifo" enforces this order. If the vehicle picks up P1, then P2, then P3, it has to unload D3, then D2, then D1.
- It reorders the unloads. In the sandbox, a van picked up two cars, a then b. Without the rule, it unloaded a before b. With the rule, it unloaded b before a.
- It only covers the capacities in its list. The
capacitiesarray names them. A capacity that isn’t listed isn’t ordered. In the sandbox, a rule that listedvolumedid nothing for orders that carriedweight. - Give it
resourceTags. WithresourceTags, only the resources that carry one of these tags follow the rule. Without them, the rule applies to every vehicle of the plan. A van without the tag is not bound. - Use it only for goods picked up during the tour. The sandbox tests cover those. This guide makes no claim about goods loaded at the depot before departure.
AdditionalConstraintRemovalStrategy for the rule.
Checking the solution
A capacity that doesn’t fit is not reported as an error. The engine plans the stops that fit, and lists the others inunaffectedStopIds. So you check the loads yourself. These fields of the solution help:
unaffectedStopIdslists the stops that the engine left out.filledCapacitiesAtBeginis the load of each tour when it leaves. It includes the deliveries that have no pickup.filledCapacitiesAfterStopis the load after one stop. It is on each stop waypoint.maxFilledCapacitiesis the highest load of each key over the tour.resourceCapacitiesrepeats the ceilings of the resource.
Example plan
This plan has one van with three deliveries of 200 kg, which open at 09:00, and a pickup of 60 kg. The pickup is close to the depot and its window is open all morning. The plan has the rule of Pickups after deliveries. Every window is wide, so the order of the stops doesn’t depend on road times.What the solution shows
The sandbox returns this tour for the plan, with the offset ofEurope/Paris and with seconds. The other fields are left out. The times come from the sandbox and depend on its routing. The order of the stops and the loads are the same in production.
- The van leaves with 600 kg, which is the three deliveries. They have no pickup in their orders, so they are loaded at departure.
- The pickup comes last, after the last delivery, when the van carries 0 kg.
maxFilledCapacitiesshows a peak of 600 kg. unaffectedStopIdsis empty, because the engine planned all four stops.- The same plan without the rule gives a different order. In the sandbox, the engine did the pickup first, at 08:01, and the van then carried 660 kg. Its
maxFilledCapacitiesshowedweightat 660. So the rule moved the pickup later.
A check in code
This check reads the loads of a tour and compares them with the ceilings. It also finds the compartment setup that fits, when the plan has setups to choose from. It takes one tour as a dictionary, such as the JSON above. Call it on each entry ofsolution["tours"].
setups only for a plan that has an atLeastOneConstraint over capacities. The depot sets the van up in the setup that the check returns, before departure.
