From 02f3cf405b229495fed6d8c9820ff375d576dc3f Mon Sep 17 00:00:00 2001 From: Scott Horowitz Date: Thu, 29 Sep 2022 09:59:48 -0600 Subject: [PATCH] Update to latest HPXML schema for standby losses. Code backported from https://github.com/NREL/OpenStudio-HPXML/pull/1150. --- BuildResidentialHPXML/measure.rb | 6 +- BuildResidentialHPXML/measure.xml | 6 +- Changelog.md | 1 + HPXMLtoOpenStudio/measure.xml | 100 +++++++++--------- HPXMLtoOpenStudio/resources/hpxml.rb | 16 ++- HPXMLtoOpenStudio/resources/hpxml_defaults.rb | 8 +- .../hpxml_schema/HPXMLBaseElements.xsd | 33 ++++-- .../resources/hpxml_schema/HPXMLDataTypes.xsd | 14 +++ .../hpxml_schematron/EPvalidator.xml | 2 +- .../hpxml_schematron/HPXMLvalidator.xml | 3 + HPXMLtoOpenStudio/resources/waterheater.rb | 11 +- HPXMLtoOpenStudio/tests/test_defaults.rb | 28 +++++ docs/source/workflow_inputs.rst | 2 +- tasks.rb | 2 - .../base-dhw-indirect-standbyloss.xml | 5 +- 15 files changed, 161 insertions(+), 76 deletions(-) diff --git a/BuildResidentialHPXML/measure.rb b/BuildResidentialHPXML/measure.rb index bde72c9099..dfe0503989 100644 --- a/BuildResidentialHPXML/measure.rb +++ b/BuildResidentialHPXML/measure.rb @@ -5228,7 +5228,8 @@ def self.set_water_heating_systems(hpxml, args) if [HPXML::WaterHeaterTypeCombiTankless, HPXML::WaterHeaterTypeCombiStorage].include? water_heater_type if args[:water_heater_standby_loss].is_initialized if args[:water_heater_standby_loss].get > 0 - standby_loss = args[:water_heater_standby_loss].get + standby_loss_units = HPXML::UnitsDegFPerHour + standby_loss_value = args[:water_heater_standby_loss].get end end end @@ -5291,7 +5292,8 @@ def self.set_water_heating_systems(hpxml, args) recovery_efficiency: recovery_efficiency, uses_desuperheater: uses_desuperheater, related_hvac_idref: related_hvac_idref, - standby_loss: standby_loss, + standby_loss_units: standby_loss_units, + standby_loss_value: standby_loss_value, jacket_r_value: jacket_r_value, temperature: temperature, heating_capacity: heating_capacity, diff --git a/BuildResidentialHPXML/measure.xml b/BuildResidentialHPXML/measure.xml index 94f2386188..a532112d46 100644 --- a/BuildResidentialHPXML/measure.xml +++ b/BuildResidentialHPXML/measure.xml @@ -3,8 +3,8 @@ 3.0 build_residential_hpxml a13a8983-2b01-4930-8af2-42030b6e4233 - 1d0058f9-2963-443a-a959-78e5f088e7d2 - 20220926T172603Z + 9a3394b8-30aa-4f07-a4c7-dfa6bbf1ba4b + 20220929T155118Z 2C38F48B BuildResidentialHPXML HPXML Builder @@ -6413,7 +6413,7 @@ measure.rb rb script - 470336B9 + C00BFDD4 diff --git a/Changelog.md b/Changelog.md index 7b77ebc86d..ee5a43ef59 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ __New Features__ - **Breaking Change**: Replaces `FrameFloors/FrameFloor` with `Floors/Floor`. - **Breaking change**: Replaces `SoftwareInfo/extension/SimulationControl/DaylightSaving/Enabled` with `Building/Site/TimeZone/DSTObserved`. +- **Breaking Change**: Replaces `StandbyLoss` with `StandbyLoss[Units="F/hr"]/Value` for an indirect water heater. - Allows SEER2/HSPF2 efficiency types for central air conditioners and heat pumps. - Allows heating/cooling seasons that don't span the entire year. - Allows calculating one or more utility bill scenarios (e.g., net metering vs feed-in tariff compensation types for a simulation with PV). diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml index 9e9820f97e..db7dfee38c 100644 --- a/HPXMLtoOpenStudio/measure.xml +++ b/HPXMLtoOpenStudio/measure.xml @@ -3,8 +3,8 @@ 3.0 hpxm_lto_openstudio b1543b30-9465-45ff-ba04-1d1f85e763bc - 0be40881-8b15-4dd9-a51f-432b4fec76cd - 20220927T180626Z + 65d82b9f-532d-400b-8f27-183b6b08f5f9 + 20220929T155120Z D8922A73 HPXMLtoOpenStudio HPXML to OpenStudio Translator @@ -325,12 +325,6 @@ resource 0F94C201 - - waterheater.rb - rb - resource - 500A5756 - meta_measure.rb rb @@ -361,30 +355,12 @@ resource 2FD98C89 - - hpxml_schema/HPXMLBaseElements.xsd - xsd - resource - 17563F8C - - - hpxml_schema/HPXMLDataTypes.xsd - xsd - resource - 2B0922EC - utility_bills.rb rb resource 35B866E9 - - hpxml_schematron/HPXMLvalidator.xml - xml - resource - 5DD85F0D - misc_loads.rb rb @@ -535,36 +511,12 @@ resource 6F6B5865 - - test_defaults.rb - rb - test - 069B537F - - - hpxml_defaults.rb - rb - resource - F954FBA8 - - - hpxml_schematron/EPvalidator.xml - xml - resource - A482C431 - test_validation.rb rb test 675AC983 - - hpxml.rb - rb - resource - CD5019F4 - hvac.rb rb @@ -594,5 +546,53 @@ resource 42C2E302 + + waterheater.rb + rb + resource + 65181200 + + + hpxml_schema/HPXMLBaseElements.xsd + xsd + resource + 32481882 + + + hpxml_schema/HPXMLDataTypes.xsd + xsd + resource + 5559DDD1 + + + hpxml_schematron/HPXMLvalidator.xml + xml + resource + 6FFC25E9 + + + test_defaults.rb + rb + test + 48381849 + + + hpxml_defaults.rb + rb + resource + 23576669 + + + hpxml_schematron/EPvalidator.xml + xml + resource + 6499B5B3 + + + hpxml.rb + rb + resource + 3450451E + diff --git a/HPXMLtoOpenStudio/resources/hpxml.rb b/HPXMLtoOpenStudio/resources/hpxml.rb index affe3ba334..46fa573fea 100644 --- a/HPXMLtoOpenStudio/resources/hpxml.rb +++ b/HPXMLtoOpenStudio/resources/hpxml.rb @@ -301,10 +301,12 @@ class HPXML < Object UnitsACHNatural = 'ACHnatural' UnitsAFUE = 'AFUE' UnitsAh = 'Ah' + UnitsBtuPerHour = 'Btu/hr' UnitsCFM = 'CFM' UnitsCFM25 = 'CFM25' UnitsCFM50 = 'CFM50' UnitsCOP = 'COP' + UnitsDegFPerHour = 'F/hr' UnitsDollars = '$' UnitsDollarsPerkW = '$/kW' UnitsEER = 'EER' @@ -316,6 +318,7 @@ class HPXML < Object UnitsKwhPerDay = 'kWh/day' UnitsKwPerTon = 'kW/ton' UnitsPercent = 'Percent' + UnitsPercentPerHour = '%/hr' UnitsSEER = 'SEER' UnitsSEER2 = 'SEER2' UnitsSLA = 'SLA' @@ -4642,8 +4645,8 @@ class WaterHeatingSystem < BaseElement ATTRS = [:id, :year_installed, :fuel_type, :water_heater_type, :location, :performance_adjustment, :tank_volume, :fraction_dhw_load_served, :heating_capacity, :energy_factor, :usage_bin, :uniform_energy_factor, :first_hour_rating, :recovery_efficiency, :uses_desuperheater, :jacket_r_value, - :related_hvac_idref, :third_party_certification, :standby_loss, :temperature, :is_shared_system, - :number_of_units_served, :tank_model_type, :operating_mode] + :related_hvac_idref, :third_party_certification, :standby_loss_units, :standby_loss_value, + :temperature, :is_shared_system, :number_of_units_served, :tank_model_type, :operating_mode] attr_accessor(*ATTRS) def related_hvac_system @@ -4700,7 +4703,11 @@ def to_oga(doc) jacket = XMLHelper.add_element(water_heater_insulation, 'Jacket') XMLHelper.add_element(jacket, 'JacketRValue', @jacket_r_value, :float) end - XMLHelper.add_element(water_heating_system, 'StandbyLoss', @standby_loss, :float, @standby_loss_isdefaulted) unless @standby_loss.nil? + if (not @standby_loss_units.nil?) && (not @standby_loss_value.nil?) + standby_loss = XMLHelper.add_element(water_heating_system, 'StandbyLoss') + XMLHelper.add_element(standby_loss, 'Units', @standby_loss_units, :string, @standby_loss_units_isdefaulted) + XMLHelper.add_element(standby_loss, 'Value', @standby_loss_value, :float, @standby_loss_value_isdefaulted) + end XMLHelper.add_element(water_heating_system, 'HotWaterTemperature', @temperature, :float, @temperature_isdefaulted) unless @temperature.nil? XMLHelper.add_element(water_heating_system, 'UsesDesuperheater', @uses_desuperheater, :boolean) unless @uses_desuperheater.nil? if not @related_hvac_idref.nil? @@ -4735,7 +4742,8 @@ def from_oga(water_heating_system) @usage_bin = XMLHelper.get_value(water_heating_system, 'UsageBin', :string) @recovery_efficiency = XMLHelper.get_value(water_heating_system, 'RecoveryEfficiency', :float) @jacket_r_value = XMLHelper.get_value(water_heating_system, 'WaterHeaterInsulation/Jacket/JacketRValue', :float) - @standby_loss = XMLHelper.get_value(water_heating_system, 'StandbyLoss', :float) + @standby_loss_units = XMLHelper.get_value(water_heating_system, 'StandbyLoss/Units', :string) + @standby_loss_value = XMLHelper.get_value(water_heating_system, 'StandbyLoss/Value', :float) @temperature = XMLHelper.get_value(water_heating_system, 'HotWaterTemperature', :float) @uses_desuperheater = XMLHelper.get_value(water_heating_system, 'UsesDesuperheater', :boolean) @related_hvac_idref = HPXML::get_idref(XMLHelper.get_element(water_heating_system, 'RelatedHVACSystem')) diff --git a/HPXMLtoOpenStudio/resources/hpxml_defaults.rb b/HPXMLtoOpenStudio/resources/hpxml_defaults.rb index 094e0cde66..4107bd0753 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_defaults.rb +++ b/HPXMLtoOpenStudio/resources/hpxml_defaults.rb @@ -1663,14 +1663,16 @@ def self.apply_water_heaters(hpxml, nbeds, eri_version, schedules_file) water_heating_system.performance_adjustment = Waterheater.get_default_performance_adjustment(water_heating_system) water_heating_system.performance_adjustment_isdefaulted = true end - if (water_heating_system.water_heater_type == HPXML::WaterHeaterTypeCombiStorage) && water_heating_system.standby_loss.nil? + if (water_heating_system.water_heater_type == HPXML::WaterHeaterTypeCombiStorage) && water_heating_system.standby_loss_value.nil? # Use equation fit from AHRI database # calculate independent variable SurfaceArea/vol(physically linear to standby_loss/skin_u under test condition) to fit the linear equation from AHRI database act_vol = Waterheater.calc_storage_tank_actual_vol(water_heating_system.tank_volume, nil) surface_area = Waterheater.calc_tank_areas(act_vol)[0] sqft_by_gal = surface_area / act_vol # sqft/gal - water_heating_system.standby_loss = (2.9721 * sqft_by_gal - 0.4732).round(3) # linear equation assuming a constant u, F/hr - water_heating_system.standby_loss_isdefaulted = true + water_heating_system.standby_loss_value = (2.9721 * sqft_by_gal - 0.4732).round(3) # linear equation assuming a constant u, F/hr + water_heating_system.standby_loss_value_isdefaulted = true + water_heating_system.standby_loss_units = HPXML::UnitsDegFPerHour + water_heating_system.standby_loss_units_isdefaulted = true end if (water_heating_system.water_heater_type == HPXML::WaterHeaterTypeStorage) if water_heating_system.heating_capacity.nil? diff --git a/HPXMLtoOpenStudio/resources/hpxml_schema/HPXMLBaseElements.xsd b/HPXMLtoOpenStudio/resources/hpxml_schema/HPXMLBaseElements.xsd index 20a1456d3a..93cfbd2f31 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_schema/HPXMLBaseElements.xsd +++ b/HPXMLtoOpenStudio/resources/hpxml_schema/HPXMLBaseElements.xsd @@ -1155,7 +1155,7 @@ - + @@ -1168,7 +1168,7 @@ - Use Ceilings for horizontal surfaces above living space (e.g., sufaces between living space and attic). + Use Ceilings for horizontal surfaces above living space (e.g., surfaces between living space and attic). @@ -1199,13 +1199,13 @@ - Use Floors for horizontal surfaces below living space (e.g., sufaces between living space and crawlspace/basement/ambient foundations). + Use Floors for horizontal surfaces below living space (e.g., surfaces between living space and crawlspace/basement/ambient foundations). - Interior partition surfaces should not be described using the Floor element. + Interior partition surfaces should not be described using the FrameFloor element. @@ -1709,6 +1709,11 @@ 430, Subpart B, Appendix E. + + + The dimensionless coefficient of performance, used to measure the efficiency of a commercial heat pump water heater. + + [gal per hour] An estimate of the maximum volume of hot water in gallons that a storage water heater can supply within an hour that begins @@ -1774,9 +1779,9 @@ - + - [degF/hr] The standby heat loss rate for, e.g., indirect water heaters in degrees per hour. Published in the AHRI Consumer’s Directory of Certified Efficiency Ratings. + The standby heat loss rate for, e.g., indirect or commercial water heaters. Published in the AHRI Consumer’s Directory of Certified Efficiency Ratings. @@ -3948,6 +3953,17 @@ Ah is computed by multiplying the discharge current (Amps) by the discharge time (hours). kWh is computed by multiplying the power output (kW) by the discharge time (hours). + + + + + + + + For %/hr enter values as a fraction, i.e. 1.20%/hr = 0.0120 and 0.68%/hr = 0.0068. + + + @@ -5582,7 +5598,7 @@ - + @@ -5591,6 +5607,9 @@ + + Structurally Insulated Panel + diff --git a/HPXMLtoOpenStudio/resources/hpxml_schema/HPXMLDataTypes.xsd b/HPXMLtoOpenStudio/resources/hpxml_schema/HPXMLDataTypes.xsd index 79c948ce7e..9607a56cf3 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_schema/HPXMLDataTypes.xsd +++ b/HPXMLtoOpenStudio/resources/hpxml_schema/HPXMLDataTypes.xsd @@ -1847,6 +1847,20 @@ + + + + + + + + + + + + + + diff --git a/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml b/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml index b3891376c4..908ef69fa5 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml +++ b/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml @@ -1571,7 +1571,7 @@ Expected 1 element(s) for xpath: RelatedHVACSystem Expected 1 element(s) for xpath: TankVolume Expected 0 or 1 element(s) for xpath: WaterHeaterInsulation/Jacket/JacketRValue - Expected 0 or 1 element(s) for xpath: StandbyLoss + Expected 0 or 1 element(s) for xpath: StandbyLoss[Units="F/hr"]/Value diff --git a/HPXMLtoOpenStudio/resources/hpxml_schematron/HPXMLvalidator.xml b/HPXMLtoOpenStudio/resources/hpxml_schematron/HPXMLvalidator.xml index 98fdc533a8..49f64f4abe 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_schematron/HPXMLvalidator.xml +++ b/HPXMLtoOpenStudio/resources/hpxml_schematron/HPXMLvalidator.xml @@ -409,6 +409,9 @@ Expected JacketRValue to be greater than or equal to 0 + + Expected Units to be 'F/hr' or '%/hr' or 'Btu/hr' + Expected PipingLength to be greater than or equal to 0 diff --git a/HPXMLtoOpenStudio/resources/waterheater.rb b/HPXMLtoOpenStudio/resources/waterheater.rb index 5365951746..89a154e77b 100644 --- a/HPXMLtoOpenStudio/resources/waterheater.rb +++ b/HPXMLtoOpenStudio/resources/waterheater.rb @@ -188,7 +188,7 @@ def self.apply_combi(model, runner, loc_space, loc_schedule, water_heating_syste obj_name_combi = Constants.ObjectNameWaterHeater if water_heating_system.water_heater_type == HPXML::WaterHeaterTypeCombiStorage - if water_heating_system.standby_loss <= 0 + if water_heating_system.standby_loss_value <= 0 fail 'A negative indirect water heater standby loss was calculated, double check water heater inputs.' end @@ -1303,6 +1303,13 @@ def self.get_tank_height() end def self.calc_indirect_ua_with_standbyloss(act_vol, water_heating_system, a_side, solar_fraction) + standby_loss_units = water_heating_system.standby_loss_units + standby_loss_value = water_heating_system.standby_loss_value + + if not [HPXML::UnitsDegFPerHour].include? standby_loss_units + fail "Unexpected standby loss units '#{standby_loss_units}' for indirect water heater. Should be '#{HPXML::UnitsDegFPerHour}'." + end + # Test conditions cp = 0.999 # Btu/lb-F rho = 8.216 # lb/gal @@ -1310,7 +1317,7 @@ def self.calc_indirect_ua_with_standbyloss(act_vol, water_heating_system, a_side t_tank_avg = 135.0 # F, Test begins at 137-138F stop at 133F # UA calculation - q = water_heating_system.standby_loss * cp * act_vol * rho # Btu/hr + q = standby_loss_value * cp * act_vol * rho # Btu/hr ua = q / (t_tank_avg - t_amb) # Btu/hr-F # jacket diff --git a/HPXMLtoOpenStudio/tests/test_defaults.rb b/HPXMLtoOpenStudio/tests/test_defaults.rb index 467793bca7..0f54c72395 100644 --- a/HPXMLtoOpenStudio/tests/test_defaults.rb +++ b/HPXMLtoOpenStudio/tests/test_defaults.rb @@ -2172,6 +2172,23 @@ def test_heat_pump_water_heaters _test_default_heat_pump_water_heater_values(hpxml_default, [HPXML::WaterHeaterOperatingModeStandard]) end + def test_indirect_water_heaters + # Test inputs not overridden by defaults + hpxml = _create_hpxml('base-dhw-indirect.xml') + hpxml.water_heating_systems[0].standby_loss_value = 0.99 + hpxml.water_heating_systems[0].standby_loss_units = HPXML::UnitsDegFPerHour + XMLHelper.write_file(hpxml.to_oga, @tmp_hpxml_path) + hpxml_default = _test_measure() + _test_default_indirect_water_heater_values(hpxml_default, [HPXML::UnitsDegFPerHour, 0.99]) + + # Test defaults + hpxml.water_heating_systems[0].standby_loss_value = nil + hpxml.water_heating_systems[0].standby_loss_units = nil + XMLHelper.write_file(hpxml.to_oga, @tmp_hpxml_path) + hpxml_default = _test_measure() + _test_default_indirect_water_heater_values(hpxml_default, [HPXML::UnitsDegFPerHour, 0.843]) + end + def test_hot_water_distribution # Test inputs not overridden by defaults -- standard hpxml = _create_hpxml('base.xml') @@ -4039,6 +4056,17 @@ def _test_default_heat_pump_water_heater_values(hpxml, *expected_wh_values) end end + def _test_default_indirect_water_heater_values(hpxml, *expected_wh_values) + indirect_water_heaters = hpxml.water_heating_systems.select { |w| w.water_heater_type == HPXML::WaterHeaterTypeCombiStorage } + assert_equal(expected_wh_values.size, indirect_water_heaters.size) + indirect_water_heaters.each_with_index do |wh_system, idx| + standby_loss_units, standby_loss_value, = expected_wh_values[idx] + + assert_equal(standby_loss_units, wh_system.standby_loss_units) + assert_equal(standby_loss_value, wh_system.standby_loss_value) + end + end + def _test_default_standard_distribution_values(hot_water_distribution, piping_length, pipe_r_value) assert_in_epsilon(piping_length, hot_water_distribution.standard_piping_length, 0.01) assert_equal(pipe_r_value, hot_water_distribution.pipe_r_value) diff --git a/docs/source/workflow_inputs.rst b/docs/source/workflow_inputs.rst index 1c375004cb..5f53bd3a1a 100644 --- a/docs/source/workflow_inputs.rst +++ b/docs/source/workflow_inputs.rst @@ -2232,7 +2232,7 @@ If a combination boiler w/ storage tank (sometimes referred to as an indirect wa ``RelatedHVACSystem`` idref See [#]_ Yes ID of boiler ``TankVolume`` double gal > 0 Yes Nominal volume of the storage tank ``WaterHeaterInsulation/Jacket/JacketRValue`` double F-ft2-hr/Btu >= 0 No 0 R-value of additional storage tank insulation wrap - ``StandbyLoss`` double F/hr > 0 No See [#]_ Storage tank standby losses + ``StandbyLoss[Units="F/hr"]/Value`` double F/hr > 0 No See [#]_ Storage tank standby losses ============================================= ======= ============ =========== ============ ======== ================================================== .. [#] RelatedHVACSystem must reference a ``HeatingSystem`` (Boiler). diff --git a/tasks.rb b/tasks.rb index d4effe8aea..8e24e680d8 100644 --- a/tasks.rb +++ b/tasks.rb @@ -706,7 +706,6 @@ def set_measure_argument_values(hpxml_file, args, sch_args, orig_parent) args['water_heater_efficiency'] = 0.95 args['water_heater_recovery_efficiency'] = 0.76 args['water_heater_heating_capacity'] = 18767 - args['water_heater_standby_loss'] = 0 args['water_heater_jacket_rvalue'] = 0 args['water_heater_setpoint_temperature'] = 125 args['water_heater_num_units_served'] = 1 @@ -967,7 +966,6 @@ def set_measure_argument_values(hpxml_file, args, sch_args, orig_parent) args['water_heater_efficiency_type'] = 'EnergyFactor' args['water_heater_efficiency'] = 0 args['water_heater_recovery_efficiency'] = 0 - args['water_heater_standby_loss'] = 0 args['water_heater_jacket_rvalue'] = 0 args['water_heater_setpoint_temperature'] = 0 args['water_heater_num_units_served'] = 0 diff --git a/workflow/sample_files/base-dhw-indirect-standbyloss.xml b/workflow/sample_files/base-dhw-indirect-standbyloss.xml index d57f23938f..8be7df5804 100644 --- a/workflow/sample_files/base-dhw-indirect-standbyloss.xml +++ b/workflow/sample_files/base-dhw-indirect-standbyloss.xml @@ -358,7 +358,10 @@ living space 50.0 1.0 - 1.0 + + F/hr + 1.0 + 125.0