Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • ece7ce6
  • /
  • source
  • /
  • road_grade_comparison.py
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge
swh:1:cnt:8cbf883b3c10a11df9c00a115a463730b1d4102d
directory badge
swh:1:dir:fa5362aac1bafdd90967fcc63c7c01b393027131

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
road_grade_comparison.py

"""
Date: Feb 13, 2024
Purpose: Compare outputs for the truck model with vs. without road grades
"""

# Import packages
import pandas as pd
import numpy as np
import scipy as scipy
import matplotlib.pyplot as plt
from matplotlib.ticker import (MultipleLocator, AutoMinorLocator)
import truck_model_tools_orig as truck_model_tools
import costing_tools_orig as costing_tools
import emissions_tools_orig as emissions_tools

new_rc_params = {'text.usetex': False,
"svg.fonttype": 'none'
}
plt.rcParams.update(new_rc_params)

KG_PER_TON = 1000
KG_PER_LB = 0.453592
SECONDS_PER_HOUR = 3600

######################################### Obtain model parameters #########################################
# Annual VMT from VIUS 2002
VMT = np.array(pd.read_csv('data/default_vmt.csv')['VMT (miles)'])

# Default drivecycle used for emission and costing analysis
# Source: Jones, R et al. (2023).Developing and Benchmarking a US Long-haul Drive Cycle forVehicle Simulations, Costing and Emissions Analysis
# https://docs.google.com/spreadsheets/d/1Q2uO-JHfwvGxir_PU8IO5zmo0vs4ooC_/edit?usp=sharing&ouid=102742490305620802920&rtpof=true&sd=true
df_drivecycle = truck_model_tools.extract_drivecycle_data('data/drivecycle.xlsx') #drive cycle as a dataframe
df_drivecycle_flat = truck_model_tools.extract_drivecycle_data('data/drivecycle_nograde.xlsx') #drive cycle with zero road grade everywhere
#print(df_drivecycle.head())

# Payload distribution from VIUS 2002
# Note: Dataset from VIUS 2002, filtered and cleaned by authors for this analysis. Source: 2002 Economic Census: Vehicle Inventory and Use Survey
# https://docs.google.com/spreadsheets/d/1Oe_jBIUb-kJ5yy9vkwaPgldVe4cloAtG/edit?usp=sharing&ouid=102742490305620802920&rtpof=true&sd=true
df_payload_distribution = pd.read_excel('data/payloaddistribution.xlsx')
df_payload_distribution['Payload (kg)'] = df_payload_distribution['Payload (lb)']*KG_PER_LB #payload distribution in kgs

# Read in default truck model parameters
parameters = truck_model_tools.read_parameters('data/default_truck_params.csv', 'data/default_economy_params.csv', 'data/constants.csv', 'data/default_vmt.csv')

# Read in default battery parameters
df_battery_data = pd.read_csv('data/default_battery_params.csv', index_col=0)

# Read in present LFP battery parameters
df_scenarios = pd.read_csv('data/scenario_data.csv', index_col=0)
e_present_density_LFP = float(df_scenarios['LFP battery energy density'].iloc[0])
eta_battery_LFP = df_battery_data['Value'].loc['LFP roundtrip efficiency']
alpha = 1 #for payload penalty factor calculations (alpha = 1 for base case, alpha = 2: complete dependency in payload measurements)

# Read in GHG emissions parameters
GHG_bat_unit_LFP = df_battery_data['Value'].loc['LFP manufacturing emissions'] #g CO2/kWh
replacements_LFP = df_battery_data['Value'].loc['LFP replacements']

# Read in costing parameters for present day scenario

# Motor and inverter cost is given per unit of drivetrain power rating (Motor peak power)
# DC-DC converter cost is given per unit of auxiliary power rating  (Auxiliary loads)
# Insurance cost is per unit of capital cost of a single BET (no payload penalty included). We computed from reported insurance cost (0.1969 $/mi) for a BET vehicle cost (0.9933 $/mi). Source: https://publications.anl.gov/anlpubs/2021/05/167399.pdf
# Glider cost from Jones, R et al. (2023). Developing and Benchmarking a US Long-haul Drive Cycle forVehicle Simulations, Costing and Emissions Analysis
capital_cost_unit = pd.DataFrame({'glider ($)': [float(df_scenarios['Cost of glider'].iloc[0])], 'motor and inverter ($/kW)': [float(df_scenarios['Cost of motor and inverter'].iloc[0])], 'DC-DC converter ($/kW)': [float(df_scenarios['Cost of DC-DC converter'].iloc[0])]})

