Skip to content

Commit 0d1efd5

Browse files
committed
add congestion externality estimation method
1 parent 2637789 commit 0d1efd5

File tree

4 files changed

+157
-5
lines changed

4 files changed

+157
-5
lines changed

tests/test_other_functions.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1188,4 +1188,76 @@ def test_construct_time_space_network():
11881188
construct_time_space_network(W)
11891189

11901190
assert W.TSN_paths["4", 0]["7", "end"][-2] == ('7', 340)
1191-
assert equal_tolerance(W.TSN_costs["4", 0]["7", "end"], W.TSN_paths["4", 0]["7", "end"][-2][1])
1191+
assert equal_tolerance(W.TSN_costs["4", 0]["7", "end"], W.TSN_paths["4", 0]["7", "end"][-2][1])
1192+
1193+
@pytest.mark.flaky(reruns=10)
1194+
def test_estimate_congestion_externality_1link():
1195+
def create_world(seed):
1196+
W = World(
1197+
name="aaa", # Scenario name
1198+
deltan=10, # Simulation aggregation unit delta n
1199+
tmax=2400, # Total simulation time (s)
1200+
print_mode=0, save_mode=0, show_mode=0, # Various options
1201+
random_seed=seed # Set the random seed
1202+
)
1203+
1204+
# Define the scenario
1205+
## Create nodes
1206+
W.addNode(name="orig1", x=0, y=0)
1207+
W.addNode("mid", 1, 1)
1208+
W.addNode("dest", 2, 1)
1209+
## Create links between nodes
1210+
link1 = W.addLink(name="link1", start_node="orig1", end_node="mid", length=2000, free_flow_speed=20, number_of_lanes=1, capacity_out=0.8)
1211+
link2 = W.addLink("link3", "mid", "dest", length=1000, free_flow_speed=20, number_of_lanes=1, capacity_in=0.4)
1212+
1213+
R1 = uxsim.Route(W, [link1, link2])
1214+
## Create OD traffic demand between nodes
1215+
W.adddemand(orig="orig1", dest="dest", t_start=0, t_end=1200, flow=0.2)
1216+
W.adddemand(orig="orig1", dest="dest", t_start=300, t_end=600, flow=0.4)
1217+
1218+
return W
1219+
1220+
dep_ts = []
1221+
acts = []
1222+
ests = []
1223+
for _ in range(50):
1224+
seed = random.randint(0,999999)
1225+
1226+
W = create_world(seed)
1227+
W.exec_simulation()
1228+
W.analyzer.print_simple_stats()
1229+
#W.analyzer.time_space_diagram_traj_links(["link1", "link3"])
1230+
1231+
key = random.choice(list(W.VEHICLES.keys()))
1232+
veh = W.VEHICLES[key]
1233+
veh_tt = veh.travel_time*W.DELTAN
1234+
route = veh.traveled_route()[0]
1235+
dep_t = veh.log_t_link[1][0]
1236+
ext = estimate_congestion_externality_route(W, route, dep_t)
1237+
ttt1 = W.analyzer.total_travel_time
1238+
1239+
1240+
W2 = create_world(seed)
1241+
W2.VEHICLES[key].state="abort"
1242+
W2.exec_simulation()
1243+
W2.analyzer.print_simple_stats()
1244+
#W2.analyzer.time_space_diagram_traj_links(["link1", "link3"])
1245+
ttt2 = W2.analyzer.total_travel_time
1246+
1247+
#print(f"selected vehicle - dep time {veh.departure_time_in_second}")
1248+
#print("actual ext:", ttt1-ttt2-veh_tt, f"{ttt1}-{ttt2}-{veh_tt}")
1249+
#print("esti. ext: ", ext)
1250+
1251+
dep_ts.append(veh.departure_time_in_second)
1252+
acts.append(ttt1-ttt2-veh_tt)
1253+
ests.append(ext)
1254+
1255+
# W.analyzer.time_space_diagram_traj_links(["link1", "link3"])
1256+
1257+
# figure()
1258+
# plot(dep_ts, acts, "o", label="actual")
1259+
# plot(dep_ts, ests, "x", label="estimated")
1260+
# legend()
1261+
# grid()
1262+
1263+
assert uxsim.equal_tolerance(average(acts), average(ests), rel_tol=0.333)

uxsim/DTAsolvers/ALNS.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22
Adaptive Large Neighborhood Search (ALNS) for DSO
33
44
- Initially coded by GPT-5, refined by human and Claude Code.
5-
6-
- 破壊/修復オペレータを辞書で差し替え可能な、本格ALNSの最小クリーン実装。
7-
- 主要API: init_alns / auto_step / run_auto / finalize
8-
- 拡張: _DESTROY_IMPLS / _REPAIR_IMPLS に関数を追加/置換するだけで新オペレータが自動採用される
5+
- Customizable minimum implementation
6+
- Includes domain-knowledge-based heuristics
97
"""
108

119
from __future__ import annotations

uxsim/Utilities/Utilities.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import tqdm
1010
import random
1111
from collections import defaultdict
12+
import warnings
1213

1314
def generate_grid_network(W, imax, jmax, **kwargs):
1415
"""
@@ -419,3 +420,82 @@ def construct_time_space_network(W, dt=None, from_origin_only=True):
419420
# print(f"Shortest path from {source[0]} to {target[0]} on t = {source[1]}: cost = {costs[source][target]}: {paths[source][target]}")
420421

