Skip to content
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
80 changes: 71 additions & 9 deletions tests/test_verification_dta_solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@ def create_World():
#################################
# DSO by GA: this is obsolete
solver_DSO_GA = DTAsolvers.SolverDSO_GA(create_World)
solver_DSO_GA.solve(max_iter=20, pop_size=20) # max_iter should be larger (e.g., 100). this is just for demonstration
solver_DSO_GA.solve(max_iter=5, pop_size=5) #deprecated; just run
W_DSO_GA = solver_DSO_GA.W_sol
W_DSO_GA.analyzer.print_simple_stats(force_print=True)
df_DSO_GA = W_DSO_GA.analyzer.basic_to_pandas()

#################################
# DSO by ALNS: this is obsolete
solver_DSO_ALNS = DTAsolvers.SolverDSO_ALNS(create_World)
solver_DSO_ALNS.solve(max_iter=200) # max_iter should be larger (e.g., 500). this is just for demonstration
solver_DSO_ALNS.solve(max_iter=10) #deprecated; just run
W_DSO_ALNS = solver_DSO_ALNS.W_sol
W_DSO_ALNS.analyzer.print_simple_stats(force_print=True)
df_DSO_ALNS = W_DSO_ALNS.analyzer.basic_to_pandas()
Expand Down Expand Up @@ -121,8 +121,8 @@ def create_World():
solver_DSO_D2D.plot_vehicle_stats(orig="4", dest="7")

assert W_DUO.analyzer.total_travel_time > W_DUE.analyzer.total_travel_time
assert W_DUE.analyzer.total_travel_time > W_DSO_GA.analyzer.total_travel_time
assert W_DUE.analyzer.total_travel_time > W_DSO_ALNS.analyzer.total_travel_time
#assert W_DUE.analyzer.total_travel_time > W_DSO_GA.analyzer.total_travel_time
#assert W_DUE.analyzer.total_travel_time > W_DSO_ALNS.analyzer.total_travel_time
assert W_DUE.analyzer.total_travel_time > W_DSO_D2D.analyzer.total_travel_time


Expand Down Expand Up @@ -182,15 +182,15 @@ def create_World():
#################################
# DSO by GA
solver_DSO_GA = SolverDSO_GA(create_World)
solver_DSO_GA.solve(max_iter=20, pop_size=20, initial_solution_World=W_DUE) # max_iter should be larger (e.g., 100). this is just for demonstration
solver_DSO_GA.solve(max_iter=5, pop_size=5, initial_solution_World=W_DUE) #deprecated; just run
W_DSO_GA = solver_DSO_GA.W_sol
W_DSO_GA.analyzer.print_simple_stats(force_print=True)
df_DSO_GA = W_DSO_GA.analyzer.basic_to_pandas()

#################################
# DSO by ALNS
solver_DSO_ALNS = SolverDSO_ALNS(create_World)
solver_DSO_ALNS.solve(max_iter=200, initial_solution_World=W_DUE) # max_iter should be larger (e.g., 500). this is just for demonstration
solver_DSO_ALNS.solve(max_iter=10, initial_solution_World=W_DUE) #deprecated; just run
W_DSO_ALNS = solver_DSO_ALNS.W_sol
W_DSO_ALNS.analyzer.print_simple_stats(force_print=True)
df_DSO_ALNS = W_DSO_ALNS.analyzer.basic_to_pandas()
Expand All @@ -217,8 +217,8 @@ def create_World():
solver_DSO_ALNS.plot_convergence()

assert W_DUO.analyzer.total_travel_time > W_DUE.analyzer.total_travel_time
assert W_DUE.analyzer.total_travel_time > W_DSO_GA.analyzer.total_travel_time
assert W_DUE.analyzer.total_travel_time > W_DSO_ALNS.analyzer.total_travel_time
#assert W_DUE.analyzer.total_travel_time > W_DSO_GA.analyzer.total_travel_time
#assert W_DUE.analyzer.total_travel_time > W_DSO_ALNS.analyzer.total_travel_time

@pytest.mark.flaky(reruns=10)
def test_DTA_with_given_route_sets():
Expand Down Expand Up @@ -299,4 +299,66 @@ def create_World():

