From dac93178f0e54d71761a97be1fd3e34b501dbc5f Mon Sep 17 00:00:00 2001 From: DavidOry Date: Mon, 4 Mar 2024 16:34:00 -0500 Subject: [PATCH] black --- tests/test_union_city.py | 2 +- tm2py/acceptance/acceptance.py | 8 +- tm2py/acceptance/canonical.py | 1 - tm2py/acceptance/observed.py | 20 +- tm2py/acceptance/simulated.py | 302 +++-- tm2py/components/demand/household.py | 10 +- tm2py/components/demand/prepare_demand.py | 427 ++++-- .../network/create_tod_scenarios.py | 123 +- .../network/transit/transit_assign.py | 1178 ++++++++++------- .../network/transit/transit_network.py | 101 +- .../network/transit/transit_skim.py | 399 +++--- tm2py/config.py | 36 +- tm2py/controller.py | 5 +- tm2py/emme/matrix.py | 10 +- 14 files changed, 1578 insertions(+), 1044 deletions(-) diff --git a/tests/test_union_city.py b/tests/test_union_city.py index 5982b59d..1a83a8c4 100644 --- a/tests/test_union_city.py +++ b/tests/test_union_city.py @@ -68,7 +68,7 @@ def test_highway(): os.path.join(_EXAMPLES_DIR, r"scenario_config.toml"), os.path.join(_EXAMPLES_DIR, r"model_config.toml"), ], - run_dir=union_city_root + run_dir=union_city_root, ) controller.run() diff --git a/tm2py/acceptance/acceptance.py b/tm2py/acceptance/acceptance.py index 4d1244af..cb259283 100644 --- a/tm2py/acceptance/acceptance.py +++ b/tm2py/acceptance/acceptance.py @@ -165,7 +165,7 @@ def _make_roadway_network_comparisons(self): s_bridge_df = self.s.simulated_bridge_details_df.copy() o_df["time_period"] = o_df.time_period.str.lower() - #o_df = o_df.drop(columns = ["standard_link_id"]) + # o_df = o_df.drop(columns = ["standard_link_id"]) s_trim_df = s_df[ s_df["ft"] <= self.MAX_FACILITY_TYPE_FOR_ROADWAY_COMPARISONS ].copy() @@ -347,7 +347,7 @@ def _make_transit_network_comparisons(self): right_on=["standard_line_name", "daily_line_name", "time_period"], ) - boards_df = pd.concat([rail_df, non_df], axis = "rows",ignore_index=True) + boards_df = pd.concat([rail_df, non_df], axis="rows", ignore_index=True) boards_df["operator"] = np.where( boards_df["operator"].isnull(), @@ -378,7 +378,9 @@ def _make_transit_network_comparisons(self): ].copy() ) daily_shape_df = pd.merge(c_df, b_df, how="left", on="LINE_ID") - daily_shape_df = daily_shape_df.rename(columns={"INODE":"emme_a_node_id","JNODE":"emme_b_node_id"}) + daily_shape_df = daily_shape_df.rename( + columns={"INODE": "emme_a_node_id", "JNODE": "emme_b_node_id"} + ) # step 4 -- join the shapes to the boardings # for daily, join boardings to shape, as I care about the boardings more than the daily shapes diff --git a/tm2py/acceptance/canonical.py b/tm2py/acceptance/canonical.py index 6d1b0045..765f6977 100644 --- a/tm2py/acceptance/canonical.py +++ b/tm2py/acceptance/canonical.py @@ -240,4 +240,3 @@ def _read_standard_to_emme_node_crosswalk(self) -> pd.DataFrame: self.standard_to_emme_node_crosswalk_df = df return - diff --git a/tm2py/acceptance/observed.py b/tm2py/acceptance/observed.py index 67d1e64d..64ab3ed8 100644 --- a/tm2py/acceptance/observed.py +++ b/tm2py/acceptance/observed.py @@ -869,10 +869,7 @@ def _reduce_pems_counts(self, read_file_from_disk=True): ) out_df = pd.merge( - self.c.pems_to_link_crosswalk_df, - out_df, - how="left", - on="station_id" + self.c.pems_to_link_crosswalk_df, out_df, how="left", on="station_id" ) out_df = self._join_tm2_node_ids(out_df) @@ -893,16 +890,23 @@ def _reduce_pems_counts(self, read_file_from_disk=True): .reset_index() ) join_df = out_df[ - ["emme_a_node_id","emme_b_node_id", "time_period", "station_id", "type", "vehicle_class"] + [ + "emme_a_node_id", + "emme_b_node_id", + "time_period", + "station_id", + "type", + "vehicle_class", + ] ].copy() return_df = pd.merge( median_df, join_df, how="left", - on=["emme_a_node_id","emme_b_node_id", "time_period", "vehicle_class"] + on=["emme_a_node_id", "emme_b_node_id", "time_period", "vehicle_class"], ).reset_index(drop=True) - #return_df = return_df.rename(columns = {"model_link_id" : "standard_link_id"}) + # return_df = return_df.rename(columns = {"model_link_id" : "standard_link_id"}) return_df = self._join_ohio_standards(return_df) return_df = self._identify_key_arterials_and_bridges(return_df) @@ -955,7 +959,7 @@ def _reduce_caltrans_counts(self): out_df = out_df[out_df["observed_flow"].notna()] # convert to one-way flow - out_df["observed_flow"] = out_df["observed_flow"]/2.0 + out_df["observed_flow"] = out_df["observed_flow"] / 2.0 return_df = self._join_tm2_node_ids(out_df) return_df["time_period"] = self.c.ALL_DAY_WORD diff --git a/tm2py/acceptance/simulated.py b/tm2py/acceptance/simulated.py index 24322781..4a0a79a4 100644 --- a/tm2py/acceptance/simulated.py +++ b/tm2py/acceptance/simulated.py @@ -73,14 +73,14 @@ class Simulated: ], columns=["plaza_name", "direction", "standard_link_id", "pay_toll"], ) - - network_shapefile_names_dict = { - "ea":"Scenario_11", - "am":"Scenario_12", - "md":"Scenario_13", - "pm":"Scenario_14", - "ev":"Scenario_15"} + network_shapefile_names_dict = { + "ea": "Scenario_11", + "am": "Scenario_12", + "md": "Scenario_13", + "pm": "Scenario_14", + "ev": "Scenario_15", + } def _load_configs(self): @@ -129,7 +129,7 @@ def _validate(self): self._read_transit_demand() self._make_transit_technology_in_vehicle_table_from_skims() self._make_district_to_district_transit_summaries() - self._reduce_simulated_transit_by_segment() + self._reduce_simulated_transit_by_segment() self._reduce_simulated_transit_boardings() self._reduce_simulated_transit_shapes() self._reduce_simulated_home_work_flows() @@ -147,21 +147,23 @@ def _validate(self): return - def _add_model_link_id(self): - df = pd.read_csv(os.path.join("acceptance", "crosswalks", "transit_link_id_mapping_am.csv")) + def _add_model_link_id(self): + df = pd.read_csv( + os.path.join("acceptance", "crosswalks", "transit_link_id_mapping_am.csv") + ) df = df.rename( - columns={ - "LINK_ID": "#link_id", - } - ) + columns={ + "LINK_ID": "#link_id", + } + ) - return df + return df - def _reduce_simulated_transit_by_segment(self): + def _reduce_simulated_transit_by_segment(self): file_prefix = "transit_segment_" - time_period = "am" # am only + time_period = "am" # am only df = pd.read_csv( os.path.join("output_summaries", file_prefix + time_period + ".csv"), @@ -172,12 +174,12 @@ def _reduce_simulated_transit_by_segment(self): # remove nodes from line name and add node fields a_df["line_long"] = df["line"].copy() - temp = a_df["line_long"].str.split(pat="-", expand=True) - a_df["LINE_ID"] = temp[0] - a_df = a_df.rename(columns = {"i_node":"INODE","j_node":"JNODE"}) - a_df = a_df[~(a_df["JNODE"]=="None")].reset_index().copy() + temp = a_df["line_long"].str.split(pat="-", expand=True) + a_df["LINE_ID"] = temp[0] + a_df = a_df.rename(columns={"i_node": "INODE", "j_node": "JNODE"}) + a_df = a_df[~(a_df["JNODE"] == "None")].reset_index().copy() a_df["JNODE"] = a_df["JNODE"].astype("float").astype("Int64") - df = a_df[["LINE_ID","line","INODE","JNODE","board"]] + df = a_df[["LINE_ID", "line", "INODE", "JNODE", "board"]] self.am_segment_simulated_boardings_df = df @@ -194,31 +196,27 @@ def _get_operator_name_from_line_name( def _reduce_simulated_transit_shapes(self): file_prefix = "boardings_by_segment_" - time_period = "am" # am only + time_period = "am" # am only gdf = gpd.read_file( os.path.join("output_summaries", file_prefix + time_period + ".geojson") ) - gdf["first_row_in_line"] = gdf.groupby("LINE_ID").cumcount() == 0 + gdf["first_row_in_line"] = gdf.groupby("LINE_ID").cumcount() == 0 df = pd.DataFrame(gdf.drop(columns=["geometry"])) # Add am boards a_df = self.am_segment_simulated_boardings_df - df = pd.merge(df, - a_df, - how = "left", - on = ["LINE_ID","INODE","JNODE"] - ) + df = pd.merge(df, a_df, how="left", on=["LINE_ID", "INODE", "JNODE"]) # Compute v/c ratio, excluding pnr dummy routes a_df = df[~(df["LINE_ID"].str.contains("pnr_"))].reset_index().copy() a_df["am_segment_capacity_total"] = ( - a_df["capt"] * self.model_morning_capacity_factor + a_df["capt"] * self.model_morning_capacity_factor ) a_df["am_segment_capacity_seated"] = ( - a_df["caps"] * self.model_morning_capacity_factor + a_df["caps"] * self.model_morning_capacity_factor ) a_df["am_segment_vc_ratio_total"] = ( a_df["VOLTR"] / a_df["am_segment_capacity_total"] @@ -247,11 +245,11 @@ def _reduce_simulated_transit_shapes(self): crosswalk_df = self._add_model_link_id() - b_gdf= pd.merge( - a_gdf, + b_gdf = pd.merge( + a_gdf, crosswalk_df, - on = ["INODE","JNODE","LINE_ID"], - how = "left", + on=["INODE", "JNODE", "LINE_ID"], + how="left", ) self.simulated_transit_segments_gdf = pd.merge( @@ -266,7 +264,7 @@ def _reduce_simulated_transit_shapes(self): "am_segment_capacity_seated", "am_segment_vc_ratio_total", "am_segment_vc_ratio_seated", - "board" + "board", ] ], on=["LINE_ID", "INODE", "JNODE"], @@ -279,7 +277,7 @@ def _join_coordinates_to_stations(self, input_df, input_column_name): station_list = input_df[input_column_name].unique().tolist() - x_df = self.c.standard_to_emme_transit_nodes_df.copy() + x_df = self.c.standard_to_emme_transit_nodes_df.copy() n_gdf = self.standard_nodes_gdf.copy() df = x_df[x_df["emme_node_id"].isin(station_list)].copy().reset_index(drop=True) @@ -302,15 +300,15 @@ def _join_coordinates_to_stations(self, input_df, input_column_name): def _read_standard_node(self): - in_file = os.path.join("inputs","trn","standard","v12_node.geojson") - gdf = gpd.read_file(in_file, driver = "GEOJSON") + in_file = os.path.join("inputs", "trn", "standard", "v12_node.geojson") + gdf = gpd.read_file(in_file, driver="GEOJSON") self.standard_nodes_gdf = gdf return def _read_standard_transit_stops(self): - + in_file = os.path.join("inputs", "trn", "standard", "v12_stops.txt") df = pd.read_csv(in_file) @@ -385,7 +383,7 @@ def _reduce_simulated_home_work_flows(self): def _make_simulated_maz_data(self): - in_file = os.path.join("inputs", "landuse", "maz_data.csv") + in_file = os.path.join("inputs", "landuse", "maz_data.csv") df = pd.read_csv(in_file) @@ -412,27 +410,20 @@ def _reduce_simulated_rail_access_summaries(self): file_prefix = "transit_segment_" - out_df = pd.DataFrame() - + out_df = pd.DataFrame() + for time_period in self.model_time_periods: - + df = pd.read_csv( os.path.join("output_summaries", file_prefix + time_period + ".csv"), dtype={"stop_name": str, "mdesc": str}, low_memory=False, ) - rail_df = df[ - df["mdesc"].isin( - [ - "HVY", - "COM" - ] - ) - ].copy() + rail_df = df[df["mdesc"].isin(["HVY", "COM"])].copy() - a_df = rail_df["line"].str.split(pat="_", expand=True).copy() - rail_df["operator"] = a_df[1] + a_df = rail_df["line"].str.split(pat="_", expand=True).copy() + rail_df["operator"] = a_df[1] rail_df["operator"] = rail_df["operator"].map( self.c.canonical_agency_names_dict ) @@ -465,13 +456,30 @@ def _reduce_simulated_rail_access_summaries(self): access_df = df.copy() access_df = access_df[access_df["j_node"].isin(station_list)].copy() - access_df["board_ptw"] = access_df["initial_board_ptw"] + access_df["direct_transfer_board_ptw"] - access_df["board_wtp"] = access_df["initial_board_wtp"] + access_df["direct_transfer_board_wtp"] - access_df["board_ktw"] = access_df["initial_board_ktw"] + access_df["direct_transfer_board_ktw"] - access_df["board_wtk"] = access_df["initial_board_wtk"] + access_df["direct_transfer_board_wtk"] - access_df["board_wtw"] = access_df["initial_board_wtw"] + access_df["direct_transfer_board_wtw"] + access_df["board_ptw"] = ( + access_df["initial_board_ptw"] + access_df["direct_transfer_board_ptw"] + ) + access_df["board_wtp"] = ( + access_df["initial_board_wtp"] + access_df["direct_transfer_board_wtp"] + ) + access_df["board_ktw"] = ( + access_df["initial_board_ktw"] + access_df["direct_transfer_board_ktw"] + ) + access_df["board_wtk"] = ( + access_df["initial_board_wtk"] + access_df["direct_transfer_board_wtk"] + ) + access_df["board_wtw"] = ( + access_df["initial_board_wtw"] + access_df["direct_transfer_board_wtw"] + ) access_df = access_df[ - ["i_node", "board_ptw", "board_wtp", "board_ktw", "board_wtk", "board_wtw"] + [ + "i_node", + "board_ptw", + "board_wtp", + "board_ktw", + "board_wtk", + "board_wtw", + ] ].copy() access_df["i_node"] = access_df.i_node.astype(int) @@ -495,7 +503,7 @@ def _reduce_simulated_rail_access_summaries(self): var_name="mode", value_name="boardings", ) - long_df["access_mode"] = long_df["mode"].map(access_dict) # check + long_df["access_mode"] = long_df["mode"].map(access_dict) # check join_df = pd.merge( long_df, @@ -522,16 +530,15 @@ def _reduce_simulated_rail_access_summaries(self): columns={"boardings": "simulated_boardings", "i_node": "boarding"} ) - running_df["time_period"] = time_period + running_df["time_period"] = time_period - out_df = pd.concat( [out_df, running_df], axis="rows", ignore_index=True) - + out_df = pd.concat([out_df, running_df], axis="rows", ignore_index=True) self.simulated_transit_access_df = out_df return - def _reduce_simulated_station_to_station(self): + def _reduce_simulated_station_to_station(self): # if self.model_time_periods is None: # self._get_model_time_periods() @@ -560,28 +567,28 @@ def _reduce_simulated_station_to_station(self): input_file_name = os.path.join( "output_summaries", f"{operator}_station_to_station_{path}_{time_period}.txt", - ) + ) file = open(input_file_name, "r") - + while True: line = file.readline() if not line: break - if line[0] == "c" or line[0] == "t" or line[0] == "d" or line[0] == "a": - continue - else: + if line[0] == "c" or line[0] == "t" or line[0] == "d" or line[0] == "a": + continue + else: data = line.split() subject_df = pd.DataFrame( [[operator, time_period, data[0], data[1], data[2]]], columns=[ "operator", "time_period", - "boarding", - "alighting", + "boarding", + "alighting", "simulated", ], ) - df = pd.concat([df,subject_df],axis="rows", ignore_index=True) + df = pd.concat([df, subject_df], axis="rows", ignore_index=True) df["boarding"] = df["boarding"].astype(int) df["alighting"] = df["alighting"].astype(int) @@ -606,7 +613,7 @@ def _join_tm2_mode_codes(self, input_df): df = self.c.gtfs_to_tm2_mode_codes_df.copy() - df = df.drop_duplicates(subset=["tm2_mode"],keep='first') # temporary 10.29 + df = df.drop_duplicates(subset=["tm2_mode"], keep="first") # temporary 10.29 return_df = pd.merge( input_df, @@ -628,7 +635,7 @@ def _reduce_simulated_transit_boardings(self): os.path.join("output_summaries", file_prefix + time_period + ".csv") ) df["time_period"] = time_period - c_df = pd.concat([c_df,df],axis="rows", ignore_index=True) + c_df = pd.concat([c_df, df], axis="rows", ignore_index=True) c_df = self._join_tm2_mode_codes(c_df) c_df["operator"] = c_df["operator"].map(self.c.canonical_agency_names_dict) @@ -743,7 +750,6 @@ def _reduce_simulated_zero_vehicle_households(self): return - def _get_station_names_from_standard_network( self, input_df: pd.DataFrame, @@ -876,14 +882,14 @@ def _get_station_names_from_standard_network( return return_df - def _make_transit_mode_dict(self): # check + def _make_transit_mode_dict(self): # check transit_mode_dict = {} for lil_dict in self.model_dict["transit"]["modes"]: add_dict = {lil_dict["mode_id"]: lil_dict["name"]} transit_mode_dict.update(add_dict) - # TODO: this will be in model_config in TM2.2 + # TODO: this will be in model_config in TM2.2 transit_mode_dict.update({"p": "pnr"}) # self.HEAVY_RAIL_NETWORK_MODE_DESCR = transit_mode_dict["h"] @@ -911,7 +917,6 @@ def _make_transit_mode_dict(self): # check return - def _make_transit_technology_in_vehicle_table_from_skims(self): path_list = [ @@ -921,11 +926,10 @@ def _make_transit_technology_in_vehicle_table_from_skims(self): "KNR_TRN_WLK", "WLK_TRN_KNR", ] - + tech_list = self.c.transit_technology_abbreviation_dict.keys() - skim_dir = os.path.join("skim_matrices", "transit") - + skim_dir = os.path.join("skim_matrices", "transit") running_df = None for (path, time_period) in itertools.product( @@ -945,17 +949,19 @@ def _make_transit_technology_in_vehicle_table_from_skims(self): omx_handle[matrix_name], matrix_name ) ivt_df = ivt_df[ivt_df[matrix_name] > 0.0].copy() - ivt_df.rename(columns={ivt_df.columns[2]: "ivt"}, inplace = True) + ivt_df.rename(columns={ivt_df.columns[2]: "ivt"}, inplace=True) # Transfers to get boardings from trips - - matrix_name = TIME_PERIOD + "_" + path + "_BOARDS" + + matrix_name = TIME_PERIOD + "_" + path + "_BOARDS" if matrix_name in omx_handle.listMatrices(): boards_df = self._make_dataframe_from_omx( omx_handle[matrix_name], matrix_name ) boards_df = boards_df[boards_df[matrix_name] > 0.0].copy() - boards_df.rename(columns={boards_df.columns[2]: "boards"}, inplace = True) + boards_df.rename( + columns={boards_df.columns[2]: "boards"}, inplace=True + ) path_time_df = pd.merge( ivt_df, boards_df, on=["origin", "destination"], how="left" @@ -964,8 +970,8 @@ def _make_transit_technology_in_vehicle_table_from_skims(self): path_time_df["time_period"] = time_period for tech in tech_list: - - matrix_name = TIME_PERIOD + "_" + path + "_IVT" + tech + + matrix_name = TIME_PERIOD + "_" + path + "_IVT" + tech if matrix_name in omx_handle.listMatrices(): df = self._make_dataframe_from_omx( omx_handle[matrix_name], matrix_name @@ -974,10 +980,10 @@ def _make_transit_technology_in_vehicle_table_from_skims(self): col_name = "{}".format(tech.lower()) df[col_name] = df[matrix_name] df = df.drop(columns=[matrix_name]).copy() - path_time_df = pd.merge( - path_time_df, - df, - how="left", + path_time_df = pd.merge( + path_time_df, + df, + how="left", on=["origin", "destination"], ) @@ -996,7 +1002,6 @@ def _make_transit_technology_in_vehicle_table_from_skims(self): def _read_transit_demand(self): - path_list = [ "WLK_TRN_WLK", "PNR_TRN_WLK", @@ -1005,12 +1010,10 @@ def _read_transit_demand(self): "WLK_TRN_KNR", ] dem_dir = os.path.join("demand_matrices", "transit") - - out_df = pd.DataFrame() - for time_period in self.model_time_periods: - filename = os.path.join( - dem_dir, "trn_demand_{}.omx".format(time_period) - ) + + out_df = pd.DataFrame() + for time_period in self.model_time_periods: + filename = os.path.join(dem_dir, "trn_demand_{}.omx".format(time_period)) omx_handle = omx.open_file(filename) running_df = None @@ -1046,8 +1049,14 @@ def _make_dataframe_from_omx(self, input_mtx: omx, core_name: str): """ a = np.array(input_mtx) - df = pd.DataFrame(a) - df= df.unstack().reset_index().rename(columns={"level_0": "origin" ,"level_1": "destination", 0:core_name}) + df = pd.DataFrame(a) + df = ( + df.unstack() + .reset_index() + .rename( + columns={"level_0": "origin", "level_1": "destination", 0: core_name} + ) + ) df["origin"] = df["origin"] + 1 df["destination"] = df["destination"] + 1 @@ -1084,8 +1093,8 @@ def _make_district_to_district_transit_summaries(self): / s_df["ivt"] ) - s_df["orig_district"] = s_df["origin"].map(taz_district_dict) - s_df["dest_district"] = s_df["destination"].map(taz_district_dict) + s_df["orig_district"] = s_df["origin"].map(taz_district_dict) + s_df["dest_district"] = s_df["destination"].map(taz_district_dict) agg_dict = {"simulated_flow": "sum"} rename_dict = {"simulated_flow": "total"} @@ -1112,45 +1121,62 @@ def _reduce_simulated_traffic_flow(self): time_of_day_df = pd.DataFrame() for time_period in self.model_time_periods: - + emme_scenario = self.network_shapefile_names_dict[time_period] gdf = gpd.read_file( os.path.join("output_summaries", emme_scenario, "emme_links.shp") ) df = gdf[ - ["ID", "@flow_da", "@flow_lrgt", "@flow_sr2", "@flow_sr3", "@flow_trk", "@flow_dato", "@flow_lrg0","@flow_sr2t", "@flow_sr3t", "@flow_trkt"] + [ + "ID", + "@flow_da", + "@flow_lrgt", + "@flow_sr2", + "@flow_sr3", + "@flow_trk", + "@flow_dato", + "@flow_lrg0", + "@flow_sr2t", + "@flow_sr3t", + "@flow_trkt", + ] ] df = df.rename(columns={"ID": "model_link_id"}) df["time_period"] = time_period - time_of_day_df = pd.concat([time_of_day_df,df], axis = "rows",ignore_index="True") + time_of_day_df = pd.concat( + [time_of_day_df, df], axis="rows", ignore_index="True" + ) time_of_day_df["simulated_flow_auto"] = time_of_day_df[ - ["@flow_da", "@flow_sr2", "@flow_sr3","@flow_dato","@flow_sr2t", "@flow_sr3t"] - ].sum( - axis=1 - ) + [ + "@flow_da", + "@flow_sr2", + "@flow_sr3", + "@flow_dato", + "@flow_sr2t", + "@flow_sr3t", + ] + ].sum(axis=1) time_of_day_df["simulated_flow_truck"] = time_of_day_df[ ["@flow_trk", "@flow_trkt"] - ].sum( - axis=1 - ) + ].sum(axis=1) time_of_day_df["simulated_flow"] = time_of_day_df[ - ["simulated_flow_auto", "simulated_flow_truck", "@flow_lrgt", "@flow_lrg0"] + [ + "simulated_flow_auto", + "simulated_flow_truck", + "@flow_lrgt", + "@flow_lrg0", + ] ].sum(axis=1) - - all_day_df = ( - time_of_day_df.groupby( - ["model_link_id"] - ) - .sum() - .reset_index() - ) + + all_day_df = time_of_day_df.groupby(["model_link_id"]).sum().reset_index() all_day_df["time_period"] = self.c.ALL_DAY_WORD + out_df = pd.concat( + [time_of_day_df, all_day_df], axis="rows", ignore_index=True + ) - out_df = pd.concat([time_of_day_df, all_day_df], axis="rows", ignore_index=True) - out_df = out_df[ [ "model_link_id", @@ -1160,8 +1186,8 @@ def _reduce_simulated_traffic_flow(self): "simulated_flow", ] ] - - self.simulated_traffic_flow_df = out_df + + self.simulated_traffic_flow_df = out_df return @@ -1212,19 +1238,19 @@ def _reduce_simulated_roadway_assignment_outcomes(self): "@tollseg", "@ft", "@flow_da", - "@flow_sr2", + "@flow_sr2", "@flow_sr3", "@flow_lrgt", "@flow_trk", - "@free_flow", - "@flow_dato", - "@flow_sr2t", - "@flow_sr3t", + "@free_flow", + "@flow_dato", + "@flow_sr2t", + "@flow_sr3t", "@flow_lrg0", "@flow_trkt", ] ] - df = df.rename( + df = df.rename( columns={ "INODE": "emme_a_node_id", "JNODE": "emme_b_node_id", @@ -1241,17 +1267,17 @@ def _reduce_simulated_roadway_assignment_outcomes(self): } ) - df['flow_da'] = df['@flow_da'] + df["@flow_dato"] - df['flow_s2'] = df['@flow_sr2'] + df["@flow_sr2t"] - df['flow_s3'] = df['@flow_sr3'] + df["@flow_sr3t"] - df['flow_lrgt'] = df['@flow_lrgt'] + df['@flow_lrg0'] - df['flow_trk'] = df['@flow_trk'] + df['@flow_trkt'] + df["flow_da"] = df["@flow_da"] + df["@flow_dato"] + df["flow_s2"] = df["@flow_sr2"] + df["@flow_sr2t"] + df["flow_s3"] = df["@flow_sr3"] + df["@flow_sr3t"] + df["flow_lrgt"] = df["@flow_lrgt"] + df["@flow_lrg0"] + df["flow_trk"] = df["@flow_trk"] + df["@flow_trkt"] df["time_period"] = t df["speed_mph"] = np.where( df["distance_in_miles"] > 0, df["distance_in_miles"] / (df["time_in_minutes"] / 60.0), - df["@free_flow"], + df["@free_flow"], ) df["flow_total"] = df[ [col for col in df.columns if col.startswith("flow_")] diff --git a/tm2py/components/demand/household.py b/tm2py/components/demand/household.py index d1bb0e47..abf0164a 100644 --- a/tm2py/components/demand/household.py +++ b/tm2py/components/demand/household.py @@ -32,7 +32,7 @@ def run(self): self._start_matrix_manager() self._run_resident_model() self._stop_java() - #self._consolidate_demand_for_assign() + # self._consolidate_demand_for_assign() self._prepare_demand_for_assignment() def _prepare_demand_for_assignment(self): @@ -146,7 +146,7 @@ def _consolidate_demand_for_assign(self): _shutil.copyfile(input_path, output_path) # transit TAP - #for period in time_period_names: + # for period in time_period_names: # for set in ["set1", "set2", "set3"]: # output_path = ( # self.controller.get_abs_path(self.config.transit_demand_file) @@ -174,7 +174,7 @@ def _consolidate_demand_for_assign(self): # core_name = mode + "_TRN_" + set + "_" + period.upper() # output_omx[core_name] = input_omx[core_name][:, :] # input_omx.close() -# + # # output_omx.close() # transit TAZ for period in time_period_names: @@ -200,8 +200,8 @@ def _consolidate_demand_for_assign(self): ) ) input_omx = omx.open_file(input_path, "r") - core_name = mode + "_TRN_" + period.upper() + core_name = mode + "_TRN_" + period.upper() output_omx[core_name] = input_omx[core_name][:, :] input_omx.close() - output_omx.close() \ No newline at end of file + output_omx.close() diff --git a/tm2py/components/demand/prepare_demand.py b/tm2py/components/demand/prepare_demand.py index 2347f48f..217ad903 100644 --- a/tm2py/components/demand/prepare_demand.py +++ b/tm2py/components/demand/prepare_demand.py @@ -219,226 +219,382 @@ def _read_demand(self, file_config, time_period, num_zones): name, num_zones, ) + @LogStartEnd("Prepare household demand matrices.") def prepare_household_demand(self): - """Prepares highway and transit household demand matrices from trip lists produced by CT-RAMP. - """ + """Prepares highway and transit household demand matrices from trip lists produced by CT-RAMP.""" iteration = self.controller.iteration - + # Create folders if they don't exist - pathlib.Path(self.controller.get_abs_path(self.controller.config.household.highway_demand_file)).parents[0].mkdir(parents=True, exist_ok=True) - pathlib.Path(self.controller.get_abs_path(self.controller.config.household.transit_demand_file)).parents[0].mkdir(parents=True, exist_ok=True) - # pathlib.Path(self.controller.get_abs_path(self.controller.config.household.active_demand_file)).parents[0].mkdir(parents=True, exist_ok=True) - - - indiv_trip_file = self.controller.config.household.ctramp_indiv_trip_file.format(iteration = iteration) - joint_trip_file = self.controller.config.household.ctramp_joint_trip_file.format(iteration = iteration) + pathlib.Path( + self.controller.get_abs_path( + self.controller.config.household.highway_demand_file + ) + ).parents[0].mkdir(parents=True, exist_ok=True) + pathlib.Path( + self.controller.get_abs_path( + self.controller.config.household.transit_demand_file + ) + ).parents[0].mkdir(parents=True, exist_ok=True) + # pathlib.Path(self.controller.get_abs_path(self.controller.config.household.active_demand_file)).parents[0].mkdir(parents=True, exist_ok=True) + + indiv_trip_file = ( + self.controller.config.household.ctramp_indiv_trip_file.format( + iteration=iteration + ) + ) + joint_trip_file = ( + self.controller.config.household.ctramp_joint_trip_file.format( + iteration=iteration + ) + ) it_full, jt_full = pd.read_csv(indiv_trip_file), pd.read_csv(joint_trip_file) - + # Add time period, expanded count - time_period_start = dict(zip( - [c.name.upper() for c in self.controller.config.time_periods], - [c.start_period for c in self.controller.config.time_periods])) + time_period_start = dict( + zip( + [c.name.upper() for c in self.controller.config.time_periods], + [c.start_period for c in self.controller.config.time_periods], + ) + ) # the last time period needs to be filled in because the first period may or may not start at midnight - time_periods_sorted = sorted(time_period_start, key = lambda x:time_period_start[x]) # in upper case + time_periods_sorted = sorted( + time_period_start, key=lambda x: time_period_start[x] + ) # in upper case first_period = time_periods_sorted[0] periods_except_last = time_periods_sorted[:-1] breakpoints = [time_period_start[tp] for tp in time_periods_sorted] - it_full['time_period'] = pd.cut(it_full.stop_period, breakpoints, right = False, labels = periods_except_last).cat.add_categories(time_periods_sorted[-1]).fillna(time_periods_sorted[-1]).astype(str) - jt_full['time_period'] = pd.cut(jt_full.stop_period, breakpoints, right = False, labels = periods_except_last).cat.add_categories(time_periods_sorted[-1]).fillna(time_periods_sorted[-1]).astype(str) - it_full['eq_cnt'] = 1/it_full.sampleRate - it_full['eq_cnt'] = np.where(it_full['trip_mode'].isin([3,4,5]), 0.5 * it_full['eq_cnt'], np.where(it_full['trip_mode'].isin([6,7,8]), 0.35 * it_full['eq_cnt'], it_full['eq_cnt'])) - jt_full['eq_cnt'] = jt_full.num_participants/jt_full.sampleRate + it_full["time_period"] = ( + pd.cut( + it_full.stop_period, + breakpoints, + right=False, + labels=periods_except_last, + ) + .cat.add_categories(time_periods_sorted[-1]) + .fillna(time_periods_sorted[-1]) + .astype(str) + ) + jt_full["time_period"] = ( + pd.cut( + jt_full.stop_period, + breakpoints, + right=False, + labels=periods_except_last, + ) + .cat.add_categories(time_periods_sorted[-1]) + .fillna(time_periods_sorted[-1]) + .astype(str) + ) + it_full["eq_cnt"] = 1 / it_full.sampleRate + it_full["eq_cnt"] = np.where( + it_full["trip_mode"].isin([3, 4, 5]), + 0.5 * it_full["eq_cnt"], + np.where( + it_full["trip_mode"].isin([6, 7, 8]), + 0.35 * it_full["eq_cnt"], + it_full["eq_cnt"], + ), + ) + jt_full["eq_cnt"] = jt_full.num_participants / jt_full.sampleRate zp_cav = self.controller.config.household.OwnedAV_ZPV_factor zp_tnc = self.controller.config.household.TNC_ZPV_factor - maz_taz_df = pd.read_csv(self.controller.get_abs_path(self.controller.config.scenario.landuse_file), usecols = ["MAZ", "TAZ"]) - it_full = it_full.merge(maz_taz_df, left_on = 'orig_mgra', right_on= 'MAZ', how = 'left').rename(columns={'TAZ': 'orig_taz'}) - it_full = it_full.merge(maz_taz_df, left_on = 'dest_mgra', right_on= 'MAZ', how = 'left').rename(columns={'TAZ': 'dest_taz'}) - jt_full = jt_full.merge(maz_taz_df, left_on = 'orig_mgra', right_on= 'MAZ', how = 'left').rename(columns={'TAZ': 'orig_taz'}) - jt_full = jt_full.merge(maz_taz_df, left_on = 'dest_mgra', right_on= 'MAZ', how = 'left').rename(columns={'TAZ': 'dest_taz'}) - it_full['trip_mode'] = np.where(it_full['trip_mode'] == 14, 13, it_full['trip_mode']) - jt_full['trip_mode'] = np.where(jt_full['trip_mode'] == 14, 13, jt_full['trip_mode']) + maz_taz_df = pd.read_csv( + self.controller.get_abs_path(self.controller.config.scenario.landuse_file), + usecols=["MAZ", "TAZ"], + ) + it_full = it_full.merge( + maz_taz_df, left_on="orig_mgra", right_on="MAZ", how="left" + ).rename(columns={"TAZ": "orig_taz"}) + it_full = it_full.merge( + maz_taz_df, left_on="dest_mgra", right_on="MAZ", how="left" + ).rename(columns={"TAZ": "dest_taz"}) + jt_full = jt_full.merge( + maz_taz_df, left_on="orig_mgra", right_on="MAZ", how="left" + ).rename(columns={"TAZ": "orig_taz"}) + jt_full = jt_full.merge( + maz_taz_df, left_on="dest_mgra", right_on="MAZ", how="left" + ).rename(columns={"TAZ": "dest_taz"}) + it_full["trip_mode"] = np.where( + it_full["trip_mode"] == 14, 13, it_full["trip_mode"] + ) + jt_full["trip_mode"] = np.where( + jt_full["trip_mode"] == 14, 13, jt_full["trip_mode"] + ) num_zones = self.num_internal_zones - OD_full_index = pd.MultiIndex.from_product([range(1,num_zones + 1), range(1,num_zones + 1)]) - + OD_full_index = pd.MultiIndex.from_product( + [range(1, num_zones + 1), range(1, num_zones + 1)] + ) + def combine_trip_lists(it, jt, trip_mode): # combines individual trip list and joint trip list - combined_trips = pd.concat([it[(it['trip_mode'] == trip_mode)], jt[(jt['trip_mode'] == trip_mode)]]) - combined_sum = combined_trips.groupby(['orig_taz','dest_taz'])['eq_cnt'].sum() + combined_trips = pd.concat( + [it[(it["trip_mode"] == trip_mode)], jt[(jt["trip_mode"] == trip_mode)]] + ) + combined_sum = combined_trips.groupby(["orig_taz", "dest_taz"])[ + "eq_cnt" + ].sum() return combined_sum.reindex(OD_full_index, fill_value=0).unstack().values - - def create_zero_passenger_trips(trips, deadheading_factor, trip_modes=[1,2,3]): - zpv_trips = trips.loc[(trips['avAvailable']==1) & (trips['trip_mode'].isin(trip_modes))] - zpv_trips['eq_cnt'] = zpv_trips['eq_cnt'] * deadheading_factor - zpv_trips = zpv_trips.rename(columns={'dest_taz': 'orig_taz', - 'orig_taz': 'dest_taz'}) + + def create_zero_passenger_trips( + trips, deadheading_factor, trip_modes=[1, 2, 3] + ): + zpv_trips = trips.loc[ + (trips["avAvailable"] == 1) & (trips["trip_mode"].isin(trip_modes)) + ] + zpv_trips["eq_cnt"] = zpv_trips["eq_cnt"] * deadheading_factor + zpv_trips = zpv_trips.rename( + columns={"dest_taz": "orig_taz", "orig_taz": "dest_taz"} + ) return zpv_trips # create zero passenger trips for auto modes - if it_full['avAvailable'].sum()>0: - it_zpav_trp = create_zero_passenger_trips(it_full, zp_cav, trip_modes=[1,2,3]) + if it_full["avAvailable"].sum() > 0: + it_zpav_trp = create_zero_passenger_trips( + it_full, zp_cav, trip_modes=[1, 2, 3] + ) it_zptnc_trp = create_zero_passenger_trips(it_full, zp_tnc, trip_modes=[9]) # Combining zero passenger trips to trip files - it_full = pd.concat([it_full, it_zpav_trp, it_zptnc_trp], ignore_index=True).reset_index(drop=True) - - if jt_full['avAvailable'].sum()>0: - jt_zpav_trp = create_zero_passenger_trips(jt_full, zp_cav, trip_modes=[1,2,3]) + it_full = pd.concat( + [it_full, it_zpav_trp, it_zptnc_trp], ignore_index=True + ).reset_index(drop=True) + + if jt_full["avAvailable"].sum() > 0: + jt_zpav_trp = create_zero_passenger_trips( + jt_full, zp_cav, trip_modes=[1, 2, 3] + ) jt_zptnc_trp = create_zero_passenger_trips(jt_full, zp_tnc, trip_modes=[9]) # Combining zero passenger trips to trip files - jt_full = pd.concat([jt_full, jt_zpav_trp, jt_zptnc_trp], ignore_index=True).reset_index(drop=True) + jt_full = pd.concat( + [jt_full, jt_zpav_trp, jt_zptnc_trp], ignore_index=True + ).reset_index(drop=True) # read properties from config - + mode_name_dict = self.controller.config.household.ctramp_mode_names income_segment_config = self.controller.config.household.income_segment - - if income_segment_config['enabled']: + + if income_segment_config["enabled"]: # This only affects highway trip tables. - - hh_file = self.controller.config.household.ctramp_hh_file.format(iteration = iteration) - hh = pd.read_csv(hh_file, usecols = ['hh_id', 'income']) - it_full = it_full.merge(hh, on = 'hh_id', how = 'left') - jt_full = jt_full.merge(hh, on = 'hh_id', how = 'left') - - suffixes = income_segment_config['segment_suffixes'] - - it_full['income_seg'] = pd.cut(it_full['income'], right =False, - bins = income_segment_config['cutoffs'] + [float('inf')], - labels = suffixes).astype(str) - - jt_full['income_seg'] = pd.cut(jt_full['income'], right =False, - bins = income_segment_config['cutoffs'] + [float('inf')], - labels = suffixes).astype(str) - else: - it_full['income_seg'] = '' - jt_full['income_seg'] = '' - suffixes = [''] - + + hh_file = self.controller.config.household.ctramp_hh_file.format( + iteration=iteration + ) + hh = pd.read_csv(hh_file, usecols=["hh_id", "income"]) + it_full = it_full.merge(hh, on="hh_id", how="left") + jt_full = jt_full.merge(hh, on="hh_id", how="left") + + suffixes = income_segment_config["segment_suffixes"] + + it_full["income_seg"] = pd.cut( + it_full["income"], + right=False, + bins=income_segment_config["cutoffs"] + [float("inf")], + labels=suffixes, + ).astype(str) + + jt_full["income_seg"] = pd.cut( + jt_full["income"], + right=False, + bins=income_segment_config["cutoffs"] + [float("inf")], + labels=suffixes, + ).astype(str) + else: + it_full["income_seg"] = "" + jt_full["income_seg"] = "" + suffixes = [""] + # groupby objects for combinations of time period - income segmentation, used for highway modes only - it_grp = it_full.groupby(['time_period', 'income_seg']) - jt_grp = jt_full.groupby(['time_period', 'income_seg']) - + it_grp = it_full.groupby(["time_period", "income_seg"]) + jt_grp = jt_full.groupby(["time_period", "income_seg"]) + for time_period in time_periods_sorted: - self.logger.debug(f"Producing household demand matrices for period {time_period}") - + self.logger.debug( + f"Producing household demand matrices for period {time_period}" + ) + highway_out_file = OMXManager( - self.controller.get_abs_path(self.controller.config.household.highway_demand_file).__str__().format(period=time_period, iter=self.controller.iteration), 'w') + self.controller.get_abs_path( + self.controller.config.household.highway_demand_file + ) + .__str__() + .format(period=time_period, iter=self.controller.iteration), + "w", + ) transit_out_file = OMXManager( - self.controller.get_abs_path(self.controller.config.household.transit_demand_file).__str__().format(period=time_period), 'w') - #active_out_file = OMXManager( + self.controller.get_abs_path( + self.controller.config.household.transit_demand_file + ) + .__str__() + .format(period=time_period), + "w", + ) + # active_out_file = OMXManager( # self.controller.get_abs_path(self.controller.config.household.active_demand_file).__str__().format(period=time_period), 'w') - - #hsr_trips_file = _omx.open_file( + + # hsr_trips_file = _omx.open_file( # self.controller.get_abs_path(self.controller.config.household.hsr_demand_file).format(year=self.controller.config.scenario.year, period=time_period)) - - #interregional_trips_file = _omx.open_file( + + # interregional_trips_file = _omx.open_file( # self.controller.get_abs_path(self.controller.config.household.interregional_demand_file).format(year=self.controller.config.scenario.year, period=time_period)) highway_out_file.open() transit_out_file.open() - #active_out_file.open() - - + # active_out_file.open() + # Transit and active modes: one matrix per time period per mode it = it_full[it_full.time_period == time_period] jt = jt_full[jt_full.time_period == time_period] - + for trip_mode in mode_name_dict: -# if trip_mode in [9,10]: -# matrix_name = mode_name_dict[trip_mode] -# self.logger.debug(f"Writing out mode {mode_name_dict[trip_mode]}") -# active_out_file.write_array(numpy_array=combine_trip_lists(it,jt, trip_mode), name = matrix_name) - + # if trip_mode in [9,10]: + # matrix_name = mode_name_dict[trip_mode] + # self.logger.debug(f"Writing out mode {mode_name_dict[trip_mode]}") + # active_out_file.write_array(numpy_array=combine_trip_lists(it,jt, trip_mode), name = matrix_name) + if trip_mode == 11: matrix_name = "WLK_TRN_WLK" self.logger.debug(f"Writing out mode WLK_TRN_WLK") - #other_trn_trips = np.array(hsr_trips_file[matrix_name])+np.array(interregional_trips_file[matrix_name]) - transit_out_file.write_array(numpy_array=(combine_trip_lists(it,jt, trip_mode)), name = matrix_name) - - elif trip_mode in [12,13]: + # other_trn_trips = np.array(hsr_trips_file[matrix_name])+np.array(interregional_trips_file[matrix_name]) + transit_out_file.write_array( + numpy_array=(combine_trip_lists(it, jt, trip_mode)), + name=matrix_name, + ) + + elif trip_mode in [12, 13]: it_outbound, it_inbound = it[it.inbound == 0], it[it.inbound == 1] jt_outbound, jt_inbound = jt[jt.inbound == 0], jt[jt.inbound == 1] - - matrix_name = f'{mode_name_dict[trip_mode].upper()}_TRN_WLK' - #other_trn_trips = np.array(hsr_trips_file[matrix_name])+np.array(interregional_trips_file[matrix_name]) - self.logger.debug(f"Writing out mode {mode_name_dict[trip_mode].upper() + '_TRN_WLK'}") + + matrix_name = f"{mode_name_dict[trip_mode].upper()}_TRN_WLK" + # other_trn_trips = np.array(hsr_trips_file[matrix_name])+np.array(interregional_trips_file[matrix_name]) + self.logger.debug( + f"Writing out mode {mode_name_dict[trip_mode].upper() + '_TRN_WLK'}" + ) transit_out_file.write_array( - numpy_array=(combine_trip_lists(it_outbound,jt_outbound, trip_mode)), - name = matrix_name) - - matrix_name = f'WLK_TRN_{mode_name_dict[trip_mode].upper()}' - #other_trn_trips = np.array(hsr_trips_file[matrix_name])+np.array(interregional_trips_file[matrix_name]) - self.logger.debug(f"Writing out mode {'WLK_TRN_' + mode_name_dict[trip_mode].upper()}") + numpy_array=( + combine_trip_lists(it_outbound, jt_outbound, trip_mode) + ), + name=matrix_name, + ) + + matrix_name = f"WLK_TRN_{mode_name_dict[trip_mode].upper()}" + # other_trn_trips = np.array(hsr_trips_file[matrix_name])+np.array(interregional_trips_file[matrix_name]) + self.logger.debug( + f"Writing out mode {'WLK_TRN_' + mode_name_dict[trip_mode].upper()}" + ) transit_out_file.write_array( - numpy_array=(combine_trip_lists(it_inbound,jt_inbound, trip_mode)), - name = matrix_name) - + numpy_array=( + combine_trip_lists(it_inbound, jt_inbound, trip_mode) + ), + name=matrix_name, + ) # Highway modes: one matrix per suffix (income class) per time period per mode for suffix in suffixes: highway_cache = {} - + if (time_period, suffix) in it_grp.groups.keys(): it = it_grp.get_group((time_period, suffix)) else: - it = pd.DataFrame(None, columns = it_full.columns) - + it = pd.DataFrame(None, columns=it_full.columns) + if (time_period, suffix) in jt_grp.groups.keys(): jt = jt_grp.get_group((time_period, suffix)) else: - jt = pd.DataFrame(None, columns = jt_full.columns) + jt = pd.DataFrame(None, columns=jt_full.columns) - for trip_mode in sorted(mode_name_dict): # Python preserves keys in the order they are inserted but # mode_name_dict originates from TOML, which does not guarantee # that the ordering of keys is preserved. See # https://github.com/toml-lang/toml/issues/162 - if trip_mode in [1,2,3,4,5,6,7,8,9,10,15,16,17]: # currently hard-coded based on Travel Mode trip mode codes - highway_cache[mode_name_dict[trip_mode]] = combine_trip_lists(it,jt, trip_mode) - out_mode = f'{mode_name_dict[trip_mode].upper()}' - matrix_name =f'{out_mode}_{suffix}_{time_period.upper()}' if suffix else f'{out_mode}_{time_period.upper()}' - highway_out_file.write_array(numpy_array = highway_cache[mode_name_dict[trip_mode]], name = matrix_name) + if trip_mode in [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 15, + 16, + 17, + ]: # currently hard-coded based on Travel Mode trip mode codes + highway_cache[mode_name_dict[trip_mode]] = combine_trip_lists( + it, jt, trip_mode + ) + out_mode = f"{mode_name_dict[trip_mode].upper()}" + matrix_name = ( + f"{out_mode}_{suffix}_{time_period.upper()}" + if suffix + else f"{out_mode}_{time_period.upper()}" + ) + highway_out_file.write_array( + numpy_array=highway_cache[mode_name_dict[trip_mode]], + name=matrix_name, + ) elif trip_mode in [15, 16]: # identify the correct mode split factors for da, sr2, sr3 - self.logger.debug(f"Splitting ridehail trips into shared ride trips") + self.logger.debug( + f"Splitting ridehail trips into shared ride trips" + ) ridehail_split_factors = defaultdict(float) splits = self.controller.config.household.rideshare_mode_split for key in splits: - out_mode_split = self.controller.config.household.__dict__[f'{key}_split'] + out_mode_split = self.controller.config.household.__dict__[ + f"{key}_split" + ] for out_mode in out_mode_split: - ridehail_split_factors[out_mode] += out_mode_split[out_mode] * splits[key] - - ridehail_trips = combine_trip_lists(it,jt, trip_mode) + ridehail_split_factors[out_mode] += ( + out_mode_split[out_mode] * splits[key] + ) + + ridehail_trips = combine_trip_lists(it, jt, trip_mode) for out_mode in ridehail_split_factors: - matrix_name =f'{out_mode}_{suffix}' if suffix else out_mode + matrix_name = f"{out_mode}_{suffix}" if suffix else out_mode self.logger.debug(f"Writing out mode {out_mode}") - highway_cache[out_mode] += (ridehail_trips * ridehail_split_factors[out_mode]).astype(float).round(2) - highway_out_file.write_array(numpy_array = highway_cache[out_mode], name = matrix_name) - + highway_cache[out_mode] += ( + (ridehail_trips * ridehail_split_factors[out_mode]) + .astype(float) + .round(2) + ) + highway_out_file.write_array( + numpy_array=highway_cache[out_mode], name=matrix_name + ) + highway_out_file.close() transit_out_file.close() - #active_out_file.close() + # active_out_file.close() @property def num_internal_zones(self): df = pd.read_csv( - self.controller.get_abs_path(self.controller.config.scenario.landuse_file), usecols = [self.controller.config.scenario.landuse_index_column]) - return len(df['TAZ'].unique()) - + self.controller.get_abs_path(self.controller.config.scenario.landuse_file), + usecols=[self.controller.config.scenario.landuse_index_column], + ) + return len(df["TAZ"].unique()) + @property def num_total_zones(self): - self._emmebank_path = self.controller.get_abs_path(self.controller.config.emme.highway_database_path) + self._emmebank_path = self.controller.get_abs_path( + self.controller.config.emme.highway_database_path + ) self._emmebank = self.controller.emme_manager.emmebank(self._emmebank_path) time_period = self.controller.config.time_periods[0].name - scenario = self.get_emme_scenario(self._emmebank.path, time_period) # any scenario id works + scenario = self.get_emme_scenario( + self._emmebank.path, time_period + ) # any scenario id works return len(scenario.zone_numbers) + class PrepareTransitDemand(EmmeDemand): """Import transit demand. @@ -521,9 +677,10 @@ def _prepare_demand( def _read_demand(self, file_config, time_period, skim_set, num_zones): # Load demand from cross-referenced source file, # the named demand model component under the key highway_demand_file - if (self.controller.config.run.warmstart.warmstart and - self.controller.iteration == 0 - ): + if ( + self.controller.config.run.warmstart.warmstart + and self.controller.iteration == 0 + ): source = self.controller.config.run.warmstart path = self.controller.get_abs_path( source.household_transit_demand_file @@ -536,8 +693,8 @@ def _read_demand(self, file_config, time_period, skim_set, num_zones): name = file_config["name"] return self._read( path.format( - period=time_period, - # set=skim_set, + period=time_period, + # set=skim_set, # iter=self.controller.iteration ), name, diff --git a/tm2py/components/network/create_tod_scenarios.py b/tm2py/components/network/create_tod_scenarios.py index 79a542f5..302527c0 100644 --- a/tm2py/components/network/create_tod_scenarios.py +++ b/tm2py/components/network/create_tod_scenarios.py @@ -222,7 +222,7 @@ def _create_transit_scenarios(self): "scenarios": 1 + n_time_periods, "regular_nodes": 650000, "links": 1900000, - "transit_vehicles": 600, # pnr vechiles + "transit_vehicles": 600, # pnr vechiles "transit_segments": 1800000, "extra_attribute_values": 200000000, } @@ -247,19 +247,15 @@ def _create_transit_scenarios(self): "@capclass", "@free_flow_speed", "@free_flow_time", - "@drive_toll" + "@drive_toll", ], "TRANSIT_LINE": [ "@invehicle_factor", "@iboard_penalty", "@xboard_penalty", - "@orig_hdw" + "@orig_hdw", ], - "NODE": [ - "@hdw_fraction", - "@wait_pfactor", - "@xboard_nodepen" - ] + "NODE": ["@hdw_fraction", "@wait_pfactor", "@xboard_nodepen"], } for domain, attrs in attributes.items(): for name in attrs: @@ -302,8 +298,8 @@ def _create_transit_scenarios(self): default_headway_fraction = self.controller.config.transit.get( "headway_fraction", 0.5 ) - default_transfer_wait_perception_factor = self.controller.config.transit.get( - "transfer_wait_perception_factor", 1 + default_transfer_wait_perception_factor = ( + self.controller.config.transit.get("transfer_wait_perception_factor", 1) ) walk_perception_factor = self.controller.config.transit.get( "walk_perception_factor", 2 @@ -330,11 +326,11 @@ def _create_transit_scenarios(self): f"mode {mode_data['id']} already exists with type {mode.type} instead of {mode_data['assign_type']}" ) mode.description = mode_data["name"] - if mode_data['assign_type'] == "AUX_TRANSIT": - if mode_data['type'] == "DRIVE": + if mode_data["assign_type"] == "AUX_TRANSIT": + if mode_data["type"] == "DRIVE": mode.speed = "ul1*%s" % drive_perception_factor else: - mode.speed = mode_data['speed_or_time_factor'] + mode.speed = mode_data["speed_or_time_factor"] # if mode_data["assign_type"] == "AUX_TRANSIT": # mode.speed = mode_data["speed_miles_per_hour"] # if mode_data["type"] == "WALK": @@ -348,15 +344,21 @@ def _create_transit_scenarios(self): # elif mode_data["type"] == "PREMIUM": # premium_modes.add(mode.id) in_vehicle_factors[mode.id] = mode_data.get( - "in_vehicle_perception_factor", default_in_vehicle_factor) + "in_vehicle_perception_factor", default_in_vehicle_factor + ) initial_boarding_penalty[mode.id] = mode_data.get( - "initial_boarding_penalty", default_initial_boarding_penalty) + "initial_boarding_penalty", default_initial_boarding_penalty + ) transfer_boarding_penalty[mode.id] = mode_data.get( - "transfer_boarding_penalty", default_transfer_boarding_penalty) + "transfer_boarding_penalty", default_transfer_boarding_penalty + ) headway_fraction[mode.id] = mode_data.get( - "headway_fraction", default_headway_fraction) + "headway_fraction", default_headway_fraction + ) transfer_wait_perception_factor[mode.id] = mode_data.get( - "transfer_wait_perception_factor", default_transfer_wait_perception_factor) + "transfer_wait_perception_factor", + default_transfer_wait_perception_factor, + ) # create vehicles # vehicle_table = self.controller.config.transit.vehicles @@ -382,17 +384,13 @@ def _create_transit_scenarios(self): "LRAIL": 30.0, "FERRY": 15.0, } - walk_speed = self.controller.config.transit.get( - "walk_speed", 3.0 - ) - transit_speed = self.controller.config.transit.get( - "transit_speed", 30.0 - ) + walk_speed = self.controller.config.transit.get("walk_speed", 3.0) + transit_speed = self.controller.config.transit.get("transit_speed", 30.0) for link in network.links(): speed = cntype_speed_map.get(link["#cntype"]) if speed is None: # speed = link["@free_flow_speed"] - speed = 30.0 # temp fix, will uncomment it when bring in highway changes + speed = 30.0 # temp fix, will uncomment it when bring in highway changes if link["@ft"] == 1 and speed > 0: link["@trantime"] = 60 * link.length / speed elif speed > 0: @@ -409,7 +407,9 @@ def _create_transit_scenarios(self): # link.length = 0.01 # 60.0 / 5280.0 for line in network.transit_lines(): # TODO: may want to set transit line speeds (not necessarily used in the assignment though) - line_veh = network.transit_vehicle(line["#vehtype"]) # use #vehtype here instead of #mode (#vehtype is vehtype_num in Lasso\mtc_data\lookups\transitSeatCap.csv) + line_veh = network.transit_vehicle( + line["#vehtype"] + ) # use #vehtype here instead of #mode (#vehtype is vehtype_num in Lasso\mtc_data\lookups\transitSeatCap.csv) if line_veh is None: raise Exception( f"line {line.id} requires vehicle ('#vehtype') {line['#vehtype']} which does not exist" @@ -421,7 +421,9 @@ def _create_transit_scenarios(self): # Set the perception factor from the mode table line["@invehicle_factor"] = in_vehicle_factors[line.vehicle.mode.id] line["@iboard_penalty"] = initial_boarding_penalty[line.vehicle.mode.id] - line["@xboard_penalty"] = transfer_boarding_penalty[line.vehicle.mode.id] + line["@xboard_penalty"] = transfer_boarding_penalty[ + line.vehicle.mode.id + ] # # set link modes to the minimum set # auto_mode = {self.controller.config.highway.generic_highway_mode_code} @@ -449,43 +451,64 @@ def _create_transit_scenarios(self): # link.modes = modes for link in network.links(): # set default values - link.i_node['@hdw_fraction'] = default_headway_fraction - link.i_node['@wait_pfactor'] = default_transfer_wait_perception_factor - link.i_node['@xboard_nodepen'] = 1 - link.j_node['@hdw_fraction'] = default_headway_fraction - link.j_node['@wait_pfactor'] = default_transfer_wait_perception_factor - link.j_node['@xboard_nodepen'] = 1 + link.i_node["@hdw_fraction"] = default_headway_fraction + link.i_node["@wait_pfactor"] = default_transfer_wait_perception_factor + link.i_node["@xboard_nodepen"] = 1 + link.j_node["@hdw_fraction"] = default_headway_fraction + link.j_node["@wait_pfactor"] = default_transfer_wait_perception_factor + link.j_node["@xboard_nodepen"] = 1 # update modes on connectors - if (link.i_node.is_centroid) and (link["@drive_link"]==0): + if (link.i_node.is_centroid) and (link["@drive_link"] == 0): link.modes = "a" - elif (link.j_node.is_centroid) and (link["@drive_link"]==0): + elif (link.j_node.is_centroid) and (link["@drive_link"] == 0): link.modes = "e" - elif (link.i_node.is_centroid or link.j_node.is_centroid ) and (link["@drive_link"]!=0): - link.modes = set([network.mode('c'), network.mode('D')]) + elif (link.i_node.is_centroid or link.j_node.is_centroid) and ( + link["@drive_link"] != 0 + ): + link.modes = set([network.mode("c"), network.mode("D")]) # calculate perceived walk time - # perceived walk time will be used in walk mode definition "ul2", + # perceived walk time will be used in walk mode definition "ul2", # link.data1 is used to save congested bus time, so use link.data2 here - if link["@area_type"] in [0,1]: - link.data2 = 60 * link.length / (walk_speed / walk_perception_factor_cbd) + if link["@area_type"] in [0, 1]: + link.data2 = ( + 60 * link.length / (walk_speed / walk_perception_factor_cbd) + ) else: - link.data2 = 60 * link.length / (walk_speed / walk_perception_factor) + link.data2 = ( + 60 * link.length / (walk_speed / walk_perception_factor) + ) # set headway fraction, transfer wait perception and transfer boarding penalty at specific nodes for line in network.transit_lines(): if line.vehicle.mode.id == "r": for seg in line.segments(): - seg.i_node['@hdw_fraction'] = headway_fraction[line.vehicle.mode.id] - seg.j_node['@hdw_fraction'] = headway_fraction[line.vehicle.mode.id] + seg.i_node["@hdw_fraction"] = headway_fraction[ + line.vehicle.mode.id + ] + seg.j_node["@hdw_fraction"] = headway_fraction[ + line.vehicle.mode.id + ] elif line.vehicle.mode.id == "f": for seg in line.segments(): - seg.i_node['@hdw_fraction'] = headway_fraction[line.vehicle.mode.id] - seg.i_node['@wait_pfactor'] = transfer_wait_perception_factor[line.vehicle.mode.id] - seg.j_node['@hdw_fraction'] = headway_fraction[line.vehicle.mode.id] - seg.j_node['@wait_pfactor'] = transfer_wait_perception_factor[line.vehicle.mode.id] - elif line.vehicle.mode.id =="h": + seg.i_node["@hdw_fraction"] = headway_fraction[ + line.vehicle.mode.id + ] + seg.i_node["@wait_pfactor"] = transfer_wait_perception_factor[ + line.vehicle.mode.id + ] + seg.j_node["@hdw_fraction"] = headway_fraction[ + line.vehicle.mode.id + ] + seg.j_node["@wait_pfactor"] = transfer_wait_perception_factor[ + line.vehicle.mode.id + ] + elif line.vehicle.mode.id == "h": for seg in line.segments(): - if seg.i_node['#node_id'] in self.controller.config.transit.timed_transfer_nodes: - seg.i_node['@xboard_nodepen'] = 0 + if ( + seg.i_node["#node_id"] + in self.controller.config.transit.timed_transfer_nodes + ): + seg.i_node["@xboard_nodepen"] = 0 ref_scenario.publish_network(network) diff --git a/tm2py/components/network/transit/transit_assign.py b/tm2py/components/network/transit/transit_assign.py index 836c5193..3e804f2f 100644 --- a/tm2py/components/network/transit/transit_assign.py +++ b/tm2py/components/network/transit/transit_assign.py @@ -121,7 +121,12 @@ def calc_segment_cost(transit_volume: float, capacity, segment) -> float: ) -def func_returns_segment_congestion(time_period_duration, scenario, weights: CongestedWeightsConfig, use_fares: bool = False): +def func_returns_segment_congestion( + time_period_duration, + scenario, + weights: CongestedWeightsConfig, + use_fares: bool = False, +): """ function that returns the calc_segment_cost function for emme assignment, with partial preloaded parameters acts like partial as emme does not take partial @@ -166,15 +171,18 @@ def calc_segment_cost(transit_volume: float, capacity, segment) -> float: congestion = 0.25 * ((transit_volume / capacity) ** 8) else: seated_capacity = ( - line.vehicle.seated_capacity * {time_period_duration} * 60 / line.headway + line.vehicle.seated_capacity + * {time_period_duration} + * 60 + / line.headway ) seated_pax = min(transit_volume, seated_capacity) standing_pax = max(transit_volume - seated_pax, 0) - seated_cost = {weights}.min_seat + ({weights}.max_seat - {weights}.min_seat) * ( - transit_volume / capacity - ) ** {weights}.power_seat + seated_cost = {weights}.min_seat + ( + {weights}.max_seat - {weights}.min_seat + ) * (transit_volume / capacity) ** {weights}.power_seat standing_cost = {weights}.min_stand + ( {weights}.max_stand - {weights}.min_stand @@ -189,7 +197,7 @@ def calc_segment_cost(transit_volume: float, capacity, segment) -> float: return congestion return textwrap.dedent(inspect.getsource(calc_segment_cost)).format( - time_period_duration=time_period_duration, weights=weights, use_fares = use_fares + time_period_duration=time_period_duration, weights=weights, use_fares=use_fares ) @@ -546,7 +554,7 @@ def run(self): for time_period in self.time_period_names: if self.controller.iteration == 0: use_ccr = False - congested_transit_assignment = False + congested_transit_assignment = False # update auto times print("update_auto_times") self.transit_network.update_auto_times(time_period) @@ -559,9 +567,11 @@ def run(self): print("warmstart") self.sub_components["prepare transit demand"].run() print("uncongested") - self.run_transit_assign(time_period, use_ccr, congested_transit_assignment) - #TODO: run skim - #TODO: trim_demand + self.run_transit_assign( + time_period, use_ccr, congested_transit_assignment + ) + # TODO: run skim + # TODO: trim_demand # create ccost attribute for skimming scenario = self.transit_emmebank.scenario(time_period) self._add_ccost_to_scenario(scenario) @@ -585,30 +595,33 @@ def run(self): # if self.config.congested.use_peaking_factor and (time_period.lower() == 'ea'): # self._apply_peaking_factor(time_period) else: - self.transit_emmebank.zero_matrix #TODO: need further test - - else: # iteration >=1 + self.transit_emmebank.zero_matrix # TODO: need further test + + else: # iteration >=1 use_ccr = self.config.use_ccr - if (time_period in ['EA','EV','MD']): + if time_period in ["EA", "EV", "MD"]: congested_transit_assignment = False else: - congested_transit_assignment = self.config.congested_transit_assignment + congested_transit_assignment = ( + self.config.congested_transit_assignment + ) # update auto times self.transit_network.update_auto_times(time_period) # import transit demands self.sub_components["prepare transit demand"].run() -# if (self.config.congested.trim_demand_before_congested_transit_assignment and -# congested_transit_assignment): -# use_ccr = False -# congested_transit_assignment = False -# self.run_transit_assign(time_period, use_ccr, congested_transit_assignment) -# #TODO: run skim -# #TODO: trim_demand - - self.run_transit_assign(time_period, use_ccr, congested_transit_assignment) - - + # if (self.config.congested.trim_demand_before_congested_transit_assignment and + # congested_transit_assignment): + # use_ccr = False + # congested_transit_assignment = False + # self.run_transit_assign(time_period, use_ccr, congested_transit_assignment) + # #TODO: run skim + # #TODO: trim_demand + + self.run_transit_assign( + time_period, use_ccr, congested_transit_assignment + ) + # output_summaries if self.config.output_stop_usage_path is not None: network, class_stop_attrs = self._calc_connector_flows(time_period) @@ -623,7 +636,9 @@ def run(self): self._export_transfer_at_stops(time_period) @LogStartEnd("Transit assignments for a time period") - def run_transit_assign(self, time_period: str, use_ccr: bool, congested_transit_assignment: bool): + def run_transit_assign( + self, time_period: str, use_ccr: bool, congested_transit_assignment: bool + ): if use_ccr: self._run_ccr_assign(time_period) @@ -642,68 +657,74 @@ def _apply_peaking_factor(self, time_period: str, ea_df=None): _network = _emme_scenario.get_network() _duration = self.time_period_durations[time_period.lower()] - if time_period.lower() == 'am': + if time_period.lower() == "am": for line in _network.transit_lines(): line["@orig_hdw"] = line.headway line_name = line.id line_veh = line.vehicle line_hdw = line.headway line_cap = 60 * _duration * line_veh.total_capacity / line_hdw - if line_name in ea_df['line_name_am'].to_list(): - ea_boardings = ea_df.loc[ea_df['line_name_am'] == line_name,'boardings'].values[0] + if line_name in ea_df["line_name_am"].to_list(): + ea_boardings = ea_df.loc[ + ea_df["line_name_am"] == line_name, "boardings" + ].values[0] else: ea_boardings = 0 - pnr_peaking_factor =(line_cap-ea_boardings)/line_cap #substract ea boardings from am parking capacity + pnr_peaking_factor = ( + line_cap - ea_boardings + ) / line_cap # substract ea boardings from am parking capacity non_pnr_peaking_factor = self.config.congested.am_peaking_factor # in Emme transit assignment, the capacity is computed for each transit line as: 60 * _duration * vehicle.total_capacity / line.headway # so instead of applying peaking factor to calculated capacity, we can divide line.headway by this peaking factor # if ea number of parkers exceed the am parking capacity, set the headway to a very large number - if pnr_peaking_factor>0: - pnr_line_hdw = line_hdw/pnr_peaking_factor + if pnr_peaking_factor > 0: + pnr_line_hdw = line_hdw / pnr_peaking_factor else: pnr_line_hdw = 999 - non_pnr_line_hdw = line_hdw*non_pnr_peaking_factor - if ('pnr' in line_name) and ('egr' in line_name): + non_pnr_line_hdw = line_hdw * non_pnr_peaking_factor + if ("pnr" in line_name) and ("egr" in line_name): continue - elif ('pnr' in line_name) and ('acc' in line_name): + elif ("pnr" in line_name) and ("acc" in line_name): line.headway = pnr_line_hdw else: line.headway = non_pnr_line_hdw - if time_period.lower() == 'pm': + if time_period.lower() == "pm": for line in _network.transit_lines(): line["@orig_hdw"] = line.headway line_name = line.id line_hdw = line.headway non_pnr_peaking_factor = self.config.congested.pm_peaking_factor - non_pnr_line_hdw = line_hdw*non_pnr_peaking_factor - if 'pnr' in line_name: + non_pnr_line_hdw = line_hdw * non_pnr_peaking_factor + if "pnr" in line_name: continue else: line.headway = non_pnr_line_hdw - if time_period.lower() == 'ea': - line_name=[] - boards=[] + if time_period.lower() == "ea": + line_name = [] + boards = [] ea_pnr_df = pd.DataFrame() for line in _network.transit_lines(): boardings = 0 for segment in line.segments(include_hidden=True): - boardings += segment.transit_boardings + boardings += segment.transit_boardings line_name.append(line.id) boards.append(boardings) - ea_pnr_df["line_name"] = line_name - ea_pnr_df["boardings"] = boards - ea_pnr_df["line_name_am"] = ea_pnr_df["line_name"].str.replace('EA','AM') #will substract ea boardings from am parking capacity + ea_pnr_df["line_name"] = line_name + ea_pnr_df["boardings"] = boards + ea_pnr_df["line_name_am"] = ea_pnr_df["line_name"].str.replace( + "EA", "AM" + ) # will substract ea boardings from am parking capacity path_boardings = self.get_abs_path( self.config.output_transit_boardings_path - ) - ea_pnr_df.to_csv(path_boardings.format(period='ea_pnr'), index=False) + ) + ea_pnr_df.to_csv(path_boardings.format(period="ea_pnr"), index=False) - _update_attributes = { - "TRANSIT_LINE": ["@orig_hdw", "headway"] - } - self.controller.emme_manager.copy_attribute_values(_network, _emme_scenario, _update_attributes) + _update_attributes = {"TRANSIT_LINE": ["@orig_hdw", "headway"]} + self.controller.emme_manager.copy_attribute_values( + _network, _emme_scenario, _update_attributes + ) def _transit_classes(self, time_period) -> List[TransitAssignmentClass]: emme_manager = self.controller.emme_manager @@ -858,7 +879,10 @@ def _run_congested_assign(self, time_period: str) -> None: _cost_func = { "type": "CUSTOM", "python_function": func_returns_segment_congestion( - _duration, _emme_scenario, _congested_weights, use_fares=self.config.use_fares + _duration, + _emme_scenario, + _congested_weights, + use_fares=self.config.use_fares, ), "congestion_attribute": "us3", "orig_func": False, @@ -866,7 +890,9 @@ def _run_congested_assign(self, time_period: str) -> None: } _stop_criteria = { - "max_iterations": self.congested_transit_assn_max_iteration[time_period.lower()], + "max_iterations": self.congested_transit_assn_max_iteration[ + time_period.lower() + ], "normalized_gap": self.config.congested.normalized_gap, "relative_gap": self.config.congested.relative_gap, } @@ -943,39 +969,55 @@ def _export_boardings_by_line(self, time_period: str) -> None: os.makedirs(os.path.dirname(output_transit_boardings_file), exist_ok=True) - with open(output_transit_boardings_file.format(period=time_period.lower()), "w", encoding="utf8" - ) as out_file: - out_file.write(",".join(["line_name", - "description", - "total_boarding", - 'total_hour_cap', - "tm2_mode", - "line_mode", - "headway", - "fare_system", - ])) + with open( + output_transit_boardings_file.format(period=time_period.lower()), + "w", + encoding="utf8", + ) as out_file: + out_file.write( + ",".join( + [ + "line_name", + "description", + "total_boarding", + "total_hour_cap", + "tm2_mode", + "line_mode", + "headway", + "fare_system", + ] + ) + ) out_file.write("\n") for line in network.transit_lines(): boardings = 0 capacity = line.vehicle.total_capacity hdw = line.headway - line_hour_cap = 60*capacity/hdw + line_hour_cap = 60 * capacity / hdw if self.config.use_fares: - mode = line['#src_mode'] + mode = line["#src_mode"] else: mode = line.mode for segment in line.segments(include_hidden=True): - boardings += segment.transit_boardings + boardings += segment.transit_boardings # total_board = sum(seg.transit_boardings for seg in line.segments) - out_file.write(",".join([str(x) for x in [line.id, - line['#description'], - boardings, - line_hour_cap, - line['#mode'], - mode, - line.headway, - line['#faresystem'], - ]])) + out_file.write( + ",".join( + [ + str(x) + for x in [ + line.id, + line["#description"], + boardings, + line_hour_cap, + line["#mode"], + mode, + line.headway, + line["#faresystem"], + ] + ] + ) + ) out_file.write("\n") def _calc_connector_flows( @@ -1000,7 +1042,7 @@ def _calc_connector_flows( ) tclass_stop_attrs = {} for tclass in self.config.classes: - attr_name = f"@aux_vol_{tclass.name}".lower() # maximum length 20 limit + attr_name = f"@aux_vol_{tclass.name}".lower() # maximum length 20 limit create_extra("LINK", attr_name, overwrite=True, scenario=_emme_scenario) spec = { "type": "EXTENDED_TRANSIT_NETWORK_RESULTS", @@ -1068,103 +1110,134 @@ def _export_transit_segment(self, time_period: str): initial_board_attr_name = f"@iboard_{tclass.name}".lower() direct_xboard_attr_name = f"@dboard_{tclass.name}".lower() auxiliary_xboard_attr_name = f"@aboard_{tclass.name}".lower() - create_extra("TRANSIT_SEGMENT", initial_board_attr_name, overwrite=True, scenario=_emme_scenario) - create_extra("TRANSIT_SEGMENT", direct_xboard_attr_name, overwrite=True, scenario=_emme_scenario) - create_extra("TRANSIT_SEGMENT", auxiliary_xboard_attr_name, overwrite=True, scenario=_emme_scenario) + create_extra( + "TRANSIT_SEGMENT", + initial_board_attr_name, + overwrite=True, + scenario=_emme_scenario, + ) + create_extra( + "TRANSIT_SEGMENT", + direct_xboard_attr_name, + overwrite=True, + scenario=_emme_scenario, + ) + create_extra( + "TRANSIT_SEGMENT", + auxiliary_xboard_attr_name, + overwrite=True, + scenario=_emme_scenario, + ) spec = { "type": "EXTENDED_TRANSIT_NETWORK_RESULTS", - "on_segments": {"initial_boardings": initial_board_attr_name, - "transfer_boardings_direct": direct_xboard_attr_name, - "transfer_boardings_indirect": auxiliary_xboard_attr_name}, + "on_segments": { + "initial_boardings": initial_board_attr_name, + "transfer_boardings_direct": direct_xboard_attr_name, + "transfer_boardings_indirect": auxiliary_xboard_attr_name, + }, } network_results(spec, class_name=tclass.name, scenario=_emme_scenario) network = _emme_scenario.get_network() path_boardings = self.get_abs_path(self.config.output_transit_segment_path) with open(path_boardings.format(period=time_period.lower()), "w") as f: - f.write(",".join(["line", - "stop_name", - "i_node", - "j_node", - "dwt", - "ttf", - "voltr", - "board", - "con_time", - "uncon_time", - "mode", - "src_mode", - "mdesc", - "hdw", - "orig_hdw", - "speed", - "vauteq", - "vcaps", - "vcapt", - "initial_board_ptw", - "initial_board_wtp", - "initial_board_ktw", - "initial_board_wtk", - "initial_board_wtw", - "direct_transfer_board_ptw", - "direct_transfer_board_wtp", - "direct_transfer_board_ktw", - "direct_transfer_board_wtk", - "direct_transfer_board_wtw", - "auxiliary_transfer_board_ptw", - "auxiliary_transfer_board_wtp", - "auxiliary_transfer_board_ktw", - "auxiliary_transfer_board_wtk", - "auxiliary_transfer_board_wtw" - ])) + f.write( + ",".join( + [ + "line", + "stop_name", + "i_node", + "j_node", + "dwt", + "ttf", + "voltr", + "board", + "con_time", + "uncon_time", + "mode", + "src_mode", + "mdesc", + "hdw", + "orig_hdw", + "speed", + "vauteq", + "vcaps", + "vcapt", + "initial_board_ptw", + "initial_board_wtp", + "initial_board_ktw", + "initial_board_wtk", + "initial_board_wtw", + "direct_transfer_board_ptw", + "direct_transfer_board_wtp", + "direct_transfer_board_ktw", + "direct_transfer_board_wtk", + "direct_transfer_board_wtw", + "auxiliary_transfer_board_ptw", + "auxiliary_transfer_board_wtp", + "auxiliary_transfer_board_ktw", + "auxiliary_transfer_board_wtk", + "auxiliary_transfer_board_wtw", + ] + ) + ) f.write("\n") for line in network.transit_lines(): for segment in line.segments(include_hidden=True): if self.config.use_fares: - mode = segment.line['#src_mode'] + mode = segment.line["#src_mode"] else: mode = segment.line.mode - if self.config.congested.use_peaking_factor and (time_period.lower() in ['am','pm']): - orig_headway = segment.line['@orig_hdw'] + if self.config.congested.use_peaking_factor and ( + time_period.lower() in ["am", "pm"] + ): + orig_headway = segment.line["@orig_hdw"] else: orig_headway = segment.line.headway - f.write(",".join([str(x) for x in [ - segment.id, - '"{0}"'.format(segment['#stop_name']), - segment.i_node, - segment.j_node, - segment.dwell_time, - segment.transit_time_func, - segment.transit_volume, - segment.transit_boardings, - segment.transit_time, - segment['@trantime_seg'], - segment.line.mode, - mode, - segment.line.mode.description, - segment.line.headway, - orig_headway, - segment.line.speed, - segment.line.vehicle.auto_equivalent, - segment.line.vehicle.seated_capacity, - segment.line.vehicle.total_capacity, - segment['@iboard_pnr_trn_wlk'], - segment['@iboard_wlk_trn_pnr'], - segment['@iboard_knr_trn_wlk'], - segment['@iboard_wlk_trn_knr'], - segment['@iboard_wlk_trn_wlk'], - segment['@dboard_pnr_trn_wlk'], - segment['@dboard_wlk_trn_pnr'], - segment['@dboard_knr_trn_wlk'], - segment['@dboard_wlk_trn_knr'], - segment['@dboard_wlk_trn_wlk'], - segment['@aboard_pnr_trn_wlk'], - segment['@aboard_wlk_trn_pnr'], - segment['@aboard_knr_trn_wlk'], - segment['@aboard_wlk_trn_knr'], - segment['@aboard_wlk_trn_wlk'] - ]])) + f.write( + ",".join( + [ + str(x) + for x in [ + segment.id, + '"{0}"'.format(segment["#stop_name"]), + segment.i_node, + segment.j_node, + segment.dwell_time, + segment.transit_time_func, + segment.transit_volume, + segment.transit_boardings, + segment.transit_time, + segment["@trantime_seg"], + segment.line.mode, + mode, + segment.line.mode.description, + segment.line.headway, + orig_headway, + segment.line.speed, + segment.line.vehicle.auto_equivalent, + segment.line.vehicle.seated_capacity, + segment.line.vehicle.total_capacity, + segment["@iboard_pnr_trn_wlk"], + segment["@iboard_wlk_trn_pnr"], + segment["@iboard_knr_trn_wlk"], + segment["@iboard_wlk_trn_knr"], + segment["@iboard_wlk_trn_wlk"], + segment["@dboard_pnr_trn_wlk"], + segment["@dboard_wlk_trn_pnr"], + segment["@dboard_knr_trn_wlk"], + segment["@dboard_wlk_trn_knr"], + segment["@dboard_wlk_trn_wlk"], + segment["@aboard_pnr_trn_wlk"], + segment["@aboard_wlk_trn_pnr"], + segment["@aboard_knr_trn_wlk"], + segment["@aboard_wlk_trn_knr"], + segment["@aboard_wlk_trn_wlk"], + ] + ] + ) + ) f.write("\n") def _export_boardings_by_station(self, time_period: str): @@ -1172,12 +1245,13 @@ def _export_boardings_by_station(self, time_period: str): _emme_scenario = self.transit_emmebank.scenario(time_period) network = _emme_scenario.get_network() sta2sta = _emme_manager.tool( - "inro.emme.transit_assignment.extended.station_to_station_analysis") + "inro.emme.transit_assignment.extended.station_to_station_analysis" + ) sta2sta_spec = { "type": "EXTENDED_TRANSIT_STATION_TO_STATION_ANALYSIS", "transit_line_selections": { "first_boarding": "mode=h", - "last_alighting": "mode=h" + "last_alighting": "mode=h", }, "analyzed_demand": None, } @@ -1191,36 +1265,47 @@ def _export_boardings_by_station(self, time_period: str): fare_modes[line.mode.id].add(line.mode.id) operator_dict = { - # mode: network_selection - 'bart': "h", - 'caltrain': "r" + # mode: network_selection + "bart": "h", + "caltrain": "r", } - + for tclass in self.config.classes: for op, cut in operator_dict.items(): demand_matrix = "mfTRN_%s_%s" % (tclass.name, time_period) - output_file_name = self.get_abs_path(self.config.output_station_to_station_flow_path) + output_file_name = self.get_abs_path( + self.config.output_station_to_station_flow_path + ) - sta2sta_spec['transit_line_selections']['first_boarding'] = "mode="+",".join(list(fare_modes[cut])) - sta2sta_spec['transit_line_selections']['last_alighting'] = "mode="+",".join(list(fare_modes[cut])) - sta2sta_spec['analyzed_demand'] = demand_matrix + sta2sta_spec["transit_line_selections"][ + "first_boarding" + ] = "mode=" + ",".join(list(fare_modes[cut])) + sta2sta_spec["transit_line_selections"][ + "last_alighting" + ] = "mode=" + ",".join(list(fare_modes[cut])) + sta2sta_spec["analyzed_demand"] = demand_matrix - output_path = output_file_name.format(operator=op, tclass=tclass.name, period=time_period.lower()) - sta2sta(specification=sta2sta_spec, - output_file=output_path, - scenario=_emme_scenario, - append_to_output_file=False, - class_name=tclass.name) + output_path = output_file_name.format( + operator=op, tclass=tclass.name, period=time_period.lower() + ) + sta2sta( + specification=sta2sta_spec, + output_file=output_path, + scenario=_emme_scenario, + append_to_output_file=False, + class_name=tclass.name, + ) def _export_transfer_at_stops(self, time_period: str): _emme_manager = self.controller.emme_manager _emme_scenario = self.transit_emmebank.scenario(time_period) network = _emme_scenario.get_network() transfers_at_stops = _emme_manager.tool( - "inro.emme.transit_assignment.extended.apps.transfers_at_stops") + "inro.emme.transit_assignment.extended.apps.transfers_at_stops" + ) stop_location = self.config.output_transfer_at_station_node_ids - stop_location_val_key = {val:key for key, val in stop_location.items()} + stop_location_val_key = {val: key for key, val in stop_location.items()} for node in network.nodes(): if stop_location_val_key.get(node["#node_id"]): @@ -1229,14 +1314,20 @@ def _export_transfer_at_stops(self, time_period: str): for tclass in self.config.classes: for stop_name, stop_id in stop_location.items(): demand_matrix = "mfTRN_%s_%s" % (tclass.name, time_period) - output_file_name = self.get_abs_path(self.config.output_transfer_at_station_path) - output_path = output_file_name.format(tclass=tclass.name, stop=stop_name, period=time_period.lower()) - - transfers_at_stops(selection=f"i={stop_id}", - export_path=output_path, - scenario=_emme_scenario, - class_name=tclass.name, - analyzed_demand=demand_matrix) + output_file_name = self.get_abs_path( + self.config.output_transfer_at_station_path + ) + output_path = output_file_name.format( + tclass=tclass.name, stop=stop_name, period=time_period.lower() + ) + + transfers_at_stops( + selection=f"i={stop_id}", + export_path=output_path, + scenario=_emme_scenario, + class_name=tclass.name, + analyzed_demand=demand_matrix, + ) def _add_ccr_vars_to_scenario(self, emme_scenario: "EmmeScenario") -> None: """Add Extra Added Wait Time and Capacity Penalty to emme scenario. @@ -1262,7 +1353,6 @@ def _add_ccr_vars_to_scenario(self, emme_scenario: "EmmeScenario") -> None: scenario=emme_scenario, ) - def _add_ccost_to_scenario(self, emme_scenario: "EmmeScenario") -> None: """Add Extra Added Wait Time and Capacity Penalty to emme scenario. @@ -1280,7 +1370,6 @@ def _add_ccost_to_scenario(self, emme_scenario: "EmmeScenario") -> None: scenario=emme_scenario, ) - def _get_network_with_ccr_scenario_attributes(self, emme_scenario): self._add_ccr_vars_to_scenario(emme_scenario) @@ -1434,7 +1523,7 @@ def emme_transit_spec(self) -> EmmeTransitSpec: "in_vehicle_time": {"perception_factor": "@invehicle_factor"}, "aux_transit_time": { "perception_factor": 1 - }, # walk and drive perception factors are specified in mode definition "speed_or_time_factor" + }, # walk and drive perception factors are specified in mode definition "speed_or_time_factor" "aux_transit_cost": None, "journey_levels": self._journey_levels, "flow_distribution_between_lines": {"consider_total_impedance": False}, @@ -1533,457 +1622,662 @@ def _journey_levels(self) -> EmmeTransitJourneyLevelSpec: os.path.join(self._spec_dir, file_name), "r", encoding="utf8" ) as jl_spec: journey_levels = _json.load(jl_spec)["journey_levels"] - + if self.name == "PNR_TRN_WLK": new_journey_levels = copy.deepcopy(journey_levels) - for i in range(0,len(new_journey_levels)): + for i in range(0, len(new_journey_levels)): jls = new_journey_levels[i] for level in jls["transition_rules"]: - level["next_journey_level"] = level["next_journey_level"]+1 + level["next_journey_level"] = level["next_journey_level"] + 1 jls["transition_rules"].extend( [ - {'mode': 'e', 'next_journey_level': i+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': i+2}, - {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2} + {"mode": "e", "next_journey_level": i + 2}, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + {"mode": "w", "next_journey_level": i + 2}, + { + "mode": "p", + "next_journey_level": len(new_journey_levels) + 2, + }, ] ) # level 0: drive access - transition_rules_drive_access = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_drive_access = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_drive_access: - level["next_journey_level"] = len(new_journey_levels)+2 + level["next_journey_level"] = len(new_journey_levels) + 2 transition_rules_drive_access.extend( [ - {'mode': 'e', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': 0}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'p', 'next_journey_level': 1} + { + "mode": "e", + "next_journey_level": len(new_journey_levels) + 2, + }, + {"mode": "D", "next_journey_level": 0}, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + {"mode": "p", "next_journey_level": 1}, ] ) # level 1: use transit - transition_rules_pnr = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_pnr = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_pnr: level["next_journey_level"] = 2 transition_rules_pnr.extend( [ - {'mode': 'e', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'p', 'next_journey_level': 1} + { + "mode": "e", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + {"mode": "p", "next_journey_level": 1}, ] ) # level len(new_journey_levels)+2: every mode is prohibited - transition_rules_prohibit = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_prohibit = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_prohibit: - level["next_journey_level"] = len(new_journey_levels)+2 + level["next_journey_level"] = len(new_journey_levels) + 2 transition_rules_prohibit.extend( [ - {'mode': 'e', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2} + { + "mode": "e", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "p", + "next_journey_level": len(new_journey_levels) + 2, + }, ] ) new_journey_levels.insert( - 0, - { - "description": "drive access", - "destinations_reachable": False, - "transition_rules": transition_rules_drive_access, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + 0, + { + "description": "drive access", + "destinations_reachable": False, + "transition_rules": transition_rules_drive_access, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + }, ) new_journey_levels.insert( - 1, - { - "description": "pnr", - "destinations_reachable": False, - "transition_rules": transition_rules_pnr, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + 1, + { + "description": "pnr", + "destinations_reachable": False, + "transition_rules": transition_rules_pnr, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + }, ) new_journey_levels.append( - { - "description": "prohibit", - "destinations_reachable": False, - "transition_rules": transition_rules_prohibit, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + { + "description": "prohibit", + "destinations_reachable": False, + "transition_rules": transition_rules_prohibit, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + } ) for level in new_journey_levels[2:-1]: level["waiting_time"] = { "headway_fraction": "@hdw_fraction", "effective_headways": effective_headway_source, "spread_factor": 1, - "perception_factor": "@wait_pfactor" + "perception_factor": "@wait_pfactor", } level["boarding_time"] = { - "on_lines": { - "penalty": "@xboard_penalty", "perception_factor": 1}, - "at_nodes": { - "penalty": "@xboard_nodepen", "perception_factor": 1}, + "on_lines": { + "penalty": "@xboard_penalty", + "perception_factor": 1, + }, + "at_nodes": { + "penalty": "@xboard_nodepen", + "perception_factor": 1, + }, } # add in the correct value of time parameter for level in new_journey_levels: if level["boarding_cost"]: level["boarding_cost"]["on_segments"][ "perception_factor" - ] = fare_perception + ] = fare_perception elif self.name == "WLK_TRN_PNR": new_journey_levels = copy.deepcopy(journey_levels) - for i in range(0,len(new_journey_levels)): - jls = new_journey_levels[i] + for i in range(0, len(new_journey_levels)): + jls = new_journey_levels[i] jls["destinations_reachable"] = False jls["transition_rules"].extend( [ - {'mode': 'a', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': i+1}, - {'mode': 'p', 'next_journey_level': len(new_journey_levels)+1} + { + "mode": "a", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + {"mode": "w", "next_journey_level": i + 1}, + { + "mode": "p", + "next_journey_level": len(new_journey_levels) + 1, + }, ] ) # level 0: walk access - transition_rules_walk_access = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_walk_access = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_walk_access: level["next_journey_level"] = 1 transition_rules_walk_access.extend( [ - {'mode': 'a', 'next_journey_level': 0}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2} + {"mode": "a", "next_journey_level": 0}, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "p", + "next_journey_level": len(new_journey_levels) + 2, + }, ] - ) + ) # level len(new_journey_levels)+1: drive home - transition_rules_drive_home = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_drive_home = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_drive_home: - level["next_journey_level"] = len(new_journey_levels)+2 + level["next_journey_level"] = len(new_journey_levels) + 2 transition_rules_drive_home.extend( [ - {'mode': 'a', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+1}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2} + { + "mode": "a", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 1, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "p", + "next_journey_level": len(new_journey_levels) + 2, + }, ] ) # level len(new_journey_levels)+2: every mode is prohibited - transition_rules_prohibit = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_prohibit = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_prohibit: - level["next_journey_level"] = len(new_journey_levels)+2 + level["next_journey_level"] = len(new_journey_levels) + 2 transition_rules_prohibit.extend( [ - {'mode': 'a', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2} + { + "mode": "a", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "p", + "next_journey_level": len(new_journey_levels) + 2, + }, ] ) new_journey_levels.insert( - 0, - { - "description": "walk access", - "destinations_reachable": True, - "transition_rules": transition_rules_walk_access, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + 0, + { + "description": "walk access", + "destinations_reachable": True, + "transition_rules": transition_rules_walk_access, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + }, ) new_journey_levels.append( - { - "description": "drive home", - "destinations_reachable": True, - "transition_rules": transition_rules_drive_home, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + { + "description": "drive home", + "destinations_reachable": True, + "transition_rules": transition_rules_drive_home, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + } ) new_journey_levels.append( - { - "description": "prohibit", - "destinations_reachable": False, - "transition_rules": transition_rules_prohibit, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + { + "description": "prohibit", + "destinations_reachable": False, + "transition_rules": transition_rules_prohibit, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + } ) for level in new_journey_levels[1:-2]: level["waiting_time"] = { "headway_fraction": "@hdw_fraction", "effective_headways": effective_headway_source, "spread_factor": 1, - "perception_factor": "@wait_pfactor" + "perception_factor": "@wait_pfactor", } level["boarding_time"] = { - "on_lines": { - "penalty": "@xboard_penalty", "perception_factor": 1}, - "at_nodes": { - "penalty": "@xboard_nodepen", "perception_factor": 1}, + "on_lines": { + "penalty": "@xboard_penalty", + "perception_factor": 1, + }, + "at_nodes": { + "penalty": "@xboard_nodepen", + "perception_factor": 1, + }, } # add in the correct value of time parameter for level in new_journey_levels: if level["boarding_cost"]: - level["boarding_cost"]["on_segments"]["perception_factor"] = fare_perception + level["boarding_cost"]["on_segments"][ + "perception_factor" + ] = fare_perception elif self.name == "KNR_TRN_WLK": new_journey_levels = copy.deepcopy(journey_levels) - for i in range(0,len(new_journey_levels)): + for i in range(0, len(new_journey_levels)): jls = new_journey_levels[i] for level in jls["transition_rules"]: - level["next_journey_level"] = level["next_journey_level"]+1 + level["next_journey_level"] = level["next_journey_level"] + 1 jls["transition_rules"].extend( [ - {'mode': 'e', 'next_journey_level': i+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': i+2}, - ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'k', 'next_journey_level': len(new_journey_levels)+2} + {"mode": "e", "next_journey_level": i + 2}, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + {"mode": "w", "next_journey_level": i + 2}, + ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, + { + "mode": "k", + "next_journey_level": len(new_journey_levels) + 2, + }, ] - ) + ) # level 0: drive access - transition_rules_drive_access = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_drive_access = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_drive_access: - level["next_journey_level"] = len(new_journey_levels)+2 + level["next_journey_level"] = len(new_journey_levels) + 2 transition_rules_drive_access.extend( [ - {'mode': 'e', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': 0}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'k', 'next_journey_level': 1} + { + "mode": "e", + "next_journey_level": len(new_journey_levels) + 2, + }, + {"mode": "D", "next_journey_level": 0}, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, + {"mode": "k", "next_journey_level": 1}, ] ) # level 1: use transit - transition_rules_knr = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_knr = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_knr: level["next_journey_level"] = 2 transition_rules_knr.extend( [ - {'mode': 'e', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'k', 'next_journey_level': 1} + { + "mode": "e", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, + {"mode": "k", "next_journey_level": 1}, ] ) # level len(new_journey_levels)+2: every mode is prohibited - transition_rules_prohibit = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_prohibit = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_prohibit: - level["next_journey_level"] = len(new_journey_levels)+2 + level["next_journey_level"] = len(new_journey_levels) + 2 transition_rules_prohibit.extend( [ - {'mode': 'e', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'k', 'next_journey_level': len(new_journey_levels)+2} + { + "mode": "e", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, + { + "mode": "k", + "next_journey_level": len(new_journey_levels) + 2, + }, ] ) new_journey_levels.insert( - 0, - { - "description": "drive access", - "destinations_reachable": False, - "transition_rules": transition_rules_drive_access, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + 0, + { + "description": "drive access", + "destinations_reachable": False, + "transition_rules": transition_rules_drive_access, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + }, ) new_journey_levels.insert( - 1, - { - "description": "knr", - "destinations_reachable": False, - "transition_rules": transition_rules_knr, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + 1, + { + "description": "knr", + "destinations_reachable": False, + "transition_rules": transition_rules_knr, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + }, ) new_journey_levels.append( - { - "description": "prohibit", - "destinations_reachable": False, - "transition_rules": transition_rules_prohibit, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + { + "description": "prohibit", + "destinations_reachable": False, + "transition_rules": transition_rules_prohibit, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + } ) for level in new_journey_levels[2:-1]: level["waiting_time"] = { "headway_fraction": "@hdw_fraction", "effective_headways": effective_headway_source, "spread_factor": 1, - "perception_factor": "@wait_pfactor" + "perception_factor": "@wait_pfactor", } level["boarding_time"] = { - "on_lines": { - "penalty": "@xboard_penalty", "perception_factor": 1}, - "at_nodes": { - "penalty": "@xboard_nodepen", "perception_factor": 1}, + "on_lines": { + "penalty": "@xboard_penalty", + "perception_factor": 1, + }, + "at_nodes": { + "penalty": "@xboard_nodepen", + "perception_factor": 1, + }, } # add in the correct value of time parameter for level in new_journey_levels: if level["boarding_cost"]: - level["boarding_cost"]["on_segments"]["perception_factor"] = fare_perception + level["boarding_cost"]["on_segments"][ + "perception_factor" + ] = fare_perception elif self.name == "WLK_TRN_KNR": new_journey_levels = copy.deepcopy(journey_levels) - for i in range(0,len(new_journey_levels)): - jls = new_journey_levels[i] + for i in range(0, len(new_journey_levels)): + jls = new_journey_levels[i] jls["destinations_reachable"] = False jls["transition_rules"].extend( [ - {'mode': 'a', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': i+1}, - ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'k', 'next_journey_level': len(new_journey_levels)+1} + { + "mode": "a", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + {"mode": "w", "next_journey_level": i + 1}, + ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, + { + "mode": "k", + "next_journey_level": len(new_journey_levels) + 1, + }, ] ) # level 0: walk access - transition_rules_walk_access = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_walk_access = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_walk_access: level["next_journey_level"] = 1 transition_rules_walk_access.extend( [ - {'mode': 'a', 'next_journey_level': 0}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'k', 'next_journey_level': len(new_journey_levels)+2} + {"mode": "a", "next_journey_level": 0}, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, + { + "mode": "k", + "next_journey_level": len(new_journey_levels) + 2, + }, ] - ) + ) # level len(new_journey_levels)+1: drive home - transition_rules_drive_home = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_drive_home = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_drive_home: - level["next_journey_level"] = len(new_journey_levels)+2 + level["next_journey_level"] = len(new_journey_levels) + 2 transition_rules_drive_home.extend( [ - {'mode': 'a', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+1}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'k', 'next_journey_level': len(new_journey_levels)+2} + { + "mode": "a", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 1, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, + { + "mode": "k", + "next_journey_level": len(new_journey_levels) + 2, + }, ] ) # level len(new_journey_levels)+2: every mode is prohibited - transition_rules_prohibit = copy.deepcopy(journey_levels[0]["transition_rules"]) + transition_rules_prohibit = copy.deepcopy( + journey_levels[0]["transition_rules"] + ) for level in transition_rules_prohibit: - level["next_journey_level"] = len(new_journey_levels)+2 + level["next_journey_level"] = len(new_journey_levels) + 2 transition_rules_prohibit.extend( [ - {'mode': 'a', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'D', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'w', 'next_journey_level': len(new_journey_levels)+2}, - ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, - {'mode': 'k', 'next_journey_level': len(new_journey_levels)+2} + { + "mode": "a", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "D", + "next_journey_level": len(new_journey_levels) + 2, + }, + { + "mode": "w", + "next_journey_level": len(new_journey_levels) + 2, + }, + ## {'mode': 'p', 'next_journey_level': len(new_journey_levels)+2}, + { + "mode": "k", + "next_journey_level": len(new_journey_levels) + 2, + }, ] ) new_journey_levels.insert( - 0, - { - "description": "walk access", - "destinations_reachable": True, - "transition_rules": transition_rules_walk_access, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + 0, + { + "description": "walk access", + "destinations_reachable": True, + "transition_rules": transition_rules_walk_access, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + }, ) new_journey_levels.append( - { - "description": "drive home", - "destinations_reachable": True, - "transition_rules": transition_rules_drive_home, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + { + "description": "drive home", + "destinations_reachable": True, + "transition_rules": transition_rules_drive_home, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + } ) new_journey_levels.append( - { - "description": "prohibit", - "destinations_reachable": False, - "transition_rules": transition_rules_prohibit, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + { + "description": "prohibit", + "destinations_reachable": False, + "transition_rules": transition_rules_prohibit, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + } ) for level in new_journey_levels[1:-2]: level["waiting_time"] = { "headway_fraction": "@hdw_fraction", "effective_headways": effective_headway_source, "spread_factor": 1, - "perception_factor": "@wait_pfactor" + "perception_factor": "@wait_pfactor", } level["boarding_time"] = { - "on_lines": { - "penalty": "@xboard_penalty", "perception_factor": 1}, - "at_nodes": { - "penalty": "@xboard_nodepen", "perception_factor": 1}, + "on_lines": { + "penalty": "@xboard_penalty", + "perception_factor": 1, + }, + "at_nodes": { + "penalty": "@xboard_nodepen", + "perception_factor": 1, + }, } # add in the correct value of time parameter for level in new_journey_levels: if level["boarding_cost"]: - level["boarding_cost"]["on_segments"]["perception_factor"] = fare_perception + level["boarding_cost"]["on_segments"][ + "perception_factor" + ] = fare_perception elif self.name == "WLK_TRN_WLK": new_journey_levels = copy.deepcopy(journey_levels) transition_rules = copy.deepcopy(journey_levels[0]["transition_rules"]) new_journey_levels.insert( - 0, - { - "description": "base", - "destinations_reachable": True, - "transition_rules": transition_rules, - "waiting_time": None, - "boarding_time": None, - "boarding_cost": None - } + 0, + { + "description": "base", + "destinations_reachable": True, + "transition_rules": transition_rules, + "waiting_time": None, + "boarding_time": None, + "boarding_cost": None, + }, ) for level in new_journey_levels[1:]: level["waiting_time"] = { "headway_fraction": "@hdw_fraction", "effective_headways": effective_headway_source, "spread_factor": 1, - "perception_factor": "@wait_pfactor" + "perception_factor": "@wait_pfactor", } level["boarding_time"] = { - "on_lines": { - "penalty": "@xboard_penalty", "perception_factor": 1}, - "at_nodes": { - "penalty": "@xboard_nodepen", "perception_factor": 1}, + "on_lines": { + "penalty": "@xboard_penalty", + "perception_factor": 1, + }, + "at_nodes": { + "penalty": "@xboard_nodepen", + "perception_factor": 1, + }, } # add in the correct value of time parameter for level in new_journey_levels: if level["boarding_cost"]: - level["boarding_cost"]["on_segments"]["perception_factor"] = fare_perception + level["boarding_cost"]["on_segments"][ + "perception_factor" + ] = fare_perception with open( - os.path.join( - self._spec_dir, - "%s_%s_journey_levels.ems" % (self._time_period, self.name) - ), - "w", + os.path.join( + self._spec_dir, + "%s_%s_journey_levels.ems" % (self._time_period, self.name), + ), + "w", ) as jl_spec_file: - spec = {"type": "EXTENDED_TRANSIT_ASSIGNMENT", "journey_levels": new_journey_levels} + spec = { + "type": "EXTENDED_TRANSIT_ASSIGNMENT", + "journey_levels": new_journey_levels, + } _json.dump(spec, jl_spec_file, indent=4) else: @@ -2011,10 +2305,8 @@ def _journey_levels(self) -> EmmeTransitJourneyLevelSpec: ] for level in new_journey_levels[1:]: level["boarding_time"] = { - "on_lines": { - "penalty": "@xboard_penalty", "perception_factor": 1}, - "at_nodes": { - "penalty": "@xboard_nodepen", "perception_factor": 1}, - } + "on_lines": {"penalty": "@xboard_penalty", "perception_factor": 1}, + "at_nodes": {"penalty": "@xboard_nodepen", "perception_factor": 1}, + } return new_journey_levels diff --git a/tm2py/components/network/transit/transit_network.py b/tm2py/components/network/transit/transit_network.py index 8422957c..a516241f 100644 --- a/tm2py/components/network/transit/transit_network.py +++ b/tm2py/components/network/transit/transit_network.py @@ -93,7 +93,7 @@ def run(self): scenario.publish_network(network) for time_period in self.time_period_names: - # self.update_auto_times(time_period) # run in transit_assign component + # self.update_auto_times(time_period) # run in transit_assign component self._update_pnr_penalty(time_period) if self.config.override_connector_times: self._update_connector_times(time_period) @@ -173,44 +173,52 @@ def update_auto_times(self, time_period: str): auto_time = _highway_link_dict[_link_id].auto_time area_type = _highway_link_dict[_link_id]["@area_type"] # use @valuetoll_dam (cents/mile) here to represent the drive alone toll - #sov_toll_per_mile = _highway_link_dict[_link_id]['@valuetoll_dam'] + # sov_toll_per_mile = _highway_link_dict[_link_id]['@valuetoll_dam'] link_length = _transit_link_dict[_link_id].length - facility_type = _transit_link_dict[_link_id]['@ft'] - #sov_toll = sov_toll_per_mile * link_length/100 + facility_type = _transit_link_dict[_link_id]["@ft"] + # sov_toll = sov_toll_per_mile * link_length/100 # using the @valuetoll_da to get drive alone toll - sov_toll = _highway_link_dict[_link_id]['@valuetoll_da'] + sov_toll = _highway_link_dict[_link_id]["@valuetoll_da"] + + _transit_link_dict[_link_id]["@drive_toll"] = sov_toll - _transit_link_dict[_link_id]["@drive_toll"] = sov_toll - if auto_time > 0: # https://github.com/BayAreaMetro/travel-model-one/blob/master/model-files/scripts/skims/PrepHwyNet.job#L106 - tran_speed = 60 * link_length/auto_time - if (facility_type<=4 or facility_type==8) and (tran_speed<6): + tran_speed = 60 * link_length / auto_time + if (facility_type <= 4 or facility_type == 8) and (tran_speed < 6): tran_speed = 6 - _transit_link_dict[_link_id]["@trantime"] = 60 * link_length/tran_speed - elif (tran_speed<3): + _transit_link_dict[_link_id]["@trantime"] = ( + 60 * link_length / tran_speed + ) + elif tran_speed < 3: tran_speed = 3 - _transit_link_dict[_link_id]["@trantime"] = 60 * link_length/tran_speed + _transit_link_dict[_link_id]["@trantime"] = ( + 60 * link_length / tran_speed + ) else: _transit_link_dict[_link_id]["@trantime"] = auto_time # data1 is the auto time used in Mixed-Mode transit assigment - _transit_link_dict[_link_id].data1 = (_transit_link_dict[_link_id]["@trantime"] + - 60*sov_toll/self.config.value_of_time) + _transit_link_dict[_link_id].data1 = ( + _transit_link_dict[_link_id]["@trantime"] + + 60 * sov_toll / self.config.value_of_time + ) # bus time calculation - if facility_type in [1,2,3,8]: + if facility_type in [1, 2, 3, 8]: delayfactor = 0.0 else: - if area_type in [0,1]: + if area_type in [0, 1]: delayfactor = 2.46 - elif area_type in [2,3]: + elif area_type in [2, 3]: delayfactor = 1.74 - elif area_type==4: + elif area_type == 4: delayfactor = 1.14 else: delayfactor = 0.08 - bus_time = _transit_link_dict[_link_id]["@trantime"] + (delayfactor * link_length) - _transit_link_dict[_link_id]["@trantime"] = bus_time + bus_time = _transit_link_dict[_link_id]["@trantime"] + ( + delayfactor * link_length + ) + _transit_link_dict[_link_id]["@trantime"] = bus_time # TODO document this! Consider copying to another method. # set us1 (segment data1), used in ttf expressions, from @trantime @@ -244,15 +252,13 @@ def _update_pnr_penalty(self, time_period: str): for segment in _transit_net.transit_segments(): if "BART_acc" in segment.id: if "West Oakland" in segment.id: - segment["@board_cost"] = 12.4 * deflator + segment["@board_cost"] = 12.4 * deflator else: segment["@board_cost"] = 3.0 * deflator elif "Caltrain_acc" in segment.id: segment["@board_cost"] = 5.5 * deflator - _update_attributes = { - "TRANSIT_SEGMENT": ["@board_cost"] - } + _update_attributes = {"TRANSIT_SEGMENT": ["@board_cost"]} self.emme_manager.copy_attribute_values( _transit_net, _transit_scenario, _update_attributes ) @@ -396,19 +402,16 @@ def _get_highway_links( ["LINK"], include_attributes=False ) - highway_attributes = {"LINK": ["#link_id", - "auto_time", - "@lanes", - "@area_type", - "@valuetoll_da"]} - + highway_attributes = { + "LINK": ["#link_id", "auto_time", "@lanes", "@area_type", "@valuetoll_da"] + } + self.emme_manager.copy_attribute_values( _highway_scenario, _highway_net, highway_attributes ) # TODO can we just get the link attributes as a DataFrame and merge them? auto_link_dict = { - auto_link["#link_id"]: auto_link - for auto_link in _highway_net.links() + auto_link["#link_id"]: auto_link for auto_link in _highway_net.links() } return auto_link_dict @@ -996,7 +999,7 @@ def generate_fromto_approx(self, network, lines, fare_matrix, fs_data): for segment in line.segments(): segment["@invehicle_cost"] = max(segment.link.invehicle_cost, 0) segment["@board_cost"] = max(segment.link.board_cost, 0) - + network.delete_attribute("LINK", "invehicle_cost") network.delete_attribute("LINK", "board_cost") @@ -1380,11 +1383,13 @@ def matching_xfer_fares(xfer_fares_list1, xfer_fares_list2): if fare1 != fare2 and ( fare1 != "TOO_FAR" and fare2 != "TOO_FAR" ): - # if the difference between two fares are less than a number, + # if the difference between two fares are less than a number, # then treat them as the same fare - if isinstance(fare1, float) and isinstance(fare2, float) and ( - abs(fare1 - fare2)<=2.0 - ): + if ( + isinstance(fare1, float) + and isinstance(fare2, float) + and (abs(fare1 - fare2) <= 2.0) + ): continue else: return False @@ -1443,9 +1448,9 @@ def matching_xfer_fares(xfer_fares_list1, xfer_fares_list2): # fare = to_fares[0] if len(to_fares) > 0 else 0.0 if len(to_fares) == 0: fare = 0.0 - elif all(isinstance(item, float) for item in to_fares): + elif all(isinstance(item, float) for item in to_fares): # caculate the average here becasue of the edits in matching_xfer_fares function - fare = round(sum(to_fares)/len(to_fares),2) + fare = round(sum(to_fares) / len(to_fares), 2) else: fare = to_fares[0] xfer_fares[fs_id] = fare @@ -1487,8 +1492,10 @@ def generate_transfer_fares(self, faresystems, faresystem_groups, network): ) transit_modes = set([m for m in network.modes() if m.type == "TRANSIT"]) - #remove PNR dummy route from transit modes - transit_modes -= set([m for m in network.modes() if m.description == "pnrdummy"]) + # remove PNR dummy route from transit modes + transit_modes -= set( + [m for m in network.modes() if m.description == "pnrdummy"] + ) mode_desc = {m.id: m.description for m in transit_modes} get_mode_id = network.available_mode_identifier get_vehicle_id = network.available_transit_vehicle_identifier @@ -1500,15 +1507,19 @@ def generate_transfer_fares(self, faresystems, faresystem_groups, network): link.modes |= set([meta_mode]) lines = _defaultdict(lambda: []) for line in network.transit_lines(): - if line.mode.id != "p": #remove PNR dummy mode + if line.mode.id != "p": # remove PNR dummy mode lines[line.vehicle.id].append(line) line["#src_mode"] = line.mode.id line["#src_veh"] = line.vehicle.id for vehicle in network.transit_vehicles(): - if vehicle.mode.id != "p": #remove PNR dummy mode - temp_veh = network.create_transit_vehicle(get_vehicle_id(), vehicle.mode.id) + if vehicle.mode.id != "p": # remove PNR dummy mode + temp_veh = network.create_transit_vehicle( + get_vehicle_id(), vehicle.mode.id + ) veh_id = vehicle.id - attributes = {a: vehicle[a] for a in network.attributes("TRANSIT_VEHICLE")} + attributes = { + a: vehicle[a] for a in network.attributes("TRANSIT_VEHICLE") + } for line in lines[veh_id]: line.vehicle = temp_veh network.delete_transit_vehicle(vehicle) diff --git a/tm2py/components/network/transit/transit_skim.py b/tm2py/components/network/transit/transit_skim.py index a2c6a017..958a0b87 100644 --- a/tm2py/components/network/transit/transit_skim.py +++ b/tm2py/components/network/transit/transit_skim.py @@ -134,7 +134,7 @@ def skim_properties(self): ("WEGR", "egress walk time"), ("IVT", "total in-vehicle time"), ("IN_VEHICLE_COST", "in-vehicle cost"), - ("CROWD", "Crowding penalty") + ("CROWD", "Crowding penalty"), ] self._skim_properties += [ Skimproperty(_name, _desc) for _name, _desc in _basic_skims @@ -213,7 +213,7 @@ def emmebank_skim_matrices( ) else: _matrix.description = _desc - #_name = f"{_skprop.name}" + # _name = f"{_skprop.name}" self._skim_matrices[_name] = _matrix _tp_tclass_skprop_list.append(_name) @@ -248,14 +248,14 @@ def run_skim_set(self, time_period: str, transit_class: str): self.skim_walk_wait_boards_fares(time_period, transit_class) with self.controller.emme_manager.logbook_trace("In-vehicle time by mode"): self.skim_invehicle_time_by_mode(time_period, transit_class, use_ccr) - #with self.controller.emme_manager.logbook_trace( + # with self.controller.emme_manager.logbook_trace( # "Transfer boarding time penalty", # "Drive toll" - #): + # ): # self.skim_penalty_toll(time_period, transit_class) with self.controller.emme_manager.logbook_trace( "Drive distance and time", - "Walk auxiliary time, walk access time and walk egress time" + "Walk auxiliary time, walk access time and walk egress time", ): self.skim_drive_walk(time_period, transit_class) with self.controller.emme_manager.logbook_trace("Calculate crowding"): @@ -263,7 +263,7 @@ def run_skim_set(self, time_period: str, transit_class: str): if use_ccr: with self.controller.emme_manager.logbook_trace("CCR related skims"): self.skim_reliability_crowding_capacity(time_period, transit_class) - #if congested_transit_assignment: + # if congested_transit_assignment: # self.mask_above_max_transfers(time_period, transit_class) #TODO: need to test # self.mask_if_not_required_modes(time_period, transit_class) #TODO: need to test @@ -357,7 +357,7 @@ def _calc_boardings(self, time_period: str, transit_class_name: str): TODO convert this type of calculation to numpy """ _tp_tclass = f"{time_period}_{transit_class_name}" - if ("PNR_TRN_WLK" in _tp_tclass) or ("WLK_TRN_PNR"in _tp_tclass): + if ("PNR_TRN_WLK" in _tp_tclass) or ("WLK_TRN_PNR" in _tp_tclass): spec = { "type": "MATRIX_CALCULATION", "constraint": { @@ -535,160 +535,160 @@ def _calc_total_ivt( num_processors=self.controller.num_processors, ) - def skim_drive_walk( - self, time_period: str, transit_class: str - ) -> None: - """ - """ - _tp_tclass = f"{time_period}_{transit_class.name}" - # _network = self.networks[time_period] - - # drive time here is perception factor*(drive time + toll penalty), - # will calculate the actual drive time and substract toll penalty in the following steps - spec1 = { - "type": "EXTENDED_TRANSIT_MATRIX_RESULTS", - "by_mode_subset": { - "modes": ["D"], - "actual_aux_transit_times": f'mf"{_tp_tclass}_DTIME"', - "distance": f'mf"{_tp_tclass}_DDIST"', - }, - } - # skim walk distance in walk time matrices first, - # will calculate the actual walk time and overwrite the distance in the following steps - spec2 = { - "type": "EXTENDED_TRANSIT_MATRIX_RESULTS", - "by_mode_subset": { - "modes": ["w"], - "distance": f'mf"{_tp_tclass}_WAUX"', - }, - } - spec3 = { - "type": "EXTENDED_TRANSIT_MATRIX_RESULTS", - "by_mode_subset": { - "modes": ["a"], - "distance": f'mf"{_tp_tclass}_WACC"', - }, - } - spec4 = { - "type": "EXTENDED_TRANSIT_MATRIX_RESULTS", - "by_mode_subset": { - "modes": ["e"], - "distance": f'mf"{_tp_tclass}_WEGR"', - }, - } + def skim_drive_walk(self, time_period: str, transit_class: str) -> None: + """""" + _tp_tclass = f"{time_period}_{transit_class.name}" + # _network = self.networks[time_period] - self.controller.emme_manager.matrix_results( - spec1, - class_name=transit_class.name, - scenario=self.scenarios[time_period], - num_processors=self.controller.num_processors, - ) - self.controller.emme_manager.matrix_results( - spec2, - class_name=transit_class.name, - scenario=self.scenarios[time_period], - num_processors=self.controller.num_processors, - ) - self.controller.emme_manager.matrix_results( - spec3, - class_name=transit_class.name, - scenario=self.scenarios[time_period], - num_processors=self.controller.num_processors, - ) - self.controller.emme_manager.matrix_results( - spec4, - class_name=transit_class.name, - scenario=self.scenarios[time_period], - num_processors=self.controller.num_processors, - ) + # drive time here is perception factor*(drive time + toll penalty), + # will calculate the actual drive time and substract toll penalty in the following steps + spec1 = { + "type": "EXTENDED_TRANSIT_MATRIX_RESULTS", + "by_mode_subset": { + "modes": ["D"], + "actual_aux_transit_times": f'mf"{_tp_tclass}_DTIME"', + "distance": f'mf"{_tp_tclass}_DDIST"', + }, + } + # skim walk distance in walk time matrices first, + # will calculate the actual walk time and overwrite the distance in the following steps + spec2 = { + "type": "EXTENDED_TRANSIT_MATRIX_RESULTS", + "by_mode_subset": { + "modes": ["w"], + "distance": f'mf"{_tp_tclass}_WAUX"', + }, + } + spec3 = { + "type": "EXTENDED_TRANSIT_MATRIX_RESULTS", + "by_mode_subset": { + "modes": ["a"], + "distance": f'mf"{_tp_tclass}_WACC"', + }, + } + spec4 = { + "type": "EXTENDED_TRANSIT_MATRIX_RESULTS", + "by_mode_subset": { + "modes": ["e"], + "distance": f'mf"{_tp_tclass}_WEGR"', + }, + } - drive_perception_factor = self.config.drive_perception_factor - walk_speed = self.config.walk_speed - vot = self.config.value_of_time - # divide drive time by mode specific perception factor to get the actual time - # for walk time, use walk distance/walk speed - # because the mode specific perception factors are hardcoded in the mode definition - spec_list = [ - { - "type": "MATRIX_CALCULATION", - "constraint": None, - "result": f'mf"{_tp_tclass}_DTIME"', - "expression": f'mf"{_tp_tclass}_DTIME"/{drive_perception_factor}', - }, - { - "type": "MATRIX_CALCULATION", - "constraint": None, - "result": f'mf"{_tp_tclass}_DTIME"', - "expression": f'mf"{_tp_tclass}_DTIME"', - }, - { - "type": "MATRIX_CALCULATION", - "constraint": None, - "result": f'mf"{_tp_tclass}_WAUX"', - "expression": f'mf"{_tp_tclass}_WAUX"/({walk_speed}/60)', - }, - { - "type": "MATRIX_CALCULATION", - "constraint": None, - "result": f'mf"{_tp_tclass}_WACC"', - "expression": f'mf"{_tp_tclass}_WACC"/({walk_speed}/60)', - }, - { - "type": "MATRIX_CALCULATION", - "constraint": None, - "result": f'mf"{_tp_tclass}_WEGR"', - "expression": f'mf"{_tp_tclass}_WEGR"/({walk_speed}/60)', + self.controller.emme_manager.matrix_results( + spec1, + class_name=transit_class.name, + scenario=self.scenarios[time_period], + num_processors=self.controller.num_processors, + ) + self.controller.emme_manager.matrix_results( + spec2, + class_name=transit_class.name, + scenario=self.scenarios[time_period], + num_processors=self.controller.num_processors, + ) + self.controller.emme_manager.matrix_results( + spec3, + class_name=transit_class.name, + scenario=self.scenarios[time_period], + num_processors=self.controller.num_processors, + ) + self.controller.emme_manager.matrix_results( + spec4, + class_name=transit_class.name, + scenario=self.scenarios[time_period], + num_processors=self.controller.num_processors, + ) + + drive_perception_factor = self.config.drive_perception_factor + walk_speed = self.config.walk_speed + vot = self.config.value_of_time + # divide drive time by mode specific perception factor to get the actual time + # for walk time, use walk distance/walk speed + # because the mode specific perception factors are hardcoded in the mode definition + spec_list = [ + { + "type": "MATRIX_CALCULATION", + "constraint": None, + "result": f'mf"{_tp_tclass}_DTIME"', + "expression": f'mf"{_tp_tclass}_DTIME"/{drive_perception_factor}', + }, + { + "type": "MATRIX_CALCULATION", + "constraint": None, + "result": f'mf"{_tp_tclass}_DTIME"', + "expression": f'mf"{_tp_tclass}_DTIME"', + }, + { + "type": "MATRIX_CALCULATION", + "constraint": None, + "result": f'mf"{_tp_tclass}_WAUX"', + "expression": f'mf"{_tp_tclass}_WAUX"/({walk_speed}/60)', + }, + { + "type": "MATRIX_CALCULATION", + "constraint": None, + "result": f'mf"{_tp_tclass}_WACC"', + "expression": f'mf"{_tp_tclass}_WACC"/({walk_speed}/60)', + }, + { + "type": "MATRIX_CALCULATION", + "constraint": None, + "result": f'mf"{_tp_tclass}_WEGR"', + "expression": f'mf"{_tp_tclass}_WEGR"/({walk_speed}/60)', + }, + ] + self.controller.emme_manager.matrix_calculator( + spec_list, + scenario=self.scenarios[time_period], + num_processors=self.controller.num_processors, + ) + + def skim_penalty_toll(self, time_period: str, transit_class: str) -> None: + """""" + # transfer boarding time penalty + self._run_strategy_analysis( + time_period, transit_class, {"boarding": "@xboard_nodepen"}, "XBOATIME" + ) + + _tp_tclass = f"{time_period}_{transit_class.name}" + if ("PNR_TRN_WLK" in _tp_tclass) or ("WLK_TRN_PNR" in _tp_tclass): + spec = { # subtract PNR boarding from total transfer boarding time penalty + "type": "MATRIX_CALCULATION", + "constraint": { + "by_value": { + "od_values": f'mf"{_tp_tclass}_XBOATIME"', + "interval_min": 0, + "interval_max": 9999999, + "condition": "INCLUDE", + } }, - ] + "result": f'mf"{_tp_tclass}_XBOATIME"', + "expression": f'(mf"{_tp_tclass}_XBOATIME" - 1).max.0', + } + self.controller.emme_manager.matrix_calculator( - spec_list, + spec, scenario=self.scenarios[time_period], num_processors=self.controller.num_processors, ) - def skim_penalty_toll( - self, time_period: str, transit_class: str - ) -> None: - """ - """ - # transfer boarding time penalty - self._run_strategy_analysis( - time_period, transit_class, {"boarding": "@xboard_nodepen"}, "XBOATIME" + # drive toll + if ("PNR_TRN_WLK" in _tp_tclass) or ("KNR_TRN_WLK" in _tp_tclass): + self._run_path_analysis( + time_period, + transit_class, + "ORIGIN_TO_INITIAL_BOARDING", + {"aux_transit": "@drive_toll"}, + "DTOLL", + ) + elif ("WLK_TRN_PNR" in _tp_tclass) or ("WLK_TRN_KNR" in _tp_tclass): + self._run_path_analysis( + time_period, + transit_class, + "FINAL_ALIGHTING_TO_DESTINATION", + {"aux_transit": "@drive_toll"}, + "DTOLL", ) - - _tp_tclass = f"{time_period}_{transit_class.name}" - if ("PNR_TRN_WLK" in _tp_tclass) or ("WLK_TRN_PNR"in _tp_tclass): - spec = { # subtract PNR boarding from total transfer boarding time penalty - "type": "MATRIX_CALCULATION", - "constraint": { - "by_value": { - "od_values": f'mf"{_tp_tclass}_XBOATIME"', - "interval_min": 0, - "interval_max": 9999999, - "condition": "INCLUDE", - } - }, - "result": f'mf"{_tp_tclass}_XBOATIME"', - "expression": f'(mf"{_tp_tclass}_XBOATIME" - 1).max.0', - } - - self.controller.emme_manager.matrix_calculator( - spec, - scenario=self.scenarios[time_period], - num_processors=self.controller.num_processors, - ) - - # drive toll - if ("PNR_TRN_WLK" in _tp_tclass) or ("KNR_TRN_WLK" in _tp_tclass): - self._run_path_analysis( - time_period, transit_class, "ORIGIN_TO_INITIAL_BOARDING", - {"aux_transit": "@drive_toll"}, "DTOLL" - ) - elif ("WLK_TRN_PNR" in _tp_tclass) or ("WLK_TRN_KNR" in _tp_tclass): - self._run_path_analysis( - time_period, transit_class, "FINAL_ALIGHTING_TO_DESTINATION", - {"aux_transit": "@drive_toll"}, "DTOLL" - ) def _get_emme_mode_ids( self, transit_class, time_period @@ -720,7 +720,9 @@ def _get_emme_mode_ids( valid_modes = [ mode for mode in self.config.modes - if mode.type in transit_class.mode_types and mode.assign_type == "TRANSIT" and mode.type != "PNR_dummy" + if mode.type in transit_class.mode_types + and mode.assign_type == "TRANSIT" + and mode.type != "PNR_dummy" ] if self.config.use_fares: # map to used modes in apply fares case @@ -769,11 +771,8 @@ def skim_reliability_crowding_capacity( time_period, transit_class, {"boarding": "@capacity_penalty"}, "CAPPEN" ) - def skim_crowding( - self, time_period: str, transit_class - ) -> None: - """ - """ + def skim_crowding(self, time_period: str, transit_class) -> None: + """""" # Crowding penalty self._run_strategy_analysis( time_period, transit_class, {"in_vehicle": "@ccost"}, "CROWD" @@ -821,12 +820,12 @@ def _run_strategy_analysis( ) def _run_path_analysis( - self, - time_period: str, - transit_class, - portion_of_path: str, - components: Dict[str, str], - matrix_name_suffix: str, + self, + time_period: str, + transit_class, + portion_of_path: str, + components: Dict[str, str], + matrix_name_suffix: str, ): """Runs path analysis in Emme and stores results in emmebank. @@ -843,19 +842,16 @@ def _run_path_analysis( ) spec = { - "portion_of_path": portion_of_path, - "trip_components": components, - "path_operator": "+", - "path_selection_threshold": { - "lower": -999999, - "upper": 999999 - }, - "path_to_od_aggregation": { - "operator": "average", - "aggregated_path_values": _matrix_name - }, - "analyzed_demand": None, - "constraint": None, + "portion_of_path": portion_of_path, + "trip_components": components, + "path_operator": "+", + "path_selection_threshold": {"lower": -999999, "upper": 999999}, + "path_to_od_aggregation": { + "operator": "average", + "aggregated_path_values": _matrix_name, + }, + "analyzed_demand": None, + "constraint": None, "type": "EXTENDED_TRANSIT_PATH_ANALYSIS", } path_analysis( @@ -936,23 +932,26 @@ def _mask_skim_set(self, time_period: str, transit_class, mask_array: NumpyArray def _export_skims(self, time_period: str, transit_class: str): """Export skims to OMX files by period.""" # NOTE: skims in separate file by period - output_skim_path = self.get_abs_path( - self.config.output_skim_path - ) + output_skim_path = self.get_abs_path(self.config.output_skim_path) omx_file_path = os.path.join( output_skim_path, - self.config.output_skim_filename_tmpl.format(time_period=time_period, tclass=transit_class.name)) + self.config.output_skim_filename_tmpl.format( + time_period=time_period, tclass=transit_class.name + ), + ) os.makedirs(os.path.dirname(omx_file_path), exist_ok=True) - - _matrices = self.emmebank_skim_matrices(time_periods=[time_period], transit_classes=[transit_class]) -# matrices = {} -# matrices_growth = {} # matrices need to be multiplied by 100 -# for skim in _matrices.keys(): -# if ("BOARDS" in skim) or ("XBOATIME" in skim): -# matrices[skim] = _matrices[skim] -# else: -# matrices_growth[skim] = _matrices[skim] + _matrices = self.emmebank_skim_matrices( + time_periods=[time_period], transit_classes=[transit_class] + ) + # matrices = {} + # matrices_growth = {} # matrices need to be multiplied by 100 + + # for skim in _matrices.keys(): + # if ("BOARDS" in skim) or ("XBOATIME" in skim): + # matrices[skim] = _matrices[skim] + # else: + # matrices_growth[skim] = _matrices[skim] with OMXManager( omx_file_path, @@ -960,19 +959,19 @@ def _export_skims(self, time_period: str, transit_class: str): self.scenarios[time_period], matrix_cache=self.matrix_cache[time_period], mask_max_value=1e7, - growth_factor=1 + growth_factor=1, ) as omx_file: omx_file.write_matrices(_matrices) -# with OMXManager( -# omx_file_path, -# "a", -# self.scenarios[time_period], -# matrix_cache=self.matrix_cache[time_period], -# mask_max_value=1e7, -# growth_factor=100 -# ) as omx_file: -# omx_file.write_matrices(matrices_growth) + # with OMXManager( + # omx_file_path, + # "a", + # self.scenarios[time_period], + # matrix_cache=self.matrix_cache[time_period], + # mask_max_value=1e7, + # growth_factor=100 + # ) as omx_file: + # omx_file.write_matrices(matrices_growth) def _log_debug_report(self, _time_period): num_zones = len(self.scenarios[_time_period].zone_numbers) diff --git a/tm2py/config.py b/tm2py/config.py index 3970c552..72ab3387 100644 --- a/tm2py/config.py +++ b/tm2py/config.py @@ -54,7 +54,7 @@ class ScenarioConfig(ConfigItem): maz_landuse_file: pathlib.Path zone_seq_file: pathlib.Path landuse_file: pathlib.Path - landuse_index_column: str + landuse_index_column: str name: str year: int = Field(ge=2005) verify: Optional[bool] = Field(default=False) @@ -79,6 +79,7 @@ class ScenarioConfig(ConfigItem): ] EmptyString = Literal[""] + @dataclass(frozen=True) class WarmStartConfig(ConfigItem): """Warm start parameters. @@ -102,6 +103,7 @@ class WarmStartConfig(ConfigItem): internal_external_highway_demand_file: Optional[str] = Field(default="") truck_highway_demand_file: Optional[str] = Field(default="") + @dataclass(frozen=True) class RunConfig(ConfigItem): """Model run parameters. @@ -227,7 +229,7 @@ class TimePeriodConfig(ConfigItem): capacites in the highway network emme_scenario_id: scenario ID to use for Emme per-period assignment (highway and transit) scenarios - congested_transit_assn_max_iteration: max iterations in congested + congested_transit_assn_max_iteration: max iterations in congested transit assignment stopping criteria """ @@ -320,13 +322,13 @@ class HouseholdConfig(ConfigItem): ctramp_indiv_trip_file: str ctramp_joint_trip_file: str ctramp_run_dir: pathlib.Path - rideshare_mode_split: Dict[str,float] - taxi_split: Dict[str,float] - single_tnc_split: Dict[str,float] - shared_tnc_split: Dict[str,float] - ctramp_mode_names: Dict[float,str] + rideshare_mode_split: Dict[str, float] + taxi_split: Dict[str, float] + single_tnc_split: Dict[str, float] + shared_tnc_split: Dict[str, float] + ctramp_mode_names: Dict[float, str] income_segment: Dict[str, Union[float, str, list]] - ctramp_hh_file : str + ctramp_hh_file: str @dataclass(frozen=True) @@ -1054,7 +1056,16 @@ def check_keywords(class_num, key, val, available): class TransitModeConfig(ConfigItem): """Transit mode definition (see also mode in the Emme API).""" - type: Literal["WALK", "ACCESS", "EGRESS", "LOCAL", "PREMIUM", "DRIVE", "PNR_dummy","KNR_dummy"] + type: Literal[ + "WALK", + "ACCESS", + "EGRESS", + "LOCAL", + "PREMIUM", + "DRIVE", + "PNR_dummy", + "KNR_dummy", + ] assign_type: Literal["TRANSIT", "AUX_TRANSIT"] mode_id: str = Field(min_length=1, max_length=1) name: str = Field(max_length=10) @@ -1202,7 +1213,7 @@ class TransitConfig(ConfigItem): apply_msa_demand: bool value_of_time: float walk_speed: float - transit_speed: float + transit_speed: float effective_headway_source: str initial_wait_perception_factor: float transfer_wait_perception_factor: float @@ -1238,7 +1249,10 @@ class TransitConfig(ConfigItem): split_connectors_to_prevent_walk: bool = False input_connector_access_times_path: Optional[str] = Field(default=None) input_connector_egress_times_path: Optional[str] = Field(default=None) - vehicles: Optional[TransitVehicleConfig] = Field(default_factory=TransitVehicleConfig) + vehicles: Optional[TransitVehicleConfig] = Field( + default_factory=TransitVehicleConfig + ) + @dataclass(frozen=True) class EmmeConfig(ConfigItem): diff --git a/tm2py/controller.py b/tm2py/controller.py index 1c29e7ac..c7d52faf 100644 --- a/tm2py/controller.py +++ b/tm2py/controller.py @@ -175,7 +175,10 @@ def time_period_durations(self) -> dict: @property def congested_transit_assn_max_iteration(self) -> dict: """Return mapping of time periods to max iteration in congested transit assignment.""" - return dict((p.name, p.congested_transit_assn_max_iteration) for p in self.config.time_periods) + return dict( + (p.name, p.congested_transit_assn_max_iteration) + for p in self.config.time_periods + ) @property def num_processors(self) -> int: diff --git a/tm2py/emme/matrix.py b/tm2py/emme/matrix.py index 829eddc7..660f0c40 100644 --- a/tm2py/emme/matrix.py +++ b/tm2py/emme/matrix.py @@ -136,7 +136,7 @@ def __init__( omx_key: str = "NAME", matrix_cache: MatrixCache = None, mask_max_value: float = None, - growth_factor: float = None + growth_factor: float = None, ): # pylint: disable=R0913 """The OMXManager constructor. @@ -266,7 +266,11 @@ def write_clipped_array( self.write_array(numpy_array, name, "float64", attrs) def write_array( - self, numpy_array: NumpyArray, name: str, data_type: str = "float64", attrs: Dict[str, str] = None + self, + numpy_array: NumpyArray, + name: str, + data_type: str = "float64", + attrs: Dict[str, str] = None, ): """Write array with name and optional attrs to OMX file. @@ -286,7 +290,7 @@ def write_array( if self._mask_max_value: numpy_array[numpy_array > self._mask_max_value] = 0 if self._growth_factor: - numpy_array = numpy_array * self._growth_factor + numpy_array = numpy_array * self._growth_factor numpy_array = numpy_array.astype(dtype=data_type, copy=False) self._omx_file.create_matrix( name, obj=numpy_array, chunkshape=chunkshape, attrs=attrs