Exemple #1
0
def calc_typical_vmt_per_year(settings, vehicle, alt, model_year, averages_dict):
    """
    This function calculates a typical annual VMT/vehicle over a set number of years as set via the General Inputs workbook. This typical annual VMT/vehicle
    can then be used to estimate the ages at which warranty and useful life will be reached. When insufficient years are available -- e.g., if the typical_vmt_thru_ageID
    is set to >5 years and the given vehicle is a MY2041 vintage vehicle and the fleet input file contains data only thru CY2045, then insufficient data exist to
    calculate the typical VMT for that vehicle -- the typical VMT for that vehicle will be set equal to the last prior MY vintage for which sufficient data were present.

    Parameters:
        settings: The SetInputs class.\n
        vehicle: Tuple; represents a sourcetype_regclass_fueltype vehicle.\n
        alt: Numeric; the Alternative or option ID.\n
        model_year: Numeric; the model year of the passed vehicle.\n
        averages_dict: Dictionary; contains cumulative annual average VMT/vehicle.

    Returns:
        A single typical annual VMT/veh value for the passed vehicle of the given model year.

    """
    calcs_avg = FleetAverages(averages_dict)

    vmt_thru_age_id = settings.repair_inputs_dict['typical_vmt_thru_ageID']['Value']
    if model_year + vmt_thru_age_id <= settings.year_max:
        year = model_year
    else:
        year = settings.year_max - vmt_thru_age_id # can't get cumulative VMT when model_year + vmt_thru_age_id exceeds data

    cumulative_vmt = calcs_avg.get_attribute_value((vehicle, alt, year, vmt_thru_age_id, 0), 'VMT_AvgPerVeh_Cumulative')
    typical_vmt = cumulative_vmt / (vmt_thru_age_id + 1)

    return typical_vmt
Exemple #2
0
def calc_direct_costs(totals_dict, averages_dict, sales_arg, program):
    """

    Parameters:
        totals_dict: Dictionary; into which tech package direct costs will be updated.\n
        averages_dict: Dictionary; contains tech package direct costs/vehicle.\n
        sales_arg: String; specifies the sales attribute to use (e.g., "VPOP" or "VPOP_withTech")\n
        program: String; the program identifier (i.e., 'CAP' or 'GHG').

    Returns:
        The totals_dict dictionary updated with tech package direct costs (package cost * sales).

    """
    if program == 'CAP': arg = 'Direct'
    else: arg = 'Tech'

    print(f'\nCalculating {program} {arg} total costs...')

    calcs = FleetTotals(totals_dict)
    calcs_avg = FleetAverages(averages_dict)

    age0_keys = [k for k, v in totals_dict.items() if v['ageID'] == 0]

    for key in age0_keys:
        cost_per_veh = calcs_avg.get_attribute_value(key, f'{arg}Cost_AvgPerVeh')
        sales = calcs.get_attribute_value(key, sales_arg)
        cost = cost_per_veh * sales

        temp_dict = {f'{arg}Cost': cost}
        calcs.update_dict(key, temp_dict)

    return totals_dict
Exemple #3
0
def calc_tech_costs(totals_dict, averages_dict, sales_arg):
    """

    Parameters::
        totals_dict: Dictionary; contains vehicle population (VPOP) data.\n
        averages_dict: Dictionary; contains average tech costs per vehicle.
        sales_arg: String; specifies the sales attribute to use (e.g., "VPOP" or "VPOP_withTech")

    Returns:
        The totals_dict dictionary updated with annual technology costs for all vehicles.

    """
    print('\nCalculating total technology costs...')

    calcs_avg = FleetAverages(averages_dict)
    calcs = FleetTotals(totals_dict)

    age0_keys = [k for k, v in totals_dict.items() if v['ageID'] == 0]

    for key in age0_keys:
        cost_per_veh = calcs_avg.get_attribute_value(key, 'TechCost_AvgPerVeh')
        sales = calcs.get_attribute_value(key, sales_arg)
        cost = cost_per_veh * sales

        temp_dict = {'TechCost': cost}
        calcs.update_dict(key, temp_dict)

    return totals_dict
