Skip to content

Commit

Permalink
Merge pull request #343 from Breakthrough-Energy/dmuldrew/invariant_i…
Browse files Browse the repository at this point in the history
…ntegration_test

refactor: standardize trip filtering; add an invariant model integration test
  • Loading branch information
dmuldrew authored Feb 9, 2023
2 parents 0d06ef5 + 69f1e0d commit 346bb4a
Show file tree
Hide file tree
Showing 10 changed files with 528 additions and 212 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def calculate_optimization(
segsum = np.sum(seg)
segcum = np.cumsum(seg)

f = np.array(rates) / charging_efficiency
f = np.array(rates)

# form all the constraints
# equality constraint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
"Total Vehicle Trips": "total_trips",
"Dwell Time": "dwell_time",
"Trip End": "trip_end",
"Total Vehicle Miles": "total vehicle miles traveled",
}

safety_coefficient = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,49 +297,32 @@ def generate_daily_weighting(year, area_type="urban"):
return daily_values


def get_total_daily_vmt(data: pd.DataFrame, input_day, daily_values):
def get_total_daily_vmt(data: pd.DataFrame, input_day, veh_type):
"""Calculates the total VMT and total vehicles for for each day of the model year,
based on if the day is a weekend (1) or weekday (2).
:param pandas.DataFrame data: the data returned from :func:`load_data`.
:param numpy.ndarray input_day: day of the week for each day in the year derived
from :func:`get_input_day`.
:param pandas.Series daily_values: daily weight factors returned from
:func:`generate_daily_weighting`.
:param pandas.Series veh_type: vehicle class (LDV, LDT, MDV, HDV)
:raises ValueError: Vehicle class is not specified
:return: (*np.array*) -- an array where each element is the daily VMT and total
vehicles for that day.
"""
weekend_vmt = data.loc[data["If Weekend"] == 1, "trip_miles"].sum()
weekday_vmt = data.loc[data["If Weekend"] == 2, "trip_miles"].sum()

annual_vmt = 0
for i in range(len(input_day)):
if input_day[i] == 1:
annual_vmt += weekend_vmt
elif input_day[i] == 2:
annual_vmt += weekday_vmt

daily_vmt_total = daily_values * annual_vmt

return daily_vmt_total


def get_total_hdv_daily_vmt(data: pd.DataFrame, veh_range):
"""Calculates the total VMT and total vehicles for for each day of the model year,
based on vehicle range.
:param pandas.DataFrame data: the data returned from :func:`load_data`.
:param int veh_range: 100, 200, or 300, represents how far vehicle can travel on single charge.
:return: (*np.array*) -- an array where each element is the daily VMT and total
vehicles for that day.
:raises ValueError: if ``veh_range`` is not 100, 200, or 300
"""
allowable_ranges = {100, 200, 300}
if veh_range not in allowable_ranges:
raise ValueError(f"veh_range must be one of {allowable_ranges}")

range_vmt = data["trip_miles"].copy()
range_vmt[data["Total Vehicle Miles"] > veh_range] = 0
daily_vmt_total = sum(range_vmt) * np.ones(365)
if veh_type in {"mdv", "hdv"}:
daily_vmt_total = data.loc[:, "trip_miles"].sum() * np.ones(len(input_day))
elif veh_type in {"ldv", "ldt"}:
weekend_vmt = data.loc[data["If Weekend"] == 1, "trip_miles"].sum()
weekday_vmt = data.loc[data["If Weekend"] == 2, "trip_miles"].sum()

daily_vmt_total = []
for i in range(len(input_day)):
if input_day[i] == 1:
daily_vmt_total.append(weekend_vmt)
elif input_day[i] == 2:
daily_vmt_total.append(weekday_vmt)
else:
raise ValueError("Vehicle class is not specified")

