My problem is the following:
I have 24 vehicles and more than a hundred locations. The largest time window in my code is (1410, 1470) in minutes. The idea is that the zero point is 7 pm because there are locations with time window during the night starting from 7 pm and the following day is covered with different time windows. So the model works from Day1 7 pm to Day2 12 pm. BUT one vehicle can only travel 9 hours during this period. I tried to set max vehicle time to 60 * 9 = 540 but I get CP solver fail. Is there a way to implement this?
from ortools.constraint_solver import pywrapcp
from ortools.constraint_solver import routing_enums_pb2
import pandas as pd
import warnings
import os
os.chdir("M:\PTibor\Github\CVRPTW")
import data_process
warnings.filterwarnings("ignore")
#CONSTANTS AND PROCESSING
DATE = "2023-08-01"
RELOAD_STATIONS = 20
VEHICLE_MAX_DISTANCE_METERS = 22200000000
VEHICLE_MAX_TIME_MINUTES = 540
RUN_TIME_SECONDS = 60*5
data = data_process.process_data(DATE, RELOAD_STATIONS, VEHICLE_MAX_DISTANCE_METERS, VEHICLE_MAX_TIME_MINUTES)
def distance_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
return data["distance_matrix"][from_node][to_node]
def time_callback(from_index, to_index):
from_node = manager.IndexToNode(from_index)
to_node = manager.IndexToNode(to_index)
service_time = 0
if from_node != data['depot']:
service_time = 30
return data["time_matrix"][from_node][to_node] + service_time
def demand_callback(from_index):
from_node = manager.IndexToNode(from_index)
return data["demands"][from_node]
def print_solution(data, manager, routing, solution):
time_dimension = routing.GetDimensionOrDie('Time')
total_time = 0
res2=[]
dropped = []
dropped_reload = []
for order in range(data['reload']+1, routing.nodes()):
index = manager.NodeToIndex(order)
if solution.Value(routing.NextVar(index)) == index:
dropped.append(order)
print(f'dropped orders: {dropped}')
for reload in range(1, data['reload']+1):
index = manager.NodeToIndex(reload)
if solution.Value(routing.NextVar(index)) == index:
dropped_reload.append(reload)
print(f'dropped reload stations: {dropped_reload}')
for vehicle_id in range(data['num_vehicles']):
vehicle=data['vehicles'][vehicle_id]
index = routing.Start(vehicle_id)
plan_output = 'Route for vehicle {}:\n\n'.format(vehicle_id)
route_load = 0
distance = 0
total_distance = 0
time=0
total_time=0
previous_node_index=0
while not routing.IsEnd(index):
node_index = manager.IndexToNode(index)
time_var = time_dimension.CumulVar(index)
if node_index>data['reload']:
route_load += max(data['demands'][node_index],0)
else:
route_load = 0
if node_index==0:
distance = 0
time=0
else:
#distance = routing.GetArcCostForVehicle(previous_index, index, vehicle_id)
distance = data['distance_matrix'][previous_node_index][node_index]
time = data['time_matrix'][previous_node_index][node_index]
total_distance+=distance
total_time+=time
plan_output += 'Place {0:>2} Arrive at {2:>2}min Depart at {3:>2}min (Load {1:>2})\n'.format(manager.IndexToNode(index), route_load, solution.Min(time_var), solution.Max(time_var))
res2.append([vehicle_id, vehicle, manager.IndexToNode(index), data['shops'][manager.IndexToNode(index)], data['locations'][manager.IndexToNode(index)][0], data['locations'][manager.IndexToNode(index)][1],
data['demands'][node_index], route_load, distance, total_distance, time, total_time, solution.Min(time_var), solution.Max(time_var)])
index = solution.Value(routing.NextVar(index))
previous_node_index=node_index
time_var = time_dimension.CumulVar(index)
#total_time += solution.Min(time_var)
node_index = manager.IndexToNode(index)
distance = data['distance_matrix'][previous_node_index][node_index]
total_distance+=distance
time=data['time_matrix'][previous_node_index][node_index]
total_time+=time
res2.append([vehicle_id, vehicle, manager.IndexToNode(index), data['shops'][manager.IndexToNode(index)], data['locations'][manager.IndexToNode(index)][0], data['locations'][manager.IndexToNode(index)][1],
data['demands'][node_index], route_load, distance, total_distance, time, total_time, solution.Min(time_var), solution.Max(time_var)])
return res2
manager = pywrapcp.RoutingIndexManager(
len(data["time_matrix"]), data["num_vehicles"], data["depot"]
)
routing = pywrapcp.RoutingModel(manager)
distance_callback_index = routing.RegisterTransitCallback(distance_callback)
routing.AddDimension(
distance_callback_index,
0, # no slack
data["vehicle_max_distance"], # vehicle maximum travel distance
True, # start cumul to zero
"Distance",
)
demand_callback_index = routing.RegisterUnaryTransitCallback(demand_callback)
routing.AddDimensionWithVehicleCapacity(
demand_callback_index,
1000, # null capacity slack
data["vehicle_capacities"], # vehicle maximum capacities
True, # start cumul to zero
"Capacity",
)
capacity_dimension = routing.GetDimensionOrDie("Capacity")
for node_index in range(1, RELOAD_STATIONS):
index = manager.NodeToIndex(node_index)
routing.AddDisjunction([node_index], 0)
capacity_dimension.SlackVar(index).SetRange(0, 1000)
for node in range(RELOAD_STATIONS, len(data['demands'])):
node_index = manager.NodeToIndex(node)
capacity_dimension.SlackVar(node_index).SetValue(0)
routing.AddDisjunction([node_index], 100000000000000)
time_callback_index = routing.RegisterTransitCallback(time_callback)
routing.SetArcCostEvaluatorOfAllVehicles(distance_callback_index)
time = "Time"
routing.AddDimension(
time_callback_index,
30, # allow waiting time
data['vehicle_max_time'], # maximum time per vehicle
False, # Don't force start cumul to zero.
time,
)
time_dimension = routing.GetDimensionOrDie(time)
time_dimension.SetGlobalSpanCostCoefficient(1000)
for i in range(data["num_vehicles"]):
routing.AddVariableMinimizedByFinalizer(
time_dimension.CumulVar(routing.Start(i))
)
routing.AddVariableMinimizedByFinalizer(time_dimension.CumulVar(routing.End(i)))
for location_idx, time_window in enumerate(data["time_windows"]):
if location_idx == data["depot"]:
continue
index = manager.NodeToIndex(location_idx)
time_dimension.CumulVar(index).SetRange(time_window[0], time_window[1])
# Add time window constraints for each vehicle start node.
depot_idx = data["depot"]
for vehicle_id in range(data["num_vehicles"]):
index = routing.Start(vehicle_id)
time_dimension.CumulVar(index).SetRange(
data["time_windows"][depot_idx][0], data["time_windows"][depot_idx][1]
)
search_parameters = pywrapcp.DefaultRoutingSearchParameters()
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC
)
search_parameters.local_search_metaheuristic = (
routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
search_parameters.time_limit.seconds = RUN_TIME_SECONDS
solution = routing.SolveWithParameters(search_parameters)
if solution:
res = print_solution(data, manager, routing, solution)
res2=pd.DataFrame(res, columns=['Vehicle_id','Vehicle','Place_id','Shop','lat','lon','Load','TotalLoad', 'Distance', 'TotalDistance', 'Time', 'TotalTime','Arrive','Departure'])
else:
print("No solution!")