Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1f884e2
Initial progress.
shorowit Nov 26, 2024
7c1759b
Bugfix, cleanup.
shorowit Nov 26, 2024
7855ab5
Merge branch 'whole_building_common_spaces' of https://github.com/NRE…
shorowit Nov 27, 2024
af6bcbf
Add a couple sample files.
shorowit Nov 27, 2024
025a7bf
Some more progress.
shorowit Nov 28, 2024
f149847
Bugfix.
shorowit Nov 28, 2024
bc70e9d
Merge branch 'whole_building_common_spaces' of https://github.com/NRE…
shorowit Jan 17, 2025
0b7531a
Merge branch 'whole_building_common_spaces' of https://github.com/NRE…
shorowit Jul 1, 2025
72a38b4
Bugfix
shorowit Jul 1, 2025
769f802
Latest results.
Jul 1, 2025
d4a7b53
Merge branch 'whole_building_common_spaces' of https://github.com/NRE…
shorowit Oct 21, 2025
e21e45e
Latest results.
Oct 21, 2025
d872d81
Update HPXMLs.
shorowit Oct 21, 2025
c5c5034
Merge branch 'central_boilers2' of https://github.com/NREL/OpenStudio…
shorowit Oct 21, 2025
3f64ea1
Latest results.
Oct 21, 2025
0a46f56
Move some code into model.rb.
shorowit Oct 23, 2025
6ec679e
Merge branch 'central_boilers2' of https://github.com/NREL/OpenStudio…
shorowit Nov 26, 2025
42ac2ef
Merge branch 'whole_building_common_spaces' of https://github.com/NRE…
shorowit Nov 26, 2025
c47070c
Latest results.
Nov 26, 2025
60bd876
Merge branch 'whole_building_common_spaces_simplify' of https://githu…
shorowit Dec 2, 2025
2263626
Latest results.
Dec 2, 2025
bf7ae34
update HPXMLs
shorowit Dec 2, 2025
f500a2a
Merge branch 'central_boilers2' of https://github.com/NREL/OpenStudio…
shorowit Dec 2, 2025
9b44d91
Latest results.
Dec 2, 2025
c3c702c
Merge branch 'whole_building_common_spaces' of https://github.com/NRE…
shorowit Dec 8, 2025
75f1999
Address merge
shorowit Dec 8, 2025
d4e1385
Merge branch 'whole_building_common_spaces' of https://github.com/NRE…
shorowit Dec 8, 2025
dde875a
Latest results.
Dec 8, 2025
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>3a95928b-a02e-48b4-aefa-bac6bce64107</version_id>
<version_modified>2025-12-08T19:49:33Z</version_modified>
<version_id>6a58497e-cf9d-4695-b17d-dac4502b2ef0</version_id>
<version_modified>2025-12-08T19:51:39Z</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>4FAA1035</checksum>
<checksum>C22EA4EB</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>4F79DDF3</checksum>
<checksum>79C0A74F</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>A0D34C5D</checksum>
<checksum>BAA2C690</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>DBCF54E6</checksum>
<checksum>C390B44B</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>01E3FE77</checksum>
<checksum>073FE13E</checksum>
</file>
<file>
<filename>hvac_sizing.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>1A612353</checksum>
<checksum>D9D27767</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>88FDF17C</checksum>
<checksum>535327BF</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>D373B68C</checksum>
<checksum>225126E7</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 @@ -1891,15 +1898,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 @@ -2424,13 +2431,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 @@ -2460,13 +2470,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 @@ -2580,19 +2590,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 @@ -2617,7 +2629,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 @@ -2859,8 +2871,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 @@ -4954,7 +4995,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