Source code for tool_main

"""
cti_bca_tool.tool_main.py

This is the main module of the tool.

"""
import pandas as pd
import shutil
from datetime import datetime
import time
import bca_tool_code
from bca_tool_code.tool_setup import SetInputs, SetPaths
from bca_tool_code.project_fleet import create_fleet_df
from bca_tool_code.fleet_totals_dict import FleetTotals
from bca_tool_code.fleet_averages_dict import FleetAverages
from bca_tool_code.direct_costs import calc_yoy_costs_per_step, calc_per_veh_direct_costs, calc_direct_costs
from bca_tool_code.indirect_costs import calc_per_veh_indirect_costs, calc_indirect_costs
from bca_tool_code.tech_costs import calc_per_veh_tech_costs, calc_tech_costs
from bca_tool_code.def_costs import calc_def_costs, calc_average_def_costs
from bca_tool_code.fuel_costs import calc_fuel_costs, calc_average_fuel_costs
from bca_tool_code.repair_costs import calc_emission_repair_costs_per_mile, calc_per_veh_emission_repair_costs, calc_emission_repair_costs
from bca_tool_code.emission_costs import calc_criteria_emission_costs
from bca_tool_code.sum_by_vehicle import calc_sum_of_costs
from bca_tool_code.discounting import discount_values
from bca_tool_code.pv_annualized_dicts import pv_annualized
from bca_tool_code.weighted_results import create_weighted_cost_dict
from bca_tool_code.calc_deltas import calc_deltas, calc_deltas_weighted
from bca_tool_code.vehicle import Vehicle
from bca_tool_code.figures import CreateFigures
from bca_tool_code.general_functions import save_dict_to_csv, inputs_filenames, get_file_datetime