Exemple #4
0
def calc_emission_repair_costs(totals_dict, averages_dict, vpop_arg):
    """

    Parameters:
        totals_dict: Dictionary; contains annual vehicle populations (VPOP).\n
        averages_dict: Dictionary; contains annual average emission repair costs/mile.\n
        vpop_arg: String; specifies the population attribute to use (e.g., "VPOP" or "VPOP_withTech")

    Returns:
        The totals_dict dictionary updated with annual emission repair costs for all vehicles.

    """
    print(f'\nCalculating total emission repair costs...')

    calcs_avg = FleetAverages(averages_dict)
    calcs = FleetTotals(totals_dict)

    for key in totals_dict.keys():
        cost_per_veh = calcs_avg.get_attribute_value(key, 'EmissionRepairCost_AvgPerVeh')
        vpop = calcs.get_attribute_value(key, vpop_arg)
        cost = cost_per_veh * vpop

        temp_dict = {'EmissionRepairCost': cost}
        calcs.update_dict(key, temp_dict)

    return totals_dict
def calc_indirect_costs(settings, totals_dict, averages_dict, sales_arg):
    """

    Parameters:
        settings: The SetInputs class.\n
        totals_dict: Dictionary; contains sales data (e.g., sales_arg at age=0).\n
        averages_dict: Dictionary; contains individual indirect costs per vehicle.\n
        sales_arg: String; specifies the sales attribute to use (e.g., "VPOP" or "VPOP_withTech")

    Returns:
        The totals_dict dictionary updated with total indirect costs for each individual indirect cost property and a summation of those.

    """
    print('\nCalculating CAP total indirect costs...')

    markup_factors = settings.markup_factors_unique_names.copy()
    markup_factors.append('Indirect')

    calcs_avg = FleetAverages(averages_dict)
    calcs = FleetTotals(totals_dict)

    age0_keys = [k for k, v in totals_dict.items() if v['ageID'] == 0]

    for key in age0_keys:
        temp_dict = dict()
        for markup_factor in markup_factors:
            cost_per_veh = calcs_avg.get_attribute_value(
                key, f'{markup_factor}Cost_AvgPerVeh')
            sales = calcs.get_attribute_value(key, sales_arg)
            cost = cost_per_veh * sales
            temp_dict[f'{markup_factor}Cost'] = cost

        calcs.update_dict(key, temp_dict)

    return totals_dict
Exemple #6
0
def calc_average_def_costs(totals_dict, averages_dict, vpop_arg):
    """

    Parameters:
        totals_dict: Dictionary; provides fleet DEF costs by vehicle. \n
        averages_dict: Dictionary, into which DEF costs/vehicle will be updated.\n
        vpop_arg: String; specifies the population attribute to use (e.g., "VPOP" or "VPOP_withTech")

    Returns:
        The passed dictionary updated with costs/mile and costs/vehicle associated with DEF consumption.

    """
    print('\nCalculating DEF average costs...')

    calcs_avg = FleetAverages(averages_dict)
    calcs = FleetTotals(totals_dict)

    # get keys where fueltype=2 (Diesel since they are the only vehicles that use DEF)
    ft2_keys = [k for k, v in averages_dict.items() if v['fuelTypeID'] == 2]

    for key in ft2_keys:
        def_cost = calcs.get_attribute_value(key, 'DEFCost')
        vmt = calcs.get_attribute_value(key, 'VMT')
        vpop = calcs.get_attribute_value(key, vpop_arg)
        cost_per_mile = def_cost / vmt
        cost_per_veh = def_cost / vpop

        temp_dict = {
            'DEFCost_AvgPerMile': cost_per_mile,
            'DEFCost_AvgPerVeh': cost_per_veh,
        }
        calcs_avg.update_dict(key, temp_dict)

    return averages_dict
