Skip to content
Open
13 changes: 11 additions & 2 deletions ochre/Equipment/HVAC.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import datetime as dt
import numpy as np
import psychrolib
import re
import pandas as pd

from ochre.utils import OCHREException, convert, load_csv
from ochre.utils.units import kwh_to_therms
Expand Down Expand Up @@ -700,9 +702,16 @@ def __init__(self, control_type='Time', **kwargs):
rated_efficiency = kwargs.get('Rated Efficiency', '(Unknown Efficiency)')
multispeed_file = kwargs.get('multispeed_file', 'HVAC Multispeed Parameters.csv')
df_speed = load_csv(multispeed_file)
speed_params = df_speed.loc[(df_speed['HVAC Name'] == self.name) &
(df_speed['HVAC Efficiency'] == rated_efficiency) &
# Convert string to efficiency numbers, find the closest match
numeric_pattern = r"([-+]?(?:\d*\.?\d+))"
rated_efficiency_float = float(re.search(numeric_pattern, rated_efficiency).group())
df_speed['Temp_Efficiency_Float'] = pd.to_numeric(
df_speed['HVAC Efficiency'].str.extract(numeric_pattern)[0])
speed_params_subset = df_speed.loc[(df_speed['HVAC Name'] == self.name) &
(df_speed['Number of Speeds'] == self.n_speeds)]
closest_match_index = (speed_params_subset['Temp_Efficiency_Float'] - rated_efficiency_float).abs().idxmin()
df_speed = df_speed.drop(columns=['Temp_Efficiency_Float'])
speed_params = df_speed.loc[[closest_match_index]]
if not len(speed_params):
raise OCHREException(f'Cannot find multispeed parameters for {self.n_speeds}-speed {rated_efficiency} {self.name}')
assert len(speed_params) == 1
Expand Down
77 changes: 47 additions & 30 deletions ochre/utils/equipment.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
},
}

AIR_SOURCE_HEAT_RATED_ODB = round(convert(47.0, "degF", "degC"), 1) # Rated outdoor drybulb for air-source systems, heating
AIR_SOURCE_HEAT_RATED_IDB = round(convert(70.0, "degF", "degC"), 1) # Rated indoor drybulb for air-source systems, heating
AIR_SOURCE_COOL_RATED_ODB = round(convert(95.0, "degF", "degC"), 1) # Rated outdoor drybulb for air-source systems, cooling
AIR_SOURCE_COOL_RATED_OWB = round(convert(75.0, "degF", "degC"), 1) # Rated outdoor wetbulb for air-source systems, cooling
AIR_SOURCE_COOL_RATED_IDB = round(convert(80.0, "degF", "degC"), 1) # Rated indoor drybulb for air-source systems, cooling
AIR_SOURCE_COOL_RATED_IWB = round(convert(67.0, "degF", "degC"), 1) # Rated indoor wetbulb for air-source systems, cooling

def get_duct_info(ducts, zones, boundaries, construction, location, **kwargs):
# Get zone type from duct_zone and zone info
Expand Down Expand Up @@ -429,39 +435,50 @@ def calculate_duct_dse(hvac, ducts, climate_file='ASHRAE152_climate_data.csv',
return dse


def calc_c_d(is_heater, name, cop, number_of_speeds):
# Calculate coefficient of degredation (c_d) of equipment based on equipment type and EER/SEER/HSPF
# Should only affect cases with single speed and two speed compressor driven equipment (ASHP/AC)
# Capacity losses based on Jon Winkler's thesis and match what's in E+ with "Advanced Research Features for startup losses"
# https://drum.lib.umd.edu/bitstream/handle/1903/9493/Winkler_umd_0117E_10504.pdf?sequence=1&isAllowed=y page 200

if is_heater:
hspf = convert(cop, "W", "Btu/hour")
if number_of_speeds == 1:
if hspf < 7.0:
c_d = 0.2
else:
c_d = 0.11
elif number_of_speeds == 2:
c_d = 0.11
else:
c_d = 0.0 # Do no capacity degradation at startup, since this isn't on/off equipment
else: #cooling equipment
seer = convert(cop, "W", "Btu/hour")
if name =='Room AC':
c_d = 0.22
elif number_of_speeds == 1:
if seer < 13.0:
c_d = 0.2
else:
c_d = 0.07
elif number_of_speeds == 2:
c_d = 0.11
else:
c_d = 0.0 # Do no capacity degradation at startup, since this isn't on/off equipment
def calc_c_d(name, number_of_speeds):
# Calculate coefficient of degredation (c_d) of equipment based on RESNET HERS Addendum 82
if name == 'Room AC':
c_d = 0.22
elif number_of_speeds in [1, 2]:
c_d = 0.08
elif number_of_speeds == 4:
c_d = 0.4
else:
c_d = 0.0 # Do no capacity degradation at startup, since this isn't on/off equipment
return c_d


def calc_eer2_from_seer2(seer2, number_of_speeds):
# Regressions based on Central ACs & HPs in ENERGY STAR product lists
if number_of_speeds == 1:
return min(0.73 * seer2 + 1.47, seer2)
elif number_of_speeds == 2:
return min(0.63 * seer2 + 2.34, seer2)
elif number_of_speeds == 4:
return min(0.31 * seer2 + 6.45, seer2)


def calc_seer2_from_seer(seer, is_ducted):
if is_ducted:
return seer * 0.95 # ducted, split and packaged system assumption from OS-HPXML
else:
return seer


def calc_eer2_from_eer(eer, is_ducted):
if is_ducted:
return eer * 0.95 # ducted, split and packaged system assumption from OS-HPXML
else:
return eer


def calc_hspf2_from_hspf(hspf, is_ducted):
if is_ducted:
return hspf * 0.85 # ducted, split system assumption from OS-HPXML
else:
return hspf * 0.9


# Psychrometric functions for HVAC
# Originally taken from BEopt python code, author: shorowit
# see: https://cbr.nrel.gov/BEopt2/svn/trunk/Modeling/util.py
Expand Down
Loading