df_DSO_link = W_DUE.analyzer.link_to_pandas()
assert df_DSO_link["traffic_volume"][df_DSO_link["link"]=="onramp"].item() == 0
assert df_DSO_link["traffic_volume"][df_DSO_link["link"]=="offramp"].item() == 0
assert df_DSO_link["traffic_volume"][df_DSO_link["link"]=="offramp"].item() == 0

@pytest.mark.flaky(reruns=20)
def test_DTA_dynamic_congestion_pricing_on_highway_bottleneck():
# scenario definition
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 = uxsim.World(
name="",
deltan=20,
tmax=9000,
print_mode=0, save_mode=1, show_mode=1,
vehicle_logging_timestep_interval=1,
hard_deterministic_mode=False,
random_seed=42
)

W.addNode("1", 0, 1)
W.addNode("2", 1, 1)
W.addNode("3", 5, 1)
W.addNode("4", 0, 0)
W.addNode("5", 1, 0)
W.addNode("6", 5, 0)
W.addNode("7", 6, 0.5)

def toll(t):
toll = 0
if 3000 < t:
toll = 120
if 6000 < t:
toll = 10000
return toll

W.addLink("highway12", "1", "2", length=1000, number_of_lanes=1, merge_priority=1)
link_highway = W.addLink("highway23", "2", "3", length=3000, number_of_lanes=1, jam_density=0.5, merge_priority=1, capacity_out=0.6, congestion_pricing=toll)
W.addLink("highway37", "3", "7", length=1000, number_of_lanes=1, merge_priority=1)
W.addLink("onramp", "5", "2", length=1000, number_of_lanes=1, merge_priority=0.5)
W.addLink("arterial45", "4", "5", length=1000, free_flow_speed=10, number_of_lanes=2, merge_priority=0.5)
W.addLink("arterial56", "5", "6", length=3000, free_flow_speed=10, number_of_lanes=2, merge_priority=0.5)
W.addLink("arterial67", "6", "7", length=1000, free_flow_speed=10, number_of_lanes=2, merge_priority=0.5)

W.adddemand("1", "7", 0, 6000, 0.3)
W.adddemand("4", "7", 0, 6000, 0.4*3)

return W

# DUE
solver_DUE = SolverDUE(create_World)
solver_DUE.solve(max_iter=40, print_progress=False)
W_DUE = solver_DUE.W_sol
W_DUE.analyzer.print_simple_stats(force_print=False)

l = W_DUE.get_link("highway23")
print("inflows:", l.inflow_between(100,500), l.inflow_between(4000,6000))

assert l.inflow_between(100,500) > 0.6 #initial: demand > bottleneck capacity
assert 0.5 < l.inflow_between(4000,6000) < 0.65 #with pricing: demand = BN capacity
assert l.average_travel_time_between(0,10) == 150 #initial: free-flow
assert l.average_travel_time_between(500,3000) > 300 #without pricing: congested
assert 150 < l.average_travel_time_between(4000,6000) < 200 #with pricing: close to free-flow
assert l.average_travel_time_between(7000,8000) == 150 #with too much pricing: free-flow (no travelers)
11 changes: 6 additions & 5 deletions uxsim/DTAsolvers/DTAsolvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def __init__(s, func_World):