Exemple #7
0
def create_weighted_cost_dict(settings, averages_dict, arg_to_weight, arg_to_weight_by):
    """
    This function weights 'arg_to_weight' attributes by the 'arg_to_weight_by' attribute.

    Parameters::
        settings: The SetInputs class.\n
        averages_dict: Dictionary; contains fleet average data (e.g., miles/year, cost/year, cost/mile).\n
        arg_to_weight: String; the attribute to be weighted by the arg_to_weight_by argument.\n
        arg_to_weight_by: String; the argument to weight by.

    Returns:
        A dictionary of arguments weighted by the weight_by argument.

    Note:
        The weighting is limited by the number of years (ages) to be included which is set in the general inputs file. The weighting is also
        limited to model years for which sufficient data exits to include all of those ages. For example, if the maximum calendar year included
        in the input data is 2045, and the maximum numbers of ages of data to include for each model year is 9 (which would be 10 years of age
        since year 1 is age 0) then the maximum model year included will be 2035.

    """
    print(f'\nCalculating weighted {arg_to_weight}...')
    calcs_avg = FleetAverages(averages_dict)

    wtd_result_dict = dict()
    weighted_results_dict = dict()
    for key in averages_dict.keys():
        vehicle, alt, model_year, age_id = key[0], key[1], key[2], key[3]
        st, rc, ft = vehicle
        if arg_to_weight == 'DEFCost_AvgPerMile' and ft != 2:
            pass
        else:
            if model_year <= (settings.year_max - settings.max_age_included - 1):
                wtd_result_dict_id = (vehicle, alt, model_year)
                numerator, denominator = 0, 0
                if wtd_result_dict_id in wtd_result_dict:
                    numerator = wtd_result_dict[wtd_result_dict_id]['numerator']
                    denominator = wtd_result_dict[wtd_result_dict_id]['denominator']
                else:
                    pass
                if age_id <= settings.max_age_included:
                    arg_weight = calcs_avg.get_attribute_value(key, arg_to_weight)
                    arg_weight_by = calcs_avg.get_attribute_value(key, arg_to_weight_by)
                    numerator += arg_weight * arg_weight_by
                    denominator += calcs_avg.get_attribute_value(key, 'VMT_AvgPerVeh')
                    wtd_result_dict[wtd_result_dict_id] = {'numerator': numerator, 'denominator': denominator}
    for key in wtd_result_dict.keys():
        numerator = wtd_result_dict[key]['numerator']
        denominator = wtd_result_dict[key]['denominator']
        vehicle, alt = key[0], key[1]
        st, rc, ft = vehicle
        source_type = Vehicle(st).sourcetype_name()
        weighted_results_dict[key] = {'optionID': alt, 'sourceTypeName': source_type, 'cents_per_mile': 100 * numerator / denominator}

    return weighted_results_dict
Exemple #8
0
def calc_per_veh_tech_costs(averages_dict):
    """
    
    Parameters::
        averages_dict: Dictionary; contains average direct and indirect costs per vehicle.

    Returns:
        The averages_dict dictionary updated with average tech costs per vehicle (direct plus indirect).

    Note:
        Direct and indirect costs apply only for ageID=0 (i.e., new sales).

    """
    print('\nCalculating per vehicle technology costs...')
    calcs_avg = FleetAverages(averages_dict)

    age0_keys = [k for k, v in averages_dict.items() if v['ageID'] == 0]

    for key in age0_keys:
        cost = calcs_avg.get_attribute_value(key, 'DirectCost_AvgPerVeh')
        cost += calcs_avg.get_attribute_value(key, 'IndirectCost_AvgPerVeh')

        temp_dict = {'TechCost_AvgPerVeh': cost}
        calcs_avg.update_dict(key, temp_dict)

    return averages_dict
Exemple #9
0
def calc_per_veh_direct_costs(yoy_costs_per_step_dict, cost_steps, averages_dict, program):
    """

    Parameters:
        yoy_costs_per_step_dict: Dictionary; contains the package cost and cumulative sales used to calculate that package cost (learning effects depend on cumulative
        sales) for the passed unit in the given model year and complying with the standards set in the given cost step. \n
        cost_steps: List; provides the cost steps (as strings) associated with the direct costs being calculated.\n
        averages_dict: Dictionary; into which tech package direct costs/vehicle will be updated.\n
        program: String; the program identifier (i.e., 'CAP' or 'GHG').

    Returns:
        The averages_dict dictionary updated with tech package costs/vehicle.

    """
    print(f'\nCalculating {program} costs per vehicle...')

    calcs_avg = FleetAverages(averages_dict)

    age0_keys = [k for k, v in averages_dict.items() if v['ageID'] == 0]

    for key in age0_keys:
        vehicle, alt, model_year, age_id, disc_rate = key
        st, rc, ft = vehicle
        engine = (rc, ft)

        if program == 'CAP': unit = engine
        else: unit = vehicle

        if alt == 0:
            cost = yoy_costs_per_step_dict[(unit, alt, model_year, cost_steps[0])]['Cost_AvgPerVeh']
        else:
            cost = yoy_costs_per_step_dict[(unit, 0, model_year, cost_steps[0])]['Cost_AvgPerVeh']
            for step in cost_steps:
                if model_year >= int(step):
                    cost += yoy_costs_per_step_dict[(unit, alt, model_year, step)]['Cost_AvgPerVeh']

        if program == 'GHG':
            # GHG program costs are to be averaged over all VPOP for the given unit
            vpop_with_tech = calcs_avg.get_attribute_value(key, 'VPOP_withTech')
            vpop = calcs_avg.get_attribute_value(key, 'VPOP')
            cost = cost * vpop_with_tech / vpop
            temp_dict = {'TechCost_AvgPerVeh': cost}
            calcs_avg.update_dict(key, temp_dict)
        else:
            temp_dict = {'DirectCost_AvgPerVeh': cost}
            calcs_avg.update_dict(key, temp_dict)

    return averages_dict
