From b0898dc0c0c0f3ede41ab57a3a00142aadd1e4bb Mon Sep 17 00:00:00 2001 From: "Wan, Hanlong" Date: Tue, 15 Oct 2024 10:47:50 -0700 Subject: [PATCH 1/7] new-ieer --- copper/library.py | 6 +++ copper/unitarydirectexpansion.py | 66 ++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/copper/library.py b/copper/library.py index 83c3ac2..eec294d 100644 --- a/copper/library.py +++ b/copper/library.py @@ -46,6 +46,8 @@ def __init__(self, path=chiller_lib, rating_std="", export=False): and not "indoor_fan_curve_coef" in p and not "indoor_fan_curve" in p and not "indoor_fan_power_unit" in p + and not "compressor_stage" in p + and not "compressor_stage_input" in p ): obj_args[p] = vals[p] elif ( @@ -123,6 +125,8 @@ def load_obj(self, data): and not "indoor_fan_curve_coef" in p and not "indoor_fan_curve" in p and not "indoor_fan_power_unit" in p + and not "compressor_stage" in p + and not "compressor_stage_input" in p ): obj_args[p] = data[p] @@ -199,6 +203,8 @@ def find_set_of_curves_from_lib(self, filters=[], part_eff_flag=False): "indoor_fan_speeds", "indoor_fan_curve_coef", "indoor_fan_power_unit", + "compressor_stage", + "compressor_stage_input", ] # Set the equipment properties diff --git a/copper/unitarydirectexpansion.py b/copper/unitarydirectexpansion.py index 5bf45a5..f80f083 100644 --- a/copper/unitarydirectexpansion.py +++ b/copper/unitarydirectexpansion.py @@ -61,6 +61,8 @@ def __init__( indoor_fan_speeds=1, indoor_fan_curve=False, indoor_fan_power_unit="kW", + compressor_stage_input=False, + compressor_stage=[0.3, 0.6], ): global log_fan self.type = "UnitaryDirectExpansion" @@ -172,6 +174,8 @@ def __init__( self.indoor_fan_curve_coef = indoor_fan_curve_coef self.indoor_fan_power_unit = indoor_fan_power_unit self.indoor_fan_curve = indoor_fan_curve + self.compressor_stage = compressor_stage + self.compressor_stage_input = compressor_stage_input # Define rated temperatures # air entering drybulb, air entering wetbulb, entering condenser temperature, leaving condenser temperature aed, self.aew, ect, lct = self.get_rated_temperatures() @@ -365,17 +369,9 @@ def calc_rated_eff( # Iterate through the different sets of rating conditions to calculate IEER ieer = 0 - for red_cap_num in range(num_of_reduced_cap): - # Determine the outdoor air conditions based on AHRI Standard - if reduced_plr[red_cap_num] > 0.444: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( - 5.0 + 30.0 * reduced_plr[red_cap_num] - ) - else: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ - eqp_type - ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] + def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): + """inner function to calculate reduced eer.""" # Calculate capacity at rating conditions tot_cap_temp_mod_fac = cap_f_t.evaluate( equipment_references[eqp_type][std][ @@ -384,7 +380,7 @@ def calc_rated_eff( outdoor_unit_inlet_air_dry_bulb_temp_reduced, ) load_factor_gross = min( - 1.0, (reduced_plr[red_cap_num] / tot_cap_temp_mod_fac) + 1.0, (ratio / tot_cap_temp_mod_fac) ) # Load percentage * Rated gross capacity / Available gross capacity indoor_fan_power = self.calc_fan_power(load_factor_gross) / 1000 net_cooling_cap_reduced = ( @@ -407,9 +403,9 @@ def calc_rated_eff( raise ValueError("Input COP is 0!") # "Load Factor" (as per AHRI Standard) which is analogous to PLR - if reduced_plr[red_cap_num] < 1.0: + if ratio < 1.0: load_factor = ( - reduced_plr[red_cap_num] + ratio # reduced_plr[red_cap_num] * net_cooling_cap_rated / net_cooling_cap_reduced if net_cooling_cap_reduced > 0.0 @@ -432,14 +428,54 @@ def calc_rated_eff( eer_reduced = (load_factor * net_cooling_cap_reduced) / ( load_factor * elec_power_reduced_cap + indoor_fan_power ) + return eer_reduced + for red_cap_num in range(num_of_reduced_cap): + # Determine the outdoor air conditions based on AHRI Standard + if reduced_plr[red_cap_num] > 0.444: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( + 5.0 + 30.0 * reduced_plr[red_cap_num] + ) + else: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ + eqp_type + ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] + if ( + self.compressor_stage_input + and (reduced_plr[red_cap_num] > min(self.compressor_stage)) + and (reduced_plr[red_cap_num] < max(self.compressor_stage)) + ): + lower_value = None + upper_value = None + for value in self.compressor_stage: + if value <= red_cap_num: + lower_value = value + elif value > red_cap_num and upper_value is None: + upper_value = value + # interpolation + eer_reduced = ( + ( + cal_reduced_eer( + lower_value, outdoor_unit_inlet_air_dry_bulb_temp_reduced + ) + - cal_reduced_eer( + upper_value, outdoor_unit_inlet_air_dry_bulb_temp_reduced + ) + ) + / (lower_value - upper_value) + ) * (reduced_plr[red_cap_num] - upper_value) + cal_reduced_eer( + upper_value, outdoor_unit_inlet_air_dry_bulb_temp_reduced + ) + else: + eer_reduced = cal_reduced_eer( + reduced_plr[red_cap_num], + outdoor_unit_inlet_air_dry_bulb_temp_reduced, + ) if eff_type == "full": ieer = eer_reduced break - # Update IEER ieer += weighting_factor[red_cap_num] * eer_reduced - # Convert efficiency to original unit unless specified if unit != "cop": ieer = Units(value=ieer, unit="cop") From b32f55db96a5480e524d13d8df6d93314b4eaf71 Mon Sep 17 00:00:00 2001 From: "Wan, Hanlong" Date: Wed, 30 Oct 2024 14:51:24 -0700 Subject: [PATCH 2/7] replace files --- DISCLAMER.md | 8 ++ copper/chiller.py | 40 ++++++--- fasd | 215 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+), 12 deletions(-) create mode 100644 DISCLAMER.md create mode 100644 fasd diff --git a/DISCLAMER.md b/DISCLAMER.md new file mode 100644 index 0000000..b3ea641 --- /dev/null +++ b/DISCLAMER.md @@ -0,0 +1,8 @@ +This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor the Contractor, nor any or their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. +Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. +PACIFIC NORTHWEST NATIONAL LABORATORY +operated by +BATTELLE +for the +UNITED STATES DEPARTMENT OF ENERGY +under Contract DE-AC05-76RL01830 \ No newline at end of file diff --git a/copper/chiller.py b/copper/chiller.py index 79d2149..af2a657 100644 --- a/copper/chiller.py +++ b/copper/chiller.py @@ -528,17 +528,33 @@ def get_ranges(self): """ norm_val = {"ect_lwt": self.ref_ect, "lct_lwt": self.ref_lct}[self.model] - ranges = { - "eir-f-t": { - "vars_range": [(4, 10), (10.0, 40.0)], - "normalization": (self.ref_lwt, norm_val), - }, - "cap-f-t": { - "vars_range": [(4, 10), (10.0, 40.0)], - "normalization": (self.ref_lwt, norm_val), - }, - "eir-f-plr": {"vars_range": [(0.0, 1.0)], "normalization": (1.0)}, - } + if self.model == "ect_lwt": + ranges = { + "eir-f-t": { + "vars_range": [(4, 10), (10.0, 40.0)], + "normalization": (self.ref_lwt, norm_val), + }, + "cap-f-t": { + "vars_range": [(4, 10), (10.0, 40.0)], + "normalization": (self.ref_lwt, norm_val), + }, + "eir-f-plr": {"vars_range": [(0.0, 1.0)], "normalization": (1.0)}, + } + elif self.model == "lct_lwt": + ranges = { + "eir-f-t": { + "vars_range": [(4, 10), (10.0, 45.0)], + "normalization": (self.ref_lwt, norm_val), + }, + "cap-f-t": { + "vars_range": [(4, 10), (10.0, 45.0)], + "normalization": (self.ref_lwt, norm_val), + }, + "eir-f-plr": { + "vars_range": [(10, 45.0), (0, 1)], + "normalization": (norm_val, 1.0), + }, + } return ranges @@ -666,4 +682,4 @@ def get_seed_curves(self, lib=None, filters=None, csets=None): return curves def get_ref_vars_for_aggregation(self): - return ["ref_cap", "full_eff"] + return ["ref_cap", "full_eff"] \ No newline at end of file diff --git a/fasd b/fasd new file mode 100644 index 0000000..beca6c7 --- /dev/null +++ b/fasd @@ -0,0 +1,215 @@ +diff --git a/DISCLAMER.md b/DISCLAMER.md +deleted file mode 100644 +index b3ea641..0000000 +--- a/DISCLAMER.md ++++ /dev/null +@@ -1,8 +0,0 @@ +-This material was prepared as an account of work sponsored by an agency of the United States Government. Neither the United States Government nor the United States Department of Energy, nor the Contractor, nor any or their employees, nor any jurisdiction or organization that has cooperated in the development of these materials, makes any warranty, express or implied, or assumes any legal liability or responsibility for the accuracy, completeness, or usefulness or any information, apparatus, product, software, or process disclosed, or represents that its use would not infringe privately owned rights. +-Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise does not necessarily constitute or imply its endorsement, recommendation, or favoring by the United States Government or any agency thereof, or Battelle Memorial Institute. The views and opinions of authors expressed herein do not necessarily state or reflect those of the United States Government or any agency thereof. +-PACIFIC NORTHWEST NATIONAL LABORATORY +-operated by +-BATTELLE +-for the +-UNITED STATES DEPARTMENT OF ENERGY +-under Contract DE-AC05-76RL01830 +\ No newline at end of file +diff --git a/copper/chiller.py b/copper/chiller.py +index 43dcde6..79d2149 100644 +--- a/copper/chiller.py ++++ b/copper/chiller.py +@@ -528,33 +528,17 @@ class Chiller(Equipment): + """ + norm_val = {"ect_lwt": self.ref_ect, "lct_lwt": self.ref_lct}[self.model] +  +- if self.model == "ect_lwt": +- ranges = { +- "eir-f-t": { +- "vars_range": [(4, 10), (10.0, 40.0)], +- "normalization": (self.ref_lwt, norm_val), +- }, +- "cap-f-t": { +- "vars_range": [(4, 10), (10.0, 40.0)], +- "normalization": (self.ref_lwt, norm_val), +- }, +- "eir-f-plr": {"vars_range": [(0.0, 1.0)], "normalization": (1.0)}, +- } +- elif self.model == "lct_lwt": +- ranges = { +- "eir-f-t": { +- "vars_range": [(4, 10), (10.0, 45.0)], +- "normalization": (self.ref_lwt, norm_val), +- }, +- "cap-f-t": { +- "vars_range": [(4, 10), (10.0, 45.0)], +- "normalization": (self.ref_lwt, norm_val), +- }, +- "eir-f-plr": { +- "vars_range": [(10, 45.0), (0, 1)], +- "normalization": (norm_val, 1.0), +- }, +- } ++ ranges = { ++ "eir-f-t": { ++ "vars_range": [(4, 10), (10.0, 40.0)], ++ "normalization": (self.ref_lwt, norm_val), ++ }, ++ "cap-f-t": { ++ "vars_range": [(4, 10), (10.0, 40.0)], ++ "normalization": (self.ref_lwt, norm_val), ++ }, ++ "eir-f-plr": {"vars_range": [(0.0, 1.0)], "normalization": (1.0)}, ++ } +  + return ranges +  +diff --git a/copper/library.py b/copper/library.py +index 83c3ac2..eec294d 100644 +--- a/copper/library.py ++++ b/copper/library.py +@@ -46,6 +46,8 @@ class Library: + and not "indoor_fan_curve_coef" in p + and not "indoor_fan_curve" in p + and not "indoor_fan_power_unit" in p ++ and not "compressor_stage" in p ++ and not "compressor_stage_input" in p + ): + obj_args[p] = vals[p] + elif ( +@@ -123,6 +125,8 @@ class Library: + and not "indoor_fan_curve_coef" in p + and not "indoor_fan_curve" in p + and not "indoor_fan_power_unit" in p ++ and not "compressor_stage" in p ++ and not "compressor_stage_input" in p + ): + obj_args[p] = data[p] +  +@@ -199,6 +203,8 @@ class Library: + "indoor_fan_speeds", + "indoor_fan_curve_coef", + "indoor_fan_power_unit", ++ "compressor_stage", ++ "compressor_stage_input", + ] +  + # Set the equipment properties +diff --git a/copper/unitarydirectexpansion.py b/copper/unitarydirectexpansion.py +index 5bf45a5..f80f083 100644 +--- a/copper/unitarydirectexpansion.py ++++ b/copper/unitarydirectexpansion.py +@@ -61,6 +61,8 @@ class UnitaryDirectExpansion(Equipment): + indoor_fan_speeds=1, + indoor_fan_curve=False, + indoor_fan_power_unit="kW", ++ compressor_stage_input=False, ++ compressor_stage=[0.3, 0.6], + ): + global log_fan + self.type = "UnitaryDirectExpansion" +@@ -172,6 +174,8 @@ class UnitaryDirectExpansion(Equipment): + self.indoor_fan_curve_coef = indoor_fan_curve_coef + self.indoor_fan_power_unit = indoor_fan_power_unit + self.indoor_fan_curve = indoor_fan_curve ++ self.compressor_stage = compressor_stage ++ self.compressor_stage_input = compressor_stage_input + # Define rated temperatures + # air entering drybulb, air entering wetbulb, entering condenser temperature, leaving condenser temperature + aed, self.aew, ect, lct = self.get_rated_temperatures() +@@ -365,17 +369,9 @@ class UnitaryDirectExpansion(Equipment): +  + # Iterate through the different sets of rating conditions to calculate IEER + ieer = 0 +- for red_cap_num in range(num_of_reduced_cap): +- # Determine the outdoor air conditions based on AHRI Standard +- if reduced_plr[red_cap_num] > 0.444: +- outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( +- 5.0 + 30.0 * reduced_plr[red_cap_num] +- ) +- else: +- outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ +- eqp_type +- ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] +  ++ def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): ++ """inner function to calculate reduced eer.""" + # Calculate capacity at rating conditions + tot_cap_temp_mod_fac = cap_f_t.evaluate( + equipment_references[eqp_type][std][ +@@ -384,7 +380,7 @@ class UnitaryDirectExpansion(Equipment): + outdoor_unit_inlet_air_dry_bulb_temp_reduced, + ) + load_factor_gross = min( +- 1.0, (reduced_plr[red_cap_num] / tot_cap_temp_mod_fac) ++ 1.0, (ratio / tot_cap_temp_mod_fac) + ) # Load percentage * Rated gross capacity / Available gross capacity + indoor_fan_power = self.calc_fan_power(load_factor_gross) / 1000 + net_cooling_cap_reduced = ( +@@ -407,9 +403,9 @@ class UnitaryDirectExpansion(Equipment): + raise ValueError("Input COP is 0!") +  + # "Load Factor" (as per AHRI Standard) which is analogous to PLR +- if reduced_plr[red_cap_num] < 1.0: ++ if ratio < 1.0: + load_factor = ( +- reduced_plr[red_cap_num] ++ ratio # reduced_plr[red_cap_num] + * net_cooling_cap_rated + / net_cooling_cap_reduced + if net_cooling_cap_reduced > 0.0 +@@ -432,14 +428,54 @@ class UnitaryDirectExpansion(Equipment): + eer_reduced = (load_factor * net_cooling_cap_reduced) / ( + load_factor * elec_power_reduced_cap + indoor_fan_power + ) ++ return eer_reduced +  ++ for red_cap_num in range(num_of_reduced_cap): ++ # Determine the outdoor air conditions based on AHRI Standard ++ if reduced_plr[red_cap_num] > 0.444: ++ outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( ++ 5.0 + 30.0 * reduced_plr[red_cap_num] ++ ) ++ else: ++ outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ ++ eqp_type ++ ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] ++ if ( ++ self.compressor_stage_input ++ and (reduced_plr[red_cap_num] > min(self.compressor_stage)) ++ and (reduced_plr[red_cap_num] < max(self.compressor_stage)) ++ ): ++ lower_value = None ++ upper_value = None ++ for value in self.compressor_stage: ++ if value <= red_cap_num: ++ lower_value = value ++ elif value > red_cap_num and upper_value is None: ++ upper_value = value ++ # interpolation ++ eer_reduced = ( ++ ( ++ cal_reduced_eer( ++ lower_value, outdoor_unit_inlet_air_dry_bulb_temp_reduced ++ ) ++ - cal_reduced_eer( ++ upper_value, outdoor_unit_inlet_air_dry_bulb_temp_reduced ++ ) ++ ) ++ / (lower_value - upper_value) ++ ) * (reduced_plr[red_cap_num] - upper_value) + cal_reduced_eer( ++ upper_value, outdoor_unit_inlet_air_dry_bulb_temp_reduced ++ ) ++ else: ++ eer_reduced = cal_reduced_eer( ++ reduced_plr[red_cap_num], ++ outdoor_unit_inlet_air_dry_bulb_temp_reduced, ++ ) + if eff_type == "full": + ieer = eer_reduced + break +- + # Update IEER + ieer += weighting_factor[red_cap_num] * eer_reduced +- + # Convert efficiency to original unit unless specified + if unit != "cop": + ieer = Units(value=ieer, unit="cop") From 8314bba6f4e8e84620faf0e2d86fc77c6a6114a8 Mon Sep 17 00:00:00 2001 From: "Wan, Hanlong" Date: Wed, 30 Oct 2024 15:17:35 -0700 Subject: [PATCH 3/7] ieer new update --- copper/chiller.py | 2 +- tests/test_unitarydirectexpansion.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/copper/chiller.py b/copper/chiller.py index af2a657..43dcde6 100644 --- a/copper/chiller.py +++ b/copper/chiller.py @@ -682,4 +682,4 @@ def get_seed_curves(self, lib=None, filters=None, csets=None): return curves def get_ref_vars_for_aggregation(self): - return ["ref_cap", "full_eff"] \ No newline at end of file + return ["ref_cap", "full_eff"] diff --git a/tests/test_unitarydirectexpansion.py b/tests/test_unitarydirectexpansion.py index 8dec0b1..00abcc3 100644 --- a/tests/test_unitarydirectexpansion.py +++ b/tests/test_unitarydirectexpansion.py @@ -29,6 +29,26 @@ class UnitaryDirectExpansion(TestCase): set_of_curves=lib.get_set_of_curves_by_name("D208122216").curves, ) + def test_new_ieer(self): + lib = cp.Library(path=DX_lib) + dx_unit_new = cp.UnitaryDirectExpansion( + compressor_type="scroll", + condenser_type="air", + compressor_speed="constant", + ref_cap_unit="W", + ref_gross_cap=471000, + full_eff=5.89, + full_eff_unit="cop", + part_eff_ref_std="ahri_340/360", + model="simplified_bf", + sim_engine="energyplus", + set_of_curves=lib.get_set_of_curves_by_name("D208122216").curves, + compressor_stage_input=True, + compressor_stage=[0.3, 0.6], + ) + ieer = round(self.dx_unit_dft.calc_rated_eff(unit="eer"), 1) + self.assertTrue(7.5 == ieer, f"{ieer} is different than 7.5") + def test_calc_eff_ect(self): ieer = round(self.dx_unit_dft.calc_rated_eff(unit="eer"), 1) self.assertTrue(7.5 == ieer, f"{ieer} is different than 7.5") From f2238f0f1515d3beb2612fbe300f27ca0cdb82dc Mon Sep 17 00:00:00 2001 From: "Wan, Hanlong" Date: Wed, 30 Oct 2024 15:27:38 -0700 Subject: [PATCH 4/7] fix bugs --- tests/test_unitarydirectexpansion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_unitarydirectexpansion.py b/tests/test_unitarydirectexpansion.py index 00abcc3..2ede1c0 100644 --- a/tests/test_unitarydirectexpansion.py +++ b/tests/test_unitarydirectexpansion.py @@ -47,7 +47,7 @@ def test_new_ieer(self): compressor_stage=[0.3, 0.6], ) ieer = round(self.dx_unit_dft.calc_rated_eff(unit="eer"), 1) - self.assertTrue(7.5 == ieer, f"{ieer} is different than 7.5") + self.assertTrue(8.4 == ieer, f"{ieer} is different than 8.4") def test_calc_eff_ect(self): ieer = round(self.dx_unit_dft.calc_rated_eff(unit="eer"), 1) From 3287afddc25fcf83ea299e9e1003dd1709c6eb1d Mon Sep 17 00:00:00 2001 From: "Wan, Hanlong" Date: Wed, 4 Dec 2024 14:04:50 -0800 Subject: [PATCH 5/7] Debugging new IEER issues' --- copper/unitarydirectexpansion.py | 301 +++++++++++++++++++++-- tests/data/DX_multispeed_input_file.json | 160 ++++++++++++ tests/test_unitarydirectexpansion.py | 52 ++-- 3 files changed, 465 insertions(+), 48 deletions(-) create mode 100644 tests/data/DX_multispeed_input_file.json diff --git a/copper/unitarydirectexpansion.py b/copper/unitarydirectexpansion.py index f80f083..c2a5b60 100644 --- a/copper/unitarydirectexpansion.py +++ b/copper/unitarydirectexpansion.py @@ -33,6 +33,8 @@ def __init__( ref_net_cap=None, part_eff_unit="", set_of_curves=[], + set_of_curves_1=[], + set_of_curves_2=[], part_eff_ref_std="ahri_340/360", part_eff_ref_std_alt=None, model="simplified_bf", @@ -62,7 +64,7 @@ def __init__( indoor_fan_curve=False, indoor_fan_power_unit="kW", compressor_stage_input=False, - compressor_stage=[0.3, 0.6], + compressor_stages=[0.3, 0.6], ): global log_fan self.type = "UnitaryDirectExpansion" @@ -150,7 +152,8 @@ def __init__( self.ref_net_cap = ref_net_cap self.ref_gross_cap = ref_gross_cap self.ref_cap_unit = ref_cap_unit - + #reorder compressor_stages + compressor_stages = sorted(compressor_stages) # Get attributes self.full_eff = full_eff self.full_eff_unit = full_eff_unit @@ -162,6 +165,8 @@ def __init__( self.part_eff_alt_unit = part_eff_unit self.compressor_type = compressor_type self.set_of_curves = set_of_curves + self.set_of_curves_1 = set_of_curves_1 + self.set_of_curves_2 = set_of_curves_2 self.part_eff_ref_std = part_eff_ref_std self.model = model self.sim_engine = sim_engine @@ -174,7 +179,7 @@ def __init__( self.indoor_fan_curve_coef = indoor_fan_curve_coef self.indoor_fan_power_unit = indoor_fan_power_unit self.indoor_fan_curve = indoor_fan_curve - self.compressor_stage = compressor_stage + self.compressor_stages = compressor_stages self.compressor_stage_input = compressor_stage_input # Define rated temperatures # air entering drybulb, air entering wetbulb, entering condenser temperature, leaving condenser temperature @@ -371,7 +376,10 @@ def calc_rated_eff( ieer = 0 def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): - """inner function to calculate reduced eer.""" + """Inner function to calculate reduced eer. + :param float ratio: capacity ratio + :param float outdoor_unit_inlet_air_dry_bulb_temp_reduced: Reduced Outdoor Unit Inlet Air Dry Bulb temperature + """ # Calculate capacity at rating conditions tot_cap_temp_mod_fac = cap_f_t.evaluate( equipment_references[eqp_type][std][ @@ -428,8 +436,9 @@ def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): eer_reduced = (load_factor * net_cooling_cap_reduced) / ( load_factor * elec_power_reduced_cap + indoor_fan_power ) - return eer_reduced + return eer_reduced, load_factor + #for stage_id, red_cap_num in enumerate(self.compressor_stages): for red_cap_num in range(num_of_reduced_cap): # Determine the outdoor air conditions based on AHRI Standard if reduced_plr[red_cap_num] > 0.444: @@ -440,34 +449,34 @@ def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ eqp_type ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] + interpolation = False + for stage_id, capacity_ratio in enumerate(self.compressor_stages): + if stage_id + 1 < len(self.compressor_stages): + if ((self.compressor_stages[stage_id + 1] >= reduced_plr[red_cap_num]) + and (reduced_plr[red_cap_num] > capacity_ratio)): + interpolation = True + lower_stage_load = capacity_ratio + upper_stage_load = self.compressor_stages[stage_id + 1] if ( self.compressor_stage_input - and (reduced_plr[red_cap_num] > min(self.compressor_stage)) - and (reduced_plr[red_cap_num] < max(self.compressor_stage)) + and interpolation ): - lower_value = None - upper_value = None - for value in self.compressor_stage: - if value <= red_cap_num: - lower_value = value - elif value > red_cap_num and upper_value is None: - upper_value = value # interpolation + _, load_factor_1 = cal_reduced_eer( + lower_stage_load, outdoor_unit_inlet_air_dry_bulb_temp_reduced + ) + _, load_factor_2 = cal_reduced_eer( + upper_stage_load, outdoor_unit_inlet_air_dry_bulb_temp_reduced + ) eer_reduced = ( ( - cal_reduced_eer( - lower_value, outdoor_unit_inlet_air_dry_bulb_temp_reduced - ) - - cal_reduced_eer( - upper_value, outdoor_unit_inlet_air_dry_bulb_temp_reduced - ) + load_factor_1 + - load_factor_2 ) - / (lower_value - upper_value) - ) * (reduced_plr[red_cap_num] - upper_value) + cal_reduced_eer( - upper_value, outdoor_unit_inlet_air_dry_bulb_temp_reduced - ) + / (lower_stage_load - upper_stage_load) + ) * (reduced_plr[red_cap_num] - upper_stage_load) + load_factor_2 else: - eer_reduced = cal_reduced_eer( + eer_reduced, _ = cal_reduced_eer( reduced_plr[red_cap_num], outdoor_unit_inlet_air_dry_bulb_temp_reduced, ) @@ -482,6 +491,215 @@ def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): ieer = ieer.conversion(new_unit=self.full_eff_unit) return ieer + def calc_rated_eff_two_curves( + self, eff_type="part", unit="cop", output_report=False, alt=False + ): + """ + This is a template function for new ieer, two curves inputs. + Calculate unitary DX equipment efficiency. + + :param str eff_type: Unitary DX equipment efficiency type, currently supported `full` (full load rating) + and `part` (part load rating) + :param str unit: Efficiency unit + :param bool output_report: Indicate output report generation + :param bool alt: Indicate the DX system alternate standard rating should be used + :return: Unitary DX Equipment rated efficiency + :rtype: float + + """ + + # Handle alternate ratings (not currently used) + if alt: + std = self.part_eff_ref_std_alt + else: + std = self.part_eff_ref_std + + # Retrieve curves + curves_1, curves_2 = self.get_two_dx_curves() + cap_f_f1 = curves_1["cap-f-ff"] + cap_f_t1 = curves_1["cap-f-t"] + eir_f_t1 = curves_1["eir-f-t"] + eir_f_f1 = curves_1["eir-f-ff"] + plf_f_plr1 = curves_1["plf-f-plr"] + cap_f_f2 = curves_1["cap-f-ff"] + cap_f_t2 = curves_1["cap-f-t"] + eir_f_t2 = curves_1["eir-f-t"] + eir_f_f2 = curves_1["eir-f-ff"] + plf_f_plr2 = curves_1["plf-f-plr"] + # Calculate capacity and efficiency degradation as a function of flow fraction + tot_cap_flow_mod_fac1 = 1 + eir_flow_mod_fac1 = 1 + tot_cap_flow_mod_fac2 = 1 + eir_flow_mod_fac2 = 1 + # Get rated conditions + eqp_type = self.type.lower() + num_of_reduced_cap = equipment_references[eqp_type][std]["coef"][ + "numofreducedcap" + ] + reduced_plr = equipment_references[eqp_type][std]["coef"]["reducedplr"] + weighting_factor = equipment_references[eqp_type][std]["coef"][ + "weightingfactor" + ] + tot_cap_temp_mod_fac1 = cap_f_t1.evaluate( + equipment_references[eqp_type][std][ + "cooling_coil_inlet_air_wet_bulb_rated" + ], + equipment_references[eqp_type][std][ + "outdoor_unit_inlet_air_dry_bulb_rated" + ], + ) + tot_cap_temp_mod_fac2 = cap_f_t2.evaluate( + equipment_references[eqp_type][std][ + "cooling_coil_inlet_air_wet_bulb_rated" + ], + equipment_references[eqp_type][std][ + "outdoor_unit_inlet_air_dry_bulb_rated" + ], + ) + # Calculate NET rated capacity + net_cooling_cap_rated1 = ( + self.ref_gross_cap * tot_cap_temp_mod_fac1 * tot_cap_flow_mod_fac1 + - self.indoor_fan_power + ) + net_cooling_cap_rated2 = ( + self.ref_gross_cap * tot_cap_temp_mod_fac2 * tot_cap_flow_mod_fac2 + - self.indoor_fan_power + ) + # Convert user-specified full load efficiency to COP + # User-specified capacity is a NET efficiency + full_eff = Units(value=self.full_eff, unit=self.full_eff_unit) + rated_cop = full_eff.conversion(new_unit="cop") + + # Iterate through the different sets of rating conditions to calculate IEER + ieer = 0 + + def cal_reduced_eer(ratio, cap_f_t, eir_f_t, tot_cap_flow_mod_fac, eir_flow_mod_fac, plf_f_plr, outdoor_unit_inlet_air_dry_bulb_temp_reduced): + """Inner function to calculate reduced eer. + :param float ratio: capacity ratio + :param float outdoor_unit_inlet_air_dry_bulb_temp_reduced: Reduced Outdoor Unit Inlet Air Dry Bulb temperature + """ + # Calculate capacity at rating conditions + tot_cap_temp_mod_fac = cap_f_t.evaluate( + equipment_references[eqp_type][std][ + "cooling_coil_inlet_air_wet_bulb_rated" + ], + outdoor_unit_inlet_air_dry_bulb_temp_reduced, + ) + load_factor_gross = min( + 1.0, (ratio / tot_cap_temp_mod_fac) + ) # Load percentage * Rated gross capacity / Available gross capacity + indoor_fan_power = self.calc_fan_power(load_factor_gross) / 1000 + net_cooling_cap_reduced = ( + self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac + - indoor_fan_power + ) + + # Calculate efficency at rating conditions + eir_temp_mod_fac = eir_f_t.evaluate( + equipment_references[eqp_type][std][ + "cooling_coil_inlet_air_wet_bulb_rated" + ], + outdoor_unit_inlet_air_dry_bulb_temp_reduced, + ) + if rated_cop > 0.0: + eir = eir_temp_mod_fac * eir_flow_mod_fac / rated_cop + else: + eir = 0.0 + logging.error("Input COP is 0!") + raise ValueError("Input COP is 0!") + net_cooling_cap_rated = ( + self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac + - self.indoor_fan_power + ) + # "Load Factor" (as per AHRI Standard) which is analogous to PLR + if ratio < 1.0: + load_factor = ( + ratio # reduced_plr[red_cap_num] + * net_cooling_cap_rated + / net_cooling_cap_reduced + if net_cooling_cap_reduced > 0.0 + else 1.0 + ) + else: + load_factor = 1 + + # Cycling degradation + degradation_coeff = 1 / plf_f_plr.evaluate(load_factor, 1) + + # Power + elec_power_reduced_cap = ( + degradation_coeff + * eir + * (self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac) + ) + + # EER + eer_reduced = (load_factor * net_cooling_cap_reduced) / ( + load_factor * elec_power_reduced_cap + indoor_fan_power + ) + return eer_reduced, load_factor + + #for stage_id, red_cap_num in enumerate(self.compressor_stages): + for red_cap_num in range(num_of_reduced_cap): + # Determine the outdoor air conditions based on AHRI Standard + if reduced_plr[red_cap_num] > 0.444: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( + 5.0 + 30.0 * reduced_plr[red_cap_num] + ) + else: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ + eqp_type + ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] + interpolation = False + for stage_id, capacity_ratio in enumerate(self.compressor_stages): + if stage_id + 1 < len(self.compressor_stages): + if (self.compressor_stages[stage_id + 1] >= reduced_plr[red_cap_num]): + if (reduced_plr[red_cap_num] > capacity_ratio): + interpolation = True + lower_stage_load = capacity_ratio + upper_stage_load = self.compressor_stages[stage_id + 1] + else: + curve_num = stage_id + 1 + if ( + self.compressor_stage_input + and interpolation + ): + # interpolation + _, load_factor_1 = cal_reduced_eer( + lower_stage_load, cap_f_t2, eir_f_t2, tot_cap_flow_mod_fac2, eir_flow_mod_fac2, plf_f_plr2,outdoor_unit_inlet_air_dry_bulb_temp_reduced + ) + _, load_factor_2 = cal_reduced_eer( + upper_stage_load, cap_f_t1, eir_f_t1, tot_cap_flow_mod_fac1, eir_flow_mod_fac1, plf_f_plr1,outdoor_unit_inlet_air_dry_bulb_temp_reduced + ) + eer_reduced = ( + ( + load_factor_1 + - load_factor_2 + ) + / (lower_stage_load - upper_stage_load) + ) * (reduced_plr[red_cap_num] - upper_stage_load) + load_factor_2 + else: + if curve_num == 1: + eer_reduced, _ = cal_reduced_eer( + reduced_plr[red_cap_num], cap_f_t2, eir_f_t2, tot_cap_flow_mod_fac2, eir_flow_mod_fac2, plf_f_plr2, + outdoor_unit_inlet_air_dry_bulb_temp_reduced, + ) + if curve_num == 2: + eer_reduced, _ = cal_reduced_eer( + reduced_plr[red_cap_num], cap_f_t2, eir_f_t1, tot_cap_flow_mod_fac1, eir_flow_mod_fac1, plf_f_plr1, + outdoor_unit_inlet_air_dry_bulb_temp_reduced, + ) + if eff_type == "full": + ieer = eer_reduced + break + # Update IEER + ieer += weighting_factor[red_cap_num] * eer_reduced + # Convert efficiency to original unit unless specified + if unit != "cop": + ieer = Units(value=ieer, unit="cop") + ieer = ieer.conversion(new_unit=self.full_eff_unit) + return ieer + def ieer_to_eer(self, ieer): """Calculate EER from IEER and system capacity. The regression function was obtained by fitting a linear model on performance data collected from AHRI database (Sample Size = 14,268). @@ -532,6 +750,39 @@ def get_dx_curves(self): curves["plf-f-plr"] = curve return curves + def get_two_dx_curves(self): + """Retrieve DX curves from the DX set_of_curves attribute. + + :return: Dictionary of the curves associated with the object + :rtype: dict + + """ + curves_1 = {} + for curve in self.set_of_curves_1: + if curve.out_var == "cap-f-t": + curves_1["cap-f-t"] = curve + elif curve.out_var == "cap-f-ff": + curves_1["cap-f-ff"] = curve + elif curve.out_var == "eir-f-t": + curves_1["eir-f-t"] = curve + elif curve.out_var == "eir-f-ff": + curves_1["eir-f-ff"] = curve + elif curve.out_var == "plf-f-plr": + curves_1["plf-f-plr"] = curve + curves_2 = {} + for curve in self.set_of_curves_2: + if curve.out_var == "cap-f-t": + curves_2["cap-f-t"] = curve + elif curve.out_var == "cap-f-ff": + curves_2["cap-f-ff"] = curve + elif curve.out_var == "eir-f-t": + curves_2["eir-f-t"] = curve + elif curve.out_var == "eir-f-ff": + curves_2["eir-f-ff"] = curve + elif curve.out_var == "plf-f-plr": + curves_2["plf-f-plr"] = curve + return curves_1, curves_2 + def get_curves_from_lib(self, lib, filters): """Function to get the sort from the library based on chiller filters. diff --git a/tests/data/DX_multispeed_input_file.json b/tests/data/DX_multispeed_input_file.json new file mode 100644 index 0000000..7c98e84 --- /dev/null +++ b/tests/data/DX_multispeed_input_file.json @@ -0,0 +1,160 @@ +{ + "HighStage": { + "eqp_type": "UnitaryDirectExpansion", + "source": "Placeholder", + "model": "simplified_bf", + "condenser_type": "air", + "compressor_type": "scroll", + "compressor_speed": "variable", + "ref_net_cap": null, + "ref_cap_unit": "W", + "full_eff": null, + "full_eff_unit": "eer", + "sim_engine": "energyplus", + "full_eff_ref_std": null, + "full_eff_ref_std_alt": null, + "part_eff": null, + "part_eff_ref_std": null, + "part_eff_ref_std_alt": null, + "min_outdoor_fan_power": null, + "max_outdoor_fan_power": null, + "indoor_fan_power_unit": "W", + "set_of_curves": { + "plf-f-plr": { + "out_var": "plf-f-plr", + "type": "linear", + "coeff1": 0.469265, + "coeff2": 0.530735, + "x_min": 0, + "x_max": 1.5, + "out_min": 0, + "out_max": 1.5 + }, + "cap-f-t": { + "out_var": "cap-f-t", + "type": "linear", + "coeff1": 44.265, + "coeff2": -0.2972, + "x_min": 20, + "x_max": 40, + "out_min": 33.5, + "out_max": 36.5 + }, + "eir-f-ff": { + "out_var": "eir-f-ff", + "type": "linear", + "coeff1": 1, + "coeff2": 0, + "x_min": 0, + "x_max": 1, + "out_min": 0, + "out_max": 1 + }, + "eir-f-t": { + "out_var": "eir-f-t", + "type": "linear", + "coeff1": 0.044, + "coeff2": 0.0013, + "x_min": 20, + "x_max": 40, + "out_min": 0.078, + "out_max": 0.092 + }, + "cap-f-ff": { + "out_var": "cap_f_ff", + "type": "quad", + "coeff1": 1, + "coeff2": 0, + "coeff3": 0, + "x_min": 10, + "x_max": 30, + "out_min": 17.8, + "out_max": 18.6 + } + }, + "indoor_fan_control_mode": null, + "indoor_fan_power": null, + "ref_gross_cap": null + }, + "LowStage": { + "eqp_type": "UnitaryDirectExpansion", + "source": "Placeholder", + "model": "simplified_bf", + "condenser_type": "air", + "compressor_type": "scroll", + "compressor_speed": "variable", + "ref_net_cap": null, + "ref_cap_unit": "W", + "full_eff": null, + "full_eff_unit": "eer", + "sim_engine": "energyplus", + "full_eff_ref_std": null, + "full_eff_ref_std_alt": null, + "part_eff": null, + "part_eff_ref_std": null, + "part_eff_ref_std_alt": null, + "min_outdoor_fan_power": null, + "max_outdoor_fan_power": null, + "indoor_fan_power_unit": "W", + "set_of_curves": { + "plf-f-plr": { + "out_var": "plf-f-plr", + "type": "quad", + "coeff1": 1.5347, + "coeff2": -1.7496, + "coeff3": 0.759, + "x_min": 0, + "x_max": 1.5, + "out_min": 0, + "out_max": 1.5 + }, + "cap-f-t": { + "out_var": "cap_f_t", + "type": "quad", + "coeff1": 23.074, + "coeff2": -0.3816, + "coeff3": 0.007, + "x_min": 10, + "x_max": 30, + "out_min": 17.8, + "out_max": 18.6 + }, + "eir-f-t": { + "out_var": "eir_f_t", + "type": "quad", + "coeff1": 0.0419, + "coeff2": 0.0019, + "coeff3": -0.00002, + "x_min": 10, + "x_max": 30, + "out_min": 0.068, + "out_max": 0.078 + }, + "cap-f-ff": { + "out_var": "cap_f_ff", + "type": "quad", + "coeff1": 1, + "coeff2": 0, + "coeff3": 0, + "x_min": 10, + "x_max": 30, + "out_min": 17.8, + "out_max": 18.6 + }, + "eir-f-ff": { + "out_var": "cap_f_ff", + "type": "quad", + "coeff1": 1, + "coeff2": 0, + "coeff3": 0, + "x_min": 10, + "x_max": 30, + "out_min": 17.8, + "out_max": 18.6 + } + }, + "indoor_fan_control_mode": null, + "indoor_fan_power": null, + "ref_gross_cap": null + } +} \ No newline at end of file diff --git a/tests/test_unitarydirectexpansion.py b/tests/test_unitarydirectexpansion.py index 2ede1c0..3f96433 100644 --- a/tests/test_unitarydirectexpansion.py +++ b/tests/test_unitarydirectexpansion.py @@ -7,30 +7,14 @@ import os location = os.path.dirname(os.path.realpath(__file__)) -DX_lib = os.path.join(location, "../copper/data", "unitarydirectexpansion_curves.json") - +#DX_lib = os.path.join(location, "../copper/data", "unitarydirectexpansion_curves.json") +DX_lib_test = os.path.join(location, "data", "DX_multispeed_input_file.json") class UnitaryDirectExpansion(TestCase): # Load curve library - lib = cp.Library(path=DX_lib) - - # Define equipment characteristics - dx_unit_dft = cp.UnitaryDirectExpansion( - compressor_type="scroll", - condenser_type="air", - compressor_speed="constant", - ref_cap_unit="W", - ref_gross_cap=471000, - full_eff=5.89, - full_eff_unit="cop", - part_eff_ref_std="ahri_340/360", - model="simplified_bf", - sim_engine="energyplus", - set_of_curves=lib.get_set_of_curves_by_name("D208122216").curves, - ) - + lib = cp.Library(path=DX_lib_test) def test_new_ieer(self): - lib = cp.Library(path=DX_lib) + lib_in = cp.Library(path=DX_lib_test) dx_unit_new = cp.UnitaryDirectExpansion( compressor_type="scroll", condenser_type="air", @@ -42,12 +26,29 @@ def test_new_ieer(self): part_eff_ref_std="ahri_340/360", model="simplified_bf", sim_engine="energyplus", - set_of_curves=lib.get_set_of_curves_by_name("D208122216").curves, + set_of_curves_1=lib_in.get_set_of_curves_by_name("HighStage").curves,# this is the part have problem + #seems it can load the json file, but cannot find the curve named 'HighStage' + set_of_curves_2=lib_in.get_set_of_curves_by_name("LowStage").curves, compressor_stage_input=True, - compressor_stage=[0.3, 0.6], + compressor_stages=[0.527, 1.067], ) - ieer = round(self.dx_unit_dft.calc_rated_eff(unit="eer"), 1) + ieer = round(dx_unit_new.calc_rated_eff_two_curves(unit="eer"), 1) self.assertTrue(8.4 == ieer, f"{ieer} is different than 8.4") +""""" + # Define equipment characteristics + dx_unit_dft = cp.UnitaryDirectExpansion( + compressor_type="scroll", + condenser_type="air", + compressor_speed="constant", + ref_cap_unit="W", + ref_gross_cap=471000, + full_eff=5.89, + full_eff_unit="cop", + part_eff_ref_std="ahri_340/360", + model="simplified_bf", + sim_engine="energyplus", + set_of_curves=lib.get_set_of_curves_by_name("D208122216").curves, + ) def test_calc_eff_ect(self): ieer = round(self.dx_unit_dft.calc_rated_eff(unit="eer"), 1) @@ -386,3 +387,8 @@ def test_NN_wght_avg(self): assert round(set_of_curves[2].evaluate(1.0, 0), 2) == 1.0 assert round(set_of_curves[3].evaluate(1.0, 0), 2) == 1.0 assert round(set_of_curves[4].evaluate(1.0, 0), 2) == 1.0 +""""" +# Run the tests +import unittest +if __name__ == '__main__': + unittest.main() From 5a59fed0b0be262c876cbd94462862fc01e843c1 Mon Sep 17 00:00:00 2001 From: "Wan, Hanlong" Date: Thu, 12 Dec 2024 10:07:18 -0800 Subject: [PATCH 6/7] for debug, request help --- copper/data/equipment_references.json | 2 +- copper/unitarydirectexpansion.py | 191 ++++++++++++----------- tests/data/DX_multispeed_input_file.json | 60 +++---- tests/test_unitarydirectexpansion.py | 8 +- 4 files changed, 131 insertions(+), 130 deletions(-) diff --git a/copper/data/equipment_references.json b/copper/data/equipment_references.json index ef72c15..c3a446f 100644 --- a/copper/data/equipment_references.json +++ b/copper/data/equipment_references.json @@ -63,7 +63,7 @@ "reducedplr": [1.0, 0.75, 0.5, 0.25], "weightingfactor": [0.02, 0.617, 0.238, 0.125] }, - "outdoor_unit_inlet_air_dry_bulb_rated": 35, + "outdoor_unit_inlet_air_dry_bulb_rated": 35.06, "outdoor_unit_inlet_air_dry_bulb_reduced": 18.3, "cooling_coil_inlet_air_wet_bulb_rated": 19.44 }, diff --git a/copper/unitarydirectexpansion.py b/copper/unitarydirectexpansion.py index c2a5b60..aa14b8e 100644 --- a/copper/unitarydirectexpansion.py +++ b/copper/unitarydirectexpansion.py @@ -256,56 +256,60 @@ def add_cycling_degradation_curve(self, overwrite=False, return_curve=False): self.default_fan_curve.coeff3 = self.indoor_fan_curve_coef["3"] self.default_fan_curve.coeff4 = self.indoor_fan_curve_coef["4"] - def calc_fan_power(self, capacity_fraction): + def calc_fan_power(self, capacity_fraction, ignore): """Calculate unitary DX equipment fan power. :param float capacity_fraction: Ratio of actual capacity to net rated capacity + :param boolean ignore: ignore fan power or not :return: Unitary DX Equipment fan power in Watts :rtype: float """ - # Full flow/power - flow_fraction = capacity_fraction # we assume flow_fraction = 1*capacity_fraction as default - if capacity_fraction == 1 or self.indoor_fan_speeds == 1: - return self.indoor_fan_power + if ignore == True: + return 0 else: - if self.indoor_fan_curve == False: - capacity_fractions = [] - fan_power_fractions = [] - for speed_info in self.indoor_fan_speeds_mapping.values(): - capacity_fractions.append(speed_info["capacity_fraction"]) - fan_power_fractions.append(speed_info["fan_power_fraction"]) - # Minimum flow/power - if capacity_fraction <= capacity_fractions[0]: - return self.indoor_fan_power * fan_power_fractions[0] - elif capacity_fraction in capacity_fractions: - return ( - self.indoor_fan_power - * fan_power_fractions[ - capacity_fractions.index(capacity_fraction) - ] - ) - else: - # In between-speeds: determine power by linear interpolation - for i, ratio in enumerate(capacity_fractions): - if ( - ratio < capacity_fraction - and capacity_fractions[i + 1] > capacity_fraction - ): - a = ( - fan_power_fractions[i + 1] - fan_power_fractions[i] - ) / (capacity_fractions[i + 1] - capacity_fractions[i]) - b = fan_power_fractions[i] - a * capacity_fractions[i] - return self.indoor_fan_power * (a * capacity_fraction + b) - else: # using curve - default_min_fan_power = ( - self.indoor_fan_power * 0.25 - ) # default min fan power - power_factor = self.default_fan_curve.evaluate(x=flow_fraction, y=0) - if self.indoor_fan_power * power_factor > default_min_fan_power: - return self.indoor_fan_power * power_factor - else: - return default_min_fan_power + # Full flow/power + flow_fraction = capacity_fraction # we assume flow_fraction = 1*capacity_fraction as default + if capacity_fraction == 1 or self.indoor_fan_speeds == 1: + return self.indoor_fan_power + else: + if self.indoor_fan_curve == False: + capacity_fractions = [] + fan_power_fractions = [] + for speed_info in self.indoor_fan_speeds_mapping.values(): + capacity_fractions.append(speed_info["capacity_fraction"]) + fan_power_fractions.append(speed_info["fan_power_fraction"]) + # Minimum flow/power + if capacity_fraction <= capacity_fractions[0]: + return self.indoor_fan_power * fan_power_fractions[0] + elif capacity_fraction in capacity_fractions: + return ( + self.indoor_fan_power + * fan_power_fractions[ + capacity_fractions.index(capacity_fraction) + ] + ) + else: + # In between-speeds: determine power by linear interpolation + for i, ratio in enumerate(capacity_fractions): + if ( + ratio < capacity_fraction + and capacity_fractions[i + 1] > capacity_fraction + ): + a = ( + fan_power_fractions[i + 1] - fan_power_fractions[i] + ) / (capacity_fractions[i + 1] - capacity_fractions[i]) + b = fan_power_fractions[i] - a * capacity_fractions[i] + return self.indoor_fan_power * (a * capacity_fraction + b) + else: # using curve + default_min_fan_power = ( + self.indoor_fan_power * 0.25 + ) # default min fan power + power_factor = self.default_fan_curve.evaluate(x=flow_fraction, y=0) + if self.indoor_fan_power * power_factor > default_min_fan_power: + return self.indoor_fan_power * power_factor + else: + return default_min_fan_power def calc_rated_eff( self, eff_type="part", unit="cop", output_report=False, alt=False @@ -390,7 +394,7 @@ def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): load_factor_gross = min( 1.0, (ratio / tot_cap_temp_mod_fac) ) # Load percentage * Rated gross capacity / Available gross capacity - indoor_fan_power = self.calc_fan_power(load_factor_gross) / 1000 + indoor_fan_power = self.calc_fan_power(load_factor_gross, False) / 1000 net_cooling_cap_reduced = ( self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac - indoor_fan_power @@ -521,11 +525,11 @@ def calc_rated_eff_two_curves( eir_f_t1 = curves_1["eir-f-t"] eir_f_f1 = curves_1["eir-f-ff"] plf_f_plr1 = curves_1["plf-f-plr"] - cap_f_f2 = curves_1["cap-f-ff"] - cap_f_t2 = curves_1["cap-f-t"] - eir_f_t2 = curves_1["eir-f-t"] - eir_f_f2 = curves_1["eir-f-ff"] - plf_f_plr2 = curves_1["plf-f-plr"] + cap_f_f2 = curves_2["cap-f-ff"] + cap_f_t2 = curves_2["cap-f-t"] + eir_f_t2 = curves_2["eir-f-t"] + eir_f_f2 = curves_2["eir-f-ff"] + plf_f_plr2 = curves_2["plf-f-plr"] # Calculate capacity and efficiency degradation as a function of flow fraction tot_cap_flow_mod_fac1 = 1 eir_flow_mod_fac1 = 1 @@ -540,31 +544,6 @@ def calc_rated_eff_two_curves( weighting_factor = equipment_references[eqp_type][std]["coef"][ "weightingfactor" ] - tot_cap_temp_mod_fac1 = cap_f_t1.evaluate( - equipment_references[eqp_type][std][ - "cooling_coil_inlet_air_wet_bulb_rated" - ], - equipment_references[eqp_type][std][ - "outdoor_unit_inlet_air_dry_bulb_rated" - ], - ) - tot_cap_temp_mod_fac2 = cap_f_t2.evaluate( - equipment_references[eqp_type][std][ - "cooling_coil_inlet_air_wet_bulb_rated" - ], - equipment_references[eqp_type][std][ - "outdoor_unit_inlet_air_dry_bulb_rated" - ], - ) - # Calculate NET rated capacity - net_cooling_cap_rated1 = ( - self.ref_gross_cap * tot_cap_temp_mod_fac1 * tot_cap_flow_mod_fac1 - - self.indoor_fan_power - ) - net_cooling_cap_rated2 = ( - self.ref_gross_cap * tot_cap_temp_mod_fac2 * tot_cap_flow_mod_fac2 - - self.indoor_fan_power - ) # Convert user-specified full load efficiency to COP # User-specified capacity is a NET efficiency full_eff = Units(value=self.full_eff, unit=self.full_eff_unit) @@ -588,12 +567,11 @@ def cal_reduced_eer(ratio, cap_f_t, eir_f_t, tot_cap_flow_mod_fac, eir_flow_mod_ load_factor_gross = min( 1.0, (ratio / tot_cap_temp_mod_fac) ) # Load percentage * Rated gross capacity / Available gross capacity - indoor_fan_power = self.calc_fan_power(load_factor_gross) / 1000 + indoor_fan_power = self.calc_fan_power(load_factor_gross, True) / 1000 net_cooling_cap_reduced = ( self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac - indoor_fan_power ) - # Calculate efficency at rating conditions eir_temp_mod_fac = eir_f_t.evaluate( equipment_references[eqp_type][std][ @@ -608,8 +586,7 @@ def cal_reduced_eer(ratio, cap_f_t, eir_f_t, tot_cap_flow_mod_fac, eir_flow_mod_ logging.error("Input COP is 0!") raise ValueError("Input COP is 0!") net_cooling_cap_rated = ( - self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac - - self.indoor_fan_power + self.ref_gross_cap ) # "Load Factor" (as per AHRI Standard) which is analogous to PLR if ratio < 1.0: @@ -622,34 +599,47 @@ def cal_reduced_eer(ratio, cap_f_t, eir_f_t, tot_cap_flow_mod_fac, eir_flow_mod_ ) else: load_factor = 1 - # Cycling degradation degradation_coeff = 1 / plf_f_plr.evaluate(load_factor, 1) - + #print(degradation_coeff, eir,self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac) # Power elec_power_reduced_cap = ( degradation_coeff * eir * (self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac) ) - + #print(load_factor, net_cooling_cap_reduced, elec_power_reduced_cap) # EER eer_reduced = (load_factor * net_cooling_cap_reduced) / ( load_factor * elec_power_reduced_cap + indoor_fan_power ) return eer_reduced, load_factor - + curve_num = 0 + interpolation = False + eer_reduced = 0 #for stage_id, red_cap_num in enumerate(self.compressor_stages): for red_cap_num in range(num_of_reduced_cap): # Determine the outdoor air conditions based on AHRI Standard - if reduced_plr[red_cap_num] > 0.444: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( - 5.0 + 30.0 * reduced_plr[red_cap_num] - ) + #if reduced_plr[red_cap_num] > 0.444: + # outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( + # 5.0 + 30.0 * reduced_plr[red_cap_num] + # ) + #else: + # outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ + # eqp_type + # ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] + #manually set temperature for debugging + if red_cap_num == 0: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = 35.05 else: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ - eqp_type - ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] + if red_cap_num == 1: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = 27.5 + else: + if red_cap_num == 2: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = 19.78 + else: + if red_cap_num == 3: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = 18.5 interpolation = False for stage_id, capacity_ratio in enumerate(self.compressor_stages): if stage_id + 1 < len(self.compressor_stages): @@ -660,39 +650,50 @@ def cal_reduced_eer(ratio, cap_f_t, eir_f_t, tot_cap_flow_mod_fac, eir_flow_mod_ upper_stage_load = self.compressor_stages[stage_id + 1] else: curve_num = stage_id + 1 + if red_cap_num == 1: + #print('cap == 1') + #print(self.compressor_stages) + #print(reduced_plr[red_cap_num]) + #manually set the lf for debugging + lower_stage_load = 0.527 + upper_stage_load = 1.067 if ( self.compressor_stage_input and interpolation ): # interpolation - _, load_factor_1 = cal_reduced_eer( + eer_1, load_factor_1 = cal_reduced_eer( lower_stage_load, cap_f_t2, eir_f_t2, tot_cap_flow_mod_fac2, eir_flow_mod_fac2, plf_f_plr2,outdoor_unit_inlet_air_dry_bulb_temp_reduced ) - _, load_factor_2 = cal_reduced_eer( + eer_2, load_factor_2 = cal_reduced_eer( upper_stage_load, cap_f_t1, eir_f_t1, tot_cap_flow_mod_fac1, eir_flow_mod_fac1, plf_f_plr1,outdoor_unit_inlet_air_dry_bulb_temp_reduced ) + #print(eer_1, eer_2) eer_reduced = ( ( - load_factor_1 - - load_factor_2 + eer_1 + - eer_2 ) / (lower_stage_load - upper_stage_load) - ) * (reduced_plr[red_cap_num] - upper_stage_load) + load_factor_2 + ) * (reduced_plr[red_cap_num] - upper_stage_load) + eer_2 else: if curve_num == 1: + #if red_cap_num == 2: + # print('red_cap_num == 2') eer_reduced, _ = cal_reduced_eer( reduced_plr[red_cap_num], cap_f_t2, eir_f_t2, tot_cap_flow_mod_fac2, eir_flow_mod_fac2, plf_f_plr2, outdoor_unit_inlet_air_dry_bulb_temp_reduced, ) - if curve_num == 2: + if curve_num == 0: eer_reduced, _ = cal_reduced_eer( - reduced_plr[red_cap_num], cap_f_t2, eir_f_t1, tot_cap_flow_mod_fac1, eir_flow_mod_fac1, plf_f_plr1, + reduced_plr[red_cap_num], cap_f_t1, eir_f_t1, tot_cap_flow_mod_fac1, eir_flow_mod_fac1, plf_f_plr1, outdoor_unit_inlet_air_dry_bulb_temp_reduced, ) if eff_type == "full": ieer = eer_reduced break # Update IEER + #print(interpolation, curve_num, weighting_factor[red_cap_num] , eer_reduced) ieer += weighting_factor[red_cap_num] * eer_reduced # Convert efficiency to original unit unless specified if unit != "cop": diff --git a/tests/data/DX_multispeed_input_file.json b/tests/data/DX_multispeed_input_file.json index 7c98e84..7fed463 100644 --- a/tests/data/DX_multispeed_input_file.json +++ b/tests/data/DX_multispeed_input_file.json @@ -6,7 +6,7 @@ "condenser_type": "air", "compressor_type": "scroll", "compressor_speed": "variable", - "ref_net_cap": null, + "ref_net_cap": 471000, "ref_cap_unit": "W", "full_eff": null, "full_eff_unit": "eer", @@ -23,8 +23,8 @@ "plf-f-plr": { "out_var": "plf-f-plr", "type": "linear", - "coeff1": 0.469265, - "coeff2": 0.530735, + "coeff1": 1, + "coeff2": 0, "x_min": 0, "x_max": 1.5, "out_min": 0, @@ -33,12 +33,12 @@ "cap-f-t": { "out_var": "cap-f-t", "type": "linear", - "coeff1": 44.265, - "coeff2": -0.2972, + "coeff1": 1.3078, + "coeff2": -0.0088, "x_min": 20, "x_max": 40, - "out_min": 33.5, - "out_max": 36.5 + "out_min": 0, + "out_max": 2 }, "eir-f-ff": { "out_var": "eir-f-ff", @@ -53,15 +53,15 @@ "eir-f-t": { "out_var": "eir-f-t", "type": "linear", - "coeff1": 0.044, - "coeff2": 0.0013, + "coeff1": 0.4875, + "coeff2": 0.0146, "x_min": 20, "x_max": 40, - "out_min": 0.078, - "out_max": 0.092 + "out_min": 0, + "out_max": 2 }, "cap-f-ff": { - "out_var": "cap_f_ff", + "out_var": "cap-f-ff", "type": "quad", "coeff1": 1, "coeff2": 0, @@ -83,7 +83,7 @@ "condenser_type": "air", "compressor_type": "scroll", "compressor_speed": "variable", - "ref_net_cap": null, + "ref_net_cap": 471000, "ref_cap_unit": "W", "full_eff": null, "full_eff_unit": "eer", @@ -100,38 +100,38 @@ "plf-f-plr": { "out_var": "plf-f-plr", "type": "quad", - "coeff1": 1.5347, - "coeff2": -1.7496, - "coeff3": 0.759, + "coeff1": 1, + "coeff2": 0, + "coeff3": 0, "x_min": 0, "x_max": 1.5, "out_min": 0, "out_max": 1.5 }, "cap-f-t": { - "out_var": "cap_f_t", + "out_var": "cap-f-t", "type": "quad", - "coeff1": 23.074, - "coeff2": -0.3816, - "coeff3": 0.007, + "coeff1": 0.6817, + "coeff2": -0.0113, + "coeff3": 0.0002, "x_min": 10, "x_max": 30, - "out_min": 17.8, - "out_max": 18.6 + "out_min": 0, + "out_max": 1 }, "eir-f-t": { - "out_var": "eir_f_t", + "out_var": "eir-f-t", "type": "quad", - "coeff1": 0.0419, - "coeff2": 0.0019, - "coeff3": -0.00002, + "coeff1": 0.465, + "coeff2": 0.0213, + "coeff3": -0.0003, "x_min": 10, "x_max": 30, - "out_min": 0.068, - "out_max": 0.078 + "out_min": 0, + "out_max": 1 }, "cap-f-ff": { - "out_var": "cap_f_ff", + "out_var": "cap-f-ff", "type": "quad", "coeff1": 1, "coeff2": 0, @@ -142,7 +142,7 @@ "out_max": 18.6 }, "eir-f-ff": { - "out_var": "cap_f_ff", + "out_var": "eir-f-ff", "type": "quad", "coeff1": 1, "coeff2": 0, diff --git a/tests/test_unitarydirectexpansion.py b/tests/test_unitarydirectexpansion.py index 3f96433..c1e5112 100644 --- a/tests/test_unitarydirectexpansion.py +++ b/tests/test_unitarydirectexpansion.py @@ -20,8 +20,8 @@ def test_new_ieer(self): condenser_type="air", compressor_speed="constant", ref_cap_unit="W", - ref_gross_cap=471000, - full_eff=5.89, + ref_gross_cap=33847,#convert the unit from Btu/hr + full_eff=1, # how to set this? need to check with Jeremy full_eff_unit="cop", part_eff_ref_std="ahri_340/360", model="simplified_bf", @@ -30,10 +30,10 @@ def test_new_ieer(self): #seems it can load the json file, but cannot find the curve named 'HighStage' set_of_curves_2=lib_in.get_set_of_curves_by_name("LowStage").curves, compressor_stage_input=True, - compressor_stages=[0.527, 1.067], + compressor_stages=[0.527,0.95],#how to set this? if use 1.06 not 0.95, 1 will use interperlation ) ieer = round(dx_unit_new.calc_rated_eff_two_curves(unit="eer"), 1) - self.assertTrue(8.4 == ieer, f"{ieer} is different than 8.4") + self.assertTrue(13 == ieer, f"{ieer} is different than 13") """"" # Define equipment characteristics dx_unit_dft = cp.UnitaryDirectExpansion( From 5d12645c1f525de742a7b19da90d055372f2cf76 Mon Sep 17 00:00:00 2001 From: "Wan, Hanlong" Date: Tue, 17 Dec 2024 09:41:15 -0800 Subject: [PATCH 7/7] update --- copper/library.py | 8 + copper/unitarydirectexpansion.py | 299 +++++++++-------------- tests/data/DX_multispeed_input_file.json | 69 ++---- tests/test_unitarydirectexpansion.py | 14 +- 4 files changed, 151 insertions(+), 239 deletions(-) diff --git a/copper/library.py b/copper/library.py index eec294d..18bea17 100644 --- a/copper/library.py +++ b/copper/library.py @@ -48,6 +48,8 @@ def __init__(self, path=chiller_lib, rating_std="", export=False): and not "indoor_fan_power_unit" in p and not "compressor_stage" in p and not "compressor_stage_input" in p + and not "control_power" in p + and not "control_power_unit" in p ): obj_args[p] = vals[p] elif ( @@ -127,6 +129,8 @@ def load_obj(self, data): and not "indoor_fan_power_unit" in p and not "compressor_stage" in p and not "compressor_stage_input" in p + and not "control_power" in p + and not "control_power_unit" in p ): obj_args[p] = data[p] @@ -205,6 +209,8 @@ def find_set_of_curves_from_lib(self, filters=[], part_eff_flag=False): "indoor_fan_power_unit", "compressor_stage", "compressor_stage_input", + "control_power_unit", + "control_power" ] # Set the equipment properties @@ -224,6 +230,8 @@ def find_set_of_curves_from_lib(self, filters=[], part_eff_flag=False): or "indoor_fan_curve_coef" in p or "indoor_fan_curve" in p or "indoor_fan_power_unit" in p + or "control_power" in p + or "control_power_unit" in p ): pass else: diff --git a/copper/unitarydirectexpansion.py b/copper/unitarydirectexpansion.py index aa14b8e..50d4a07 100644 --- a/copper/unitarydirectexpansion.py +++ b/copper/unitarydirectexpansion.py @@ -63,6 +63,8 @@ def __init__( indoor_fan_speeds=1, indoor_fan_curve=False, indoor_fan_power_unit="kW", + control_power = 100, + control_power_unit = "kW", compressor_stage_input=False, compressor_stages=[0.3, 0.6], ): @@ -138,7 +140,7 @@ def __init__( value=Units(value=ref_gross_cap, unit=ref_cap_unit).conversion( new_unit=indoor_fan_power_unit ) - - indoor_fan_power, + - indoor_fan_power[0], unit=indoor_fan_power_unit, ).conversion(ref_cap_unit) self.ref_cap_unit = ref_cap_unit @@ -152,6 +154,12 @@ def __init__( self.ref_net_cap = ref_net_cap self.ref_gross_cap = ref_gross_cap self.ref_cap_unit = ref_cap_unit + #To do: add indoor fan power/control power unit conversion + if isinstance(indoor_fan_power, list): + if len(indoor_fan_power) > 1: + self.indoor_fan_power = indoor_fan_power + elif len(indoor_fan_power) == 1: + self.indoor_fan_power = indoor_fan_power[0] #reorder compressor_stages compressor_stages = sorted(compressor_stages) # Get attributes @@ -175,16 +183,17 @@ def __init__( self.compressor_speed = compressor_speed self.indoor_fan_speeds_mapping = indoor_fan_speeds_mapping self.indoor_fan_speeds = indoor_fan_speeds - self.indoor_fan_power = indoor_fan_power self.indoor_fan_curve_coef = indoor_fan_curve_coef self.indoor_fan_power_unit = indoor_fan_power_unit self.indoor_fan_curve = indoor_fan_curve self.compressor_stages = compressor_stages self.compressor_stage_input = compressor_stage_input + self.control_power = control_power + self.control_power_unit = control_power_unit # Define rated temperatures # air entering drybulb, air entering wetbulb, entering condenser temperature, leaving condenser temperature aed, self.aew, ect, lct = self.get_rated_temperatures() - self.ect = ect[0] + self.ect = ect # Defined plotting ranges and (rated) temperature for normalization nb_val = 50 @@ -197,7 +206,7 @@ def __init__( "nbval": nb_val, "x2_min": 15, "x2_max": 40, - "x2_norm": self.ect, + "x2_norm": self.ect[0], }, "cap-f-t": { "x1_min": self.aew, @@ -206,7 +215,7 @@ def __init__( "nbval": 50, "x2_min": 15, "x2_max": 40, - "x2_norm": self.ect, + "x2_norm": self.ect[0], "nbval": nb_val, }, "eir-f-ff": {"x1_min": 0, "x1_max": 2, "x1_norm": 1, "nbval": nb_val}, @@ -216,19 +225,19 @@ def __init__( # Cycling degradation self.degradation_coefficient = degradation_coefficient - self.add_cycling_degradation_curve() + self.add_cycling_degradation_curve(set_of_curves=self.set_of_curves) - def add_cycling_degradation_curve(self, overwrite=False, return_curve=False): + def add_cycling_degradation_curve(self, set_of_curves, overwrite=False, return_curve=False): """Determine and assign a part load fraction as a function of part load ratio curve to a unitary DX equipment. - + :param curves set of curves :param str overwrite: Flag to overwrite the existing degradation curve. Default is False. :param str assign_curve: Add curve to equipment's. Default is True. """ # Remove exisiting curve if it exists if overwrite: - for curve in self.set_of_curves: + for curve in set_of_curves: if curve.out_var == "plf-f-plr": - self.set_of_curves.remove(curve) + set_of_curves.remove(curve) break # Add new curve @@ -245,7 +254,7 @@ def add_cycling_degradation_curve(self, overwrite=False, return_curve=False): if return_curve: return plf_f_plr else: - self.set_of_curves.append(plf_f_plr) + set_of_curves.append(plf_f_plr) # default fan curve self.default_fan_curve = Curve( @@ -339,7 +348,7 @@ def calc_rated_eff( eir_f_t = curves["eir-f-t"] eir_f_f = curves["eir-f-ff"] if not "plf-f-plr" in curves.keys(): - self.add_cycling_degradation_curve() + self.add_cycling_degradation_curve(set_of_curves=self.set_of_curves) curves = self.get_dx_curves() plf_f_plr = curves["plf-f-plr"] @@ -378,12 +387,17 @@ def calc_rated_eff( # Iterate through the different sets of rating conditions to calculate IEER ieer = 0 + for red_cap_num in range(num_of_reduced_cap): + # Determine the outdoor air conditions based on AHRI Standard + if reduced_plr[red_cap_num] > 0.444: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( + 5.0 + 30.0 * reduced_plr[red_cap_num] + ) + else: + outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ + eqp_type + ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] - def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): - """Inner function to calculate reduced eer. - :param float ratio: capacity ratio - :param float outdoor_unit_inlet_air_dry_bulb_temp_reduced: Reduced Outdoor Unit Inlet Air Dry Bulb temperature - """ # Calculate capacity at rating conditions tot_cap_temp_mod_fac = cap_f_t.evaluate( equipment_references[eqp_type][std][ @@ -392,9 +406,9 @@ def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): outdoor_unit_inlet_air_dry_bulb_temp_reduced, ) load_factor_gross = min( - 1.0, (ratio / tot_cap_temp_mod_fac) + 1.0, (reduced_plr[red_cap_num] / tot_cap_temp_mod_fac) ) # Load percentage * Rated gross capacity / Available gross capacity - indoor_fan_power = self.calc_fan_power(load_factor_gross, False) / 1000 + indoor_fan_power = self.calc_fan_power(load_factor_gross) / 1000 net_cooling_cap_reduced = ( self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac - indoor_fan_power @@ -415,9 +429,9 @@ def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): raise ValueError("Input COP is 0!") # "Load Factor" (as per AHRI Standard) which is analogous to PLR - if ratio < 1.0: + if reduced_plr[red_cap_num] < 1.0: load_factor = ( - ratio # reduced_plr[red_cap_num] + reduced_plr[red_cap_num] * net_cooling_cap_rated / net_cooling_cap_reduced if net_cooling_cap_reduced > 0.0 @@ -440,55 +454,14 @@ def cal_reduced_eer(ratio, outdoor_unit_inlet_air_dry_bulb_temp_reduced): eer_reduced = (load_factor * net_cooling_cap_reduced) / ( load_factor * elec_power_reduced_cap + indoor_fan_power ) - return eer_reduced, load_factor - #for stage_id, red_cap_num in enumerate(self.compressor_stages): - for red_cap_num in range(num_of_reduced_cap): - # Determine the outdoor air conditions based on AHRI Standard - if reduced_plr[red_cap_num] > 0.444: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( - 5.0 + 30.0 * reduced_plr[red_cap_num] - ) - else: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ - eqp_type - ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] - interpolation = False - for stage_id, capacity_ratio in enumerate(self.compressor_stages): - if stage_id + 1 < len(self.compressor_stages): - if ((self.compressor_stages[stage_id + 1] >= reduced_plr[red_cap_num]) - and (reduced_plr[red_cap_num] > capacity_ratio)): - interpolation = True - lower_stage_load = capacity_ratio - upper_stage_load = self.compressor_stages[stage_id + 1] - if ( - self.compressor_stage_input - and interpolation - ): - # interpolation - _, load_factor_1 = cal_reduced_eer( - lower_stage_load, outdoor_unit_inlet_air_dry_bulb_temp_reduced - ) - _, load_factor_2 = cal_reduced_eer( - upper_stage_load, outdoor_unit_inlet_air_dry_bulb_temp_reduced - ) - eer_reduced = ( - ( - load_factor_1 - - load_factor_2 - ) - / (lower_stage_load - upper_stage_load) - ) * (reduced_plr[red_cap_num] - upper_stage_load) + load_factor_2 - else: - eer_reduced, _ = cal_reduced_eer( - reduced_plr[red_cap_num], - outdoor_unit_inlet_air_dry_bulb_temp_reduced, - ) if eff_type == "full": ieer = eer_reduced break + # Update IEER ieer += weighting_factor[red_cap_num] * eer_reduced + # Convert efficiency to original unit unless specified if unit != "cop": ieer = Units(value=ieer, unit="cop") @@ -511,24 +484,26 @@ def calc_rated_eff_two_curves( :rtype: float """ - # Handle alternate ratings (not currently used) if alt: std = self.part_eff_ref_std_alt else: std = self.part_eff_ref_std - # Retrieve curves curves_1, curves_2 = self.get_two_dx_curves() cap_f_f1 = curves_1["cap-f-ff"] cap_f_t1 = curves_1["cap-f-t"] eir_f_t1 = curves_1["eir-f-t"] eir_f_f1 = curves_1["eir-f-ff"] + if not "plf-f-plr" in curves_1.keys(): + curves_1["plf-f-plr"]=self.add_cycling_degradation_curve(set_of_curves=curves_1,return_curve=True) plf_f_plr1 = curves_1["plf-f-plr"] cap_f_f2 = curves_2["cap-f-ff"] cap_f_t2 = curves_2["cap-f-t"] eir_f_t2 = curves_2["eir-f-t"] eir_f_f2 = curves_2["eir-f-ff"] + if not "plf-f-plr" in curves_2.keys(): + curves_2["plf-f-plr"]=self.add_cycling_degradation_curve(set_of_curves=curves_2,return_curve=True) plf_f_plr2 = curves_2["plf-f-plr"] # Calculate capacity and efficiency degradation as a function of flow fraction tot_cap_flow_mod_fac1 = 1 @@ -547,148 +522,92 @@ def calc_rated_eff_two_curves( # Convert user-specified full load efficiency to COP # User-specified capacity is a NET efficiency full_eff = Units(value=self.full_eff, unit=self.full_eff_unit) - rated_cop = full_eff.conversion(new_unit="cop") - # Iterate through the different sets of rating conditions to calculate IEER ieer = 0 - - def cal_reduced_eer(ratio, cap_f_t, eir_f_t, tot_cap_flow_mod_fac, eir_flow_mod_fac, plf_f_plr, outdoor_unit_inlet_air_dry_bulb_temp_reduced): - """Inner function to calculate reduced eer. - :param float ratio: capacity ratio - :param float outdoor_unit_inlet_air_dry_bulb_temp_reduced: Reduced Outdoor Unit Inlet Air Dry Bulb temperature - """ - # Calculate capacity at rating conditions - tot_cap_temp_mod_fac = cap_f_t.evaluate( - equipment_references[eqp_type][std][ - "cooling_coil_inlet_air_wet_bulb_rated" - ], - outdoor_unit_inlet_air_dry_bulb_temp_reduced, - ) - load_factor_gross = min( - 1.0, (ratio / tot_cap_temp_mod_fac) - ) # Load percentage * Rated gross capacity / Available gross capacity - indoor_fan_power = self.calc_fan_power(load_factor_gross, True) / 1000 - net_cooling_cap_reduced = ( - self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac - - indoor_fan_power - ) - # Calculate efficency at rating conditions - eir_temp_mod_fac = eir_f_t.evaluate( - equipment_references[eqp_type][std][ - "cooling_coil_inlet_air_wet_bulb_rated" - ], - outdoor_unit_inlet_air_dry_bulb_temp_reduced, - ) - if rated_cop > 0.0: - eir = eir_temp_mod_fac * eir_flow_mod_fac / rated_cop - else: - eir = 0.0 - logging.error("Input COP is 0!") - raise ValueError("Input COP is 0!") - net_cooling_cap_rated = ( - self.ref_gross_cap - ) - # "Load Factor" (as per AHRI Standard) which is analogous to PLR - if ratio < 1.0: - load_factor = ( - ratio # reduced_plr[red_cap_num] - * net_cooling_cap_rated - / net_cooling_cap_reduced - if net_cooling_cap_reduced > 0.0 - else 1.0 - ) - else: - load_factor = 1 - # Cycling degradation - degradation_coeff = 1 / plf_f_plr.evaluate(load_factor, 1) - #print(degradation_coeff, eir,self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac) - # Power - elec_power_reduced_cap = ( - degradation_coeff - * eir - * (self.ref_gross_cap * tot_cap_temp_mod_fac * tot_cap_flow_mod_fac) - ) - #print(load_factor, net_cooling_cap_reduced, elec_power_reduced_cap) - # EER - eer_reduced = (load_factor * net_cooling_cap_reduced) / ( - load_factor * elec_power_reduced_cap + indoor_fan_power - ) - return eer_reduced, load_factor - curve_num = 0 interpolation = False + degradation = False eer_reduced = 0 - #for stage_id, red_cap_num in enumerate(self.compressor_stages): + + def cal_eer(cap_f_t,eir_f_t,net_cap, ref_gross_cap,indoor_fan_power,control_power): + """Inner function to calculate reduced eer&LF + :param float cap_f_t: Capacity factor + :param float eir_f_t: EIR factor + :param float net_cap: Reduced Net Capacity + :param float ref_gross_cap: Reference Gross Capacity + :param float indoor_fan_power: Indoor Fan Power + :param float control_power: Control Power + :return: Unitary DX Equipment EER and load factor + :rtype: float + """ + net_cooling_cap_reduced = cap_f_t*ref_gross_cap-indoor_fan_power + pc_pcd = eir_f_t*self.full_eff*(net_cooling_cap_reduced+indoor_fan_power) + eer = net_cooling_cap_reduced/(pc_pcd + indoor_fan_power + control_power) + load_factor = net_cooling_cap_reduced/net_cap + return eer, load_factor + def cal_reduced_eer(cap_f_t,eir_f_t,net_cap, ref_gross_cap,indoor_fan_power,control_power,reduced_plr): + """Inner function to calculate reduced eer&LF + :param float cap_f_t: Capacity factor + :param float eir_f_t: EIR factor + :param float net_cap: Reduced Net Capacity + :param float ref_gross_cap: Reference Gross Capacity + :param float indoor_fan_power: Indoor Fan Power + :param float control_power: Control Power + :return: Unitary DX Equipment EER and load factor + :rtype: float + """ + net_cooling_cap_reduced = cap_f_t*ref_gross_cap-indoor_fan_power + pc_pcd = eir_f_t*self.full_eff*(net_cooling_cap_reduced+indoor_fan_power) + LF = reduced_plr*net_cap/net_cooling_cap_reduced + Cd = -0.13*LF + 1.13 + eer_reduced = LF*net_cooling_cap_reduced/(LF*(Cd*(pc_pcd))+control_power+indoor_fan_power) + return eer_reduced for red_cap_num in range(num_of_reduced_cap): - # Determine the outdoor air conditions based on AHRI Standard - #if reduced_plr[red_cap_num] > 0.444: - # outdoor_unit_inlet_air_dry_bulb_temp_reduced = ( - # 5.0 + 30.0 * reduced_plr[red_cap_num] - # ) - #else: - # outdoor_unit_inlet_air_dry_bulb_temp_reduced = equipment_references[ - # eqp_type - # ][std]["outdoor_unit_inlet_air_dry_bulb_reduced"] - #manually set temperature for debugging - if red_cap_num == 0: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = 35.05 - else: - if red_cap_num == 1: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = 27.5 - else: - if red_cap_num == 2: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = 19.78 - else: - if red_cap_num == 3: - outdoor_unit_inlet_air_dry_bulb_temp_reduced = 18.5 + outdoor_unit_inlet_air_dry_bulb_temp_reduced = self.ect[red_cap_num] interpolation = False + degradation = False for stage_id, capacity_ratio in enumerate(self.compressor_stages): if stage_id + 1 < len(self.compressor_stages): - if (self.compressor_stages[stage_id + 1] >= reduced_plr[red_cap_num]): + if (self.compressor_stages[stage_id + 1] > reduced_plr[red_cap_num]): if (reduced_plr[red_cap_num] > capacity_ratio): interpolation = True - lower_stage_load = capacity_ratio - upper_stage_load = self.compressor_stages[stage_id + 1] - else: - curve_num = stage_id + 1 - if red_cap_num == 1: - #print('cap == 1') - #print(self.compressor_stages) - #print(reduced_plr[red_cap_num]) - #manually set the lf for debugging - lower_stage_load = 0.527 - upper_stage_load = 1.067 + if not interpolation: + if reduced_plr[red_cap_num]<=self.compressor_stages[0]: + degradation = True + #if reduced_plr[red_cap_num] == 0.75: + # interpolation = True + #if (reduced_plr[red_cap_num] == 0.50) | (reduced_plr[red_cap_num] == 0.25): + # degradation = True + net_cooling_cap_reduced = cap_f_t1.evaluate(self.ect[0],1)*self.ref_gross_cap-self.indoor_fan_power[0] if ( self.compressor_stage_input and interpolation ): - # interpolation - eer_1, load_factor_1 = cal_reduced_eer( - lower_stage_load, cap_f_t2, eir_f_t2, tot_cap_flow_mod_fac2, eir_flow_mod_fac2, plf_f_plr2,outdoor_unit_inlet_air_dry_bulb_temp_reduced - ) - eer_2, load_factor_2 = cal_reduced_eer( - upper_stage_load, cap_f_t1, eir_f_t1, tot_cap_flow_mod_fac1, eir_flow_mod_fac1, plf_f_plr1,outdoor_unit_inlet_air_dry_bulb_temp_reduced - ) - #print(eer_1, eer_2) + eer_1, load_1 = cal_eer(cap_f_t1.evaluate(outdoor_unit_inlet_air_dry_bulb_temp_reduced,1), + eir_f_t1.evaluate(outdoor_unit_inlet_air_dry_bulb_temp_reduced,1), + net_cooling_cap_reduced, self.ref_gross_cap,self.indoor_fan_power[0], + self.control_power[0]) + eer_2, load_2 = cal_eer(cap_f_t2.evaluate(outdoor_unit_inlet_air_dry_bulb_temp_reduced,1), + eir_f_t2.evaluate(outdoor_unit_inlet_air_dry_bulb_temp_reduced,1), + net_cooling_cap_reduced, self.ref_gross_cap/2,self.indoor_fan_power[1], + self.control_power[1]) eer_reduced = ( ( eer_1 - eer_2 ) - / (lower_stage_load - upper_stage_load) - ) * (reduced_plr[red_cap_num] - upper_stage_load) + eer_2 + * (reduced_plr[red_cap_num] - load_2) + ) / (load_1 - load_2) + eer_2 else: - if curve_num == 1: - #if red_cap_num == 2: - # print('red_cap_num == 2') - eer_reduced, _ = cal_reduced_eer( - reduced_plr[red_cap_num], cap_f_t2, eir_f_t2, tot_cap_flow_mod_fac2, eir_flow_mod_fac2, plf_f_plr2, - outdoor_unit_inlet_air_dry_bulb_temp_reduced, - ) - if curve_num == 0: - eer_reduced, _ = cal_reduced_eer( - reduced_plr[red_cap_num], cap_f_t1, eir_f_t1, tot_cap_flow_mod_fac1, eir_flow_mod_fac1, plf_f_plr1, - outdoor_unit_inlet_air_dry_bulb_temp_reduced, - ) + if degradation: + eer_reduced = cal_reduced_eer(cap_f_t2.evaluate(outdoor_unit_inlet_air_dry_bulb_temp_reduced,1), + eir_f_t2.evaluate(outdoor_unit_inlet_air_dry_bulb_temp_reduced,1), + net_cooling_cap_reduced, self.ref_gross_cap/2,self.indoor_fan_power[1], + self.control_power[1], reduced_plr[red_cap_num]) + else: + net_cap = net_cooling_cap_reduced + pc_pcd = eir_f_t1.evaluate(outdoor_unit_inlet_air_dry_bulb_temp_reduced,1)*self.full_eff*(net_cap+self.indoor_fan_power[0]) + eer_reduced = net_cap/(pc_pcd + self.indoor_fan_power[0] + self.control_power[0]) + print(reduced_plr[red_cap_num], eer_reduced) if eff_type == "full": ieer = eer_reduced break @@ -850,12 +769,12 @@ def get_ranges(self): ranges = { "eir-f-t": { "vars_range": [(12.8, 26), (10.0, 40.0)], - "normalization": (self.aew, self.ect), + "normalization": (self.aew, self.ect[0]), }, "eir-f-ff": {"vars_range": [(0.0, 1.5)], "normalization": (1.0)}, "cap-f-t": { "vars_range": [(12.8, 26), (10.0, 40.0)], - "normalization": (self.aew, self.ect), + "normalization": (self.aew, self.ect[0]), }, "cap-f-ff": {"vars_range": [(0.0, 1.5)], "normalization": (1.0)}, "plf-f-plr": {"vars_range": [(0.0, 1.0)], "normalization": (1.0)}, diff --git a/tests/data/DX_multispeed_input_file.json b/tests/data/DX_multispeed_input_file.json index 7fed463..f5adf90 100644 --- a/tests/data/DX_multispeed_input_file.json +++ b/tests/data/DX_multispeed_input_file.json @@ -20,25 +20,15 @@ "max_outdoor_fan_power": null, "indoor_fan_power_unit": "W", "set_of_curves": { - "plf-f-plr": { - "out_var": "plf-f-plr", - "type": "linear", - "coeff1": 1, - "coeff2": 0, - "x_min": 0, - "x_max": 1.5, - "out_min": 0, - "out_max": 1.5 - }, "cap-f-t": { "out_var": "cap-f-t", "type": "linear", - "coeff1": 1.3078, - "coeff2": -0.0088, + "coeff1": 1.304669191, + "coeff2": -0.008704834, "x_min": 20, "x_max": 40, "out_min": 0, - "out_max": 2 + "out_max": 999 }, "eir-f-ff": { "out_var": "eir-f-ff", @@ -48,17 +38,17 @@ "x_min": 0, "x_max": 1, "out_min": 0, - "out_max": 1 + "out_max": 999 }, "eir-f-t": { "out_var": "eir-f-t", "type": "linear", - "coeff1": 0.4875, - "coeff2": 0.0146, + "coeff1": 0.457735, + "coeff2": 0.015493299, "x_min": 20, "x_max": 40, "out_min": 0, - "out_max": 2 + "out_max": 999 }, "cap-f-ff": { "out_var": "cap-f-ff", @@ -68,8 +58,8 @@ "coeff3": 0, "x_min": 10, "x_max": 30, - "out_min": 17.8, - "out_max": 18.6 + "out_min": 0, + "out_max": 999 } }, "indoor_fan_control_mode": null, @@ -97,38 +87,29 @@ "max_outdoor_fan_power": null, "indoor_fan_power_unit": "W", "set_of_curves": { - "plf-f-plr": { - "out_var": "plf-f-plr", - "type": "quad", - "coeff1": 1, - "coeff2": 0, - "coeff3": 0, - "x_min": 0, - "x_max": 1.5, - "out_min": 0, - "out_max": 1.5 - }, "cap-f-t": { "out_var": "cap-f-t", - "type": "quad", - "coeff1": 0.6817, - "coeff2": -0.0113, - "coeff3": 0.0002, + "type": "cubic", + "coeff1": 1.44913, + "coeff2": -0.04264, + "coeff3": 0.001574633, + "coeff4": -2.06552e-05, "x_min": 10, "x_max": 30, "out_min": 0, - "out_max": 1 + "out_max": 999 }, "eir-f-t": { "out_var": "eir-f-t", - "type": "quad", - "coeff1": 0.465, - "coeff2": 0.0213, - "coeff3": -0.0003, + "type": "cubic", + "coeff1": 0.467928, + "coeff2": 0.032332, + "coeff3": -0.00114, + "coeff4": 1.8501e-05, "x_min": 10, "x_max": 30, "out_min": 0, - "out_max": 1 + "out_max": 999 }, "cap-f-ff": { "out_var": "cap-f-ff", @@ -138,8 +119,8 @@ "coeff3": 0, "x_min": 10, "x_max": 30, - "out_min": 17.8, - "out_max": 18.6 + "out_min": 0, + "out_max": 999 }, "eir-f-ff": { "out_var": "eir-f-ff", @@ -149,8 +130,8 @@ "coeff3": 0, "x_min": 10, "x_max": 30, - "out_min": 17.8, - "out_max": 18.6 + "out_min": 0, + "out_max": 999 } }, "indoor_fan_control_mode": null, diff --git a/tests/test_unitarydirectexpansion.py b/tests/test_unitarydirectexpansion.py index c1e5112..231a195 100644 --- a/tests/test_unitarydirectexpansion.py +++ b/tests/test_unitarydirectexpansion.py @@ -20,20 +20,24 @@ def test_new_ieer(self): condenser_type="air", compressor_speed="constant", ref_cap_unit="W", - ref_gross_cap=33847,#convert the unit from Btu/hr - full_eff=1, # how to set this? need to check with Jeremy + ref_gross_cap=119075.6/3.412,#convert the unit from Btu/hr + full_eff=0.26548, # EIR at full? full_eff_unit="cop", part_eff_ref_std="ahri_340/360", model="simplified_bf", sim_engine="energyplus", + indoor_fan_power=[1.050, 0.262], + control_power = [0.100, 0.150], + indoor_fan_power_unit="kW", + control_power_unit = "kW", set_of_curves_1=lib_in.get_set_of_curves_by_name("HighStage").curves,# this is the part have problem #seems it can load the json file, but cannot find the curve named 'HighStage' set_of_curves_2=lib_in.get_set_of_curves_by_name("LowStage").curves, compressor_stage_input=True, - compressor_stages=[0.527,0.95],#how to set this? if use 1.06 not 0.95, 1 will use interperlation + compressor_stages=[0.5,1],#how to set this? if use 1.06 not 0.95, 1 will use interperlation ) - ieer = round(dx_unit_new.calc_rated_eff_two_curves(unit="eer"), 1) - self.assertTrue(13 == ieer, f"{ieer} is different than 13") + ieer = round(dx_unit_new.calc_rated_eff_two_curves(unit="cop"), 1) + self.assertTrue(3.8 == ieer, f"{ieer} is different than 3.8") """"" # Define equipment characteristics dx_unit_dft = cp.UnitaryDirectExpansion(