Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
42 changes: 34 additions & 8 deletions Scripts/assignment/assignment_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ def assign(self, matrices: dict, iteration: Union[int,str]) -> Dict:
self.event_handler.on_assignment_started(self, iteration, matrices)
self._set_emmebank_matrices(matrices, iteration=="last")
if iteration=="init":
self._create_bike_auto_mode()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you need this new mode for? Can you not simply use f?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

f mode is aux_transit, not aux_auto. I think aux_transit is not accepted in car assignment. Or would it be a good idea to change it during the model run?

Copy link
Collaborator

@zptro zptro Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you are right! So why not just change f into aux_auto? I see no reason to have it as aux_transit anymore.

Originally, bike LOS was taken from pedestrian assignment (which is technically transit assignment), but when we started developing the bike assignment, we realized that car assignment would work better. I did not change f into aux_auto of pure laziness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, after discussing this issue with the development team we decided to change the "f" mode to AUX_AUTO and adjust the scenarios accordingly. "F" mode will not be used. This will be backwards incompatible, but the change is easy to do if there is need to run older scenarios.

self._assign_pedestrians()
self._set_bike_vdfs()
self._assign_bikes(self.emme_matrices["bike"]["dist"], "all")
Expand All @@ -131,6 +132,7 @@ def assign(self, matrices: dict, iteration: Union[int,str]) -> Dict:
self._calc_extra_wait_time()
self._assign_congested_transit() if param.always_congested else self._assign_transit()
elif iteration==0:
self._create_bike_auto_mode()
self._set_bike_vdfs()
self._assign_bikes(self.emme_matrices["bike"]["dist"], "all")
self._set_car_and_transit_vdfs()
Expand Down Expand Up @@ -159,6 +161,7 @@ def assign(self, matrices: dict, iteration: Union[int,str]) -> Dict:
self._calc_extra_wait_time()
self._assign_congested_transit() if param.always_congested else self._assign_transit()
elif iteration=="last":
self._create_bike_auto_mode()
self._set_bike_vdfs()
self._assign_bikes(self.emme_matrices["bike"]["dist"], "all")
self._set_car_and_transit_vdfs()
Expand All @@ -167,6 +170,7 @@ def assign(self, matrices: dict, iteration: Union[int,str]) -> Dict:
self._calc_boarding_penalties(is_last_iteration=True)
self._calc_extra_wait_time()
self._assign_congested_transit()
self._delete_bike_auto_mode()
else:
raise ValueError("Iteration number not valid")

Expand Down Expand Up @@ -335,7 +339,7 @@ def _set_car_and_transit_vdfs(self):
break
else:
# Link with no car traffic
link.volume_delay_func = 0
link.volume_delay_func = 10 #good enough as default function

# Transit function definition
for modeset in param.transit_delay_funcs:
Expand Down Expand Up @@ -376,8 +380,7 @@ def _set_car_and_transit_vdfs(self):
segment.transit_time_func = func
if car_mode in link.modes:
link.modes |= {main_mode}
Comment on lines 378 to 379
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have already allowed main_mode on all links, so this is unnecessary now, is it not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can indeed be dropped.

elif main_mode in link.modes:
link.modes -= {main_mode}
if link.num_lanes == 0: link.num_lanes = 1
self.event_handler.on_car_and_transit_vdfs_set(self, network)
self.emme_scenario.publish_network(network)