[docs]def main(): """ Returns: The results of the current run of the tool. """ start_time_calcs = time.time() set_paths = SetPaths() run_id = set_paths.run_id() settings = SetInputs() print("\nDoing the work...\n") if settings.calc_cap: # create project fleet DataFrame which will include adjustments to the MOVES input file that are unique to the project. cap_fleet_df = create_fleet_df(settings, settings.moves_cap, settings.options_cap_dict, settings.moves_adjustments_cap_dict, 'VPOP', 'VMT', 'Gallons') # create totals, averages and sales by regclass dictionaries cap_totals_dict, cap_averages_dict, regclass_sales_dict = dict(), dict(), dict() cap_totals_dict = FleetTotals(cap_totals_dict).create_fleet_totals_dict(settings, cap_fleet_df) cap_averages_dict = FleetAverages(cap_averages_dict).create_fleet_averages_dict(settings, cap_fleet_df) regclass_sales_dict = FleetTotals(regclass_sales_dict).create_regclass_sales_dict(cap_fleet_df) # calculate direct costs per reg class based on cumulative regclass sales (learning is applied to cumulative sales) regclass_yoy_costs_per_step = calc_yoy_costs_per_step(settings, regclass_sales_dict, 'VPOP_withTech', 'CAP') # calculate average (per vehicle) then total direct costs cap_averages_dict = calc_per_veh_direct_costs(regclass_yoy_costs_per_step, settings.cost_steps_regclass, cap_averages_dict, 'CAP') cap_totals_dict = calc_direct_costs(cap_totals_dict, cap_averages_dict, 'VPOP_withTech', 'CAP') # calculate average then total indirect costs cap_averages_dict = calc_per_veh_indirect_costs(settings, cap_averages_dict) cap_totals_dict = calc_indirect_costs(settings, cap_totals_dict, cap_averages_dict, 'VPOP_withTech') # calculate average then total tech costs (direct + indirect) cap_averages_dict = calc_per_veh_tech_costs(cap_averages_dict) cap_totals_dict = calc_tech_costs(cap_totals_dict, cap_averages_dict, 'VPOP_withTech') # calculate total then average DEF costs cap_totals_dict = calc_def_costs(settings, cap_totals_dict, 'Gallons_withTech') cap_averages_dict = calc_average_def_costs(cap_totals_dict, cap_averages_dict, 'VPOP_withTech') # calculate total then average fuel costs, including adjustments for fuel consumption associated with ORVR cap_totals_dict = calc_fuel_costs(settings, cap_totals_dict, 'Gallons_withTech', 'CAP') cap_averages_dict = calc_average_fuel_costs(cap_totals_dict, cap_averages_dict, 'VPOP_withTech', 'VMT_withTech') # calculate average then total emission repair costs cap_averages_dict, repair_cpm_dict, estimated_ages_dict = calc_emission_repair_costs_per_mile(settings, cap_averages_dict) cap_averages_dict = calc_per_veh_emission_repair_costs(cap_averages_dict) cap_totals_dict = calc_emission_repair_costs(cap_totals_dict, cap_averages_dict, 'VPOP_withTech') # sum operating costs and operating-tech costs into a single key, value # the totals_dict here uses pre-tax fuel price since it serves as the basis for social costs # the averages_dict uses retail fuel prices since it serves as the basis for average operating costs which are relevant to owners cap_totals_dict = calc_sum_of_costs(cap_totals_dict, 'OperatingCost', 'DEFCost', 'FuelCost_Pretax', 'EmissionRepairCost') cap_totals_dict = calc_sum_of_costs(cap_totals_dict, 'TechAndOperatingCost', 'TechCost', 'OperatingCost') cap_averages_dict = calc_sum_of_costs(cap_averages_dict, 'OperatingCost_Owner_AvgPerMile', 'DEFCost_AvgPerMile', 'FuelCost_Retail_AvgPerMile', 'EmissionRepairCost_AvgPerMile') cap_averages_dict = calc_sum_of_costs(cap_averages_dict, 'OperatingCost_Owner_AvgPerVeh', 'DEFCost_AvgPerVeh', 'FuelCost_Retail_AvgPerVeh', 'EmissionRepairCost_AvgPerVeh') # calc emission effects, if applicable if settings.calc_cap_pollution_effects: cap_totals_dict = calc_criteria_emission_costs(settings, cap_totals_dict) # calculate some weighted (wtd) cost per mile (cpm) operating costs wtd_def_cpm_dict = create_weighted_cost_dict(settings, cap_averages_dict, 'DEFCost_AvgPerMile', 'VMT_AvgPerVeh') wtd_repair_cpm_dict = create_weighted_cost_dict(settings, cap_averages_dict, 'EmissionRepairCost_AvgPerMile', 'VMT_AvgPerVeh') wtd_cap_fuel_cpm_dict = create_weighted_cost_dict(settings, cap_averages_dict, 'FuelCost_Retail_AvgPerMile', 'VMT_AvgPerVeh') # discount monetized values cap_totals_dict = discount_values(settings, cap_totals_dict, 'CAP', 'totals') cap_averages_dict = discount_values(settings, cap_averages_dict, 'CAP', 'averages') # calc annual sums, present and annualized values cap_pv_annualized_dict = pv_annualized(settings, cap_totals_dict, 'CAP') # calculate deltas relative to the passed no action alternative ID cap_totals_dict = calc_deltas(settings, cap_totals_dict) cap_averages_dict = calc_deltas(settings, cap_averages_dict) cap_pv_annualized_dict = calc_deltas(settings, cap_pv_annualized_dict) wtd_def_cpm_dict = calc_deltas_weighted(settings, wtd_def_cpm_dict) wtd_repair_cpm_dict = calc_deltas_weighted(settings, wtd_repair_cpm_dict) wtd_cap_fuel_cpm_dict = calc_deltas_weighted(settings, wtd_cap_fuel_cpm_dict) if settings.calc_ghg: # create project fleet DataFrame which will include adjustments to the MOVES input file that are unique to the project. ghg_fleet_df = create_fleet_df(settings, settings.moves_ghg, settings.options_ghg_dict, settings.moves_adjustments_ghg_dict, 'VPOP') # create totals, averages and sales by sourcetype dictionaries sourcetype_sales_dict, ghg_totals_dict, ghg_averages_dict = dict(), dict(), dict() ghg_totals_dict = FleetTotals(ghg_totals_dict).create_fleet_totals_dict(settings, ghg_fleet_df) ghg_averages_dict = FleetAverages(ghg_averages_dict).create_fleet_averages_dict(settings, ghg_fleet_df) sourcetype_sales_dict = FleetTotals(sourcetype_sales_dict).create_sourcetype_sales_dict(ghg_fleet_df) # calculate tech costs per sourcetype based on cumulative sourcetype sales (learning is applied to cumulative sales) sourcetype_yoy_costs_per_step = calc_yoy_costs_per_step(settings, sourcetype_sales_dict, 'VPOP_withTech', 'GHG') # calculate average (per vehicle) then total tech costs ghg_averages_dict = calc_per_veh_direct_costs(sourcetype_yoy_costs_per_step, settings.cost_steps_sourcetype, ghg_averages_dict, 'GHG') ghg_totals_dict = calc_direct_costs(ghg_totals_dict, ghg_averages_dict, 'VPOP', 'GHG') # calculate total then average fuel costs ghg_totals_dict = calc_fuel_costs(settings, ghg_totals_dict, 'Gallons', 'GHG') ghg_averages_dict = calc_average_fuel_costs(ghg_totals_dict, ghg_averages_dict, 'VPOP', 'VMT') # sum operating costs and operating-tech costs into a single key, value # the totals_dict here uses pre-tax fuel price since it serves as the basis for social costs # the averages_dict uses retail fuel prices since it serves as the basis for average operating costs which are relevant to owners ghg_totals_dict = calc_sum_of_costs(ghg_totals_dict, 'OperatingCost', 'FuelCost_Pretax') ghg_totals_dict = calc_sum_of_costs(ghg_totals_dict, 'TechAndOperatingCost', 'TechCost', 'OperatingCost') ghg_averages_dict = calc_sum_of_costs(ghg_averages_dict, 'OperatingCost_Owner_AvgPerVeh', 'FuelCost_Retail_AvgPerVeh') # calc emission effects, if applicable if settings.calc_ghg_pollution_effects: pass # ghg_totals_dict = calc_ghg_emission_costs(settings, ghg_totals_dict) # calculate some weighted (wtd) cost per mile (cpm) operating costs # wtd_ghg_fuel_cpm_dict = create_weighted_cost_dict(settings, ghg_averages_dict, 'FuelCost_Retail_AvgPerMile', 'VMT_AvgPerVeh') # discount monetized values ghg_totals_dict = discount_values(settings, ghg_totals_dict, 'GHG', 'totals') ghg_averages_dict = discount_values(settings, ghg_averages_dict, 'GHG', 'averages') # calc annual sums, present and annualized values ghg_pv_annualized_dict = pv_annualized(settings, ghg_totals_dict, 'GHG') # calculate deltas relative to the passed no action alternative ID ghg_totals_dict = calc_deltas(settings, ghg_totals_dict) ghg_averages_dict = calc_deltas(settings, ghg_averages_dict) ghg_pv_annualized_dict = calc_deltas(settings, ghg_pv_annualized_dict) # wtd_ghg_fuel_cpm_dict = calc_deltas_weighted(settings, wtd_ghg_fuel_cpm_dict, 'FuelCost_Retail_AvgPerMile') elapsed_time_calcs = time.time() - start_time_calcs # determine run output paths if run_id == 'test': path_of_run_results_folder = set_paths.path_test path_of_run_results_folder.mkdir(exist_ok=True) path_of_run_folder = path_of_run_results_folder else: path_of_run_folder, path_of_run_inputs_folder, path_of_run_results_folder, path_of_modified_inputs_folder, path_of_code_folder \ = set_paths.create_output_paths(settings.start_time_readable, run_id) start_time_postproc = time.time() # pass dicts thru the vehicle_name and/or option_name function to add some identifiers and generate some figures if settings.calc_cap: # add identifier attributes cap_totals_dict = Vehicle().vehicle_name(settings, settings.options_cap_dict, cap_totals_dict) cap_averages_dict = Vehicle().vehicle_name(settings, settings.options_cap_dict, cap_averages_dict) cap_pv_annualized_dict = Vehicle().option_name(settings, settings.options_cap_dict, cap_pv_annualized_dict) # rearrange columns for better presentation cap_totals_df = pd.DataFrame(cap_totals_dict).transpose() cols = [col for col in cap_totals_df.columns if col not in settings.row_header_for_fleet_files] cap_totals_df = pd.DataFrame(cap_totals_df, columns=settings.row_header_for_fleet_files + cols) cap_averages_df = pd.DataFrame(cap_averages_dict).transpose() cols = [col for col in cap_averages_df.columns if col not in settings.row_header_for_fleet_files] cap_averages_df = pd.DataFrame(cap_averages_df, columns=settings.row_header_for_fleet_files + cols) cap_pv_annualized_df = pd.DataFrame(cap_pv_annualized_dict).transpose() cols = [col for col in cap_pv_annualized_df.columns if col not in settings.row_header_for_annual_summary_files] cap_pv_annualized_df = pd.DataFrame(cap_pv_annualized_df, columns=settings.row_header_for_annual_summary_files + cols) if settings.calc_ghg: # add identifier attributes ghg_totals_dict = Vehicle().vehicle_name(settings, settings.options_ghg_dict, ghg_totals_dict) ghg_averages_dict = Vehicle().vehicle_name(settings, settings.options_ghg_dict, ghg_averages_dict) ghg_pv_annualized_dict = Vehicle().option_name(settings, settings.options_ghg_dict, ghg_pv_annualized_dict) # rearrange columns for better presentation ghg_totals_df = pd.DataFrame(ghg_totals_dict).transpose() cols = [col for col in ghg_totals_df.columns if col not in settings.row_header_for_fleet_files] ghg_totals_df = pd.DataFrame(ghg_totals_df, columns=settings.row_header_for_fleet_files + cols) ghg_averages_df = pd.DataFrame(ghg_averages_dict).transpose() cols = [col for col in ghg_averages_df.columns if col not in settings.row_header_for_fleet_files] ghg_averages_df = pd.DataFrame(ghg_averages_df, columns=settings.row_header_for_fleet_files + cols) ghg_pv_annualized_df = pd.DataFrame(ghg_pv_annualized_dict).transpose() cols = [col for col in ghg_pv_annualized_df.columns if col not in settings.row_header_for_annual_summary_files] ghg_pv_annualized_df = pd.DataFrame(ghg_pv_annualized_df, columns=settings.row_header_for_annual_summary_files + cols) elapsed_time_postproc = time.time() - start_time_postproc start_time_outputs = time.time() # copy input files into results folder; also save fuel_prices and reshaped files to this folder print('\nCopying input files and code to the outputs folder...\n') if run_id == 'test': pass else: inputs_filename_list = inputs_filenames(settings.input_files_pathlist) for file in inputs_filename_list: path_source = set_paths.path_inputs / file path_destination = path_of_run_inputs_folder / file shutil.copy2(path_source, path_destination) for file in set_paths.files_in_code_folder(): try: shutil.copy2(file, path_of_code_folder / file.name) except: print('\nUnable to copy Python code to run results folder when using the executable.\n') settings.fuel_prices.to_csv(path_of_modified_inputs_folder / f'fuel_prices_{settings.aeo_case}.csv', index=False) settings.regclass_costs.to_csv(path_of_modified_inputs_folder / 'regclass_costs.csv', index=False) settings.sourcetype_costs.to_csv(path_of_modified_inputs_folder / 'sourcetype_costs.csv', index=False) settings.repair_and_maintenance.to_csv(path_of_modified_inputs_folder / 'repair_and_maintenance.csv') settings.def_prices.to_csv(path_of_modified_inputs_folder / 'def_prices.csv', index=False) gdp_deflators = pd.DataFrame(settings.gdp_deflators) # from dict to df gdp_deflators.to_csv(path_of_modified_inputs_folder / 'gdp_deflators.csv', index=True) # save dictionaries to csv and also add some identifying info using the vehicle_name function print("\nSaving the output files...\n") if settings.calc_cap: cap_totals_df.to_csv(path_of_run_results_folder / f'CAP_bca_tool_fleet_totals_{settings.start_time_readable}.csv', index=False) cap_averages_df.to_csv(path_of_run_results_folder / f'CAP_bca_tool_fleet_averages_{settings.start_time_readable}.csv', index=False) cap_pv_annualized_df.to_csv(path_of_run_results_folder / f'CAP_bca_tool_annual_summary_{settings.start_time_readable}.csv', index=False) save_dict_to_csv(Vehicle().vehicle_name(settings, settings.options_cap_dict, estimated_ages_dict), path_of_run_results_folder / f'CAP_bca_tool_estimated_ages_{settings.start_time_readable}', list(), 'vehicle', 'optionID', 'modelYearID', 'identifier') save_dict_to_csv(Vehicle().vehicle_name(settings, settings.options_cap_dict, repair_cpm_dict), path_of_run_results_folder / f'CAP_bca_tool_repair_cpm_details_{settings.start_time_readable}', list(), 'vehicle', 'optionID', 'modelYearID', 'ageID', 'DiscountRate') save_dict_to_csv(wtd_def_cpm_dict, path_of_run_results_folder / f'CAP_bca_tool_vmt_weighted_def_cpm_{settings.start_time_readable}', list(), 'vehicle', 'optionID', 'modelYearID') save_dict_to_csv(wtd_repair_cpm_dict, path_of_run_results_folder / f'CAP_bca_tool_vmt_weighted_emission_repair_cpm_{settings.start_time_readable}', list(), 'vehicle', 'optionID', 'modelYearID') save_dict_to_csv(wtd_cap_fuel_cpm_dict, path_of_run_results_folder / f'CAP_bca_tool_vmt_weighted_fuel_cpm_{settings.start_time_readable}', list(), 'vehicle', 'optionID', 'modelYearID') # create figures arg_list = ['TechCost', 'EmissionRepairCost', 'DEFCost', 'FuelCost_Pretax', 'TechAndOperatingCost'] CreateFigures(cap_pv_annualized_df, 'US Dollars', path_of_run_results_folder, 'CAP').create_figures(arg_list) if settings.calc_ghg: ghg_totals_df.to_csv(path_of_run_results_folder / f'GHG_bca_tool_fleet_totals_{settings.start_time_readable}.csv', index=False) ghg_averages_df.to_csv(path_of_run_results_folder / f'GHG_bca_tool_fleet_averages_{settings.start_time_readable}.csv', index=False) ghg_pv_annualized_df.to_csv(path_of_run_results_folder / f'GHG_bca_tool_annual_summary_{settings.start_time_readable}.csv', index=False) # create figures arg_list = ['TechCost', 'FuelCost_Pretax', 'TechAndOperatingCost'] CreateFigures(ghg_pv_annualized_df, 'US Dollars', path_of_run_results_folder, 'GHG').create_figures(arg_list) elapsed_time_outputs = time.time() - start_time_outputs end_time = time.time() end_time_readable = datetime.now().strftime('%Y%m%d-%H%M%S') elapsed_time = end_time - settings.start_time summary_log = pd.DataFrame(data={'Item': ['Version', 'Run folder', 'Calc CAP costs', 'Calc CAP pollution', 'Calc GHG costs', 'Calc GHG pollution', 'Start of run', 'Elapsed time read inputs', 'Elapsed time calculations', 'Elapsed time post-processing', 'Elapsed time save outputs', 'End of run', 'Elapsed runtime'], 'Results': [bca_tool_code.__version__, path_of_run_folder, settings.calc_cap_value, settings.calc_cap_pollution_effects_value, settings.calc_ghg_value, settings.calc_ghg_pollution_effects_value, settings.start_time_readable, settings.elapsed_time_read, elapsed_time_calcs, elapsed_time_postproc, elapsed_time_outputs, end_time_readable, elapsed_time], 'Units': ['', '', '', '', '', '', 'YYYYmmdd-HHMMSS', 'seconds', 'seconds', 'seconds', 'seconds', 'YYYYmmdd-HHMMSS', 'seconds']}) summary_log = pd.concat([summary_log, get_file_datetime(settings.input_files_pathlist)], axis=0, sort=False, ignore_index=True) summary_log.to_csv(path_of_run_results_folder.joinpath('summary_log.csv'), index=False) print(f'\nOutput files have been saved to {path_of_run_folder}\n')
if __name__ == '__main__': main()