Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whole SFA/MF buildings: shared boilers #1599

Draft
wants to merge 16 commits into
base: master
Choose a base branch
from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 45 additions & 0 deletions BuildResidentialHPXML/measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3764,6 +3764,7 @@ def self.create(runner, model, args, epw_path, hpxml_path, existing_hpxml_path)
collapse_surfaces(hpxml_bldg, args)
renumber_hpxml_ids(hpxml_bldg)

set_sameas_for_shared_elements(hpxml)
hpxml_doc = hpxml.to_doc()
hpxml.set_unique_hpxml_ids(hpxml_doc, true) if hpxml.buildings.size > 1
XMLHelper.write_file(hpxml_doc, hpxml_path)
Expand Down Expand Up @@ -7259,6 +7260,50 @@ def self.renumber_hpxml_ids(hpxml_bldg)
end
end
end

def self.set_sameas_for_shared_elements(hpxml)
# Applies to the last building only
return if hpxml.buildings.size == 1
return if !hpxml.header.whole_sfa_or_mf_building_sim

hpxml_elements_with_sameas = ['heating_systems',
'cooling_systems',
'heat_pumps']

last_building = hpxml.buildings[-1]
hpxml.buildings[0..-2].each do |building|
hpxml_elements_with_sameas.each do |hpxml_el|
last_building.send(hpxml_el).each do |last_obj|
next unless last_obj.is_shared_system

building.send(hpxml_el).each do |obj|
next unless obj.is_shared_system
next if last_obj.to_s != obj.to_s

if last_obj.respond_to?(:distribution_system) && !last_obj.distribution_system.nil?
set_sameas_id_for_object(last_obj.distribution_system, obj.distribution_system.id)
end
set_sameas_id_for_object(last_obj, obj.id)
end
end
end
end
end

def self.set_sameas_id_for_object(obj, sameas_id)
# Sets the sameas id and also sets all other attributes (other than IDrefs)
# to nil.
obj.class::ATTRS.each do |attribute|
next if [:id,
:primary_system,
:primary_heating_system,
:primary_cooling_system,
:distribution_system_idref].include?(attribute)

obj.send("#{attribute}=", nil)
end
obj.sameas_id = sameas_id
end
end

# register the measure to be used by the application
Expand Down
6 changes: 3 additions & 3 deletions BuildResidentialHPXML/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>build_residential_hpxml</name>
<uid>a13a8983-2b01-4930-8af2-42030b6e4233</uid>
<version_id>500e0245-d9de-4bbc-9f39-5842e3db0427</version_id>
<version_modified>2024-03-12T16:57:13Z</version_modified>
<version_id>7cf9a809-593b-4193-b126-66afd4b7ba95</version_id>
<version_modified>2024-03-15T19:11:11Z</version_modified>
<xml_checksum>2C38F48B</xml_checksum>
<class_name>BuildResidentialHPXML</class_name>
<display_name>HPXML Builder</display_name>
Expand Down Expand Up @@ -7262,7 +7262,7 @@
<filename>measure.rb</filename>
<filetype>rb</filetype>
<usage_type>script</usage_type>
<checksum>5E5697F8</checksum>
<checksum>8EFA9BC2</checksum>
</file>
<file>
<filename>geometry.rb</filename>
Expand Down
54 changes: 25 additions & 29 deletions HPXMLtoOpenStudio/measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,14 @@ def run(model, runner, user_arguments)
hpxml_defaults_path = File.join(output_dir, 'in.xml')
XMLHelper.write_file(hpxml.to_doc, 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 model
hpxml_osm_map = {}
hpxml.buildings.each_with_index do |hpxml_bldg, i|
Expand All @@ -184,9 +192,12 @@ def run(model, runner, user_arguments)
end
end

# Merge unit models into final model
if hpxml.buildings.size > 1
add_unit_model_to_model(model, hpxml_osm_map)
# Merge unit models into final model
add_unit_models_to_model(model, hpxml_osm_map)
# Apply shared systems
shared_boilers_map = shared_systems_map # FIXME: Need to pull out the shared boilers from any other shared systems
HVAC.apply_shared_boilers_for_whole_building_model(model, hpxml, shared_boilers_map, @hvac_unavailable_periods)
end

# Output
Expand Down Expand Up @@ -215,7 +226,9 @@ def run(model, runner, user_arguments)
return true
end

def add_unit_model_to_model(model, hpxml_osm_map)
def add_unit_models_to_model(model, hpxml_osm_map)
# Note: ZoneCapacitanceMultiplierResearchSpecial is not actually unique, but we don't assign it to
# individual thermal zones, so we'll treat it as unique here.
unique_objects = { 'OS:ConvergenceLimits' => 'ConvergenceLimits',
'OS:Foundation:Kiva:Settings' => 'FoundationKivaSettings',
'OS:OutputControl:Files' => 'OutputControlFiles',
Expand All @@ -232,7 +245,8 @@ def add_unit_model_to_model(model, hpxml_osm_map)
'OS:Site:WaterMainsTemperature' => 'SiteWaterMainsTemperature',
'OS:SurfaceConvectionAlgorithm:Inside' => 'InsideSurfaceConvectionAlgorithm',
'OS:SurfaceConvectionAlgorithm:Outside' => 'OutsideSurfaceConvectionAlgorithm',
'OS:Timestep' => 'Timestep' }
'OS:Timestep' => 'Timestep',
'OS:ZoneCapacitanceMultiplier:ResearchSpecial' => 'ZoneCapacitanceMultiplierResearchSpecial' }

