Skip to content

Commit 764522e

Browse files
committed
add Route object based route specification
1 parent cc943ad commit 764522e

File tree

2 files changed

+116
-19
lines changed

2 files changed

+116
-19
lines changed

tests/test_other_functions.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,7 @@ def test_route_vehicle_methods():
11141114
assert equal_tolerance(tt_from_vehicle_route, tt_from_route_by_departure_time)
11151115

11161116

1117-
def test_route_enforce_route():
1117+
def test_route_enforce_route_old():
11181118
W = World(
11191119
name="",
11201120
deltan=5,
@@ -1154,6 +1154,78 @@ def test_route_enforce_route():
11541154
for l in r2:
11551155
assert df[df["link"]==l.name]["traffic_volume"].values[0] == 2130
11561156

1157+
def test_route_enforce_route_by_route_object():
1158+
1159+
W = World(
1160+
name="looproute", # Scenario name
1161+
deltan=5, # Simulation aggregation unit delta n
1162+
tmax=2400, # Total simulation time (s)
1163+
print_mode=1, save_mode=0, show_mode=1, # Various options
1164+
random_seed=0 # Set the random seed
1165+
)
1166+
1167+
# Define the scenario
1168+
## Create nodes
1169+
W.addNode(name="O", x=0, y=0)
1170+
W.addNode("D", 2, 0)
1171+
W.addNode("A", 1, 0)
1172+
W.addNode("B", 1.5, 1)
1173+
W.addNode("C", 0.5, 1)
1174+
W.addNode("E", 1, -1)
1175+
## Create links between nodes
1176+
W.addLink(None, "O", "A", length=500, free_flow_speed=20, number_of_lanes=1)
1177+
W.addLink(None, "A", "D", length=500, free_flow_speed=20, number_of_lanes=1)
1178+
W.addLink(None, "A", "B", length=500, free_flow_speed=20, number_of_lanes=1)
1179+
W.addLink(None, "B", "C", length=500, free_flow_speed=20, number_of_lanes=1)
1180+
W.addLink(None, "C", "A", length=500, free_flow_speed=20, number_of_lanes=1)
1181+
W.addLink(None, "O", "E", length=1000, free_flow_speed=20, number_of_lanes=1)
1182+
W.addLink(None, "E", "D", length=1000, free_flow_speed=20, number_of_lanes=1)
1183+
W.addLink(None, "O", "D", length=500, free_flow_speed=40, number_of_lanes=1)
1184+
1185+
## Create OD traffic demand between nodes
1186+
W.adddemand("O","D", t_start=0, t_end=1000, volume=400)
1187+
1188+
#W.show_network()
1189+
1190+
route_straight = W.defRoute(["O-A", "A-D"])
1191+
route_detour_loop = W.defRoute(["O-A", "A-B", "B-C", "C-A", "A-D"])
1192+
route_detour_loop_triple = W.defRoute(["O-A", "A-B", "B-C", "C-A", "A-B", "B-C", "C-A", "A-B", "B-C", "C-A", "A-D"])
1193+
route_detour = W.defRoute(["O-E", "E-D"])
1194+
1195+
for i,veh in enumerate(W.VEHICLES.values()):
1196+
if i%4 == 0:
1197+
veh.enforce_route(route_straight)
1198+
elif i%4 == 1:
1199+
veh.enforce_route(route_detour)
1200+
elif i%4 == 2:
1201+
veh.enforce_route(route_detour_loop)
1202+
else:
1203+
veh.enforce_route(route_detour_loop_triple)
1204+
1205+
# Run the simulation to the end
1206+
W.exec_simulation()
1207+
1208+
# Print summary of simulation result
1209+
W.analyzer.print_simple_stats()
1210+
1211+
W.analyzer.network_average()
1212+
df = W.analyzer.link_to_pandas()
1213+
print(df)
1214+
1215+
assert df.query("link == 'O-A'")["traffic_volume"].item() == 300
1216+
assert df.query("link == 'A-D'")["traffic_volume"].item() == 300
1217+
assert df.query("link == 'A-B'")["traffic_volume"].item() == 400
1218+
assert df.query("link == 'B-C'")["traffic_volume"].item() == 400
1219+
assert df.query("link == 'C-A'")["traffic_volume"].item() == 400
1220+
assert df.query("link == 'O-E'")["traffic_volume"].item() == 100
1221+
assert df.query("link == 'E-D'")["traffic_volume"].item() == 100
1222+
assert df.query("link == 'O-D'")["traffic_volume"].item() == 0
1223+
1224+
assert W.VEHICLES["0"].traveled_route()[0] == route_straight
1225+
assert W.VEHICLES["1"].traveled_route()[0] == route_detour
1226+
assert W.VEHICLES["2"].traveled_route()[0] == route_detour_loop
1227+
assert W.VEHICLES["3"].traveled_route()[0] == route_detour_loop_triple
1228+
11571229
def test_construct_time_space_network():
11581230
W = World(
11591231
name="",

uxsim/uxsim.py

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,8 @@ def __init__(s, W, name, start_node, end_node, length, free_flow_speed=20, jam_d
370370
----------
371371
W : object
372372
The world to which the link belongs.
373-
name : str
374-
The name of the link.
373+
name : str | None
374+
The name of the link. If None, the name is automatically generated as "{start_node.name}-{end_node.name}".
375375
start_node : str | Node
376376
The name of the start node of the link.
377377
end_node : str | Node
@@ -572,6 +572,8 @@ def __init__(s, W, name, start_node, end_node, length, free_flow_speed=20, jam_d
572572

573573
s.id = len(s.W.LINKS)
574574
s.name = name
575+
if s.name == None:
576+
s.name = s.start_node.name+"-"+s.end_node.name
575577
s.auto_rename = auto_rename
576578
if s.name in s.W.LINKS_NAME_DICT.keys():
577579
if auto_rename:
@@ -959,10 +961,12 @@ def __init__(s, W, orig, dest, departure_time, name=None, route_pref=None, route
959961
else:
960962
s.route_pref = {l.id:route_pref[l] for l in route_pref.keys()}
961963

962-
#these links will be always chosen or not chosen when choosing next link at each node
964+
#these links will be always chosen or not chosen when choosing next link at each node. This is not used anymore. keeping for backward compatibility
963965
s.links_prefer = [s.W.get_link(l) for l in links_prefer]
964966
s.links_avoid = [s.W.get_link(l) for l in links_avoid]
965967

968+
s.specified_route = None
969+
966970
#行き止まりに行ってしまったときにトリップを止める
967971
s.trip_abort = trip_abort
968972
s.flag_trip_aborted = 0
@@ -1135,6 +1139,11 @@ def enforce_route(s, route, set_avoid=False):
11351139
set_avoid : bool
11361140
If True, the vehicle only travel the links in `route` argument. This is very strict. If the route is not consistent (e.g., the route does not completely connect between the origin and the destination), it will raise an exception.
11371141
"""
1142+
route = [s.W.get_link(l) for l in route]
1143+
1144+
s.specified_route = route
1145+
1146+
#for backward compatibility
11381147
s.links_prefer = [s.W.get_link(l) for l in route]
11391148
if set_avoid:
11401149
s.links_avoid = [s.W.get_link(l) for l in s.W.LINKS if l not in s.links_prefer]
@@ -1182,29 +1191,42 @@ def route_next_link_choice(s):
11821191
"""
11831192
Select a next link from the current link.
11841193
"""
1185-
if s.dest != s.link.end_node:
1186-
outlinks = list(s.link.end_node.outlinks.values())
1194+
#print("aaa", end="")
1195+
if s.specified_route == None:
1196+
if s.dest != s.link.end_node:
1197+
outlinks = list(s.link.end_node.outlinks.values())
11871198

1188-
if len(outlinks):
1199+
if len(outlinks):
11891200

1190-
#if links_prefer is given and available at the node, select only from the links in the list. if links_avoid is given, select links not in the list.
1191-
if set(outlinks) & set(s.links_prefer):
1192-
outlinks = sorted(set(outlinks) & set(s.links_prefer), key=lambda l:l.name)
1193-
if set(outlinks) & set(s.links_avoid):
1194-
outlinks = sorted(set(outlinks) - set(s.links_avoid), key=lambda l:l.name)
1201+
#if links_prefer is given and available at the node, select only from the links in the list. if links_avoid is given, select links not in the list.
1202+
if set(outlinks) & set(s.links_prefer):
1203+
outlinks = sorted(set(outlinks) & set(s.links_prefer), key=lambda l:l.name)
1204+
if set(outlinks) & set(s.links_avoid):
1205+
outlinks = sorted(set(outlinks) - set(s.links_avoid), key=lambda l:l.name)
11951206

1196-
preference = np.array([s.route_pref[l.id] for l in outlinks], dtype=float)
1197-
if s.W.hard_deterministic_mode == False:
1198-
if sum(preference) > 0:
1199-
s.route_next_link = s.W.rng.choice(outlinks, p=preference/sum(preference))
1207+
preference = np.array([s.route_pref[l.id] for l in outlinks], dtype=float)
1208+
if s.W.hard_deterministic_mode == False:
1209+
if sum(preference) > 0:
1210+
s.route_next_link = s.W.rng.choice(outlinks, p=preference/sum(preference))
1211+
else:
1212+
s.route_next_link = s.W.rng.choice(outlinks)
12001213
else:
1201-
s.route_next_link = s.W.rng.choice(outlinks)
1202-
else:
1203-
s.route_next_link = max(zip(preference, outlinks), key=lambda x:x[0])[1]
1214+
s.route_next_link = max(zip(preference, outlinks), key=lambda x:x[0])[1]
12041215

1216+
else:
1217+
s.route_next_link = None
1218+
else:
1219+
outlinks = list(s.link.end_node.outlinks.values())
1220+
if len(outlinks):
1221+
number_of_traveled_links = len(s.log_t_link)-1
1222+
if s.specified_route[number_of_traveled_links] in outlinks:
1223+
s.route_next_link = s.specified_route[number_of_traveled_links]
1224+
else:
1225+
raise ValueError(f"Vehicle {s.name}: specified route {s.specified_route} is inconsistent at the current link {s.link}. Debug info: {number_of_traveled_links=}, {outlinks=}")
12051226
else:
12061227
s.route_next_link = None
12071228

1229+
12081230
def add_dest(s, dest, order=-1):
12091231
"""
12101232
Add a destination to the vehicle's destination list.
@@ -2700,6 +2722,9 @@ def __iter__(s):
27002722
def __len__(s):
27012723
return len(s.links)
27022724

2725+
def __getitem__(s, index):
2726+
return s.links[index]
2727+
27032728
def __eq__(self, other):
27042729
"""
27052730
Override `==` operator. If the links of two route are the same, then the routes are the same.

0 commit comments

Comments
 (0)