diff --git a/demos_and_examples/example_25en_dynamic_user_equilibrium.py b/demos_and_examples/example_25en_dynamic_user_equilibrium.py index a6a11cd..5242688 100644 --- a/demos_and_examples/example_25en_dynamic_user_equilibrium.py +++ b/demos_and_examples/example_25en_dynamic_user_equilibrium.py @@ -17,37 +17,44 @@ from collections import defaultdict # scenario definition -seed = None -W_orig = uxsim.World( - name="", - deltan=10, - tmax=4000, - print_mode=0, save_mode=1, show_mode=1, - random_seed=seed -) -random.seed(seed) - -n_nodes = 4 -imax = n_nodes -jmax = n_nodes -nodes = {} -for i in range(imax): - for j in range(jmax): - nodes[i,j] = W_orig.addNode(f"n{(i,j)}", i, j, flow_capacity=1.6) - -links = {} -for i in range(imax): - for j in range(jmax): - if i != imax-1: - links[i,j,i+1,j] = W_orig.addLink(f"l{(i,j,i+1,j)}", nodes[i,j], nodes[i+1,j], length=1000) - if i != 0: - links[i,j,i-1,j] = W_orig.addLink(f"l{(i,j,i-1,j)}", nodes[i,j], nodes[i-1,j], length=1000) - if j != jmax-1: - links[i,j,i,j+1] = W_orig.addLink(f"l{(i,j,i,j+1)}", nodes[i,j], nodes[i,j+1], length=1000) - if j != 0: - links[i,j,i,j-1] = W_orig.addLink(f"l{(i,j,i,j-1)}", nodes[i,j], nodes[i,j-1], length=1000) - -W_orig.adddemand_nodes2nodes2(nodes.values(), nodes.values(), 0, 3000, volume=20000) +def create_World(): + """ + A function that returns World object with scenario informaiton. This is faster way to reuse the same scenario, as `World.copy` or `World.load_scenario` takes some computation time. + """ + W_orig = uxsim.World( + name="", + deltan=10, + tmax=4000, + print_mode=0, save_mode=1, show_mode=1, + vehicle_logging_timestep_interval=-1, + random_seed=42 #fix seed to reproduce random demand + ) + + n_nodes = 4 + imax = n_nodes + jmax = n_nodes + nodes = {} + for i in range(imax): + for j in range(jmax): + nodes[i,j] = W_orig.addNode(f"n{(i,j)}", i, j, flow_capacity=1.6) + + links = {} + for i in range(imax): + for j in range(jmax): + if i != imax-1: + links[i,j,i+1,j] = W_orig.addLink(f"l{(i,j,i+1,j)}", nodes[i,j], nodes[i+1,j], length=1000) + if i != 0: + links[i,j,i-1,j] = W_orig.addLink(f"l{(i,j,i-1,j)}", nodes[i,j], nodes[i-1,j], length=1000) + if j != jmax-1: + links[i,j,i,j+1] = W_orig.addLink(f"l{(i,j,i,j+1)}", nodes[i,j], nodes[i,j+1], length=1000) + if j != 0: + links[i,j,i,j-1] = W_orig.addLink(f"l{(i,j,i,j-1)}", nodes[i,j], nodes[i,j-1], length=1000) + + W_orig.adddemand_nodes2nodes2(nodes.values(), nodes.values(), 0, 3000, volume=20000) + + return W_orig + +W_orig = create_World() W_orig.print_scenario_stats() @@ -62,7 +69,7 @@ dict_od_to_routes = {} for o,d in dict_od_to_vehid.keys(): - routes = enumerate_k_shortest_routes(W_orig, o, d, k=n_routes_per_od) + routes = enumerate_k_shortest_routes(W_orig, o, d, k=n_routes_per_od) #TODO: to be replaced with many-to-many shortest path search dict_od_to_routes[o,d] = routes #print(o, d, routes) @@ -74,10 +81,10 @@ potential_swaps = [] total_t_gaps = [] swap_prob = 0.05 -max_iter = 100 +max_iter = 25 #50 or 100 is better for i in range(max_iter): - W = W_orig.copy() + W = create_World() if i != 0: for key in W.VEHICLES: diff --git a/uxsim/scenario_reader_writer.py b/uxsim/scenario_reader_writer.py index 617a326..89df093 100644 --- a/uxsim/scenario_reader_writer.py +++ b/uxsim/scenario_reader_writer.py @@ -175,13 +175,13 @@ def load_scenario(W, fname, network=True, demand=True): # from pprint import pprint # pprint(dat) - print(f"loading scenario from '{fname}'") + W.print(f"loading scenario from '{fname}'") if dat["meta_data"]: if type(dat["meta_data"]) is dict: for key in dat["meta_data"]: print("", key, ":", dat["meta_data"][key]) else: - print("", dat["meta_data"]) + W.print("", dat["meta_data"]) if network: @@ -189,35 +189,35 @@ def load_scenario(W, fname, network=True, demand=True): W.addNode(**n) for l in dat["Links"]: W.addLink(**l) - print(" Number of loaded nodes:", len(dat["Nodes"])) - print(" Number of loaded links:", len(dat["Links"])) + W.print(" Number of loaded nodes:", len(dat["Nodes"])) + W.print(" Number of loaded links:", len(dat["Links"])) if demand: for demand_type in dat: if demand_type == 'adddemand': for dem in dat[demand_type]: W.adddemand(**dem) - print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) + W.print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) elif demand_type == 'adddemand_point2point': for dem in dat[demand_type]: W.adddemand_point2point(**dem) - print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) + W.print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) elif demand_type == 'adddemand_area2area': for dem in dat[demand_type]: W.adddemand_area2area(**dem) - print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) + W.print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) elif demand_type == 'adddemand_nodes2nodes': for dem in dat[demand_type]: W.adddemand_nodes2nodes(**dem) - print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) + W.print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) elif demand_type == 'adddemand_area2area2': for dem in dat[demand_type]: W.adddemand_area2area2(**dem) - print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) + W.print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) elif demand_type == 'adddemand_nodes2nodes2': for dem in dat[demand_type]: W.adddemand_nodes2nodes2(**dem) - print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) + W.print(f" Number of loaded `{demand_type}`s:", len( dat[demand_type])) else: pass diff --git a/uxsim/uxsim.py b/uxsim/uxsim.py index 1aeaaa8..bed6878 100644 --- a/uxsim/uxsim.py +++ b/uxsim/uxsim.py @@ -942,7 +942,7 @@ def __init__(s, W, orig, dest, departure_time, name=None, route_pref=None, route else: s.route_pref = {l.id:route_pref[l] for l in route_pref.keys()} - #好むリンクと避けるリンク(近視眼的) + #these links will be always chosen or not chosen when choosing next link at each node s.links_prefer = [s.W.get_link(l) for l in links_prefer] s.links_avoid = [s.W.get_link(l) for l in links_avoid] @@ -950,7 +950,8 @@ def __init__(s, W, orig, dest, departure_time, name=None, route_pref=None, route s.trip_abort = trip_abort s.flag_trip_aborted = 0 - #ログなど + #log + s.vehicle_logging_timestep_interval = s.W.vehicle_logging_timestep_interval s.log_t = [] #時刻 s.log_state = [] #状態 s.log_link = [] #リンク @@ -960,7 +961,8 @@ def __init__(s, W, orig, dest, departure_time, name=None, route_pref=None, route s.log_lane = [] #車線 s.color = (s.W.rng.random(), s.W.rng.random(), s.W.rng.random()) - s.log_t_link = [[int(s.departure_time*s.W.DELTAT), "home"]] #新たなリンクに入った時にその時刻とリンクのみを保存.経路分析用 + s.log_t_link = [[int(s.departure_time*s.W.DELTAT), "home"]] #route-level log. It records the time and link when the vehicle entered new link + s.link_old = None s.distance_traveled = 0 @@ -1244,28 +1246,58 @@ def add_dests(s, dests): for dest in dests: s.add_dest(dest) - def traveled_route(s): + def traveled_route(s, include_arrival_time=True, include_departure_time=False): """ Returns the route this vehicle traveled. + Parameters + ---------- + include_arrival_time : bool + If true, return the arrival time to the destination as well. `-1` means it did not reach the destination. + include_departure_time : bool + If true, return the departure time from the origin as well. It will be different from the entering time to the first link if there are congestion (i.e., the vehicle need to enter the network). + Returns ------- Route The route this vehicle traveled. - ts - The time at which the vehicle entered each link. The last element is the time the vehicle reached the destination. + list + The time at which the vehicle entered each link. If `include_arrival_time` is true, the last element is the time the vehicle reached the destination. If `include_departure_time` is true, the first element is the time the vehicle departed from the origin. This complexity is actually due to a design failure in the past. These options are added to keep the backward compatibility. """ - link_old = -1 - t = -1 + # link_old = -1 + # t = -1 + # route = [] + # ts = [] + # for i, link in enumerate(s.log_link): + # if link_old != link: + # route.append(link) + # ts.append(s.log_t[i]) + # link_old = link + + # return Route(s.W, route[:-1]), ts + route = [] ts = [] - for i, link in enumerate(s.log_link): - if link_old != link: - route.append(link) - ts.append(s.log_t[i]) - link_old = link - return Route(s.W, route[:-1]), ts + for log in s.log_t_link: + t = log[0] + l = log[1] + if l == "home" and include_departure_time: + ts.append(t) + if type(l) == Link: + ts.append(t) + route.append(l) + + log = s.log_t_link[-1] + t = log[0] + l = log[1] + if include_arrival_time: + if l == "end": + ts.append(t) + else: + ts.append(-1) + + return Route(s.W, route), ts def get_xy_coords(s, t=-1): """ @@ -1302,11 +1334,11 @@ def record_log(s, enforce_log=0): enforce_log : bool, optional Record log regardless of the logging interval, default is 0. """ - if s.W.vehicle_logging_timestep_interval != -1: - if (s.W.T%s.W.vehicle_logging_timestep_interval == 0 or enforce_log): + if s.vehicle_logging_timestep_interval != -1: + if s.vehicle_logging_timestep_interval == 1 or s.W.T%s.W.vehicle_logging_timestep_interval == 0 or enforce_log: if s.state != "run": - if s.state == "end" and s.log_t_link[-1][1] != "end": - s.log_t_link.append([s.W.T*s.W.DELTAT, "end"]) + # if s.state == "end" and s.log_t_link[-1][1] != "end": + # s.log_t_link.append([t, "end"]) s.log_t.append(s.W.T*s.W.DELTAT) s.log_state.append(s.state) @@ -1320,8 +1352,8 @@ def record_log(s, enforce_log=0): s.W.analyzer.average_speed_count += 1 s.W.analyzer.average_speed += 0 else: - if len(s.log_link) == 0 or s.log_link[-1] != s.link: - s.log_t_link.append([s.W.T*s.W.DELTAT, s.link]) + # if len(s.log_link) == 0 or s.log_link[-1] != s.link: + # s.log_t_link.append([t, s.link]) s.log_t.append(s.W.T*s.W.DELTAT) s.log_state.append(s.state) @@ -1336,6 +1368,13 @@ def record_log(s, enforce_log=0): s.W.analyzer.average_speed_count += 1 s.W.analyzer.average_speed += (s.v - s.W.analyzer.average_speed)/s.W.analyzer.average_speed_count + + if s.link != s.link_old: + if s.state == "run": + s.log_t_link.append([s.W.T*s.W.DELTAT, s.link]) + elif s.state == "end": + s.log_t_link.append([s.W.T*s.W.DELTAT, "end"]) + s.link_old = s.link class RouteChoice: