Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First draft of relaxed time periods #59

Merged
merged 18 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions Scripts/assignment/assignment_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ def assign(self, modes: Iterable[str]
mtxs = self._get_impedances(modes)
for ass_cl in param.car_classes:
mtxs["cost"][ass_cl] += self._dist_unit_cost[ass_cl] * mtxs["dist"][ass_cl]
del mtxs["dist"]
return mtxs

def end_assign(self) -> Dict[str, Dict[str, numpy.ndarray]]:
Expand Down
10 changes: 7 additions & 3 deletions Scripts/assignment/emme_assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import parameters.assignment as param
from assignment.abstract_assignment import AssignmentModel
from assignment.assignment_period import AssignmentPeriod
import assignment.off_peak_period as periods
from assignment.freight_assignment import FreightAssignmentPeriod
if TYPE_CHECKING:
from assignment.emme_bindings.emme_project import EmmeProject
Expand Down Expand Up @@ -44,8 +45,11 @@ class EmmeAssignmentModel(AssignmentModel):
delete_extra_matrices : bool (optional)
If True, only matrices needed for demand calculation will be
returned from end assignment.
time_periods : list of str (optional)
time_periods : dict (optional)
key : str
Time period names, default is aht, pt, iht
value : str
Name of `AssignmentPeriod` sub-class
first_matrix_id : int (optional)
Where to save matrices (if saved),
300 matrix ids will be reserved, starting from first_matrix_id.
Expand All @@ -59,7 +63,7 @@ def __init__(self,
use_free_flow_speeds: bool = False,
use_stored_speeds: bool = False,
delete_extra_matrices: bool = False,
time_periods: List[str] = param.time_periods,
time_periods: dict[str, str] = param.time_periods,
first_matrix_id: int = 100):
self.separate_emme_scenarios = separate_emme_scenarios
self.save_matrices = save_matrices
Expand Down Expand Up @@ -111,7 +115,7 @@ def prepare_network(self, car_dist_unit_cost: Dict[str, float]):
scen_id = self.mod_scenario.number
emme_matrices = self._create_matrices(
tp, i*hundred + self.first_matrix_id, id_ten)
self.assignment_periods.append(AssignmentPeriod(
self.assignment_periods.append(vars(periods)[self.time_periods[tp]](
tp, scen_id, self.emme_project, emme_matrices,
separate_emme_scenarios=self.separate_emme_scenarios,
use_free_flow_speeds=self.use_free_flow_speeds,
Expand Down
52 changes: 46 additions & 6 deletions Scripts/assignment/mock_assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class MockAssignmentModel(AssignmentModel):
def __init__(self, matrices: MatrixData,
use_free_flow_speeds: bool = False,
time_periods: List[str]=param.time_periods,
time_periods: Dict[str, str]=param.time_periods,
delete_extra_matrices: bool = False):
self.matrices = matrices
log.info("Reading matrices from " + str(self.matrices.path))
Expand All @@ -30,21 +30,23 @@ def __init__(self, matrices: MatrixData,
end_assignment_classes -= set(
param.long_distance_transit_classes)
self.time_periods = time_periods
self.assignment_periods = [MockPeriod(
cls = globals()
for tp in time_periods:
cl_name = time_periods[tp]
time_periods[tp] = cls[cl_name] if cl_name in cls else MockPeriod
self.assignment_periods = [time_periods[tp](
tp, matrices, end_assignment_classes)
for tp in time_periods]

@property
def zone_numbers(self) -> numpy.array:
"""Numpy array of all zone numbers."""
with self.matrices.open("time", self.time_periods[0]) as mtx:
zone_numbers = mtx.zone_numbers
return zone_numbers
return next(iter(self.assignment_periods)).zone_numbers

@property
def mapping(self):
"""dict: Dictionary of zone numbers and corresponding indices."""
with self.matrices.open("time", self.time_periods[0]) as mtx:
with self.matrices.open("time", next(iter(self.assignment_periods))) as mtx:
mapping = mtx.mapping
return mapping

Expand Down Expand Up @@ -91,6 +93,9 @@ def zone_numbers(self):
zone_numbers = mtx.zone_numbers
return zone_numbers

def init_assign(self):
pass

def assign_trucks_init(self):
pass

Expand Down Expand Up @@ -179,3 +184,38 @@ def set_matrix(self,
matrix: numpy.ndarray):
with self.matrices.open("demand", self.name, self.zone_numbers, m='a') as mtx:
mtx[ass_class] = matrix


class TransitAssignmentPeriod(MockPeriod):
def assign(self, *args) -> Dict[str, Dict[str, numpy.ndarray]]:
"""Get local transit impedance matrices for one time period.

Returns
-------
dict
Type (time/cost/dist) : dict
Assignment class (transit_work/transit_leisure) : numpy 2-d matrix
"""
mtxs = self._get_impedances(param.local_transit_classes)
del mtxs["dist"]
return mtxs

def end_assign(self) -> Dict[str, Dict[str, numpy.ndarray]]:
"""Get transit impedance matrices for one time period.

Long-distance mode impedances are included if assignment period
was created with delete_extra_matrices option disabled.

Returns
-------
dict
Type (time/cost/dist) : dict
Assignment class (transit_work/...) : numpy 2-d matrix
"""
self._end_assignment_classes -= set(
param.private_classes + param.freight_classes)
return self._get_impedances(self._end_assignment_classes)

class EndAssignmentOnlyPeriod(MockPeriod):
def assign(self, *args) -> None:
return None
98 changes: 98 additions & 0 deletions Scripts/assignment/off_peak_period.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from typing import Dict, Iterable
from numpy.core import ndarray
import copy

from assignment.assignment_period import AssignmentPeriod
import parameters.assignment as param


class OffPeakPeriod(AssignmentPeriod):
"""Off-peak assignment period.

The major difference compared to a regular assignment period is that
bus speeds are taken from free-flow assignment in demand-calculation loop
and transit assignment is hence not iterated.

Car assignment is performed as usual.
"""

def init_assign(self):
"""Assign transit for one time period with free-flow bus speed."""
self._set_car_vdfs(use_free_flow_speeds=True)
stopping_criteria = copy.copy(
param.stopping_criteria["coarse"])
stopping_criteria["max_iterations"] = 0
self._assign_cars(stopping_criteria)
self._assign_transit(param.transit_classes)

def assign(self, modes: Iterable[str]) -> Dict[str, Dict[str, ndarray]]:
"""Assign cars for one time period.

Get travel impedance matrices for one time period from assignment.
Transit impedance is fetched from free-flow init assignment.

Parameters
----------
modes : Set of str
The assignment classes for which impedance matrices will be returned

Returns
-------
dict
Type (time/cost/dist) : dict
Assignment class (car_work/transit/...) : numpy 2-d matrix
"""
if not self._separate_emme_scenarios:
self._calc_background_traffic(include_trucks=True)
self._assign_cars(self.stopping_criteria["coarse"])
mtxs = self._get_impedances(modes)
for ass_cl in param.car_classes:
mtxs["cost"][ass_cl] += self._dist_unit_cost[ass_cl] * mtxs["dist"][ass_cl]
del mtxs["dist"]
return mtxs


class TransitAssignmentPeriod(OffPeakPeriod):
"""Transit-only assignment period.

The major difference compared to a regular assignment period is that
bus speeds are taken from free-flow assignment and transit assignment
is hence not iterated.

Car assignment is not performed at all.
"""

def assign(self, *args) -> Dict[str, Dict[str, ndarray]]:
"""Get local transit impedance matrices for one time period.

Returns
-------
dict
Type (time/cost/dist) : dict
Assignment class (transit_work/transit_leisure) : numpy 2-d matrix
"""
mtxs = self._get_impedances(param.local_transit_classes)
del mtxs["dist"]
return mtxs

def end_assign(self) -> Dict[str, Dict[str, ndarray]]:
"""Get transit impedance matrices for one time period.

Long-distance mode impedances are included if assignment period
was created with delete_extra_matrices option disabled.

Returns
-------
dict
Type (time/cost/dist) : dict
Assignment class (transit_work/...) : numpy 2-d matrix
"""
self._calc_transit_network_results()
self._end_assignment_classes -= set(
param.private_classes + param.freight_classes)
return self._get_impedances(self._end_assignment_classes)


class EndAssignmentOnlyPeriod(AssignmentPeriod):
def assign(self, *args) -> None:
return None
3 changes: 2 additions & 1 deletion Scripts/lem.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import utils.log as log
from assignment.emme_assignment import EmmeAssignmentModel
from assignment.mock_assignment import MockAssignmentModel
from assignment.assignment_period import AssignmentPeriod
from modelsystem import ModelSystem, AgentModelSystem
from datahandling.matrixdata import MatrixData

Expand Down Expand Up @@ -72,7 +73,7 @@ def main(args):
"delete_extra_matrices": args.delete_extra_matrices,
}
if calculate_long_dist_demand:
kwargs["time_periods"] = ["vrk"]
kwargs["time_periods"] = {"vrk": "AssignmentPeriod"}
if args.do_not_use_emme:
log.info("Initializing MockAssignmentModel...")
mock_result_path = results_path / "Matrices" / args.submodel
Expand Down
3 changes: 2 additions & 1 deletion Scripts/modelsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ def assign_base_demand(self,
self.dtm = dt.DirectDepartureTimeModel(self.ass_model)

if not self.ass_model.use_free_flow_speeds:
self.ass_model.init_assign()
log.info("Get long-distance trip matrices")
self._add_external_demand(self.long_dist_matrices)
log.info("Get freight matrices")
Expand Down Expand Up @@ -285,6 +284,8 @@ def assign_base_demand(self,
transport_classes=transport_classes) as mtx:
for ass_class in transport_classes:
self.dtm.demand[tp][ass_class] = mtx[ass_class]
if not self.ass_model.use_free_flow_speeds:
ap.init_assign()
ap.assign_trucks_init()
impedance[tp] = (ap.end_assign() if is_end_assignment
else ap.assign(self.travel_modes))
Expand Down
9 changes: 8 additions & 1 deletion Scripts/parameters/assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"aht": 6,
"pt": 6,
"iht": 6,
"ilt": 6,
"vrk": 6,
},
}
Expand Down Expand Up @@ -402,6 +403,7 @@
volume_factors["aux_transit"] = volume_factors["transit"]
for mode in volume_factors:
volume_factors[mode]["vrk"] = 1
volume_factors[mode]["ilt"] = 0
# Factor for converting weekday traffic into yearly day average
years_average_day_factor = 0.85
# Factor for converting day traffic into 7:00-22:00 traffic
Expand All @@ -427,7 +429,12 @@
}

### ASSIGNMENT REFERENCES ###
time_periods: List[str] = ["aht", "pt", "iht"]
time_periods = {
"aht": "EndAssignmentOnlyPeriod",
"pt": "OffPeakPeriod",
"iht": "AssignmentPeriod",
"ilt": "TransitAssignmentPeriod",
}
car_classes = (
"car_work",
"car_leisure",
Expand Down
3 changes: 2 additions & 1 deletion Scripts/tests/test_data/Network/netfield_links_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ t network_fields
#car_time_aht LINK REAL ''
#car_time_pt LINK REAL ''
#car_time_iht LINK REAL ''
#car_time_ilt LINK REAL ''
#car_time_vrk LINK REAL ''
#buslane LINK BOOLEAN ''
end network_fields
inode jnode #hinta_aht #hinta_pt #hinta_iht #hinta_vrk #car_time_aht #car_time_pt #car_time_iht #car_time_vrk #buslane
inode jnode #hinta_aht #hinta_pt #hinta_iht #hinta_vrk #car_time_aht #car_time_pt #car_time_iht #car_time_ilt #car_time_vrk #buslane
27 changes: 14 additions & 13 deletions Scripts/tests/test_data/Network/netfield_transit_lines_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ t network_fields
#hdw_aht TRANSIT_LINE REAL ''
#hdw_pt TRANSIT_LINE REAL ''
#hdw_iht TRANSIT_LINE REAL ''
#hdw_ilt TRANSIT_LINE REAL ''
#hdw_vrk TRANSIT_LINE REAL ''
#keep_stops TRANSIT_LINE BOOLEAN ''
end network_fields
line #hdw_aht #hdw_pt #hdw_iht #hdw_vrk #keep_stops
'10101 ' 6 7.5 6 7.5 0
'10102 ' 6 7.5 6 7.5 0
'10191 ' 40 40 40 40 0
'10192 ' 40 40 40 40 0
'22001 ' 10 10 10 10 0
'22002 ' 10 10 10 10 0
'3002A1' 10 10 10 10 0
'3002A2' 10 10 10 10 0
'72801 ' 30 30 15 30 1
'72802 ' 15 30 30 30 0
'V18761' 180 120 180 120 0
'V18762' 180 120 90 120 0
line #hdw_aht #hdw_pt #hdw_iht #hdw_ilt #hdw_vrk #keep_stops
'10101 ' 6 7.5 6 7.5 7.5 0
'10102 ' 6 7.5 6 7.5 7.5 0
'10191 ' 40 40 40 40 40 0
'10192 ' 40 40 40 40 40 0
'22001 ' 10 10 10 10 10 0
'22002 ' 10 10 10 10 10 0
'3002A1' 10 10 10 10 10 0
'3002A2' 10 10 10 10 10 0
'72801 ' 30 30 15 30 30 1
'72802 ' 15 30 30 30 30 0
'V18761' 180 120 180 120 120 0
'V18762' 180 120 90 120 120 0
Binary file not shown.
Binary file not shown.
Loading