def calc_per_veh_indirect_costs(settings, averages_dict):
    """
    
    Parameters:
        settings: The SetInputs class.\n
        averages_dict: Dictionary; contains tech package direct costs/vehicle.

    Returns:
        The averages_dict dictionary updated with indirect costs associated with each markup value along with the summation of those individual indirect
        costs as "IndirectCost_AvgPerVeh."

    """
    print('\nCalculating CAP per vehicle indirect costs...')
    calcs_avg = FleetAverages(averages_dict)
    markup_factors = settings.markup_factors_unique_names.copy()

    age0_keys = [k for k, v in averages_dict.items() if v['ageID'] == 0]

    for key in age0_keys:
        vehicle, alt, model_year, age_id, disc_rate = key
        st, rc, ft = vehicle
        engine = (rc, ft)

        temp_dict = dict()
        ic_sum = 0
        for markup_factor in markup_factors:
            markup_value = calc_project_markup_value(settings, engine, alt,
                                                     markup_factor, model_year)
            per_veh_direct_cost = calcs_avg.get_attribute_value(
                key, 'DirectCost_AvgPerVeh')
            cost = markup_value * per_veh_direct_cost
            temp_dict[f'{markup_factor}Cost_AvgPerVeh'] = cost
            ic_sum += cost

        temp_dict['IndirectCost_AvgPerVeh'] = ic_sum
        calcs_avg.update_dict(key, temp_dict)

    return averages_dict
Exemple #11
0
def calc_average_fuel_costs(totals_dict, averages_dict, vpop_arg, vmt_arg):
    """

    Parameters:
        totals_dict: Dictionary; provides fleet fuel costs for all vehicles.\n
        averages_dict: Dictionary; the destination for fuel costs/vehicle and costs/mile results.\n
        vpop_arg: String; specifies the population attribute to use (e.g., "VPOP" or "VPOP_withTech")\n
        vmt_arg: String; specifies the VMT attribute to use (e.g., "VMT" or "VMT_withTech")

    Returns:
        The passed averages_dict updated to include fuel costs/vehicle and costs/mile.

    """
    print('\nCalculating average fuel costs...')
    calcs_avg = FleetAverages(averages_dict)
    calcs = FleetTotals(totals_dict)

    for key in averages_dict.keys():
        fuel_cost = calcs.get_attribute_value(key, 'FuelCost_Retail')
        vmt = calcs.get_attribute_value(key, vmt_arg)
        vpop = calcs.get_attribute_value(key, vpop_arg)

        # try/except block to protect against divide by 0 error
        try:
            cost_per_mile = fuel_cost / vmt
            cost_per_veh = fuel_cost / vpop
        except:
            cost_per_mile = 0
            cost_per_veh = 0

        temp_dict = {'FuelCost_Retail_AvgPerMile': cost_per_mile,
                     'FuelCost_Retail_AvgPerVeh': cost_per_veh,
                     }
        calcs_avg.update_dict(key, temp_dict)

    return averages_dict
Exemple #12
0
def calc_per_veh_emission_repair_costs(averages_dict):
    """

    Parameters:
        averages_dict: Dictionary; contains annual emission repair costs/mile.

    Returns:
        The passed dictionary updated with annual emission repair costs/vehicle for each dictionary key.

    """
    print('\nCalculating emission repair costs per vehicle...')
    calcs_avg = FleetAverages(averages_dict)

    for key in averages_dict.keys():
        repair_cpm = calcs_avg.get_attribute_value(key, 'EmissionRepairCost_AvgPerMile')
        vmt_per_veh = calcs_avg.get_attribute_value(key, 'VMT_AvgPerVeh')
        cost_per_veh = repair_cpm * vmt_per_veh

        temp_dict = {'EmissionRepairCost_AvgPerVeh': cost_per_veh}
        calcs_avg.update_dict(key, temp_dict)

    return averages_dict
