Optimize multi-lane implementation (by handeling it as a single lane)
EwoutH opened this issue · 4 comments
UXsim is a mesoscopic traffic simulation package, which typically operates at a higher level of abstraction than microscopic models. However, the current multi-lane implementation seems more detailed than necessary for mesoscopic simulation, potentially impacting performance without proportional benefits to accuracy.
Issue
The current multi-lane model in UXsim may be unnecessarily complex for a mesoscopic simulation, leading to increased computational overhead and memory usage. This complexity might not provide significant benefits in simulation accuracy at the mesoscopic level.
Currently, the Node transfer()
method takes up >20% of runtime in large simulations. Much of this overhead originates between tracking and choosing between lanes. I don't think this whole exercise is necessary for a mesoscopic traffic simulation package like UXsim.
Examples of current complexity
- Vehicles have a
lane
attribute and follow leaders in the same lane:class Vehicle: def __init__(s, ...): s.lane = 0 s.leader = None
- Links maintain lane-specific information:
class Link: def __init__(s, ..., number_of_lanes=1, ...): s.number_of_lanes = int(number_of_lanes)
- Lane-based outlink generation:
for outlink in outlink_candidates.keys(): for i in range(outlink.number_of_lanes): # Iterate over each lane outlinks.append(outlink)
- Complex condition for vehicle transfer:
if (len(outlink.vehicles) < outlink.number_of_lanes or outlink.vehicles[-outlink.number_of_lanes].x > outlink.delta_per_lane*s.W.DELTAN) and \ outlink.capacity_in_remain >= s.W.DELTAN and s.flow_capacity_remain >= s.W.DELTAN:
- Lane-specific leader-follower assignment:
if len(outlink.vehicles) >= outlink.number_of_lanes: veh.leader = outlink.vehicles[-outlink.number_of_lanes] veh.leader.follower = veh assert veh.leader.lane == veh.lane
- Lane-by-lane trip ending checks:
for link in s.inlinks.values(): for lane in range(link.number_of_lanes): if len(link.vehicles) and link.vehicles[0].flag_waiting_for_trip_end: link.vehicles[0].end_trip() else: break
Proposed solution
Simplify the multi-lane model to align better with mesoscopic simulation principles:
- Remove lane-specific attributes from vehicles.
- Represent lanes as a capacity multiplier for links rather than discrete entities.
- Simplify merging and diverging logic at nodes.
Example of simplified Link class:
class Link:
def __init__(s, ..., number_of_lanes=1, ...):
s.capacity_multiplier = int(number_of_lanes)
s.capacity = s.base_capacity * s.capacity_multiplier
Benefits
- Reduced computational complexity
- Lower memory usage
- Faster simulation execution, especially for large networks
- Better alignment with mesoscopic simulation principles
Potential Concerns
- Slight reduction in detail for merge/diverge behavior
- May affect some existing scenarios that rely on lane-specific logic
Next Steps
Reimplement multi-lane behavior in a simpler way (partly revert #68).
Interesting. Can't you work around that by also compensating for density/lengths/shockwave speeds?
If we assume densities, speeds, and flow characteristics are identical across all lanes, we can safely aggregate them without violating the LWR traffic flow theory, right? In this case, the total flow, density, and speed can be scaled by the number of lanes.
Since traffic dynamics (e.g., shockwave propagation) are uniform across lanes, the multi-lane behavior effectively becomes a single-pipe model.
There are no known simple workaround. There could be complicated tricks (e.g., model vehicle like train where passengers are actual vehicle, or LTM-like approach), but I dont want to go there for simplicity and generality
Okay, thanks for getting back anyways!
Then there are two functions that would benefit a lot from a speedup: The Node transfer
and the Vehicle route_next_link_choice
. Both take up 15% to 20% of simulation time in a large-scale simulation.