def solve(s, max_iter, n_routes_per_od=10, swap_prob=0.05, route_sets=None, print_progress=True):
"""
Solve quasi Dynamic User Equilibrium (DUE) problem using day-to-day dynamics. WIP.
Solve quasi Dynamic User Equilibrium (DUE) problem using day-to-day dynamics.

Parameters
----------
Expand Down Expand Up @@ -205,11 +205,12 @@ def solve(s, max_iter, n_routes_per_od=10, swap_prob=0.05, route_sets=None, prin
route_changed = None
t_gap = 0

cost_current = r.actual_travel_time(ts[0])
cost_current = r.actual_travel_time(ts[0]) + sum([l.get_toll(ts[i]) for i,l in enumerate(r)])

potential_n_swap_updated = potential_n_swap
for alt_route in route_set[o,d]:
cost_alt = alt_route.actual_travel_time(ts[0])
alt_route_tts = alt_route.actual_travel_time(ts[0], return_details=True)[1]
cost_alt = alt_route.actual_travel_time(ts[0]) + sum([l.get_toll(ts[0]+sum(alt_route_tts[:i])) for i,l in enumerate(alt_route)])
if cost_alt < cost_current:
if flag_route_changed == False or (cost_alt < cost_current):
t_gap = cost_current - cost_alt
Expand Down Expand Up @@ -245,7 +246,7 @@ def solve(s, max_iter, n_routes_per_od=10, swap_prob=0.05, route_sets=None, prin
last_iters = int(max_iter/4)
print(f" total travel time: initial {s.ttts[0]:.1f} -> average of last {last_iters} iters {np.average(s.ttts[-last_iters:]):.1f}")
print(f" number of potential route changes: initial {s.potential_swaps[0]:.1f} -> average of last {last_iters} iters {np.average(s.potential_swaps[-last_iters:]):.1f}")
print(f" route travel time gap: initial {s.t_gaps[0]:.1f} -> average of last {last_iters} iters {np.average(s.t_gaps[-last_iters:]):.1f}")
print(f" route travel cost gap: initial {s.t_gaps[0]:.1f} -> average of last {last_iters} iters {np.average(s.t_gaps[-last_iters:]):.1f}")
print(f" computation time: {s.end_time - s.start_time:.1f} seconds")

s.W_sol = W
Expand Down Expand Up @@ -273,7 +274,7 @@ def plot_convergence(s):
plt.xlabel("iter")

plt.figure(figsize=(6,2))
plt.title("travel time difference between chosen route and minimum cost route")
plt.title("travel cost difference between chosen route and minimum cost route")
plt.plot(s.t_gaps)
plt.ylim(0,None)
plt.xlabel("iter")
Expand Down
2 changes: 1 addition & 1 deletion uxsim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .analyzer import *
from .scenario_reader_writer import *

__version__ = "1.11.0"
__version__ = "1.12.0"
__author__ = "Toru Seo"
__copyright__ = "Copyright (c) 2023 Toru Seo"
__license__ = "MIT License"
42 changes: 39 additions & 3 deletions uxsim/uxsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,9 +459,7 @@ def __init__(s, W: "World", name: str, start_node: Node|str, end_node: Node|str,
if s.number_of_lanes != number_of_lanes:
raise ValueError(f"number_of_lanes must be an integer. Got {number_of_lanes} at {s}.")


#フローモデルパラメータ:per link

s.u = free_flow_speed
s.kappa = jam_density
if jam_density == 0.2 and jam_density_per_lane != None:
Expand Down Expand Up @@ -634,7 +632,6 @@ def set_traveltime_instant(s):
else:
s.traveltime_instant.append(s.traveltime_instant[-1])


def arrival_count(s, t: float) -> float:
"""
Get cumulative vehicle count of arrival to this link on time t.
Expand Down Expand Up @@ -677,6 +674,45 @@ def departure_count(s, t: float) -> float:
return s.cum_departure[0]
return s.cum_departure[tt]

def inflow_between(s, t0: float, t1: float) -> float:
"""
Get inflow to this link between time t0 and t1.

Parameters
----------
t0 : float
Start time in seconds.
t1 : float
End time in seconds.

Returns
-------
float
The inflow.
"""
return (s.arrival_count(t1)-s.arrival_count(t0))/(t1-t0)

def average_travel_time_between(s, t0: float, t1: float) -> float:
"""
Get average travel time on this link between time t0 and t1 based on the cumulative curves. More precisely, average travel time of vehicles that entered this link during [t0, t1).

Parameters
----------
t0 : float
Start time in seconds.
t1 : float
End time in seconds.

Returns
-------
float
The average travel time.
"""
t_list = [t for t in s.vehicles_enter_log.keys() if t0 <= t < t1]
if len(t_list) == 0:
return s.length/s.u
return np.average([s.actual_travel_time(t) for t in t_list])

def num_vehicles_t(s, t: float) -> float:
"""
Get number of vehicles on this link on time t.
Expand Down