Exemple #13
0

if __name__ == '__main__':
    import pandas as pd
    from pathlib import Path
    from bca_tool_code.tool_setup import SetInputs
    from bca_tool_code.project_fleet import create_fleet_df

    settings = SetInputs()

    path_project = Path(__file__).parent.parent
    path_dev = path_project / 'dev'
    path_dev.mkdir(exist_ok=True)

    # 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')
    df = pd.DataFrame(regclass_yoy_costs_per_step).transpose()

    df.to_csv(path_dev / 'regclass_yoy_costs_per_step.csv', index=True)
    print(f'\nOutput files have been saved to {path_dev}\n')
Exemple #14
0
def calc_emission_repair_costs_per_mile(settings, averages_dict):
    """

    Parameters:
        settings: The SetInputs class.\n
        averages_dict: Dictionary; contains tech package direct costs/vehicle and cumulative annual average VMT/vehicle.

    Returns:
        The averages_dict dictionary updated to include emission repair costs/mile for each dictionary key.\n
        A repair cost/mile dictionary containing details used in the calculation of repair cost/mile and which is then written to an output file for the given run.\n
        An 'estimated ages' dictionary containing details behind the calculations and which is then written to an output file for the given run.

    """
    print('\nCalculating emission repair costs per mile...')
    calcs_avg = FleetAverages(averages_dict)

    repair_cpm_dict = dict()
    estimated_ages_dict = dict()
    for key in averages_dict.keys():
        vehicle, alt, model_year, age_id, disc_rate = key

        reference_direct_cost = calcs_avg.get_attribute_value(((61, 47, 2), 0, model_year, 0, 0), 'DirectCost_AvgPerVeh') # sourcetype here is arbitrary provided it is of diesel regclass 47
        direct_cost = calcs_avg.get_attribute_value((vehicle, alt, model_year, 0, 0), 'DirectCost_AvgPerVeh')
        direct_cost_scaler = direct_cost / reference_direct_cost

        typical_vmt = calc_typical_vmt_per_year(settings, vehicle, alt, model_year, averages_dict)
        warranty_estimated_age, estimated_ages_dict = calc_estimated_age(settings, vehicle, alt, model_year, 'Warranty', typical_vmt, estimated_ages_dict)
        usefullife_estimated_age, estimated_ages_dict = calc_estimated_age(settings, vehicle, alt, model_year, 'Usefullife', typical_vmt, estimated_ages_dict)

        in_warranty_cpm = settings.repair_inputs_dict['in-warranty_R&M_CPM']['Value'] \
                          * settings.repair_inputs_dict['emission_repair_share']['Value'] \
                          * direct_cost_scaler
        at_usefullife_cpm = settings.repair_inputs_dict['at-usefullife_R&M_CPM']['Value'] \
                          * settings.repair_inputs_dict['emission_repair_share']['Value'] \
                          * direct_cost_scaler

        if usefullife_estimated_age > warranty_estimated_age:
            slope_within_usefullife = (at_usefullife_cpm - in_warranty_cpm) / (usefullife_estimated_age - warranty_estimated_age)
        else:
            slope_within_usefullife = 0

        max_cpm = settings.repair_inputs_dict['max_R&M_CPM']['Value'] \
                  * settings.repair_inputs_dict['emission_repair_share']['Value'] \
                  * direct_cost_scaler

        # now calulate the cost per mile
        if (age_id + 1) < warranty_estimated_age:
            cpm = in_warranty_cpm
        elif warranty_estimated_age <= (age_id + 1) < usefullife_estimated_age:
            cpm = slope_within_usefullife * ((age_id + 1) - warranty_estimated_age) + in_warranty_cpm
        elif (age_id + 1) == usefullife_estimated_age:
            cpm = at_usefullife_cpm
        else:
            cpm = max_cpm

        temp_dict = {'EmissionRepairCost_AvgPerMile': cpm}
        calcs_avg.update_dict(key, temp_dict)

        repair_cpm_dict[key] = {'reference_direct_cost': reference_direct_cost,
                                'direct_cost_scaler': direct_cost_scaler,
                                'warranty_estimated_age': warranty_estimated_age,
                                'usefullife_estimated_age': usefullife_estimated_age,
                                'in_warranty_cpm': in_warranty_cpm,
                                'at_usefullife_cpm': at_usefullife_cpm,
                                'slope_within_usefullife': slope_within_usefullife,
                                'max_cpm': max_cpm,
                                'cpm': cpm
                                }
    return averages_dict, repair_cpm_dict, estimated_ages_dict
