Source code for prereise.gather.demanddata.bldg_electrification.ff2elec_profile_generator_dhw
import os
import numpy as np
import pandas as pd
from prereise.gather.demanddata.bldg_electrification import const
[docs]def func_dhw_cop(temp_c, model):
"""Generate COPs for input hourly temperatures with a given heat pump model
:param list temp_c: hourly temperatures
:param str model : type of heat pump
:return list cop: Coefficient of performance list
"""
cop_base = [0] * len(temp_c)
model_params = const.hp_param_dhw.set_index("model").loc[model]
t_low = model_params.loc["t_low"]
t_cp = model_params.loc["t_cp"]
t_high = model_params.loc["t_high"]
cop_er = model_params.loc["cop_er"]
cop_low = model_params.loc["cop_low"]
cop_cp = model_params.loc["cop_cp"]
cop_high = model_params.loc["cop_high"]
for i in range(len(temp_c)):
if temp_c[i] <= t_cp:
cop_base[i] = ((temp_c[i] - t_low) / (t_cp - t_low)) * (
cop_cp - cop_low
) + cop_low
else:
cop_base[i] = ((temp_c[i] - t_cp) / (t_high - t_cp)) * (
cop_high - cop_cp
) + cop_cp
cop = [max(c, cop_er) for c in cop_base]
return cop
# Create folder for output profiles
os.makedirs("Profiles", exist_ok=True)
[docs]def generate_dhw_profiles(
yr_temps=const.base_year,
states=None,
bldg_class="res",
hp_model="advperfhp",
):
"""Create time series for electricity loads from converting fossil fuel
water heating to heat pump water heaters
:param int yr_temps: year for temperature, defaults to ``const.base_year``.
:param list states: list of states to loop through, defaults to None, in which
case ``const.state_list`` is used.
:param str bldg_class: type of building. Default is residential.
:param str hp_model: type of heat pump. Default is advanced performance cold
climate heat pump.
:raises TypeError:
if ``yr_temps`` is not an int.
if ``bldg_class`` and ``hp_model`` are not str.
:raises ValueError:
if ``bldg_class`` is not 'res' or 'com'
if ``hp_model`` is not 'advperfhp', 'midperfhp' or 'futurehp'
"""
if not isinstance(yr_temps, int):
raise TypeError("yr_temps must be an int")
if states is None:
states = const.state_list
if not isinstance(bldg_class, str):
raise TypeError("bldg_class must be a str")
if not isinstance(hp_model, str):
raise TypeError("hp_model must be a str")
if yr_temps not in const.yr_temps_all:
raise ValueError(
"yr_temps must be among available temperature years: "
"{const.yr_temps_first}-{const.yr_temps_last}"
)
if bldg_class not in ["res", "com"]:
raise ValueError(
"bldg_class must be one of: \n", "res: residential \n", "com: commercial"
)
if hp_model not in ["advperfhp", "midperfhp", "futurehp"]:
raise ValueError(
"hp_model must be one of: \n",
"midperfhp: mid-performance cold climate heat pump \n",
"advperfhp: advanced performance cold climate heat pump \n",
"futurehp: future performance heat pump",
)
# parse user data
temp_ref_it = const.temp_ref[bldg_class]
dhw_mult = const.dhw_com_mult if bldg_class == "com" else const.dhw_res_mult
dir_path = os.path.dirname(os.path.abspath(__file__))
state_slopes = pd.read_csv(
os.path.join(dir_path, "data", f"state_slopes_ff_{bldg_class}.csv"),
index_col="state",
)
# Loop through states to create profile outputs
for state in states:
# Load and subset relevant data for the state
puma_data_it = const.puma_data.query("state == @state")
temps_pumas_it = pd.read_csv(
f"https://besciences.blob.core.windows.net/datasets/bldg_el/pumas/{yr_temps}/temps/temps_pumas_{state}_{yr_temps}.csv"
)
hours = pd.date_range(
f"{yr_temps}-01-01", periods=len(temps_pumas_it), freq="H", tz="UTC"
)
# Load DHW constant and slope for state_it
dhw_const_mmbtu_m2 = state_slopes.loc[state, "dhw_const"]
dhw_slope_mmbtu_m2_degC = ( # noqa: N806
state_slopes.loc[state, "dhw_slope"] if bldg_class == "res" else 0
)
# Based on the timezone for each PUMA
# the dhw multiplier list is arranged to match local time
dhw_mult_df = puma_data_it.apply(
lambda x: pd.Series(
hours.tz_convert(x.timezone).hour.map(lambda y: dhw_mult[y])
),
axis=1,
)
temps_pumas_transpose_it = temps_pumas_it.T
temp_dev_from_ref_degC = temps_pumas_transpose_it.applymap( # noqa: N806
lambda x: temp_ref_it - x
)
cop_inverse = temps_pumas_transpose_it.apply(
lambda x: np.reciprocal(func_dhw_cop(x, hp_model)), 1
)
cop_list_df = pd.DataFrame(
cop_inverse.to_list(), list(temp_dev_from_ref_degC.index)
)
temp_dev_from_ref_degC_slope = ( # noqa: N806
temp_dev_from_ref_degC * dhw_slope_mmbtu_m2_degC
+ dhw_const_mmbtu_m2 # noqa: N806
)
temp_dev_cop = (
temp_dev_from_ref_degC_slope.multiply(cop_list_df) * const.eff_dhw_ff_base
) # mmbtu-heat pump electricity load per m2 for 100% of puma floor area
temp_dev_cop *= dhw_mult_df
ff_area_scalars = list(
puma_data_it[f"{bldg_class}_area_{const.base_year}_m2"]
* puma_data_it[f"frac_ff_dhw_{bldg_class}_{const.base_year}"]
* (const.conv_mmbtu_to_kwh * const.conv_kw_to_mw)
)
elec_dhw_ff2hp_puma_mw_it = temp_dev_cop.mul(ff_area_scalars, axis=0).T
elec_dhw_ff2hp_puma_mw_it.columns = temps_pumas_it.columns
# Export profile file as CSV
elec_dhw_ff2hp_puma_mw_it.to_csv(
os.path.join(
os.path.dirname(__file__),
"Profiles",
f"elec_dhw_ff2hp_{bldg_class}_{state}_{yr_temps}_{hp_model}_mw.csv",
),
index=False,
)