Expand All @@ -394,6 +397,7 @@ def _set_bike_vdfs(self):
network = self.emme_scenario.get_network()
main_mode = network.mode(param.main_mode)
bike_mode = network.mode(param.bike_mode)
bike_mode_traffic = network.mode(param.bike_mode_traffic)
for turn in network.turns():
if turn.penalty_func == 0:
turn.penalty_func = 1
Expand All @@ -405,7 +409,7 @@ def _set_bike_vdfs(self):
roadtype = param.custom_roadtypes[linktype]
else:
roadtype = None
if (roadtype == "motorway" and network.mode('f') in link.modes
if (roadtype == "motorway" and network.mode(param.bike_mode) in link.modes
and link["@pyoratieluokka"] == 0):
# Force bikes on motorways onto separate bikepaths
link["@pyoratieluokka"] = 3
Expand All @@ -418,10 +422,21 @@ def _set_bike_vdfs(self):
except KeyError:
link.volume_delay_func = 98
if bike_mode in link.modes:
link.modes |= {main_mode}
elif main_mode in link.modes:
link.modes -= {main_mode}
link.modes |= {bike_mode_traffic}
self.event_handler.on_bike_vdfs_set(self, network)
self.emme_scenario.publish_network(network)

def _create_bike_auto_mode(self):
"""Create special bike mode just for the purpose of bike assignment"""
network = self.emme_scenario.get_network()
if not any(m.id == param.bike_mode_traffic for m in network.modes()):
network.create_mode("AUX_AUTO", param.bike_mode_traffic)
self.emme_scenario.publish_network(network)

def _delete_bike_auto_mode(self):
"""Remove special bike mode from results"""
network = self.emme_scenario.get_network()
network.delete_mode(param.bike_mode_traffic, cascade = True)
self.emme_scenario.publish_network(network)

def _set_emmebank_matrices(self,
Expand Down Expand Up @@ -627,7 +642,7 @@ def _specify(self):
"type": "STANDARD_TRAFFIC_ASSIGNMENT",
"classes": [
{
"mode": param.main_mode,
"mode": param.bike_mode_traffic,
"demand": self.emme_matrices["bike"]["demand"],
"results": {
"od_travel_times": {
Expand Down Expand Up @@ -702,6 +717,10 @@ def _assign_cars(self,
time_attr = self.extra("car_time")
for link in network.links():
link[time_attr] = link.auto_time
#prevent errors from non-car links
#assignment only uses mode-based subnetworks,
# these should not be used in practice
if link.auto_time > 1e3: link.auto_time = 1e3
self.emme_scenario.publish_network(network)
log.info("Car assignment performed for scenario {}".format(
self.emme_scenario.id))
Expand Down Expand Up @@ -750,6 +769,13 @@ def _assign_bikes(self,

def _assign_pedestrians(self):
"""Perform pedestrian assignment for one scenario."""
#Add h mode everywhere just to be sure
network = self.emme_scenario.get_network()
main_mode = network.mode(param.main_mode)
for link in network.links():
link.modes |= {main_mode}
self.emme_scenario.publish_network(network)

Comment on lines +752 to +758
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you do this here, when it has nothing to do with pedestrian assignment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more like a fix for the older networks to bring them to newer standard.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so maybe you could move this fix to some more natural place, like prepare()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, I moved them to prepare. Prepare does not seem to be used for mock assignment, but that does not seem to be a big issue as of now.

log.info("Pedestrian assignment started...")
self.emme_project.pedestrian_assignment(
specification=self.walk_spec, scenario=self.emme_scenario)
Expand Down
7 changes: 7 additions & 0 deletions Scripts/assignment/emme_bindings/mock_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,13 @@ def create_mode(self, mode_type: str, idx: str) -> 'Mode':
mode = Mode(idx, mode_type)
self._modes[idx] = mode
return mode

def delete_mode(self, idx: str, cascade: bool = False):
mode = self._modes[idx]
if cascade:
for link in self._links:
self._links[link].modes -= {mode}
self._modes.pop(idx)

def node(self, idx: int) -> 'Node':
idx = int(idx)
Expand Down
1 change: 1 addition & 0 deletions Scripts/parameters/assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ def calc_segment_cost(transit_volume, line_capacity, segment):
}
main_mode = 'h'
bike_mode = 'f'
bike_mode_traffic = 'F'
assignment_modes = {
"car_work": 'c',
"car_leisure": 'c',
Expand Down