From e0483bf4c4af45403bfacc2ea32edec731c94c46 Mon Sep 17 00:00:00 2001 From: tkoleary Date: Thu, 9 Oct 2025 13:36:04 -0500 Subject: [PATCH 1/5] Translates delete.initial.batchin.scenarios to python, and deletes the old macro. Reads batch_file.yaml instead of requiring 3-digit scenario year. Informs the user through print statements if a scenario does not exist (but otherwise agnostic to non-existent scenarios). --- .../delete.initial.batchin.scenarios | 47 ---------------- .../delete_initial_batchin_scenarios.py | 54 +++++++++++++++++++ 2 files changed, 54 insertions(+), 47 deletions(-) delete mode 100644 Database/useful_macros/delete.initial.batchin.scenarios create mode 100644 Database/useful_macros/delete_initial_batchin_scenarios.py diff --git a/Database/useful_macros/delete.initial.batchin.scenarios b/Database/useful_macros/delete.initial.batchin.scenarios deleted file mode 100644 index 07f53cd..0000000 --- a/Database/useful_macros/delete.initial.batchin.scenarios +++ /dev/null @@ -1,47 +0,0 @@ -~/ delete.initial.batchin.scenarios -~/ Craig Heither, 3/31/09 -~/ Deletes scenarios -~/ submit with 3-digit scenario number -~/ -~# Heither, modified 07-26-2016: delete build_turn.rpt -~# ------------------------------------------------------------- -~x=%0% -~+;~?!x=1;~$>error -~/ -~! if exist report\build_turn.rpt (del report\build_turn.rpt) -~/ -~/ DELETE SCENARIOS xxx0,xxx00-xxx08 IF THEY EXIST -~/ -~r1=%1% -~r1*10 -1.22 -2 -%r1% -~?e -~+;~/Scenario %r1% does not exist; ;q;~$>next1 -y -q -~:next1 -~/ -~r1*10 -~/ -~:loop -1.22 -2 -%r1% -~?e -~+;~/Scenario %r1% does not exist; ;q;~$>next2 -y -q -~:next2 -~r1+1 -~+;~?r1<%1%09;~$loop -~/ END LOOP -~/ -~/ -~$>end -~:error -~/ SUBMIT WITH PROJECT DIRECTORY NAME!!!!!!! -~/ -~:end -~/end of macro diff --git a/Database/useful_macros/delete_initial_batchin_scenarios.py b/Database/useful_macros/delete_initial_batchin_scenarios.py new file mode 100644 index 0000000..0073043 --- /dev/null +++ b/Database/useful_macros/delete_initial_batchin_scenarios.py @@ -0,0 +1,54 @@ +''' +delete_initial_batchin_scenarios.py + +author: Craig Heither, 3/31/09 + - deletes scenarios + - submit with 3-digit scenario number + - modified 7/26/2016: also delete build.turn.rpt +translated: Tim O'Leary, 9/26/2025 to Python 3 + - reads batch_file.yaml to retrieve 3-digit scenario number +''' + +import os +import sys +from pathlib import Path +import yaml + +proj_dir = Path(__file__).resolve().parents[2] +sys.path.append(str(proj_dir.joinpath('Scripts'))) +from tbmtools import project as tbm + +print('delete initial batchin scenarios (*0001-*0008)') +print('(and build_turn.rpt if it exists)') +print(' executing...') + +#connect to modeller +modeller = tbm.connect(proj_dir) +emmebank = modeller.emmebank + +#define tools +delete_scenario = modeller.tool('inro.emme.data.scenario.delete_scenario') + +#get scenario info from batch_file.yaml for scenario numbers to delete +db = proj_dir.joinpath('Database') +with open(os.path.join(db, 'batch_file.yaml')) as f: + lines_without_backslashes = ''.join([line.replace('\\','/') for line in f]) + config = yaml.safe_load(lines_without_backslashes) +scen_yr = config['scenario_code'] # e.g., '200' +scenarios_to_delete = [f'{scen_yr}0{i}' for i in range(1,9)] + [f'{scen_yr}0'] + +#delete scenarios +for scen in scenarios_to_delete: + if emmebank.scenario(scen): #returns None if DNE + delete_scenario(emmebank.scenario(scen)) + else: + print(f'scenario {scen} does not exist') + +#delete build_turn.rpt if it exists +rptfile = db.joinpath('report/build_turn.rpt') +if os.path.exists(rptfile): + os.remove(rptfile) +else: + print('build_turn.rpt does not exist') + +print('done') \ No newline at end of file From e90bd8efebfb4ae1114eaf2288e2f0509b5f9ec0 Mon Sep 17 00:00:00 2001 From: tkoleary Date: Thu, 9 Oct 2025 13:40:43 -0500 Subject: [PATCH 2/5] Translates delete_matrix_named and delete.matrices to a single python script. The "delete_matrix()" function can be imported by other python scripts, or the script can run interactively in a terminal. Takes one of the following as input, and deletes if exists: (a) a single matrix name (e.g., "mf14", or "mo10"); (b) a range of matrices of a single type, separated by a hyphen (e.g., "mf10-mf25", or "ms1-ms9"); (c) a list of individual matrices of any type (e.g., "mf10, mo5, ms3"). --- Database/useful_macros/delete.matrices | 58 ----------- Database/useful_macros/delete_matrices.py | 108 +++++++++++++++++++++ Database/useful_macros/delete_matrix_named | 12 --- 3 files changed, 108 insertions(+), 70 deletions(-) delete mode 100644 Database/useful_macros/delete.matrices create mode 100644 Database/useful_macros/delete_matrices.py delete mode 100644 Database/useful_macros/delete_matrix_named diff --git a/Database/useful_macros/delete.matrices b/Database/useful_macros/delete.matrices deleted file mode 100644 index ac46569..0000000 --- a/Database/useful_macros/delete.matrices +++ /dev/null @@ -1,58 +0,0 @@ -~/ -~/********************************************************************** -~/********************************************************************** -~/ -~/ Macro Delete.matx.mac %1% %2% -~/ where %1% = low end range of matrix number to delete -~/ %2% = high end range of matrix number to delete -~/ -~/********************************************************************** -~/ Macro deletes a range of full matrices. -~/ -~/********************************************************************** -~/ -~/ Written by DBE 11May2004 -~/ -~/********************************************************************** -~/ -~/ register x = matrix number to be deleted, incremented & checked. -~/ REMOVED LINE BETWEEM ~:ERR AND ~:END IT WAS CAUSING THE PROGRAM TO FAIL -~/ WHEN THE MATRICES DID NOT EXIST BOZIC 11-2-2017 -~/********************************************************************** -~/ -~x=%1% -~:startloop -3.12 -2 -mf%x% -~?e -~$>err -yes -~$>end -~:err - -~:end -q -~/ -~/********************************************************************** -~/ -~/ All requested matrices deleted? -~/ -~/********************************************************************** -~/ -~x+1 -~?x>%2% -~$>done -~$startloop -~:done -~/ -~/********************************************************************** -~/ -~/ End of macro -~/ -~/********************************************************************** -~/ -3.14 -2 -m -1 \ No newline at end of file diff --git a/Database/useful_macros/delete_matrices.py b/Database/useful_macros/delete_matrices.py new file mode 100644 index 0000000..8afd251 --- /dev/null +++ b/Database/useful_macros/delete_matrices.py @@ -0,0 +1,108 @@ +''' +`delete_matrices.py` +Author: Tim O'Leary +Date: 9/26/2025 + +Description: This script deletes specified matrices from the emme databank. It can +be called as a subscript or by itself. Contains delete_matrices() function that can take +a string or a list of strings as input. + +Translated from 'delete.matrices' Emme macro written by DBE 5/11/2004 +- Added functionality to call a single matrix, range, or list +- Runs interactively if called by itself, or function could be imported and called by another script +''' + +print('delete_matrices.py\nstarting up...') +import os +import sys +from pathlib import Path +import textwrap + +proj_dir = Path(__file__).resolve().parents[2] +sys.path.append(str(proj_dir.joinpath('Scripts'))) +from tbmtools import project as tbm + +#connect to modeller +modeller = tbm.connect(proj_dir) +emmebank = modeller.emmebank + +#define tools +delete_matrix = modeller.tool('inro.emme.data.matrix.delete_matrix') + +def delete_matrices(mats_to_delete): + ''' + Deletes specified matrices from the databank. + Input may be one of the following: + - a single matrix (e.g., 'mf2') + - a range of matrices separated by a hyphen (e.g., 'mf25-mf90') + - a list of numbers (e.g., 'mf25, mf30, mf45')) + ''' + + bad_input_msg = textwrap.dedent(f'''\ + Input not recognized. Please input IDs with prefix (e.g., "mf"), in one of the following formats: + \t- a single matrix ID (e.g., "mf54"); + \t- a range of IDs with hyphen between (e.g., "mf25-mf90"); or + \t- a list of IDs (e.g., [id1,id2,...]).''') + + #if passed function with no input + if mats_to_delete is None or mats_to_delete == '': + raise ValueError(bad_input_msg) + + #check for incorrectly formatted matrix IDs + if '-' in str(mats_to_delete): + input_type = 'range' + mats_check = str(mats_to_delete).split('-') + mats_check = [m.strip() for m in mats_check] + else: + input_type = 'list' + mats_check = str(mats_to_delete).replace('[','').replace(']','').split(',') + mats_check = [m.strip() for m in mats_check] + + #make sure prefixes and suffixes aren't invalid, and check if mixed matrix types + bad_prefix = [m for m in mats_check if m[:2] not in ['mf', 'mo', 'md', 'ms']] + mixed_types = [m[:2] for m in mats_check] + bad_suffix = [m for m in mats_check if not m[2:].isdigit()] + if len(bad_prefix + bad_suffix) > 0: + raise ValueError(bad_input_msg) + if len(set(mixed_types)) > 1: + raise ValueError('Sorry, cannot mix matrix types in one call for now. Please only do one matrix type at a time.') + + #handle range input + if input_type == 'range': + mtx_type = set(mixed_types)[0] + min = int(mats_check[0][2:]) + max = int(mats_check[1][2:]) + mats = [f'{mtx_type}{m}' for m in range(min, max+1)] + else: + mats = mats_check + + #delete matrices + for mat in mats: + if emmebank.matrix(mat): + delete_matrix(emmebank.matrix(mat)) + else: + print(f' - matrix {mat} not in dabatank, skipping...') + +print(''' + DELETE_MATRICES.PY + This script will delete specified matrices from the emme databank! + Input may be one of the following: + - a single matrix (e.g., 'mf2') + - a range of matrices, min and max separated by a hyphen (e.g., 'mf25-mf90') + - a list of matrix IDs, separated by commas (e.g., 'mf25, mf30, mf45')) + ''') + +while True: + try: + user_input = input('Enter matrices to delete (or "exit" to quit): ') + if user_input.lower() == 'exit': + break + delete_matrices(user_input) + print('\n - done! want to go again?\n') + except ValueError as e: + print(f'\n!! Error: {e}\n') + except Exception as e: + print(f'\n!! Unexpected error occurred: {e}\n') +print('bye-bye, then!') +input('press any key to exit') +sys.exit() diff --git a/Database/useful_macros/delete_matrix_named b/Database/useful_macros/delete_matrix_named deleted file mode 100644 index 4bbbfbf..0000000 --- a/Database/useful_macros/delete_matrix_named +++ /dev/null @@ -1,12 +0,0 @@ -3.12 -2 -%1% -~?e -~$>err1 -yes -~$>end1 -~:err1 - -r -~:end1 -q \ No newline at end of file From e890aacbe19202df81e13b362b897117092945c6 Mon Sep 17 00:00:00 2001 From: tkoleary Date: Thu, 9 Oct 2025 13:43:43 -0500 Subject: [PATCH 3/5] Translates delete.scenarios to python. delete_scenarios() function can be imported by other scripts or run independently in the terminal. Takes input and deletes scenario if it exists. One of three input types: (a) single scenario ID (e.g., "1" or "30022"); (b) a range of scenarios, separated by a hyphen (e.g., "1-10" or "30001-30028"); (c) a collection of individual scenarios, separated by commas (e.g., "1, 321, 30021") --- Database/useful_macros/delete.scenarios | 21 ---- Database/useful_macros/delete_scenarios.py | 107 +++++++++++++++++++++ 2 files changed, 107 insertions(+), 21 deletions(-) delete mode 100644 Database/useful_macros/delete.scenarios create mode 100644 Database/useful_macros/delete_scenarios.py diff --git a/Database/useful_macros/delete.scenarios b/Database/useful_macros/delete.scenarios deleted file mode 100644 index 7a4396a..0000000 --- a/Database/useful_macros/delete.scenarios +++ /dev/null @@ -1,21 +0,0 @@ -~/ macro for deleting a range of network scenarios -~/ -~/ r11 = counter -~/ Created by DBE 21May2004, modified kww 6/6 -~/ modified by cmh 3/09: continues running if scenario does not exist -~x=%1% -~:loop -~x+1 -~r11+1 -1.22 -2 -%x% -~?e -~+; ;q;~$>next -yes -q -~:next -~?!x=%2% -~$loop -~:done -s= \ No newline at end of file diff --git a/Database/useful_macros/delete_scenarios.py b/Database/useful_macros/delete_scenarios.py new file mode 100644 index 0000000..d30b5cf --- /dev/null +++ b/Database/useful_macros/delete_scenarios.py @@ -0,0 +1,107 @@ +''' +`delete_scenarios.py` +Author: Tim O'Leary +Date: 9/26/2025 + +Description: This script deletes specified scenarios from the emme databank. It can +be called as a subscript or by itself. Contains delete_scenarios() function that can take +a string or a list of strings as input. + +Translated to Python 3 from 'delete.scenarios' Emme macro +Written by DBE 5/2004 and modified to handle non-existent scenarios by CH 3/2009 +Added functionality with Python translation: +- Can call a single scenario, a range of scenarios, or list +- Runs interactively with user input if called by itself, or the function 'delete_scenarios()' can be imported and called by another script +''' + +print('delete_scenarios.py\nstarting up...') +import os +import sys +from pathlib import Path +import textwrap + +proj_dir = Path(__file__).resolve().parents[2] +sys.path.append(str(proj_dir.joinpath('Scripts'))) +from tbmtools import project as tbm + +#connect to modeller +modeller = tbm.connect(proj_dir) +emmebank = modeller.emmebank + +#define tools +delete_scenario = modeller.tool('inro.emme.data.scenario.delete_scenario') + +def delete_scenarios(scens_to_delete): + ''' + Deletes specified scenarios from the databank. + Input may be one of the following: + - a single scenario (e.g., '20003') + - a range of scenarios separated by a hyphen (e.g., '20021-20028') + - a list of scenarios (e.g., '221, 22003, 22004')) + ''' + + bad_input_msg = textwrap.dedent(f'''\ + Input not recognized. Please input scenario IDs as integers in one of the following formats: + \t- a single scenario ID (e.g., '20003'); + \t- a range of IDs with hyphen between (e.g., '20021-20028'); or + \t- a list of IDs (e.g., '221, 22003, 22004').''') + + #if passed function with no input + if scens_to_delete is None or scens_to_delete == '': + raise ValueError(bad_input_msg) + + #check for incorrectly formatted input + if '-' in str(scens_to_delete): + input_type = 'range' + scens_check = str(scens_to_delete).split('-') + scens_check = [m.strip() for m in scens_check] + else: + input_type = 'list' + scens_check = str(scens_to_delete).replace('[','').replace(']','').split(',') + scens_check = [m.strip() for m in scens_check] + + #make sure prefixes and suffixes aren't invalid, and check if mixed matrix types + not_a_number = [m for m in scens_check if not m.isnumeric()] + if not_a_number: + raise ValueError(bad_input_msg) + + #handle range input + if input_type == 'range': + min = int(scens_check[0]) + max = int(scens_check[1]) + if min >= max: + raise ValueError('Your min should be less than your max.') + scens = [m for m in range(min, max+1)] + else: + scens = scens_check + + #delete matrices + for scen in scens: + if emmebank.scenario(scen): + delete_scenario(emmebank.scenario(scen)) + else: + print(f' - scenario {scen} not in dabatank, skipping...') + +print(''' + DELETE_SCENARIOS.PY + This script will delete specified scenarios from the emme databank! + Input may be one of the following: + - a single scenario ID (e.g., '20027') + - a range of scenarios, min and max separated by a hyphen (e.g., '221-227') + - a list of scenario IDs, separated by commas (e.g., '5, 8, 20005, 20008')) + ''') + +while True: + try: + user_input = input('Enter scenarios to delete (or "exit" to quit): ') + if user_input.lower() == 'exit': + break + delete_scenarios(user_input) + print('\n - done! want to go again?\n') + except ValueError as e: + print(f'\n!! Error: {e}\n') + except Exception as e: + print(f'\n!! Unexpected error occurred: {e}\n') +print('bye-bye, then!') +input('press any key to exit') +sys.exit() From c59ab6515b0e9981183e944347da8691fd527e9b Mon Sep 17 00:00:00 2001 From: tkoleary Date: Thu, 9 Oct 2025 13:47:12 -0500 Subject: [PATCH 4/5] Translates summarize_transit_boardings.mac into python. Reads batch_file.yaml instead of requiring 3-digit scenario code as input. Along with "Boardings_summary.csv," which has daily boarding info by mode, it also creates an additional output csv in the report folder, "transit_segments_punch.csv," that is effectively a transit equivalent to "punchlink.csv," containing transit segment info for boardings, volumes, PMT, and PHT. --- .../summarize_transit_boardings.mac | 97 ---------- .../summarize_transit_boardings.py | 177 ++++++++++++++++++ 2 files changed, 177 insertions(+), 97 deletions(-) delete mode 100644 Database/transit_asmt_macros/summarize_transit_boardings.mac create mode 100644 Database/transit_asmt_macros/summarize_transit_boardings.py diff --git a/Database/transit_asmt_macros/summarize_transit_boardings.mac b/Database/transit_asmt_macros/summarize_transit_boardings.mac deleted file mode 100644 index 900821c..0000000 --- a/Database/transit_asmt_macros/summarize_transit_boardings.mac +++ /dev/null @@ -1,97 +0,0 @@ - -~# SUMMARIZE_TRANSIT_BOARDINGS.MAC -~# Craig Heither, 10-25-2022 -~# -~# Summarize transit boardings for congested transit assignment. -~# submit with: ~ -~# e.g.: " ~>%t3% -~"Line,Scenario,TotalBoard,PassengerMiles -~> -~# #### -~# -~:full_loop -~# -~:scenario_loop -s=%x% -~?y=1 -~+;~t1=CTA_Rail_Blue_Line;~t2=cbl___ -~?y=2 -~+;~t1=CTA_Rail_Brown_Line;~t2=cbr___ -~?y=3 -~+;~t1=CTA_Rail_Green_Line;~t2=cg____ -~?y=4 -~+;~t1=CTA_Rail_Orange_Line;~t2=cor___ -~?y=5 -~+;~t1=CTA_Rail_Pink_Line;~t2=cpk___ -~?y=6 -~+;~t1=CTA_Rail_Purple_Line;~t2=cpr___ -~?y=7 -~+;~t1=CTA_Rail_Red_Line;~t2=crd___ -~?y=8 -~+;~t1=CTA_Rail_Yellow_Line;~t2=cye___ -~?y=9 -~+;~t1=Metra_BNSF;~t2=mbn___ -~?y=10 -~+;~t1=Metra_Heritage_Corridor;~t2=mhc___ -~?y=11 -~+;~t1=Metra_Electric;~t2=mme___ -~?y=12 -~+;~t1=Metra_Milwaukee_North;~t2=mmn___ -~?y=13 -~+;~t1=Metra_Milwaukee_West;~t2=mmw___ -~?y=14 -~+;~t1=Metra_North_Central;~t2=mnc___ -~?y=15 -~+;~t1=Metra_Rock_Island;~t2=mri___ -~?y=16 -~+;~t1=Metra_SouthWest_Service;~t2=msw___ -~?y=17 -~+;~t1=Metra_UP_North;~t2=mun___ -~?y=18 -~+;~t1=Metra_UP_Northwest;~t2=mnw___ -~?y=19 -~+;~t1=Metra_UP_West;~t2=muw___ -~?y=20 -~+;~t1=NICTD_South_Shore;~t2=mss___ -~?y=21 -~+;~t1=CTA_Bus_Regular;~t2=mod=B -~?y=22 -~+;~t1=CTA_Bus_Express;~t2=mod=E -~?y=23 -~+;~t1=Pace_Bus_Regular;~t2=mod=P -~?y=24 -~+;~t1=Pace_Bus_Express;~t2=mod=Q -~?y=25 -~+;~t1=Pace_Bus_Local;~t2=mod=L -~# -~# ##-- Boardings -- ## -2.41 -~+;1;n;board; ;%t2%; ;*;5;4;ms900;allbrd;All line boards;1 -~+;1;n;voltr*len; ;%t2%; ;*;5;4;ms901;pssmle;Passenger miles;1;q -~# -~>>%t3% -~"%t1%,%s%,%ms900.0%,%ms901.0% -~> -~x+2 -~+;~?x<%z%;~$scenario_loop -~# ##-- next line --## -~+;~x-8;~y+1 -~+;~?y<26;~$full_loop -~# -~# ##-- Delete matrices --## -3.12 -~+;2;ms900;y -~+;2;ms901;y;q -~/ summary done! -q diff --git a/Database/transit_asmt_macros/summarize_transit_boardings.py b/Database/transit_asmt_macros/summarize_transit_boardings.py new file mode 100644 index 0000000..ececcc1 --- /dev/null +++ b/Database/transit_asmt_macros/summarize_transit_boardings.py @@ -0,0 +1,177 @@ +''' +SUMMARIZE_TRANSIT_BOARDINGS.PY + Craig Heither, 10-25-2022 + Translated from Emme macro to Python by Tim O'Leary, 9-26-2025 + + Summarize transit boardings for congested transit assignment. + No inputs necessary-- reads batch_file.yaml to determine scen numbers. + Will not run unless transit assignment has been completed. +''' + +#libraries +import os +import sys +from pathlib import Path +import yaml +import pandas as pd + +#startup emme +proj_dir = Path(__file__).resolve().parents[2] +db = proj_dir.joinpath('Database') +sys.path.append(str(proj_dir.joinpath('Scripts'))) +from tbmtools import project as tbm + +#connect to modeller +modeller = tbm.connect(proj_dir) +emmebank = modeller.emmebank + +#output location +out_boarding_csv = os.path.join( + db, 'transit_asmt_macros/report/Boarding_summary.csv' +) +out_transit_punch = os.path.join( + db, 'transit_asmt_macros/report/transit_punch_segment.csv' +) + +if os.path.exists(out_boarding_csv): + os.remove(out_boarding_csv) + +#get scenario info from batch_file.yaml for transit asmt scenarios +db = proj_dir.joinpath('Database') +with open(os.path.join(db, 'batch_file.yaml')) as f: + lines_without_backslashes = ''.join([line.replace('\\','/') for line in f]) + config = yaml.safe_load(lines_without_backslashes) +yr = str(config['scenario_code'])[0] # e.g., '2' from '200' + +trnt_scens = { + f'Night (6pm-6am)': f'{yr}21', + f'AM (6am-9am)': f'{yr}23', + f'Midday (9am-4pm)': f'{yr}25', + f'PM (4pm-6pm)': f'{yr}27', +} + +tr_dfs = [] + +for period_name, scen_num in trnt_scens.items(): + + scen = emmebank.scenario(scen_num) + if scen is None: + raise ValueError(f'Scenario {scen_num} not found in Emmebank') + + network = scen.get_network() + links = network.links() + + trpunch_list = [] + for link in network.links(): + segments = link.segments() #empty if link does not have transit segments + inode = network.node(link.i_node) + jnode = network.node(link.j_node) + #for each segment (if it has any), add this info to table: + for segment in segments: + line = network.transit_line(str(segment.id).split('-')[0]) + seg = [ + link.id, + segment.id, + link.length, + line.headway, + segment['transit_boardings'], + segment['transit_volume'], + segment['data1'], + inode['@zone'], + jnode['@zone'] + ] + + trpunch_list.append(seg) + + columns = ['id', 'segment_id', 'length', 'headway', 'transit_boardings','transit_volume','ltime','inode_zone','jnode_zone'] + trpunch = pd.DataFrame(data=trpunch_list, columns=columns) + + trpunch.eval('pmt = transit_volume * length', inplace=True) + trpunch.eval('pht = transit_volume * ltime / 60', inplace=True) + + #use transit line info to determine mode + def line_mode(x): + ''' + function for .map() method used directly below this function + to determine mode name from segment_id + + segment_id format: [alpha-code][5-digit number], e.g., 'b12535', 'cbl10011' + + b -> CTA local bus + e -> CTA express + p -> Pace regular bus + l -> Pace feeder bus + q -> Pace express bus + c** -> CTA rail lines (3-letter code) + m** -> Metra lines (3-letter code) + + returns: the english name representation of the mode code + + ''' + + line = x.split('-')[0] + name = ''.join([char for char in line if char.isalpha()]) + name_key = { + 'cbl': 'CTA Blue Line', + 'cbr': 'CTA Brown Line', + 'cg': 'CTA Green Line', + 'cor': 'CTA Orange Line', + 'cpk': 'CTA Pink Line', + 'cpr': 'CTA Purple Line', + 'crd': 'CTA Red Line', + 'cye': 'CTA Yellow Line', + 'mbn': 'Metra BNSF', + 'mhc': 'Metra Heritage Corridor', + 'mme': 'Metra Electric', + 'mmn': 'Metra Milwaukee North', + 'mmw': 'Metra Milwaukee West', + 'mnc': 'Metra North Central', + 'mri': 'Metra Rock Island', + 'msw': 'Metra SouthWest Service', + 'mun': 'Metra UP North', + 'mnw': 'Metra UP Northwest', + 'muw': 'Metra UP West', + 'mss': 'NICTD South Shore', + 'b': 'CTA Regular Bus', + 'e': 'CTA Express Bus', + 'p': 'Pace Regular Bus', + 'q': 'Pace Express Bus', + 'l': 'Pace Feeder Bus', + } + nam = [code for code in name_key.keys() if code in name] + nam = max(nam, key=len) if len(nam) > 0 else None + return name_key[nam] + + trpunch['mode'] = trpunch['segment_id'].map(line_mode) + + if len(trpunch.loc[trpunch['mode'].isnull()]) > 0: + print(f'SOME TRANSIT SEGMENTS WERE NOT MAPPED TO A MODE!') + print(trpunch.loc[trpunch['mode'].isnull()]) + raise ValueError('Some transit segments were not mapped to a mode (must be c, m, p, q, l, b, or e). Look at printed output of errors above.') + + trpunch['tod'] = scen_num + trpunch['tod_name'] = period_name + + tr_dfs.append(trpunch) + +tr_all = pd.concat(tr_dfs, ignore_index=True) + +#output all segment info to csv (similar to punch link) +tr_all.to_csv(out_transit_punch, index=False) + +#summary by time-of-day +summary_tod = tr_all.groupby(['tod_name', 'tod', 'mode']).agg({ + 'transit_boardings':'sum', + 'pmt':'sum' +}).reset_index() +#total daily +summary_daily = summary_tod.groupby('mode').agg({ + 'transit_boardings':'sum', + 'pmt':'sum' +}).reset_index() +summary_daily['tod'] = 'All' +summary_daily['tod_name'] = 'Daily' + +summary = pd.concat([summary_tod, summary_daily], ignore_index=True) +summary.to_csv(out_boarding_csv, index=False) +print(f'Summary transit boarding information written to: {out_boarding_csv}') \ No newline at end of file From 0ff911968d9f53a83eef347b39fa734e394c35f2 Mon Sep 17 00:00:00 2001 From: tkoleary Date: Thu, 9 Oct 2025 14:06:34 -0500 Subject: [PATCH 5/5] Adds summarize_transit_boardings.py to Submit_Full at the end of transit assignment --- Database/Submit_Full_Regional_Model_SOLA.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/Database/Submit_Full_Regional_Model_SOLA.bat b/Database/Submit_Full_Regional_Model_SOLA.bat index a769936..1e9ba53 100644 --- a/Database/Submit_Full_Regional_Model_SOLA.bat +++ b/Database/Submit_Full_Regional_Model_SOLA.bat @@ -454,6 +454,7 @@ if "%transitAsmt%" EQU "T" ( if %ERRORLEVEL% GTR 0 (goto issue) @ECHO -- Completed Select Line Boarding Analysis >> model_run_timestamp.txt ) + call python transit_asmt_macros\summarize_transit_boardings.py @ECHO End Transit Assignment: %date% %time% >> model_run_timestamp.txt ) goto last