-
Notifications
You must be signed in to change notification settings - Fork 3
/
EvaluateTruckingEnergyDemand.py
248 lines (180 loc) · 8.5 KB
/
EvaluateTruckingEnergyDemand.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Apr 18 15:15:00 2024
@author: danikam
"""
import pandas as pd
import geopandas as gpd
from CommonTools import get_top_dir, mergeShapefile, saveShapefile
import os
LB_PER_TON = 2000.0
KWH_PER_MWH = 1000.0
TONS_PER_KILOTON = 1000.0
DAYS_PER_YEAR = 365.0
def save_links_without_geo(top_dir):
"""
Reads in the FAF5 highway links with freight flow and state data, and removes the geometry data to make the file smaller and easier to work with, and saves them to a csv file
Parameters
----------
top_dir (string): Path to top-level directory of the repository
Returns
-------
file_save_path (string): Path that the file gets saved to
"""
# Check if the output file exists, and only reproduce if it doesn't
file_save_path = f"{top_dir}/data/highway_assignment_links/highway_assignment_links_nomin_nogeo.csv"
if os.path.isfile(file_save_path):
return
else:
# Read in the merged shapefile for highway links with state and freight flow data
highway_links_gdf = gpd.read_file(
f"{top_dir}/data/highway_assignment_links/highway_assignment_links_nomin.shp"
)
# Drop the geometry column
highway_links_df = highway_links_gdf.drop(columns=["geometry"])
# Save to a csv file
highway_links_df.to_csv(
f"{top_dir}/data/highway_assignment_links/highway_assignment_links_nomin_nogeo.csv"
)
return
def evaluate_average_payload(highway_data_df):
"""
Evaluates the average payload carried per trip, in tons
Parameters
----------
highway_data_df (Pandas DataFrame): Pandas dataframe containing the info for each link
Returns
-------
highway_data_df (Pandas DataFrame): Pandas dataframe read in, with an additional column containing the average payload per trip (in lb)
"""
# Convert 'Tot Tons' from kilotons/year to tons/day
tons_per_day = highway_data_df["Tot Tons"] * TONS_PER_KILOTON / DAYS_PER_YEAR
trips_per_day = highway_data_df["Tot Trips"]
highway_data_df["Av Payload"] = (tons_per_day / trips_per_day) * LB_PER_TON
return highway_data_df
def evaluate_average_mileage(top_dir, highway_data_df):
"""
Evaluates the average mileage per trip for each link based on the average payload carried.
Parameters
----------
top_dir (string): Path to top-level directory of the repository
highway_data_df (Pandas DataFrame): Pandas dataframe containing the info for each link
Returns
-------
highway_data_df (Pandas DataFrame): Pandas dataframe read in, with an additional column containing the mileage
NOTE: The assessment of mileage uses a linear fit of mileage vs. payload evaluated using NACFE pilot data from the Tesla Semi. The code used to evaluate this best fit line can be found in the following repo: https://github.com/mcsc-impact-climate/PepsiCo_NACFE_Analysis
"""
# Read in the linear parameters for the linear approximation of mileage vs. payload
linear_params = pd.read_csv(
f"{top_dir}/data/payload_vs_mileage_best_fit_params.csv"
)
payloads = highway_data_df["Av Payload"]
slope = linear_params["slope (kWh/lb-mi)"].iloc[0]
intercept = linear_params["b (kWh/mi)"].iloc[0]
highway_data_df["Av Mileage"] = slope * payloads + intercept
return highway_data_df
def evaluate_annual_e_demand_link(top_dir, highway_data_df, charging_efficiency=0.92):
"""
Evaluates the annual energy demand associated with trucks traveling over each highway link
Parameters
----------
top_dir (string): Path to top-level directory of the repository
highway_data_df (Pandas DataFrame): Pandas dataframe containing the info for each link
charging_efficiency (float): Efficiency with which power taken from the grid is converted into battery power
Returns
-------
highway_data_df (Pandas DataFrame): Pandas dataframe read in, with an additional column containing the annual energy demand
"""
# Convert truck trips per day to trips per year
trips_per_year = highway_data_df["Tot Trips"] * DAYS_PER_YEAR
highway_data_df["An E Dem"] = (
highway_data_df["Av Mileage"]
* highway_data_df["len_miles"]
* trips_per_year
/ KWH_PER_MWH
) / charging_efficiency
return highway_data_df
def evaluate_annual_e_demand_state(highway_data_df):
"""
Aggregates the annual energy demand for all highway links in each state to evaluate the total annual energy demand associated with operating an EV for each state
Parameters
----------
highway_data_df (Pandas DataFrame): Pandas dataframe containing the info for each link
Returns
-------
highway_data_df (Pandas DataFrame): Pandas dataframe read in, with an additional column containing the annual energy demand
"""
state_sum_df = highway_data_df.groupby("STATE")["An E Dem"].sum().reset_index()
# Convert the state column name to STUSPS to match the state border file
state_sum_df = state_sum_df.rename(columns={"STATE": "STUSPS"})
# Drop Hawaii because it essentially has no highways
state_sum_df = state_sum_df[state_sum_df["STUSPS"] != "HI"]
return state_sum_df
def add_gen_cap_ratios(top_dir, state_data_df):
"""
Adds in ratios of energy demand for electrified trucking to:
- total annual electricity generated (Ann_Gen column)
- total theoretical annual electricity generating capacity (based on net summer capacity) (Ann_Cap column)
- difference between theoretical maximum electricity generating capacity and annual electricity generated (Ann_Diff column)
Parameters
----------
top_dir (string): Path to top-level directory of the repository
highway_data_df (Pandas DataFrame): Pandas dataframe containing the info for each link
Returns
-------
highway_data_df (Pandas DataFrame): Pandas dataframe read in, with additional columns containing the ratios
"""
# Read in the dataframe containing annual electricity generated and theoretical max generating capacity for each state
gen_cap_data_df = gpd.read_file(
f"{top_dir}/data/eia2022_state_merged/gen_cap_2022_state_merged.shp",
columns=["STUSPS", "Ann_Gen", "Ann_Cap", "Ann_Diff"],
)
# Drop the geometry info
gen_cap_data_df = gen_cap_data_df.drop(columns=["geometry"])
state_data_df = state_data_df.merge(
gen_cap_data_df, on="STUSPS", how="left"
).dropna()
# Evaluate the ratios of interest
state_data_df["Perc Gen"] = (
100 * state_data_df["An E Dem"] / state_data_df["Ann_Gen"]
)
state_data_df["Perc Cap"] = (
100 * state_data_df["An E Dem"] / state_data_df["Ann_Cap"]
)
state_data_df["Perc Diff"] = (
100 * state_data_df["An E Dem"] / state_data_df["Ann_Diff"]
)
return state_data_df
def main():
# Get the path to the top level of the Git repo
top_dir = get_top_dir()
# Save the highway links without geometry info to a csv file (can comment this out if the file has already been produced)
save_links_without_geo(top_dir)
# Open the highway link data without geometry info
highway_data_df = pd.read_csv(
f"{top_dir}/data/highway_assignment_links/highway_assignment_links_nomin_nogeo.csv"
)
# Evaluate the average payload carried per trip for each link
highway_data_df = evaluate_average_payload(highway_data_df)
# Evaluate the average truck mileage per trip (in kWh/mile) based on the payload (calibrated to the Tesla Semi)
highway_data_df = evaluate_average_mileage(top_dir, highway_data_df)
# Evaluate the annual energy demand per link
highway_data_df = evaluate_annual_e_demand_link(top_dir, highway_data_df)
# Evaluate the aggregated annual energy demand per state
state_data_df = evaluate_annual_e_demand_state(highway_data_df)
# Add in ratios of annual energy demand to annual electricity generated and theoretical maximum generating capacity
state_data_df = add_gen_cap_ratios(top_dir, state_data_df)
# Merge the aggregated annual energy demand per state with the state borders
merged_state_data_gdf = mergeShapefile(
state_data_df, f"{top_dir}/data/state_boundaries/tl_2012_us_state.shp", "STUSPS"
).dropna()
merged_state_data_gdf = merged_state_data_gdf.drop(
columns=["ALAND", "AWATER", "Shape_Area"]
)
# Save the merged shapefile
saveShapefile(
merged_state_data_gdf,
f"{top_dir}/data/trucking_energy_demand/trucking_energy_demand.shp",
)
main()