Exemple #15
0
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')
Exemple #16
0
def discount_values(settings, dict_of_values, program, arg):
    """
    The discount function determines metrics appropriate for discounting (those contained in dict_of_values) and does the discounting
    calculation to a given year and point within that year.

    Parameters:
        settings: The SetInputs class.\n
        dict_of_values: Dictionary; provides values to be discounted with keys consisting of vehicle, model_year, age_id and discount rate.\n
        program: String; indicates what program is being passed.
        arg: String; indicates whether totals or averages are being discounted.

    Returns:
        The passed dictionary with new key, value pairs where keys stipulate the discount rate and monetized values are discounted at the same rate as the discount rate of the input stream of values.

    Note:
        The costs_start entry of the BCA_General_Inputs file should be set to 'start-year' or 'end-year', where start-year represents costs
        starting at time t=0 (i.e., first year costs are undiscounted), and end-year represents costs starting at time t=1 (i.e., first year
        costs are discounted).

    """
    print(f'\nDiscounting values for {program} {arg}...')
    if arg == 'totals': calcs = FleetTotals(dict_of_values)
    else: calcs = FleetAverages(dict_of_values)

    # get cost attributes
    d = [nested_dict for key, nested_dict in dict_of_values.items()][0]
    all_costs = [k for k, v in d.items() if 'Cost' in k]
    emission_cost_args_25 = [item for item in all_costs if '_0.025' in item]
    emission_cost_args_3 = [item for item in all_costs if '_0.03' in item]
    emission_cost_args_5 = [item for item in all_costs if '_0.05' in item]
    emission_cost_args_7 = [item for item in all_costs if '_0.07' in item]
    non_emission_cost_args = [item for item in all_costs if '_0.0' not in item]

    if settings.costs_start == 'start-year': discount_offset = 0
    elif settings.costs_start == 'end-year': discount_offset = 1
    discount_to_year = settings.discount_to_yearID

    for key in dict_of_values.keys():
        vehicle, alt, model_year, age_id, rate = key
        if rate == 0:
            pass  # no need to discount undiscounted values with 0 percent discount rate
        else:
            year = model_year + age_id

            temp_dict = dict()

            for arg in non_emission_cost_args:
                arg_value = calcs.get_attribute_value(key, arg)
                arg_value_discounted = arg_value / (
                    (1 + rate)**(year - discount_to_year + discount_offset))
                temp_dict[arg] = arg_value_discounted

            emission_rate = 0.025
            for arg in emission_cost_args_25:
                arg_value = calcs.get_attribute_value(key, arg)
                arg_value_discounted = arg_value / ((1 + emission_rate)**(
                    year - discount_to_year + discount_offset))
                temp_dict[arg] = arg_value_discounted

            emission_rate = 0.03
            for arg in emission_cost_args_3:
                arg_value = calcs.get_attribute_value(key, arg)
                arg_value_discounted = arg_value / ((1 + emission_rate)**(
                    year - discount_to_year + discount_offset))
                temp_dict[arg] = arg_value_discounted

            emission_rate = 0.05
            for arg in emission_cost_args_5:
                arg_value = calcs.get_attribute_value(key, arg)
                arg_value_discounted = arg_value / ((1 + emission_rate)**(
                    year - discount_to_year + discount_offset))
                temp_dict[arg] = arg_value_discounted

            emission_rate = 0.07
            for arg in emission_cost_args_7:
                arg_value = calcs.get_attribute_value(key, arg)
                arg_value_discounted = arg_value / ((1 + emission_rate)**(
                    year - discount_to_year + discount_offset))
                temp_dict[arg] = arg_value_discounted

            calcs.update_dict(key, temp_dict)

    return dict_of_values