Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vehicle route information is now recoverable even when detailed logging is turned off #160

Merged
merged 2 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 41 additions & 34 deletions demos_and_examples/example_25en_dynamic_user_equilibrium.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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)

Expand All @@ -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:
Expand Down
20 changes: 10 additions & 10 deletions uxsim/scenario_reader_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,49 +175,49 @@ 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:
for n in dat["Nodes"]:
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

Expand Down
79 changes: 59 additions & 20 deletions uxsim/uxsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -942,15 +942,16 @@ 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]

#行き止まりに行ってしまったときにトリップを止める
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 = [] #リンク
Expand All @@ -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

Expand Down Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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:
Expand Down