421422

423+
424+
def estimate_congestion_externality_link(W, link, t):
425+
"""
426+
Estimate externality caused by a hypothetical vehicle traveling the link in the world. Subfunction for `estimate_congestion_externality_route`.
427+
428+
Parameters
429+
----------
430+
W : World
431+
The World object.
432+
link : str | Link
433+
Link object or link name.
434+
t : float
435+
The departure time of the hypothetical vehicle.
436+
"""
437+
try:
438+
link = W.get_link(link)
439+
ts = int(t/W.DELTAT)
440+
441+
#同じ待ち行列で自分より後ろで待っている車両台数の算出
442+
ts_end = ts
443+
for i in range(ts, len(link.traveltime_actual), 1):
444+
if link.traveltime_actual[i] > link.length/link.free_flow_speed*1.01 and link.cum_arrival[i+int(link.traveltime_actual[i]/W.DELTAT)]-link.cum_departure[i+int(link.traveltime_actual[i]/W.DELTAT)] > 0:
445+
#if link.traveltime_actual[i] > link.length/link.free_flow_speed*1.01:
446+
ts_end = i
447+
else:
448+
break
449+
450+
veh_count = link.cum_arrival[ts_end]-link.cum_arrival[ts]
451+
452+
#局所的容量の算出
453+
#TODO:微妙に系統的にずれる
454+
#TODO:スピルオーバー時の除外
455+
#TODO:信号待ちの一台目のときに破綻してしまう.頑健で賢い方法あるか?信号,このリンクの容量制約,下流側リンク・ノード容量制約を全部考えられるもの
456+
ts_out = int(ts+int(link.traveltime_actual[ts]/W.DELTAN))+1
457+
ts_out_leader = ts_out
458+
for i in range(ts_out, 0, -1):
459+
if link.cum_departure[i] < link.cum_departure[ts_out]:
460+
ts_out_leader = i
461+
break
462+
463+
headway_consumed = (ts_out-ts_out_leader)*W.DELTAT
464+
465+
ext = headway_consumed*veh_count
466+
467+
return ext
468+
469+
except Exception as e:
470+
#細かいインデックスエラーなどを一時的に回避 TODO: fix
471+
472+
warnings.warn(f"Error at `estimate_congestion_externality_link`: {e}", UserWarning)
473+
474+
return 0
475+
476+
477+
478+
def estimate_congestion_externality_route(W, route, t):
479+
"""
480+
Estimate externality caused by a hypothetical vehicle traveling the route in the world.
481+
482+
Specifically, it estimates the total travel time that would be achieved if the vehicle did not exist and then calculates the difference. This estimation is based on a simple queuing model for each link of the route that the vehicle passes through. Does not fully capture queue spilovers.
483+
484+
Parameters
485+
----------
486+
W : World
487+
The World object.
488+
route : Route
489+
Route object.
490+
t : float
491+
The departure time of the hypothetical vehicle.
492+
"""
493+
494+
tts = route.actual_travel_time(t, return_details=True)[1]
495+
exts = 0
496+
for i,link in enumerate(route):
497+
exts += estimate_congestion_externality_link(W, link, t)
498+
t += tts[i]
499+
500+
return exts
501+

uxsim/analyzer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ def print_simple_stats(s, force_print=False):
325325
s.W.print(" number of completed trips:\t", s.trip_completed, "/", s.trip_all)
326326
#s.W.print(" number of completed trips:\t", s.trip_completed, "/", len(s.W.VEHICLES)*s.W.DELTAN)
327327
if s.trip_completed > 0:
328+
s.W.print(f" total travel time:\t\t {s.total_travel_time:.1f} s")
328329
s.W.print(f" average travel time of trips:\t {s.average_travel_time:.1f} s")
329330
s.W.print(f" average delay of trips:\t {s.average_delay:.1f} s")
330331
s.W.print(f" delay ratio:\t\t\t {s.average_delay/s.average_travel_time:.3f}")
@@ -336,6 +337,7 @@ def print_simple_stats(s, force_print=False):
336337
print(" number of completed trips:\t", s.trip_completed, "/", s.trip_all)
337338
#print(" number of completed trips:\t", s.trip_completed, "/", len(s.W.VEHICLES)*s.W.DELTAN)
338339
if s.trip_completed > 0:
340+
print(f" total travel time:\t\t {s.total_travel_time:.1f} s")
339341
print(f" average travel time of trips:\t {s.average_travel_time:.1f} s")
340342
print(f" average delay of trips:\t {s.average_delay:.1f} s")
341343
print(f" delay ratio:\t\t\t {s.average_delay/s.average_travel_time:.3f}")

0 commit comments

Comments
 (0)