From 5530301d6c662f42f6aa2ceb2f1777c3692f753a Mon Sep 17 00:00:00 2001 From: Joe Robertson Date: Mon, 28 Oct 2024 09:53:40 -0700 Subject: [PATCH] Update validator and indicate defaulted panel load types. --- HPXMLtoOpenStudio/measure.xml | 12 +-- HPXMLtoOpenStudio/resources/defaults.rb | 97 ++++++++++++++----- HPXMLtoOpenStudio/resources/hpxml.rb | 2 +- .../hpxml_schematron/EPvalidator.xml | 11 ++- HPXMLtoOpenStudio/resources/xmlhelper.rb | 5 +- docs/source/workflow_inputs.rst | 7 +- 6 files changed, 96 insertions(+), 38 deletions(-) diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml index 8f4aa72f0e..7b28f19672 100644 --- a/HPXMLtoOpenStudio/measure.xml +++ b/HPXMLtoOpenStudio/measure.xml @@ -3,8 +3,8 @@ 3.1 hpxm_lto_openstudio b1543b30-9465-45ff-ba04-1d1f85e763bc - 1fcd16f3-4b2f-4f92-adb9-9c693a36555e - 2024-10-25T22:48:31Z + 8b94d1c4-cade-4be1-ba4e-4a47853194c6 + 2024-10-28T16:53:14Z D8922A73 HPXMLtoOpenStudio HPXML to OpenStudio Translator @@ -327,7 +327,7 @@ defaults.rb rb resource - 4A568B89 + 39AE738F electric_panel.rb @@ -363,7 +363,7 @@ hpxml.rb rb resource - 99438361 + 3D6FDD59 hpxml_schema/HPXML.xsd @@ -381,7 +381,7 @@ hpxml_schematron/EPvalidator.xml xml resource - 83B9D350 + 9D8A62DF hpxml_schematron/iso-schematron.xsd @@ -645,7 +645,7 @@ xmlhelper.rb rb resource - DA4456A1 + C3112FAC xmlvalidator.rb diff --git a/HPXMLtoOpenStudio/resources/defaults.rb b/HPXMLtoOpenStudio/resources/defaults.rb index a4fb910572..b0de75f772 100644 --- a/HPXMLtoOpenStudio/resources/defaults.rb +++ b/HPXMLtoOpenStudio/resources/defaults.rb @@ -3184,23 +3184,31 @@ def self.apply_electric_panels(hpxml_bldg, unit_num) next if !heating_system.panel_loads.nil? panel_loads.add(type: HPXML::ElectricPanelLoadTypeHeating, - system_idrefs: [heating_system.id]) + type_isdefaulted: true, + system_idrefs: [heating_system.id], + system_idrefs_isdefaulted: true) end hpxml_bldg.cooling_systems.each do |cooling_system| next if !cooling_system.panel_loads.nil? panel_loads.add(type: HPXML::ElectricPanelLoadTypeCooling, - system_idrefs: [cooling_system.id]) + type_isdefaulted: true, + system_idrefs: [cooling_system.id], + system_idrefs_isdefaulted: true) end hpxml_bldg.heat_pumps.each do |heat_pump| next if !heat_pump.panel_loads.nil? panel_loads.add(type: HPXML::ElectricPanelLoadTypeHeating, - system_idrefs: [heat_pump.id]) + type_isdefaulted: true, + system_idrefs: [heat_pump.id], + system_idrefs_isdefaulted: true) panel_loads.add(type: HPXML::ElectricPanelLoadTypeCooling, - system_idrefs: [heat_pump.id]) + type_isdefaulted: true, + system_idrefs: [heat_pump.id], + system_idrefs_isdefaulted: true) end hpxml_bldg.water_heating_systems.each do |water_heating_system| @@ -3208,7 +3216,9 @@ def self.apply_electric_panels(hpxml_bldg, unit_num) next if water_heating_system.fuel_type != HPXML::FuelTypeElectricity panel_loads.add(type: HPXML::ElectricPanelLoadTypeWaterHeater, - system_idrefs: [water_heating_system.id]) + type_isdefaulted: true, + system_idrefs: [water_heating_system.id], + system_idrefs_isdefaulted: true) end hpxml_bldg.clothes_dryers.each do |clothes_dryer| @@ -3216,14 +3226,18 @@ def self.apply_electric_panels(hpxml_bldg, unit_num) next if clothes_dryer.fuel_type != HPXML::FuelTypeElectricity panel_loads.add(type: HPXML::ElectricPanelLoadTypeClothesDryer, - system_idrefs: [clothes_dryer.id]) + type_isdefaulted: true, + system_idrefs: [clothes_dryer.id], + system_idrefs_isdefaulted: true) end hpxml_bldg.dishwashers.each do |dishwasher| next if !dishwasher.panel_loads.nil? panel_loads.add(type: HPXML::ElectricPanelLoadTypeDishwasher, - system_idrefs: [dishwasher.id]) + type_isdefaulted: true, + system_idrefs: [dishwasher.id], + system_idrefs_isdefaulted: true) end hpxml_bldg.cooking_ranges.each do |cooking_range| @@ -3231,31 +3245,41 @@ def self.apply_electric_panels(hpxml_bldg, unit_num) next if cooking_range.fuel_type != HPXML::FuelTypeElectricity panel_loads.add(type: HPXML::ElectricPanelLoadTypeRangeOven, - system_idrefs: [cooking_range.id]) + type_isdefaulted: true, + system_idrefs: [cooking_range.id], + system_idrefs_isdefaulted: true) end hpxml_bldg.permanent_spas.each do |permanent_spa| next if !permanent_spa.panel_loads.nil? panel_loads.add(type: HPXML::ElectricPanelLoadTypePermanentSpaPump, - system_idrefs: [permanent_spa.pump_id]) + type_isdefaulted: true, + system_idrefs: [permanent_spa.pump_id], + system_idrefs_isdefaulted: true) next if ![HPXML::HeaterTypeElectricResistance, HPXML::HeaterTypeHeatPump].include?(permanent_spa.heater_type) panel_loads.add(type: HPXML::ElectricPanelLoadTypePermanentSpaHeater, - system_idrefs: [permanent_spa.heater_id]) + type_isdefaulted: true, + system_idrefs: [permanent_spa.heater_id], + system_idrefs_isdefaulted: true) end hpxml_bldg.pools.each do |pool| next if !pool.panel_loads.nil? panel_loads.add(type: HPXML::ElectricPanelLoadTypePoolPump, - system_idrefs: [pool.pump_id]) + type_isdefaulted: true, + system_idrefs: [pool.pump_id], + system_idrefs_isdefaulted: true) next if ![HPXML::HeaterTypeElectricResistance, HPXML::HeaterTypeHeatPump].include?(pool.heater_type) panel_loads.add(type: HPXML::ElectricPanelLoadTypePoolHeater, - system_idrefs: [pool.heater_id]) + type_isdefaulted: true, + system_idrefs: [pool.heater_id], + system_idrefs_isdefaulted: true) end hpxml_bldg.plug_loads.each do |plug_load| @@ -3263,7 +3287,9 @@ def self.apply_electric_panels(hpxml_bldg, unit_num) next if plug_load.plug_load_type != HPXML::PlugLoadTypeWellPump panel_loads.add(type: HPXML::ElectricPanelLoadTypeWellPump, - system_idrefs: [plug_load.id]) + type_isdefaulted: true, + system_idrefs: [plug_load.id], + system_idrefs_isdefaulted: true) end hpxml_bldg.plug_loads.each do |plug_load| @@ -3271,7 +3297,9 @@ def self.apply_electric_panels(hpxml_bldg, unit_num) next if plug_load.plug_load_type != HPXML::PlugLoadTypeElectricVehicleCharging panel_loads.add(type: HPXML::ElectricPanelLoadTypeElectricVehicleCharging, - system_idrefs: [plug_load.id]) + type_isdefaulted: true, + system_idrefs: [plug_load.id], + system_idrefs_isdefaulted: true) end ventilation_fan_ids = [] @@ -3283,20 +3311,27 @@ def self.apply_electric_panels(hpxml_bldg, unit_num) end if not ventilation_fan_ids.empty? panel_loads.add(type: HPXML::ElectricPanelLoadTypeOther, - system_idrefs: ventilation_fan_ids) + type_isdefaulted: true, + system_idrefs: ventilation_fan_ids, + system_idrefs_isdefaulted: true) end if panel_loads.count { |pl| pl.type == HPXML::ElectricPanelLoadTypeOther && pl.system_idrefs.empty? } == 0 - panel_loads.add(type: HPXML::ElectricPanelLoadTypeOther, system_idrefs: []) # for garbage disposal and garage door opener + panel_loads.add(type: HPXML::ElectricPanelLoadTypeOther, + type_isdefaulted: true, + system_idrefs: []) # for garbage disposal and garage door opener end if panel_loads.count { |pl| pl.type == HPXML::ElectricPanelLoadTypeLighting } == 0 - electric_panel.panel_loads.add(type: HPXML::ElectricPanelLoadTypeLighting) + electric_panel.panel_loads.add(type: HPXML::ElectricPanelLoadTypeLighting, + type_isdefaulted: true) end if panel_loads.count { |pl| pl.type == HPXML::ElectricPanelLoadTypeKitchen } == 0 - electric_panel.panel_loads.add(type: HPXML::ElectricPanelLoadTypeKitchen) + electric_panel.panel_loads.add(type: HPXML::ElectricPanelLoadTypeKitchen, + type_isdefaulted: true) end if panel_loads.count { |pl| pl.type == HPXML::ElectricPanelLoadTypeLaundry } == 0 - electric_panel.panel_loads.add(type: HPXML::ElectricPanelLoadTypeLaundry) + electric_panel.panel_loads.add(type: HPXML::ElectricPanelLoadTypeLaundry, + type_isdefaulted: true) end panel_loads.each do |panel_load| @@ -5793,6 +5828,17 @@ def self.get_breaker_spaces_from_heating_capacity(capacity) return (UnitConversions.convert(capacity, 'btu/hr', 'kw') / 12.0).ceil * 2.0 + 2 end + # TODO + def self.heat_pump_backup_simultaneous_operation(heat_pump) + if !heat_pump.compressor_lockout_temp.nil? && + !heat_pump.backup_heating_lockout_temp.nil? && + (heat_pump.backup_heating_lockout_temp > heat_pump.compressor_lockout_temp) + return true + end + + return false + end + # TODO def self.get_breaker_spaces_from_backup_heating_capacity(capacity) if UnitConversions.convert(capacity, 'btu/hr', 'kw') <= 10 @@ -5887,11 +5933,12 @@ def self.get_panel_load_watts_breaker_spaces_values(hpxml_bldg, panel_load) distribution_system = heat_pump.distribution_system if heat_pump.backup_type == HPXML::HeatPumpBackupTypeIntegrated - if !heat_pump.backup_heating_switchover_temp.nil? # max - watts += [get_dx_coil_load_from_capacity(UnitConversions.convert(heat_pump.heating_capacity, 'btu/hr', 'kbtu/hr')), UnitConversions.convert(heat_pump.backup_heating_capacity, 'btu/hr', 'w')].max - else # sum + + if heat_pump_backup_simultaneous_operation(heat_pump) # sum watts += get_dx_coil_load_from_capacity(UnitConversions.convert(heat_pump.heating_capacity, 'btu/hr', 'kbtu/hr')) watts += UnitConversions.convert(heat_pump.backup_heating_capacity, 'btu/hr', 'w') + else # max + watts += [get_dx_coil_load_from_capacity(UnitConversions.convert(heat_pump.heating_capacity, 'btu/hr', 'kbtu/hr')), UnitConversions.convert(heat_pump.backup_heating_capacity, 'btu/hr', 'w')].max end if heat_pump.backup_heating_fuel == HPXML::FuelTypeElectricity if !distribution_system.nil? @@ -5920,7 +5967,9 @@ def self.get_panel_load_watts_breaker_spaces_values(hpxml_bldg, panel_load) watts += get_dx_coil_load_from_capacity(UnitConversions.convert(cooling_system.cooling_capacity, 'btu/hr', 'kbtu/hr')) if !distribution_system.nil? heating_system = cooling_system.attached_heating_system - if !heating_system.nil? && (heating_system.heating_system_fuel != HPXML::FuelTypeElectricity) + if !heating_system.nil? && + (((heating_system.is_a? HPXML::HeatingSystem) && (heating_system.heating_system_fuel != HPXML::FuelTypeElectricity)) || + ((heating_system.is_a? HPXML::HeatPump) && (heating_system.heat_pump_fuel != HPXML::FuelTypeElectricity))) watts += get_120v_air_handler_load_from_capacity(UnitConversions.convert(cooling_system.cooling_capacity, 'btu/hr', 'kbtu/hr')) breaker_spaces += 1 else @@ -6085,8 +6134,10 @@ def self.get_panel_load_watts_breaker_spaces_values(hpxml_bldg, panel_load) end elsif type == HPXML::ElectricPanelLoadTypeLighting watts += 3 * hpxml_bldg.building_construction.conditioned_floor_area + breaker_spaces += 0 elsif type == HPXML::ElectricPanelLoadTypeKitchen watts += 3000 + breaker_spaces += 0 elsif type == HPXML::ElectricPanelLoadTypeLaundry watts = + 1500 breaker_spaces += 1 diff --git a/HPXMLtoOpenStudio/resources/hpxml.rb b/HPXMLtoOpenStudio/resources/hpxml.rb index e533ad02b7..f8a2bff713 100644 --- a/HPXMLtoOpenStudio/resources/hpxml.rb +++ b/HPXMLtoOpenStudio/resources/hpxml.rb @@ -9425,7 +9425,7 @@ def to_doc(electric_panel) if (not @system_idrefs.nil?) && (not @system_idrefs.empty?) @system_idrefs.each do |system_idref| system = XMLHelper.add_element(panel_load, 'System') - XMLHelper.add_attribute(system, 'idref', system_idref) + XMLHelper.add_attribute(system, 'idref', system_idref, @system_idrefs_isdefaulted) end end end diff --git a/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml b/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml index c88c270c6b..4a53604d19 100644 --- a/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml +++ b/HPXMLtoOpenStudio/resources/hpxml_schematron/EPvalidator.xml @@ -2463,20 +2463,23 @@ [PanelLoads] - + Expected 1 or more element(s) for xpath: PanelLoad [PanelLoad] - - Expected 1 element(s) for xpath: Type[text()="Heating" or text()="Cooling" or text()="Hot Water" or text()="Clothes Dryer"] + + Expected 1 element(s) for xpath: Type + Expected 1 element(s) for xpath: Type[text()="Heating" or text()="Cooling" or text()="Hot Water" or text()="Clothes Dryer"] Expected 0 or 1 element(s) for xpath: Watts Expected 0 or 1 element(s) for xpath: Voltage Expected Voltage to be '120' or '240' Expected 0 or 1 element(s) for xpath: Addition - Expected Addition to be 'false' or 'true' + Expected Addition to be 'false' or 'true' + Expected 0 or more element(s) for xpath: System + Expected 1 element(s) for xpath: System diff --git a/HPXMLtoOpenStudio/resources/xmlhelper.rb b/HPXMLtoOpenStudio/resources/xmlhelper.rb index 05b66f4b7a..e47e554068 100644 --- a/HPXMLtoOpenStudio/resources/xmlhelper.rb +++ b/HPXMLtoOpenStudio/resources/xmlhelper.rb @@ -206,8 +206,11 @@ def self.has_element(parent, element_name) # @param attr_name [String] Name of the attribute # @param attr_val [*] Value for the attribute # @return [nil] - def self.add_attribute(element, attr_name, attr_val) + def self.add_attribute(element, attr_name, attr_val, defaulted = false) element.set(attr_name, attr_val) + if defaulted + XMLHelper.add_attribute(element, 'dataSource', 'software') + end end # Gets the value of the specified attribute for the given element. diff --git a/docs/source/workflow_inputs.rst b/docs/source/workflow_inputs.rst index a1be2221f3..961ddd6daa 100644 --- a/docs/source/workflow_inputs.rst +++ b/docs/source/workflow_inputs.rst @@ -4626,10 +4626,10 @@ Individual panel loads entered in ``extension/PanelLoads/PanelLoad``. ``Voltage`` string V See [#]_ No See [#]_ ``BreakerSpaces`` integer No See [#]_ ``Addition`` boolean No false - ``System`` idref See [#]_ No See [#]_ Can reference one or more systems + ``System`` idref See [#]_ See [#]_ See [#]_ Can reference one or more systems ============================================== ======== ============== =========== ======== ========= ========================================== - .. [#] Type choices are "Heating", "Cooling", "Hot Water", "Clothes Dryer", "Dishwasher", "Range/Oven", "Permanent Spa Heater", "Permanent Spa Pump", "Pool Heater", "Pool Pump", "Well Pump", and "Electric Vehicle Charging". + .. [#] Type choices are "Heating", "Cooling", "Hot Water", "Clothes Dryer", "Dishwasher", "Range/Oven", "Permanent Spa Heater", "Permanent Spa Pump", "Pool Heater", "Pool Pump", "Well Pump", "Electric Vehicle Charging", and "Other". .. [#] If Watts not provided, defaults as follows: \- **Heating**: TODO @@ -4667,7 +4667,7 @@ Individual panel loads entered in ``extension/PanelLoads/PanelLoad``. .. [#] Voltage choices are "120" or "240". .. [#] If Voltage not provided, defaults as follows: - \- **Heating, Cooling, Hot Water, Clothes Dryer, Range/Oven, Permanent Spa Heater, Pool Heater**: 240 + \- **Heating, Cooling, Hot Water, Clothes Dryer, Range/Oven, Permanent Spa Heater, Pool Heater**: 240 (120 if Cooling references a room air conditioner) \- **Dishwasher, Permanent Spa Pump, Pool Pump, Well Pump, Electric Vehicle Charging, Lighting, Kitchen, Laundry, Other**: 120 @@ -4678,6 +4678,7 @@ Individual panel loads entered in ``extension/PanelLoads/PanelLoad``. \- **Heating, Cooling, Hot Water, Clothes Dryer, Dishwasher, Range/Oven, Permanent Spa Heater, Permanent Spa Pump, Pool Heater, Pool Pump, Well Pump, Electric Vehicle Charging, Other**: 120=1, 240=2 .. [#] System must reference a ``HeatingSystem``, ``CoolingSystem``, ``HeatPump``, ``WaterHeatingSystem``, ``ClothesDryer``, ``Dishwasher``, ``CookingRange``, ``PermanentSpa/Heater``, ``PermanentSpa/Pumps/Pump``, ``Pool/Heater``, ``Pool/Pump``, ``PlugLoad``, or ``VentilationFan``. + .. [#] Not required if Type is "Other"; otherwise, required. .. [#] A panel load is created for any system not already referenced by a panel load. .. _hpxml_batteries: