From 1e8e3c66a7abb7c6eeb8dc8bc640cbc28a337f42 Mon Sep 17 00:00:00 2001 From: Scott Horowitz Date: Sun, 29 Sep 2024 12:05:13 -0600 Subject: [PATCH] Move a few more defaulting methods. --- HPXMLtoOpenStudio/measure.xml | 14 +-- HPXMLtoOpenStudio/resources/airflow.rb | 4 +- HPXMLtoOpenStudio/resources/geometry.rb | 4 +- HPXMLtoOpenStudio/resources/hpxml_defaults.rb | 105 +++++++++++++++++- HPXMLtoOpenStudio/resources/hvac.rb | 89 +-------------- HPXMLtoOpenStudio/resources/hvac_sizing.rb | 2 +- 6 files changed, 114 insertions(+), 104 deletions(-) diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml index b629e29475..41c4d7e7a8 100644 --- a/HPXMLtoOpenStudio/measure.xml +++ b/HPXMLtoOpenStudio/measure.xml @@ -3,8 +3,8 @@ 3.1 hpxm_lto_openstudio b1543b30-9465-45ff-ba04-1d1f85e763bc - e3bad851-b1bc-4f8b-93fb-73f6c8f78e3e - 2024-09-28T21:30:55Z + ff7eac20-94f1-4b91-89fd-9df3900e3451 + 2024-09-29T18:04:49Z D8922A73 HPXMLtoOpenStudio HPXML to OpenStudio Translator @@ -189,7 +189,7 @@ airflow.rb rb resource - 2CDDC527 + EFB81F7D battery.rb @@ -339,7 +339,7 @@ geometry.rb rb resource - D302A89B + D792E2E4 hotwater_appliances.rb @@ -357,7 +357,7 @@ hpxml_defaults.rb rb resource - 29ADB091 + D16B3ACE hpxml_schema/HPXML.xsd @@ -387,13 +387,13 @@ hvac.rb rb resource - 4FEA518E + 20D3FE05 hvac_sizing.rb rb resource - CA9FBBD6 + 266671E2 internal_gains.rb diff --git a/HPXMLtoOpenStudio/resources/airflow.rb b/HPXMLtoOpenStudio/resources/airflow.rb index 79ee37cc40..6fa73c923b 100644 --- a/HPXMLtoOpenStudio/resources/airflow.rb +++ b/HPXMLtoOpenStudio/resources/airflow.rb @@ -547,8 +547,8 @@ def self.apply_natural_ventilation_and_whole_house_fan(runner, model, spaces, hp vent_program.addLine("Set Tnvsp = (#{htg_sp_sensor.name} + #{clg_sp_sensor.name}) / 2") else # No HVAC system; use the average of defaulted heating/cooling setpoints. - htg_weekday_setpoints, htg_weekend_setpoints = HVAC.get_default_heating_setpoint(HPXML::HVACControlTypeManual, hpxml_header.eri_calculation_version) - clg_weekday_setpoints, clg_weekend_setpoints = HVAC.get_default_cooling_setpoint(HPXML::HVACControlTypeManual, hpxml_header.eri_calculation_version) + htg_weekday_setpoints, htg_weekend_setpoints = HPXMLDefaults.get_default_heating_setpoint(HPXML::HVACControlTypeManual, hpxml_header.eri_calculation_version) + clg_weekday_setpoints, clg_weekend_setpoints = HPXMLDefaults.get_default_cooling_setpoint(HPXML::HVACControlTypeManual, hpxml_header.eri_calculation_version) if htg_weekday_setpoints.split(', ').uniq.size == 1 && htg_weekend_setpoints.split(', ').uniq.size == 1 && htg_weekday_setpoints.split(', ').uniq == htg_weekend_setpoints.split(', ').uniq default_htg_sp = UnitConversions.convert(htg_weekend_setpoints.split(', ').uniq[0].to_f, 'F', 'C') else diff --git a/HPXMLtoOpenStudio/resources/geometry.rb b/HPXMLtoOpenStudio/resources/geometry.rb index 589de7176d..745961be1b 100644 --- a/HPXMLtoOpenStudio/resources/geometry.rb +++ b/HPXMLtoOpenStudio/resources/geometry.rb @@ -1808,14 +1808,14 @@ def self.get_space_temperature_schedule(model, location, spaces) space_values = get_temperature_scheduled_space_values(location) - htg_weekday_setpoints, htg_weekend_setpoints = HVAC.get_default_heating_setpoint(HPXML::HVACControlTypeManual, @eri_version) + htg_weekday_setpoints, htg_weekend_setpoints = HPXMLDefaults.get_default_heating_setpoint(HPXML::HVACControlTypeManual, @eri_version) if htg_weekday_setpoints.split(', ').uniq.size == 1 && htg_weekend_setpoints.split(', ').uniq.size == 1 && htg_weekday_setpoints.split(', ').uniq == htg_weekend_setpoints.split(', ').uniq default_htg_sp = htg_weekend_setpoints.split(', ').uniq[0].to_f # F else fail 'Unexpected heating setpoints.' end - clg_weekday_setpoints, clg_weekend_setpoints = HVAC.get_default_cooling_setpoint(HPXML::HVACControlTypeManual, @eri_version) + clg_weekday_setpoints, clg_weekend_setpoints = HPXMLDefaults.get_default_cooling_setpoint(HPXML::HVACControlTypeManual, @eri_version) if clg_weekday_setpoints.split(', ').uniq.size == 1 && clg_weekend_setpoints.split(', ').uniq.size == 1 && clg_weekday_setpoints.split(', ').uniq == clg_weekend_setpoints.split(', ').uniq default_clg_sp = clg_weekend_setpoints.split(', ').uniq[0].to_f # F else diff --git a/HPXMLtoOpenStudio/resources/hpxml_defaults.rb b/HPXMLtoOpenStudio/resources/hpxml_defaults.rb index 264b0839c5..096f566a27 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_defaults.rb +++ b/HPXMLtoOpenStudio/resources/hpxml_defaults.rb @@ -791,7 +791,7 @@ def self.apply_site(hpxml_bldg) # Shielding Class 5 is ACCA MJ8 default for Table 5B/5E for townhouses and condos hpxml_bldg.site.shielding_of_home = HPXML::ShieldingWellShielded else - # Shielding Class 4 is ACCA MJ8 default for Table 5A/5D and ANSI/RESNET 301 default + # Shielding Class 4 is ACCA MJ8 default for Table 5A/5D and ANSI/RESNET/ICC 301 default hpxml_bldg.site.shielding_of_home = HPXML::ShieldingNormal end hpxml_bldg.site.shielding_of_home_isdefaulted = true @@ -1923,7 +1923,7 @@ def self.apply_hvac(runner, hpxml_bldg, weather, convert_shared_systems, unit_nu next if [HPXML::HVACTypeHeatPumpGroundToAir, HPXML::HVACTypeHeatPumpWaterLoopToAir].include? heat_pump.heat_pump_type next unless heat_pump.heating_detailed_performance_data.empty? # set after hvac sizing - heat_pump.heating_capacity_retention_temp, heat_pump.heating_capacity_retention_fraction = HVAC.get_default_heating_capacity_retention(heat_pump.compressor_type, heat_pump.heating_efficiency_hspf) + heat_pump.heating_capacity_retention_temp, heat_pump.heating_capacity_retention_fraction = get_default_heating_capacity_retention(heat_pump.compressor_type, heat_pump.heating_efficiency_hspf) heat_pump.heating_capacity_retention_fraction_isdefaulted = true heat_pump.heating_capacity_retention_temp_isdefaulted = true end @@ -2419,7 +2419,7 @@ def self.apply_hvac_control(hpxml_bldg, schedules_file, eri_version) schedules_file_includes_heating_setpoint_temp = (schedules_file.nil? ? false : schedules_file.includes_col_name(SchedulesFile::Columns[:HeatingSetpoint].name)) if hvac_control.heating_setpoint_temp.nil? && hvac_control.weekday_heating_setpoints.nil? && !schedules_file_includes_heating_setpoint_temp # No heating setpoints; set a default heating setpoint for, e.g., natural ventilation - htg_weekday_setpoints, htg_weekend_setpoints = HVAC.get_default_heating_setpoint(HPXML::HVACControlTypeManual, eri_version) + htg_weekday_setpoints, htg_weekend_setpoints = get_default_heating_setpoint(HPXML::HVACControlTypeManual, eri_version) if htg_weekday_setpoints.split(', ').uniq.size == 1 && htg_weekend_setpoints.split(', ').uniq.size == 1 && htg_weekday_setpoints.split(', ').uniq == htg_weekend_setpoints.split(', ').uniq hvac_control.heating_setpoint_temp = htg_weekend_setpoints.split(', ').uniq[0].to_f else @@ -2431,7 +2431,7 @@ def self.apply_hvac_control(hpxml_bldg, schedules_file, eri_version) schedules_file_includes_cooling_setpoint_temp = (schedules_file.nil? ? false : schedules_file.includes_col_name(SchedulesFile::Columns[:CoolingSetpoint].name)) if hvac_control.cooling_setpoint_temp.nil? && hvac_control.weekday_cooling_setpoints.nil? && !schedules_file_includes_cooling_setpoint_temp # No cooling setpoints; set a default cooling setpoint for, e.g., natural ventilation - clg_weekday_setpoints, clg_weekend_setpoints = HVAC.get_default_cooling_setpoint(HPXML::HVACControlTypeManual, eri_version) + clg_weekday_setpoints, clg_weekend_setpoints = HPXMLDefaults.get_default_cooling_setpoint(HPXML::HVACControlTypeManual, eri_version) if clg_weekday_setpoints.split(', ').uniq.size == 1 && clg_weekend_setpoints.split(', ').uniq.size == 1 && clg_weekday_setpoints.split(', ').uniq == clg_weekend_setpoints.split(', ').uniq hvac_control.cooling_setpoint_temp = clg_weekend_setpoints.split(', ').uniq[0].to_f else @@ -4546,7 +4546,7 @@ def self.get_default_recirc_pump_power() # # @return [Double] Pump power (W) def self.get_default_shared_recirc_pump_power() - # From ANSI/RESNET 301-2019 Equation 4.2-15b + # From ANSI/RESNET/ICC 301-2019 Equation 4.2-15b pump_horsepower = 0.25 motor_efficiency = 0.85 pump_kw = pump_horsepower * 0.746 / motor_efficiency @@ -5394,4 +5394,99 @@ def self.get_default_boiler_eae(heating_system) end end end + + # Gets the default interior/garage/exterior lighting fractions per ANSI/RESNET/ICC 301. + # Used by OS-ERI, OS-HEScore, etc. + # + # @return [Hash] Map of [HPXML::LocationXXX, HPXML::LightingTypeXXX] => lighting fraction + def self.get_default_lighting_fractions() + ltg_fracs = {} + [HPXML::LocationInterior, HPXML::LocationExterior, HPXML::LocationGarage].each do |location| + [HPXML::LightingTypeCFL, HPXML::LightingTypeLFL, HPXML::LightingTypeLED].each do |lighting_type| + if (location == HPXML::LocationInterior) && (lighting_type == HPXML::LightingTypeCFL) + ltg_fracs[[location, lighting_type]] = 0.1 + else + ltg_fracs[[location, lighting_type]] = 0 + end + end + end + return ltg_fracs + end + + # Gets the default heating setpoints per ANSI/RESNET/ICC 301. + # + # @param control_type [String] Thermostat control type (HPXML::HVACControlTypeXXX) + # @param eri_version [String] Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions + # @return [Array] 24 hourly comma-separated weekday and weekend setpoints + def self.get_default_heating_setpoint(control_type, eri_version) + # Per ANSI/RESNET/ICC 301 + htg_wd_setpoints = '68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68' + htg_we_setpoints = '68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68' + if control_type == HPXML::HVACControlTypeProgrammable + if Constants::ERIVersions.index(eri_version) >= Constants::ERIVersions.index('2022') + htg_wd_setpoints = '66, 66, 66, 66, 66, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 66' + htg_we_setpoints = '66, 66, 66, 66, 66, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 66' + else + htg_wd_setpoints = '66, 66, 66, 66, 66, 66, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 66' + htg_we_setpoints = '66, 66, 66, 66, 66, 66, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 66' + end + elsif control_type != HPXML::HVACControlTypeManual + fail "Unexpected control type #{control_type}." + end + return htg_wd_setpoints, htg_we_setpoints + end + + # Gets the default cooling setpoints per ANSI/RESNET/ICC 301. + # + # @param control_type [String] Thermostat control type (HPXML::HVACControlTypeXXX) + # @param eri_version [String] Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions + # @return [Array] 24 hourly comma-separated weekday and weekend setpoints + def self.get_default_cooling_setpoint(control_type, eri_version) + # Per ANSI/RESNET/ICC 301 + clg_wd_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78' + clg_we_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78' + if control_type == HPXML::HVACControlTypeProgrammable + if Constants::ERIVersions.index(eri_version) >= Constants::ERIVersions.index('2022') + clg_wd_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 80, 80, 80, 80, 80, 79, 78, 78, 78, 78, 78, 78, 78, 78, 78' + clg_we_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 80, 80, 80, 80, 80, 79, 78, 78, 78, 78, 78, 78, 78, 78, 78' + else + clg_wd_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 80, 80, 80, 80, 80, 80, 78, 78, 78, 78, 78, 78, 78, 78, 78' + clg_we_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 80, 80, 80, 80, 80, 80, 78, 78, 78, 78, 78, 78, 78, 78, 78' + end + elsif control_type != HPXML::HVACControlTypeManual + fail "Unexpected control type #{control_type}." + end + return clg_wd_setpoints, clg_we_setpoints + end + + # Gets the default heating capacity retention at 5F for a heat pump. + # + # @param compressor_type [String] Type of compressor (HPXML::HVACCompressorTypeXXX) + # @param hspf [Double] Heat pump efficiency + # @return [Array] Temperature (F), heating capacity retention at the temperature (frac) + def self.get_default_heating_capacity_retention(compressor_type, hspf = nil) + retention_temp = 5.0 + if [HPXML::HVACCompressorTypeSingleStage, HPXML::HVACCompressorTypeTwoStage].include? compressor_type + retention_fraction = 0.425 + elsif [HPXML::HVACCompressorTypeVariableSpeed].include? compressor_type + # Default maximum capacity maintenance based on NEEP data for all var speed heat pump types, if not provided + retention_fraction = (0.0461 * hspf + 0.1594).round(4) + end + return retention_temp, retention_fraction + end + + # Gets a 12-element array of 1s and 0s that reflects months for which the ceiling fan operates + # (i.e., when the average drybulb temperature is greater than 63F, per ANSI/RESNET/ICC 301). + # + # @param weather [WeatherFile] Weather object containing EPW information + # @return [Array] monthly array of 1s and 0s + def self.get_default_ceiling_fan_months(weather) + months = [0] * 12 + weather.data.MonthlyAvgDrybulbs.each_with_index do |val, m| + next unless val > 63.0 # F + + months[m] = 1 + end + return months + end end diff --git a/HPXMLtoOpenStudio/resources/hvac.rb b/HPXMLtoOpenStudio/resources/hvac.rb index c0c2d3cb06..1b02762515 100644 --- a/HPXMLtoOpenStudio/resources/hvac.rb +++ b/HPXMLtoOpenStudio/resources/hvac.rb @@ -1317,7 +1317,7 @@ def self.apply_ceiling_fans(runner, model, spaces, weather, hpxml_bldg, hpxml_he if !label_energy_use.nil? # priority if both provided annual_kwh = UnitConversions.convert(count * label_energy_use * hrs_per_day * 365.0, 'Wh', 'kWh') elsif !cfm_per_w.nil? - medium_cfm = get_default_ceiling_fan_medium_cfm() + medium_cfm = 3000.0 # cfm, per ANSI 301-2019 annual_kwh = UnitConversions.convert(count * medium_cfm / cfm_per_w * hrs_per_day * 365.0, 'Wh', 'kWh') end @@ -1587,68 +1587,6 @@ def self.get_cooling_setpoints(hvac_control, has_ceiling_fan, year, weather, off return clg_wd_setpoints, clg_we_setpoints end - # TODO - # - # @param control_type [TODO] TODO - # @param eri_version [String] Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions - # @return [TODO] TODO - def self.get_default_heating_setpoint(control_type, eri_version) - # Per ANSI/RESNET/ICC 301 - htg_wd_setpoints = '68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68' - htg_we_setpoints = '68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68' - if control_type == HPXML::HVACControlTypeProgrammable - if Constants::ERIVersions.index(eri_version) >= Constants::ERIVersions.index('2022') - htg_wd_setpoints = '66, 66, 66, 66, 66, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 66' - htg_we_setpoints = '66, 66, 66, 66, 66, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 66' - else - htg_wd_setpoints = '66, 66, 66, 66, 66, 66, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 66' - htg_we_setpoints = '66, 66, 66, 66, 66, 66, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 66' - end - elsif control_type != HPXML::HVACControlTypeManual - fail "Unexpected control type #{control_type}." - end - return htg_wd_setpoints, htg_we_setpoints - end - - # TODO - # - # @param control_type [TODO] TODO - # @param eri_version [String] Version of the ANSI/RESNET/ICC 301 Standard to use for equations/assumptions - # @return [TODO] TODO - def self.get_default_cooling_setpoint(control_type, eri_version) - # Per ANSI/RESNET/ICC 301 - clg_wd_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78' - clg_we_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78' - if control_type == HPXML::HVACControlTypeProgrammable - if Constants::ERIVersions.index(eri_version) >= Constants::ERIVersions.index('2022') - clg_wd_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 80, 80, 80, 80, 80, 79, 78, 78, 78, 78, 78, 78, 78, 78, 78' - clg_we_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 80, 80, 80, 80, 80, 79, 78, 78, 78, 78, 78, 78, 78, 78, 78' - else - clg_wd_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 80, 80, 80, 80, 80, 80, 78, 78, 78, 78, 78, 78, 78, 78, 78' - clg_we_setpoints = '78, 78, 78, 78, 78, 78, 78, 78, 78, 80, 80, 80, 80, 80, 80, 78, 78, 78, 78, 78, 78, 78, 78, 78' - end - elsif control_type != HPXML::HVACControlTypeManual - fail "Unexpected control type #{control_type}." - end - return clg_wd_setpoints, clg_we_setpoints - end - - # TODO - # - # @param compressor_type [TODO] TODO - # @param hspf [TODO] TODO - # @return [TODO] TODO - def self.get_default_heating_capacity_retention(compressor_type, hspf = nil) - retention_temp = 5.0 - if [HPXML::HVACCompressorTypeSingleStage, HPXML::HVACCompressorTypeTwoStage].include? compressor_type - retention_fraction = 0.425 - elsif [HPXML::HVACCompressorTypeVariableSpeed].include? compressor_type - # Default maximum capacity maintenance based on NEEP data for all var speed heat pump types, if not provided - retention_fraction = (0.0461 * hspf + 0.1594).round(4) - end - return retention_temp, retention_fraction - end - # TODO # # @param compressor_type [TODO] TODO @@ -2092,29 +2030,6 @@ def self.set_curves_gshp(heat_pump) hp_ap.heat_rated_cops = [1.0 / heat_eir] end - # TODO - # - # @return [TODO] TODO - def self.get_default_ceiling_fan_medium_cfm() - # From ANSI 301-2019 - return 3000.0 # cfm - end - - # Return a 12-element array of 1s and 0s that reflects months for which the average drybulb temperature is greater than 63F. - # - # @param weather [WeatherFile] Weather object containing EPW information - # @return [Array] monthly array of 1s and 0s - def self.get_default_ceiling_fan_months(weather) - # Per ANSI/RESNET/ICC 301 - months = [0] * 12 - weather.data.MonthlyAvgDrybulbs.each_with_index do |val, m| - next unless val > 63.0 # F - - months[m] = 1 - end - return months - end - # TODO # # @param weather [WeatherFile] Weather object containing EPW information @@ -3124,7 +3039,7 @@ def self.correct_ft_cap_eir(data_array, mode) indoor_t = [50.0, rated_t_i, 80.0] else # default capacity retention for single speed - retention_temp, retention_fraction = get_default_heating_capacity_retention(HPXML::HVACCompressorTypeSingleStage) + retention_temp, retention_fraction = HPXMLDefaults.get_default_heating_capacity_retention(HPXML::HVACCompressorTypeSingleStage) cap_ft_spec_ss, eir_ft_spec_ss = get_heat_cap_eir_ft_spec(HPXML::HVACCompressorTypeSingleStage, retention_temp, retention_fraction) rated_t_i = HVAC::AirSourceHeatRatedIDB indoor_t = [60.0, rated_t_i, 80.0] diff --git a/HPXMLtoOpenStudio/resources/hvac_sizing.rb b/HPXMLtoOpenStudio/resources/hvac_sizing.rb index a8e4a26aa2..d1ac54d14d 100644 --- a/HPXMLtoOpenStudio/resources/hvac_sizing.rb +++ b/HPXMLtoOpenStudio/resources/hvac_sizing.rb @@ -2498,7 +2498,7 @@ def self.adjust_indoor_condition_var_speed(outdoor_temp, indoor_temp, mode) coefficients_1speed = HVAC.get_cool_cap_eir_ft_spec(HPXML::HVACCompressorTypeSingleStage)[0][0] elsif mode == :htg rated_indoor_temp = HVAC::AirSourceHeatRatedIDB - capacity_retention_temp_1speed, capacity_retention_fraction_1speed = HVAC.get_default_heating_capacity_retention(HPXML::HVACCompressorTypeSingleStage) + capacity_retention_temp_1speed, capacity_retention_fraction_1speed = HPXMLDefaults.get_default_heating_capacity_retention(HPXML::HVACCompressorTypeSingleStage) coefficients_1speed = HVAC.get_heat_cap_eir_ft_spec(HPXML::HVACCompressorTypeSingleStage, capacity_retention_temp_1speed, capacity_retention_fraction_1speed)[0][0] end return MathTools.biquadratic(indoor_temp, outdoor_temp, coefficients_1speed) / MathTools.biquadratic(rated_indoor_temp, outdoor_temp, coefficients_1speed)