operating_cost_unit = pd.DataFrame({'maintenance & repair ($/mi)': [float(df_scenarios['Maintenance and repair cost'].iloc[0])], 'labor ($/mi)': [float(df_scenarios['Labor cost'].iloc[0])], 'insurance ($/mi)': [float(df_scenarios['Insurance cost'].iloc[0])], 'misc ($/mi)': [float(df_scenarios[' Miscellaneous costs'].iloc[0])]})

electricity_unit = [float(df_scenarios['Electricity price'].iloc[0])]

SCC = [float(df_scenarios['Social cost of carbon'].iloc[0])] #social cost of carbon in $/ton CO2. Source: https://www.whitehouse.gov/wp-content/uploads/2021/02/TechnicalSupportDocument_SocialCostofCarbonMethaneNitrousOxide.pdf

battery_unit_cost_LFP = [float(df_scenarios['LFP battery unit cost'].iloc[0])] #LFP unit cost in $/kWh
###########################################################################################################


###################################### Analyze road grade distribution ####################################

########### Road grade over time ###########
fig, axs = plt.subplots(2, 1, figsize=(10, 7), gridspec_kw={'height_ratios': [2, 1]})  # 2 rows, 1 column
axs[0].set_title('Long-haul Drivecycle', fontsize=18)
axs[0].set_ylabel('Speed (m/s)', fontsize=16)
axs[1].set_ylabel('Road grade (%)', fontsize=16)
axs[1].set_xlabel('Drive time (h)', fontsize=16)
axs[0].tick_params(axis='both', which='major', labelsize=14)
axs[1].tick_params(axis='both', which='major', labelsize=14)

# Add major/minor ticks and gridlines
axs[0].xaxis.set_major_locator(MultipleLocator(2))
axs[0].xaxis.set_minor_locator(MultipleLocator(0.5))
axs[0].grid(which='minor', linestyle='--', linewidth=0.5, color='gray')
axs[0].grid(which='major', linestyle='-', linewidth=0.5, color='black')
axs[1].xaxis.set_major_locator(MultipleLocator(2))
axs[1].xaxis.set_minor_locator(MultipleLocator(0.5))
axs[1].grid(which='minor', linestyle='--', linewidth=0.5, color='gray')
axs[1].grid(which='major', linestyle='-', linewidth=0.5, color='black')

axs[0].plot(df_drivecycle['Time (s)']/SECONDS_PER_HOUR, df_drivecycle['Vehicle speed (m/s)'], color='blue')
axs[1].plot(df_drivecycle['Time (s)']/SECONDS_PER_HOUR, df_drivecycle['Road grade (%)'], color='green')
plt.savefig('plots/long_haul_drivecycle.png')
plt.close()
############################################

########### Road grade over time ###########
fig, ax = plt.subplots(figsize=(10, 6))
ax.tick_params(axis='both', which='major', labelsize=14)
n, bins, patches = ax.hist(df_drivecycle['Road grade (%)'], bins=100)
bin_width = bins[1] - bins[0]
ax.set_xlabel('Road grade (%)', fontsize=16)
ax.set_ylabel(f'Events / {bin_width:.2f}%', fontsize=16)

avg_grade = np.mean(df_drivecycle['Road grade (%)'])
std_grade = np.std(df_drivecycle['Road grade (%)'])
max_grade = np.max(df_drivecycle['Road grade (%)'])
min_grade = np.min(df_drivecycle['Road grade (%)'])

plt.text(0.6, 0.7, f'Average Grade: {avg_grade:.1f}%\nStandard deviation: {std_grade:.1f}%\n(Min, Max): ({min_grade:.1f}, {max_grade:.1f})%', transform=plt.gcf().transFigure, bbox=dict(facecolor='white', edgecolor='lightgray', alpha=0.7), fontsize=16)
plt.savefig('plots/long_haul_roadgrade_distribution.png')
############################################


###########################################################################################################

######################### Compare model results with vs. without road grade data ##########################

drivecycles = {
'With road grade': df_drivecycle,
'Without road grade': df_drivecycle_flat
}