return daily_vmt_total
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def generate_bev_vehicle_profiles(
for geographic_area, bev_vmt in geographic_area_bev_vmt.items():
if charging_strategy == "immediate":
if veh_type.lower() in {"ldv", "ldt"}:
normalized_demand = immediate.immediate_charging(
normalized_demand, _, _ = immediate.immediate_charging(
census_region=census_region,
model_year=projection_year,
veh_range=veh_range,
Expand All @@ -117,7 +117,7 @@ def generate_bev_vehicle_profiles(
filepath=vehicle_trip_data_filepath,
)
elif veh_type.lower() in {"mdv", "hdv"}:
normalized_demand = immediate_charging_HDV.immediate_charging(
normalized_demand, _, _ = immediate_charging_HDV.immediate_charging(
model_year=projection_year,
veh_range=veh_range,
power=power,
Expand All @@ -137,7 +137,7 @@ def generate_bev_vehicle_profiles(
)

elif charging_strategy == "smart":
final_demand = smart_charging.smart_charging(
final_demand, _, _ = smart_charging.smart_charging(
census_region=census_region,
model_year=projection_year,
veh_range=veh_range,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
import pandas as pd

from prereise.gather.demanddata.transportation_electrification import const, data_helper

Expand Down Expand Up @@ -109,6 +110,7 @@ def immediate_charging(
veh_type,
filepath,
trip_strategy=1,
input_day=None,
):
"""Immediate charging function
Expand All @@ -133,9 +135,26 @@ def immediate_charging(
elif veh_type.lower() == "hdv":
trips = data_helper.load_hdv_data("hhdv", filepath)

# filter for cyclical trips
filtered_census_data = pd.DataFrame(columns=const.nhts_census_column_names)
i = 0
while i < len(trips):
total_trips = int(trips.iloc[i, trips.columns.get_loc("total_trips")])
# copy one vehicle information to the block
individual = trips.iloc[i : i + total_trips].copy()
if individual["why_from"].iloc[0] == individual["dwell_location"].iloc[-1]:
filtered_census_data = pd.concat(
[filtered_census_data, individual], ignore_index=True
)
i += total_trips
trips = filtered_census_data

#####

# Constants
kwhmi = data_helper.get_kwhmi(model_year, veh_type, veh_range)
battery_capacity = kwhmi * veh_range

input_day = data_helper.get_input_day(data_helper.get_model_year_dti(model_year))

# updates the weekend and weekday values in the nhts data
Expand Down Expand Up @@ -182,14 +201,29 @@ def immediate_charging(
]
trips["charging_allowed"] = trips[allowed_cols].apply(all, axis=1)

trips["dwell_charging"] = (
trips["charging_allowed"] * trips["dwell_time"] * power * charging_efficiency
)

grouped_trips = trips.groupby("vehicle_number")
for vehicle_num, group in grouped_trips:
trips.loc[group.index, "max_charging"] = trips.loc[
group.index, "dwell_charging"
].sum()
trips.loc[group.index, "required_charging"] = (
trips.loc[group.index, "trip_miles"].sum() * kwhmi
)

# Filter for whenever available charging is insufficient to meet required charging
trips = trips.loc[(trips["required_charging"] <= trips["max_charging"])]

# Filter by vehicle range
trips = trips.loc[trips["total vehicle miles traveled"] < veh_range * const.ER]

# Evaluate weekend vs. weekday for each trip
data_day = data_helper.get_data_day(trips)
weekday_trips = trips.loc[
(data_day == 2) & (trips["total vehicle miles traveled"] < veh_range * const.ER)
].copy()
weekend_trips = trips.loc[
(data_day == 1) & (trips["total vehicle miles traveled"] < veh_range * const.ER)
].copy()
weekday_trips = trips.loc[data_day == 2].copy()
weekend_trips = trips.loc[data_day == 1].copy()

# Calculate the charge times and SOC for each trip, then resample resolution
calculate_charging(
Expand All @@ -207,6 +241,8 @@ def immediate_charging(
flag_translation = {1: "weekend", 2: "weekday"}
for i, weekday_flag in enumerate(input_day):
daily_profile = daily_resampled_profiles[flag_translation[weekday_flag]]
# print(f"day: {i}")
# print(f"daily sum: {np.sum(daily_profile)}")

# create wrap-around indexing function
trip_window_indices = np.arange(i * 24, i * 24 + 72) % len(model_year_profile)
Expand All @@ -217,7 +253,12 @@ def immediate_charging(
# Normalize the output so that it sums to 1
summed_profile = model_year_profile / model_year_profile.sum()

return summed_profile
output_load_sum_list = [
np.sum(daily_resampled_profiles["weekend"]),
np.sum(daily_resampled_profiles["weekday"]),
]

return summed_profile, output_load_sum_list, trips


def adjust_bev(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def resample_daily_charging(trips, charging_power):
return output_array


def immediate_charging(
def immediate_hdv_charging(
model_year,
veh_range,
power,
Expand Down Expand Up @@ -256,6 +256,25 @@ def immediate_charging(

trips.loc[trips["charging_allowed"], "charging power"] = power

trips["dwell_charging"] = (
trips["charging_allowed"] * trips["dwell_time"] * power * charging_efficiency
)

grouped_trips = trips.groupby("vehicle_number")
for vehicle_num, group in grouped_trips:
trips.loc[group.index, "max_charging"] = trips.loc[
group.index, "dwell_charging"
].sum()
trips.loc[group.index, "required_charging"] = (
trips.loc[group.index, "trip_miles"].sum() * kwhmi
)

# Filter for whenever available charging is insufficient to meet required charging
trips = trips.loc[(trips["required_charging"] <= trips["max_charging"])]

# Filter for vehicle's battery range
trips = trips.loc[(trips["total vehicle miles traveled"] < veh_range)]

hdv_trips = trips.copy()

# Calculate the charge times and SOC for each trip, then resample resolution
Expand All @@ -277,4 +296,4 @@ def immediate_charging(
# Normalize the output so that it sums to 1
summed_profile = model_year_profile / model_year_profile.sum()

return summed_profile
return summed_profile, daily_profile, trips
Loading

0 comments on commit 346bb4a

Please sign in to comment.