# Handle unique objects first: Grab one from the first model we find the
# object on (may not be the first unit).
Expand Down Expand Up @@ -449,7 +463,7 @@ def create_unit_model(hpxml, hpxml_bldg, runner, model, epw_path, epw_file, weat
airloop_map = {} # Map of HPXML System ID -> AirLoopHVAC (or ZoneHVACFourPipeFanCoil)
add_ideal_system(model, spaces, epw_path)
add_cooling_system(model, runner, weather, spaces, airloop_map)
add_heating_system(runner, model, weather, spaces, airloop_map)
add_heating_system(model, runner, weather, spaces, airloop_map)
add_heat_pump(runner, model, weather, spaces, airloop_map)
add_dehumidifiers(runner, model, spaces)
add_ceiling_fans(runner, model, weather, spaces)
Expand Down Expand Up @@ -570,7 +584,7 @@ def add_num_occupants(model, runner, spaces)

def create_or_get_space(model, spaces, location)
if spaces[location].nil?
Geometry.create_space_and_zone(model, spaces, location, @hpxml_bldg.building_construction.number_of_units)
Geometry.create_space_and_zone(model, spaces, location, @hpxml_bldg.building_construction.number_of_units, @hpxml_bldg.building_id)
end
return spaces[location]
end
Expand Down Expand Up @@ -1580,7 +1594,7 @@ def add_cooling_system(model, runner, weather, spaces, airloop_map)
cooling_system = hvac_system[:cooling]
heating_system = hvac_system[:heating]

check_distribution_system(cooling_system.distribution_system, cooling_system.cooling_system_type)
HVAC.check_distribution_system(cooling_system.distribution_system, cooling_system.cooling_system_type)

# Calculate cooling sequential load fractions
sequential_cool_load_fracs = HVAC.calc_sequential_load_fractions(cooling_system.fraction_cool_load_served.to_f, @remaining_cool_load_frac, @cooling_days)
Expand Down Expand Up @@ -1616,7 +1630,7 @@ def add_cooling_system(model, runner, weather, spaces, airloop_map)
end
end

def add_heating_system(runner, model, weather, spaces, airloop_map)
def add_heating_system(model, runner, weather, spaces, airloop_map)
conditioned_zone = spaces[HPXML::LocationConditionedSpace].thermalZone.get

HVAC.get_hpxml_hvac_systems(@hpxml_bldg).each do |hvac_system|
Expand All @@ -1626,7 +1640,7 @@ def add_heating_system(runner, model, weather, spaces, airloop_map)
cooling_system = hvac_system[:cooling]
heating_system = hvac_system[:heating]

check_distribution_system(heating_system.distribution_system, heating_system.heating_system_type)
HVAC.check_distribution_system(heating_system.distribution_system, heating_system.heating_system_type)

if (heating_system.heating_system_type == HPXML::HVACTypeFurnace) && (not cooling_system.nil?)
next # Already processed combined AC+furnace
Expand Down Expand Up @@ -1654,7 +1668,7 @@ def add_heating_system(runner, model, weather, spaces, airloop_map)

elsif [HPXML::HVACTypeBoiler].include? heating_system.heating_system_type

airloop_map[sys_id] = HVAC.apply_boiler(model, runner, heating_system, sequential_heat_load_fracs, conditioned_zone,
airloop_map[sys_id] = HVAC.apply_boiler(model, heating_system, sequential_heat_load_fracs, conditioned_zone,
@hvac_unavailable_periods)

elsif [HPXML::HVACTypeElectricResistance].include? heating_system.heating_system_type
Expand Down Expand Up @@ -1689,7 +1703,7 @@ def add_heat_pump(runner, model, weather, spaces, airloop_map)

heat_pump = hvac_system[:cooling]

check_distribution_system(heat_pump.distribution_system, heat_pump.heat_pump_type)
HVAC.check_distribution_system(heat_pump.distribution_system, heat_pump.heat_pump_type)

# Calculate heating sequential load fractions
sequential_heat_load_fracs = HVAC.calc_sequential_load_fractions(heat_pump.fraction_heat_load_served, @remaining_heat_load_frac, @heating_days)
Expand Down Expand Up @@ -1801,24 +1815,6 @@ def add_dehumidifiers(runner, model, spaces)
@hpxml_bldg.building_construction.number_of_units)
end

def check_distribution_system(hvac_distribution, system_type)
return if hvac_distribution.nil?

hvac_distribution_type_map = { HPXML::HVACTypeFurnace => [HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE],
HPXML::HVACTypeBoiler => [HPXML::HVACDistributionTypeHydronic, HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE],
HPXML::HVACTypeCentralAirConditioner => [HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE],
HPXML::HVACTypeEvaporativeCooler => [HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE],
HPXML::HVACTypeMiniSplitAirConditioner => [HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE],
HPXML::HVACTypeHeatPumpAirToAir => [HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE],
HPXML::HVACTypeHeatPumpMiniSplit => [HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE],
HPXML::HVACTypeHeatPumpGroundToAir => [HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE],
HPXML::HVACTypeHeatPumpWaterLoopToAir => [HPXML::HVACDistributionTypeAir, HPXML::HVACDistributionTypeDSE] }

if not hvac_distribution_type_map[system_type].include? hvac_distribution.distribution_system_type
fail "Incorrect HVAC distribution system type for HVAC type: '#{system_type}'. Should be one of: #{hvac_distribution_type_map[system_type]}"
end
end

def add_mels(runner, model, spaces)
# Misc
@hpxml_bldg.plug_loads.each do |plug_load|
Expand Down
24 changes: 9 additions & 15 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>10220f4e-bd2e-4a6e-8f71-4063c902f6e2</version_id>
<version_modified>2024-03-14T23:17:23Z</version_modified>
<version_id>f3193070-c386-4ed4-81f7-20e0bda8a89e</version_id>
<version_modified>2024-03-15T19:11:14Z</version_modified>
<xml_checksum>D8922A73</xml_checksum>
<class_name>HPXMLtoOpenStudio</class_name>
<display_name>HPXML to OpenStudio Translator</display_name>
Expand Down Expand Up @@ -142,7 +142,7 @@
<filename>measure.rb</filename>
<filetype>rb</filetype>
<usage_type>script</usage_type>
<checksum>D8568042</checksum>
<checksum>5FB25A6E</checksum>
</file>
<file>
<filename>airflow.rb</filename>
Expand Down Expand Up @@ -292,7 +292,7 @@
<filename>geometry.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>21C2553F</checksum>
<checksum>B13F0B8A</checksum>
</file>
<file>
<filename>hotwater_appliances.rb</filename>
Expand All @@ -304,13 +304,13 @@
<filename>hpxml.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>4B23EC6D</checksum>
<checksum>7A6E2136</checksum>
</file>
<file>
<filename>hpxml_defaults.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>628A6E9C</checksum>
<checksum>27025FD4</checksum>
</file>
<file>
<filename>hpxml_schema/HPXML.xsd</filename>
Expand All @@ -328,7 +328,7 @@
<filename>hpxml_schematron/EPvalidator.xml</filename>
<filetype>xml</filetype>
<usage_type>resource</usage_type>
<checksum>8ECB4742</checksum>
<checksum>02A941B3</checksum>
</file>
<file>
<filename>hpxml_schematron/iso-schematron.xsd</filename>
Expand All @@ -340,13 +340,13 @@
<filename>hvac.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>5C9FA161</checksum>
<checksum>0110CF0D</checksum>
</file>
<file>
<filename>hvac_sizing.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>903730A2</checksum>
<checksum>2AA5E8B7</checksum>
</file>
<file>
<filename>lighting.rb</filename>
Expand Down Expand Up @@ -582,12 +582,6 @@
<usage_type>resource</usage_type>
<checksum>87937B84</checksum>
</file>
<file>
<filename>in.schedules.csv</filename>
<filetype>csv</filetype>
<usage_type>test</usage_type>
<checksum>CF0E0450</checksum>
</file>
<file>
<filename>test_airflow.rb</filename>
<filetype>rb</filetype>
Expand Down
3 changes: 2 additions & 1 deletion HPXMLtoOpenStudio/resources/geometry.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# frozen_string_literal: true

class Geometry
def self.create_space_and_zone(model, spaces, location, zone_multiplier)
def self.create_space_and_zone(model, spaces, location, zone_multiplier, building_id)
if not spaces.keys.include? location
thermal_zone = OpenStudio::Model::ThermalZone.new(model)
thermal_zone.setName(location)
thermal_zone.additionalProperties.setFeature('ObjectType', location)
thermal_zone.additionalProperties.setFeature('BuildingID', building_id)
thermal_zone.setMultiplier(zone_multiplier)

space = OpenStudio::Model::Space.new(model)
Expand Down