results = {}
for drivecycle in drivecycles:
    results[drivecycle] = {}
    
    # Truck model results
    results[drivecycle]['Truck Model'] = {}
    
    vehicle_model_results_LFP = pd.DataFrame(columns = ['Energy battery (kWh)', 'Battery mass (lbs)', 'Fuel economy (kWh/mi)', 'Payload penalty factor', 'Total vehicle mass (lbs)'])
    m_bat, e_bat, mileage, m = truck_model_tools.truck_model(parameters).get_battery_size(drivecycles[drivecycle], eta_battery_LFP, e_present_density_LFP)
    payload_penalty_factor=truck_model_tools.payload(parameters).get_penalty(df_payload_distribution, m_bat, alpha)
    vehicle_model_results_LFP.loc[len(vehicle_model_results_LFP)] = [e_bat, m_bat/KG_PER_LB, mileage, payload_penalty_factor, m/KG_PER_LB]
    
    for model_component in vehicle_model_results_LFP:
        results[drivecycle]['Truck Model'][model_component] = vehicle_model_results_LFP[model_component].iloc[0]

    results[drivecycle]['Battery mass (lbs)'] = m_bat/KG_PER_LB
    results[drivecycle]['Battery capacity (kWh)'] = e_bat
    results[drivecycle]['Fuel economy (kWh/mi)'] = mileage
    results[drivecycle]['Total vehicle mass (lbs)'] = m
    results[drivecycle]['Payload penalty factor'] = payload_penalty_factor
    
    # Emissions model results
    results[drivecycle]['Emissions Model'] = {}
    
    GHG_emissions_LFP = emissions_tools.emission(parameters).get_WTW(vehicle_model_results_LFP, GHG_bat_unit_LFP,  replacements_LFP)
    
    for emissions_component in GHG_emissions_LFP:
        emissions_component_short = emissions_component.replace('GHGs ', '').split('(')[0].capitalize()
        results[drivecycle]['Emissions Model'][emissions_component_short] = GHG_emissions_LFP[emissions_component].iloc[0]

    # Costing model results
    results[drivecycle]['Costing Model'] = {}
    TCS_LFP = costing_tools.cost(parameters).get_TCS(vehicle_model_results_LFP, capital_cost_unit, battery_unit_cost_LFP, operating_cost_unit, electricity_unit, replacements_LFP, GHG_emissions_LFP, SCC)
    
    for cost_component in TCS_LFP:
        cost_component_short = cost_component.split('(')[0].replace('Total ', '').replace('GHGs ', '').replace(' ', '\n')
        cost_component_short = cost_component_short[0].upper() + cost_component_short[1:]
        results[drivecycle]['Costing Model'][cost_component_short] = TCS_LFP[cost_component].iloc[0]
    
# Evaluate percent differences between results with vs. without road grade
results['Percent Difference'] = {}
for category in ['Truck Model', 'Emissions Model', 'Costing Model']:
    results['Percent Difference'][category] = {}
    for result in results['With road grade'][category]:
        results['Percent Difference'][category][result] = 100*(results['Without road grade'][category][result] - results['With road grade'][category][result]) / results['With road grade'][category][result]

# Make plots for the emissions and costing categories to visualize the differences
for category in ['Emissions Model', 'Costing Model']:
    if category == 'Emissions Model':
        fig, axs = plt.subplots(2, 1, figsize=(7, 7), gridspec_kw={'height_ratios': [2, 1]})  # 2 rows, 1 column
    else:
        fig, axs = plt.subplots(2, 1, figsize=(10, 7), gridspec_kw={'height_ratios': [2, 1]})  # 2 rows, 1 column
    title_label = category.split(' ')[0]
    axs[0].set_title(f'Impact of Road Grade on Present Day {title_label}', fontsize=18)
    if category == 'Costing Model':
        axs[0].set_ylabel('Cost ($/mile)', fontsize=16)
    else:
        axs[0].set_ylabel('Emissions (gCO2e/mile)', fontsize=16)
    axs[1].set_ylabel('% Change Without Grade', fontsize=16)
    axs[0].tick_params(axis='both', which='major', labelsize=14)
    axs[1].tick_params(axis='both', which='major', labelsize=14)

    # Add major/minor ticks and gridlines
    axs[1].xaxis.set_major_locator(MultipleLocator(1))
    #axs[1].xaxis.set_minor_locator(MultipleLocator(0.25))
    axs[1].grid(which='major', linestyle='-', linewidth=0.5, color='black', axis='y')
    
    indices = np.arange(len(results['With road grade'][category].keys()))
    axs[0].bar(indices+0.875, results['With road grade'][category].values(), width=0.25, label = 'With road grade')
    axs[0].bar(indices+1.125, results['Without road grade'][category].values(), width=0.25, label = 'Without road grade')
    xmin, xmax = axs[0].get_xlim()
    axs[1].set_xlim(xmin, xmax)
    axs[0].set_xticks([])
    axs[1].set_xticks(indices + 1)
    axs[1].set_xticklabels(results['With road grade'][category].keys())
    axs[1].errorbar(indices+1, results['Percent Difference'][category].values(), xerr=0.25, fmt='o', color='green', linewidth=2)
    save_label = category.split(' ')[0].lower()
    axs[0].legend(fontsize=16)
    plt.tight_layout()
    plt.savefig(f'plots/results_comparison_{save_label}.png')
    plt.close()
    
###########################################################################################################

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API