Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion HPXMLtoOpenStudio/measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ def run(model, runner, user_arguments)
# Write updated HPXML object (w/ defaults) to file for inspection
XMLHelper.write_file(hpxml.to_doc, args[:hpxml_defaults_path])

# When modeling whole SFA/MF buildings, remove shared systems upfront
# from the HPXML object; handle them at the end once the full model
# has been created.
shared_systems_map = {}
if hpxml.header.whole_sfa_or_mf_building_sim
shared_systems_map = hpxml.delete_shared_systems_serving_multiple_dwelling_units()
end

# Create OpenStudio unit model(s)
hpxml_osm_map = {}
hpxml.buildings.each do |hpxml_bldg|
Expand All @@ -147,9 +155,12 @@ def run(model, runner, user_arguments)
end
end

# Merge unit models into final model
if hpxml.buildings.size > 1
# Merge unit models into final model
Model.merge_unit_models(model, hpxml_osm_map)

# Apply shared systems
HVAC.apply_shared_systems(runner, model, hpxml, hpxml_osm_map, shared_systems_map)
end

# Create EnergyPlus outputs
Expand Down
20 changes: 10 additions & 10 deletions HPXMLtoOpenStudio/measure.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<schema_version>3.1</schema_version>
<name>hpxm_lto_openstudio</name>
<uid>b1543b30-9465-45ff-ba04-1d1f85e763bc</uid>
<version_id>630ef544-5cc2-45f5-8e47-f5e11aabdfda</version_id>
<version_modified>2025-10-21T15:47:00Z</version_modified>
<version_id>ec93b84f-6f84-4abd-8ad0-a658cebe0fa5</version_id>
<version_modified>2025-10-21T19:07:52Z</version_modified>
<xml_checksum>D8922A73</xml_checksum>
<class_name>HPXMLtoOpenStudio</class_name>
<display_name>HPXML to OpenStudio Translator</display_name>
Expand Down Expand Up @@ -192,7 +192,7 @@
<filename>measure.rb</filename>
<filetype>rb</filetype>
<usage_type>script</usage_type>
<checksum>B532C73E</checksum>
<checksum>ECA14D60</checksum>
</file>
<file>
<filename>airflow.rb</filename>
Expand Down Expand Up @@ -348,7 +348,7 @@
<filename>defaults.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>2C1F8D25</checksum>
<checksum>F702337C</checksum>
</file>
<file>
<filename>electric_panel.rb</filename>
Expand Down Expand Up @@ -384,7 +384,7 @@
<filename>hpxml.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>DD8B41A6</checksum>
<checksum>401C729D</checksum>
</file>
<file>
<filename>hpxml_schema/HPXML.xsd</filename>
Expand All @@ -402,7 +402,7 @@
<filename>hpxml_schematron/EPvalidator.sch</filename>
<filetype>sch</filetype>
<usage_type>resource</usage_type>
<checksum>1961EB81</checksum>
<checksum>CEA3D253</checksum>
</file>
<file>
<filename>hpxml_schematron/iso-schematron.xsd</filename>
Expand All @@ -414,13 +414,13 @@
<filename>hvac.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>C775FAF8</checksum>
<checksum>32303BFB</checksum>
</file>
<file>
<filename>hvac_sizing.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>4D1214E7</checksum>
<checksum>152A3B22</checksum>
</file>
<file>
<filename>internal_gains.rb</filename>
Expand Down Expand Up @@ -474,7 +474,7 @@
<filename>model.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>4FD33DD9</checksum>
<checksum>A5625A01</checksum>
</file>
<file>
<filename>output.rb</filename>
Expand Down Expand Up @@ -696,7 +696,7 @@
<filename>waterheater.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>003609A7</checksum>
<checksum>46200810</checksum>
</file>
<file>
<filename>weather.rb</filename>
Expand Down
81 changes: 61 additions & 20 deletions HPXMLtoOpenStudio/resources/defaults.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def self.apply(runner, hpxml, hpxml_bldg, weather, schedules_file: nil, convert_
apply_doors(hpxml_bldg)
apply_partition_wall_mass(hpxml_bldg)
apply_furniture_mass(hpxml_bldg)
apply_hvac(runner, hpxml_bldg, weather, convert_shared_systems, unit_num, hpxml.header)
apply_hvac(runner, hpxml.header, hpxml_bldg, weather, convert_shared_systems, unit_num)
apply_hvac_control(hpxml_bldg, schedules_file, eri_version)
apply_hvac_distribution(hpxml_bldg)
apply_infiltration(hpxml_bldg, unit_num)
Expand Down Expand Up @@ -240,6 +240,13 @@ def self.apply_header(hpxml_header, hpxml_bldg, weather)
unavailable_period.natvent_availability_isdefaulted = true
end
end

if hpxml_header.shared_boiler_operation.nil?
if hpxml_bldg.heating_systems.select { |htg| htg.heating_system_type == HPXML::HVACTypeBoiler && htg.is_shared_system_serving_multiple_dwelling_units }.size > 0
hpxml_header.shared_boiler_operation = HPXML::SharedBoilerOperationSequenced
hpxml_header.shared_boiler_operation_isdefaulted = true
end
end
end

# Assigns default values for omitted optional inputs in the HPXML::BuildingHeader object
Expand Down Expand Up @@ -1889,15 +1896,15 @@ def self.apply_furniture_mass(hpxml_bldg)
# HPXML::CoolingSystem, and HPXML::HeatPump objects
#
# @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings
# @param hpxml_header [HPXML::Header] HPXML Header object (one per HPXML file)
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @param weather [WeatherFile] Weather object containing EPW information
# @param convert_shared_systems [Boolean] Whether to convert shared systems to equivalent in-unit systems per ANSI/RESNET/ICC 301
# @param unit_num [Integer] Dwelling unit number
# @param hpxml_header [HPXML::Header] HPXML Header object
# @return [nil]
def self.apply_hvac(runner, hpxml_bldg, weather, convert_shared_systems, unit_num, hpxml_header)
def self.apply_hvac(runner, hpxml_header, hpxml_bldg, weather, convert_shared_systems, unit_num)
if convert_shared_systems
apply_shared_systems(hpxml_bldg)
convert_shared_systems_to_in_unit_systems(hpxml_bldg, hpxml_header)
end

# Convert negative values (e.g., -1) to nil as appropriate
Expand Down Expand Up @@ -2422,13 +2429,16 @@ def self.apply_hvac(runner, hpxml_bldg, weather, convert_shared_systems, unit_nu
end
end

# Converts shared systems to equivalent in-unit systems per ANSI/RESNET/ICC 301.
# Converts shared systems to equivalent in-unit systems when modeling individual dwelling units (or,
# when modeling a whole SFA/MF building, if OS-HPXML does not yet support explicitly modeling the
# shared system we fall back to these conversions).
#
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @param hpxml_header [HPXML::Header] HPXML Header object (one per HPXML file)
# @return [nil]
def self.apply_shared_systems(hpxml_bldg)
converted_clg = apply_shared_cooling_systems(hpxml_bldg)
converted_htg = apply_shared_heating_systems(hpxml_bldg)
def self.convert_shared_systems_to_in_unit_systems(hpxml_bldg, hpxml_header)
converted_clg = convert_shared_cooling_systems_to_in_unit_systems(hpxml_bldg)
converted_htg = convert_shared_heating_systems_to_in_unit_systems(hpxml_bldg, hpxml_header)
return unless (converted_clg || converted_htg)

# Remove WLHP if not serving heating nor cooling
Expand Down Expand Up @@ -2458,13 +2468,13 @@ def self.apply_shared_systems(hpxml_bldg)
# Converts shared cooling systems to equivalent in-unit systems per ANSI/RESNET/ICC 301.
#
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @return [Boolean] True if any shared systems were converted
def self.apply_shared_cooling_systems(hpxml_bldg)
converted = false
# @return [Boolean] Whether a shared cooling system was converted to an in-unit system
def self.convert_shared_cooling_systems_to_in_unit_systems(hpxml_bldg)
applied = false
hpxml_bldg.cooling_systems.each do |cooling_system|
next unless cooling_system.is_shared_system

converted = true
applied = true
wlhp = nil
distribution_system = cooling_system.distribution_system
distribution_type = distribution_system.distribution_system_type
Expand Down Expand Up @@ -2578,19 +2588,21 @@ def self.apply_shared_cooling_systems(hpxml_bldg)
end
end

return converted
return applied
end

# Converts shared heating systems to equivalent in-unit systems per ANSI/RESNET/ICC 301.
#
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @return [Boolean] True if any shared systems were converted
def self.apply_shared_heating_systems(hpxml_bldg)
converted = false
# @param hpxml_header [HPXML::Header] HPXML Header object (one per HPXML file)
# @return [Boolean] Whether a shared heating system was converted to an in-unit system
def self.convert_shared_heating_systems_to_in_unit_systems(hpxml_bldg, hpxml_header)
applied = false
hpxml_bldg.heating_systems.each do |heating_system|
next if hpxml_header.whole_sfa_or_mf_building_sim # Central boilers are explicitly modeled for whole SFA/MF buildings
next unless heating_system.is_shared_system

converted = true
applied = true
distribution_system = heating_system.distribution_system
hydronic_type = distribution_system.hydronic_type

Expand All @@ -2615,7 +2627,7 @@ def self.apply_shared_heating_systems(hpxml_bldg)
heating_system.heating_capacity = nil # Autosize the equipment
end

return converted
return applied
end

# Assigns default values for omitted optional inputs in the HPXML::CoolingPerformanceDataPoint
Expand Down Expand Up @@ -2857,8 +2869,37 @@ def self.apply_hvac_control(hpxml_bldg, schedules_file, eri_version)
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @return [nil]
def self.apply_hvac_distribution(hpxml_bldg)
ncfl = hpxml_bldg.building_construction.number_of_conditioned_floors
# Hydronic distribution
hpxml_bldg.hvac_distributions.each do |hvac_distribution|
next unless hvac_distribution.hvac_systems.any? { |h| h.is_a?(HPXML::HeatingSystem) && h.heating_system_type == HPXML::HVACTypeBoiler }

# Supply/return loop temperatures
default_delta_t = 20.0 # deg-F
if hvac_distribution.hydronic_supply_temp.nil?
if not hvac_distribution.hydronic_return_temp.nil?
hvac_distribution.hydronic_supply_temp = hvac_distribution.hydronic_return_temp + default_delta_t # deg-F
else
hvac_distribution.hydronic_supply_temp = 180.0 # deg-F
end
hvac_distribution.hydronic_supply_temp_isdefaulted = true
end
if hvac_distribution.hydronic_return_temp.nil?
hvac_distribution.hydronic_return_temp = hvac_distribution.hydronic_supply_temp - default_delta_t # deg-F
hvac_distribution.hydronic_return_temp_isdefaulted = true
end
if hvac_distribution.hydronic_trvs.nil?
hvac_distribution.hydronic_trvs = true
hvac_distribution.hydronic_trvs_isdefaulted = true
end
if hvac_distribution.hydronic_variable_speed_pump.nil?
hvac_distribution.hydronic_variable_speed_pump = false
hvac_distribution.hydronic_variable_speed_pump_isdefaulted = true
end
end

# Air distribution
ncfl_ag = hpxml_bldg.building_construction.number_of_conditioned_floors_above_grade
ncfl = hpxml_bldg.building_construction.number_of_conditioned_floors

hpxml_bldg.hvac_distributions.each do |hvac_distribution|
next unless hvac_distribution.distribution_system_type == HPXML::HVACDistributionTypeAir
Expand Down Expand Up @@ -4947,7 +4988,7 @@ def self.apply_fuel_loads(hpxml_bldg, schedules_file)
# @param runner [OpenStudio::Measure::OSRunner] Object typically used to display warnings
# @param hpxml_bldg [HPXML::Building] HPXML Building object representing an individual dwelling unit
# @param weather [WeatherFile] Weather object containing EPW information
# @param hpxml_header [HPXML::Header] HPXML Header object
# @param hpxml_header [HPXML::Header] HPXML Header object (one per HPXML file)
# @return [Array<Hash, Hash>] Maps of HPXML::Zones => DesignLoadValues object, HPXML::Spaces => DesignLoadValues object
def self.apply_hvac_sizing(runner, hpxml_bldg, weather, hpxml_header)
hvac_systems = HVAC.get_hpxml_hvac_systems(hpxml_bldg)
Expand Down
Loading