diff --git a/HPXMLtoOpenStudio/measure.xml b/HPXMLtoOpenStudio/measure.xml
index a75c7de113..8474766d15 100644
--- a/HPXMLtoOpenStudio/measure.xml
+++ b/HPXMLtoOpenStudio/measure.xml
@@ -3,8 +3,8 @@
3.1
hpxm_lto_openstudio
b1543b30-9465-45ff-ba04-1d1f85e763bc
- 2bb7d5cd-06e3-406b-9eb0-96dfe7e2c6d2
- 2024-11-19T01:12:59Z
+ d26078da-8691-4396-af39-a5cb563e1407
+ 2024-11-21T02:54:20Z
D8922A73
HPXMLtoOpenStudio
HPXML to OpenStudio Translator
@@ -345,7 +345,7 @@
geometry.rb
rb
resource
- 945B3E0D
+ 3DBAF60E
hotwater_appliances.rb
@@ -447,7 +447,7 @@
model.rb
rb
resource
- ECCE7792
+ ED1985F0
output.rb
diff --git a/HPXMLtoOpenStudio/resources/geometry.rb b/HPXMLtoOpenStudio/resources/geometry.rb
index 1080a1404c..5b19369f40 100644
--- a/HPXMLtoOpenStudio/resources/geometry.rb
+++ b/HPXMLtoOpenStudio/resources/geometry.rb
@@ -138,8 +138,11 @@ def self.apply_walls(runner, model, spaces, hpxml_bldg, hpxml_header)
_walls_top, foundation_top = get_foundation_and_walls_top(hpxml_bldg)
hpxml_bldg.walls.each do |wall|
- store_adjacent_surface_ids_to_space(get_interior_space(model, spaces, HPXML::LocationConditionedSpace, hpxml_bldg), wall)
- next if wall.sameas_id
+ hpxml_id = wall.id
+ if wall.sameas_id
+ hpxml_sameas_id = wall.sameas_id
+ wall = wall.sameas
+ end
next if wall.net_area < 1.0 # skip modeling net surface area for surfaces comprised entirely of subsurface area
if wall.azimuth.nil?
@@ -151,6 +154,9 @@ def self.apply_walls(runner, model, spaces, hpxml_bldg, hpxml_header)
else
azimuths = [wall.azimuth]
end
+ if hpxml_sameas_id
+ azimuths = azimuths.map { |a| (a + 180) % 360 }
+ end
surfaces = []
@@ -173,15 +179,17 @@ def self.apply_walls(runner, model, spaces, hpxml_bldg, hpxml_header)
end
surface.setSurfaceType(EPlus::SurfaceTypeWall)
set_surface_interior(model, spaces, surface, wall, hpxml_bldg)
- set_surface_exterior(model, spaces, surface, wall, hpxml_bldg)
+ set_surface_exterior(model, spaces, surface, wall, hpxml_bldg) if hpxml_sameas_id.nil?
if wall.is_interior
surface.setSunExposure(EPlus::SurfaceSunExposureNo)
surface.setWindExposure(EPlus::SurfaceWindExposureNo)
end
- surface.additionalProperties.setFeature('hpxmlID', wall.id)
+ surface.additionalProperties.setFeature('hpxmlID', hpxml_id)
+ surface.additionalProperties.setFeature('hpxmlSameasID', hpxml_sameas_id) unless hpxml_sameas_id.nil?
end
next if surfaces.empty?
+ next unless hpxml_sameas_id.nil?
# Apply construction
# The code below constructs a reasonable wall construction based on the
@@ -223,8 +231,11 @@ def self.apply_rim_joists(runner, model, spaces, hpxml_bldg)
_walls_top, foundation_top = get_foundation_and_walls_top(hpxml_bldg)
hpxml_bldg.rim_joists.each do |rim_joist|
- store_adjacent_surface_ids_to_space(get_interior_space(model, spaces, HPXML::LocationConditionedSpace, hpxml_bldg), rim_joist)
- next if rim_joist.sameas_id
+ hpxml_id = rim_joist.id
+ if rim_joist.sameas_id
+ hpxml_sameas_id = rim_joist.sameas_id
+ rim_joist = rim_joist.sameas
+ end
if rim_joist.azimuth.nil?
if rim_joist.is_exterior
@@ -235,6 +246,9 @@ def self.apply_rim_joists(runner, model, spaces, hpxml_bldg)
else
azimuths = [rim_joist.azimuth]
end
+ if hpxml_sameas_id
+ azimuths = azimuths.map { |a| (a + 180) % 360 }
+ end
surfaces = []
@@ -257,14 +271,17 @@ def self.apply_rim_joists(runner, model, spaces, hpxml_bldg)
end
surface.setSurfaceType(EPlus::SurfaceTypeWall)
set_surface_interior(model, spaces, surface, rim_joist, hpxml_bldg)
- set_surface_exterior(model, spaces, surface, rim_joist, hpxml_bldg)
+ set_surface_exterior(model, spaces, surface, rim_joist, hpxml_bldg) if hpxml_sameas_id.nil?
if rim_joist.is_interior
surface.setSunExposure(EPlus::SurfaceSunExposureNo)
surface.setWindExposure(EPlus::SurfaceWindExposureNo)
end
- surface.additionalProperties.setFeature('hpxmlID', rim_joist.id)
+ surface.additionalProperties.setFeature('hpxmlID', hpxml_id)
+ surface.additionalProperties.setFeature('hpxmlSameasID', hpxml_sameas_id) unless hpxml_sameas_id.nil?
end
+ next unless hpxml_sameas_id.nil?
+
# Apply construction
inside_film = Material.AirFilmVertical
@@ -310,8 +327,14 @@ def self.apply_floors(runner, model, spaces, hpxml_bldg, hpxml_header)
walls_top, foundation_top = get_foundation_and_walls_top(hpxml_bldg)
hpxml_bldg.floors.each do |floor|
- store_adjacent_surface_ids_to_space(get_interior_space(model, spaces, HPXML::LocationConditionedSpace, hpxml_bldg), floor)
- next if floor.sameas_id
+ hpxml_id = floor.id
+ if floor.sameas_id
+ is_ceiling = !floor.sameas.is_ceiling
+ hpxml_sameas_id = floor.sameas_id
+ floor = floor.sameas
+ else
+ is_ceiling = floor.is_ceiling
+ end
next if floor.net_area < 1.0 # skip modeling net surface area for surfaces comprised entirely of subsurface area
area = floor.net_area
@@ -323,7 +346,7 @@ def self.apply_floors(runner, model, spaces, hpxml_bldg, hpxml_header)
z_origin = foundation_top
end
- if floor.is_ceiling
+ if is_ceiling
vertices = create_ceiling_vertices(length, width, z_origin, default_azimuths)
surface = OpenStudio::Model::Surface.new(vertices, model)
surface.additionalProperties.setFeature('SurfaceType', 'Ceiling')
@@ -334,7 +357,7 @@ def self.apply_floors(runner, model, spaces, hpxml_bldg, hpxml_header)
end
surface.additionalProperties.setFeature('Tilt', 0.0)
set_surface_interior(model, spaces, surface, floor, hpxml_bldg)
- set_surface_exterior(model, spaces, surface, floor, hpxml_bldg)
+ set_surface_exterior(model, spaces, surface, floor, hpxml_bldg) if hpxml_sameas_id.nil?
surface.setName(floor.id)
if floor.is_interior
surface.setSunExposure(EPlus::SurfaceSunExposureNo)
@@ -348,7 +371,9 @@ def self.apply_floors(runner, model, spaces, hpxml_bldg, hpxml_header)
end
end
end
- surface.additionalProperties.setFeature('hpxmlID', floor.id)
+ surface.additionalProperties.setFeature('hpxmlID', hpxml_id)
+ surface.additionalProperties.setFeature('hpxmlSameasID', hpxml_sameas_id) unless hpxml_sameas_id.nil?
+ next unless hpxml_sameas_id.nil?
# Apply construction
@@ -457,10 +482,13 @@ def self.apply_foundation_walls_slabs(runner, model, spaces, weather, hpxml_bldg
# calculate heat flow between two zones through the ground.
int_fnd_walls = hpxml_bldg.foundation_walls.select { |fw| (fw.is_interior && fw.interior_adjacent_to == foundation_type) || fw.sameas_id }
int_fnd_walls.each do |fnd_wall|
- next unless (fnd_wall.is_interior || fw.sameas.is_interior)
+ hpxml_id = fnd_wall.id
+ if fnd_wall.sameas_id
+ hpxml_sameas_id = fnd_wall.sameas_id
+ fnd_wall = fnd_wall.sameas
+ end
- store_adjacent_surface_ids_to_space(get_interior_space(model, spaces, HPXML::LocationConditionedSpace, hpxml_bldg), fnd_wall)
- next if fnd_wall.sameas_id
+ next unless fnd_wall.is_interior
ag_height = fnd_wall.height - fnd_wall.depth_below_grade
ag_net_area = fnd_wall.net_area * ag_height / fnd_wall.height
@@ -473,6 +501,9 @@ def self.apply_foundation_walls_slabs(runner, model, spaces, weather, hpxml_bldg
else
azimuth = fnd_wall.azimuth
end
+ if hpxml_sameas_id
+ azimuth = (azimuth + 180) % 360
+ end
vertices = create_wall_vertices(length, ag_height, z_origin, azimuth)
surface = OpenStudio::Model::Surface.new(vertices, model)
@@ -483,10 +514,13 @@ def self.apply_foundation_walls_slabs(runner, model, spaces, weather, hpxml_bldg
surface.setName(fnd_wall.id)
surface.setSurfaceType(EPlus::SurfaceTypeWall)
set_surface_interior(model, spaces, surface, fnd_wall, hpxml_bldg)
- set_surface_exterior(model, spaces, surface, fnd_wall, hpxml_bldg)
+ set_surface_exterior(model, spaces, surface, fnd_wall, hpxml_bldg) if hpxml_sameas_id.nil?
surface.setSunExposure(EPlus::SurfaceSunExposureNo)
surface.setWindExposure(EPlus::SurfaceWindExposureNo)
- surface.additionalProperties.setFeature('hpxmlID', fnd_wall.id)
+ surface.additionalProperties.setFeature('hpxmlID', hpxml_id)
+ surface.additionalProperties.setFeature('hpxmlSameasID', hpxml_sameas_id) unless hpxml_sameas_id.nil?
+
+ next unless hpxml_sameas_id.nil?
# Apply construction
@@ -1142,24 +1176,6 @@ def self.get_occupancy_default_num(nbeds:)
return Float(nbeds) # Per ANSI 301 for an asset calculation
end
- # Store the adjacent surface ID in Space additional properties, to process after merging unit models
- #
- # @param space [OpenStudio::Model::Space] the OpenStudio::Model::Space object of the dwelling unit
- # @param hpxml_surface [HPXML::Wall or HPXML::Floor or HPXML::RimJoist or HPXML::FoundationWall] any HPXML surface
- # @return [nil]
- def self.store_adjacent_surface_ids_to_space(space, hpxml_surface)
- return if hpxml_surface.sameas_id.nil?
-
- # Store adjacent surface id in the space to process later
- adjacent_surface_ids = space.additionalProperties.getFeatureAsString('adjacentSurfaceIDs')
- if adjacent_surface_ids.is_initialized
- adjacent_surface_ids_new = [adjacent_surface_ids, hpxml_surface.sameas_id].join(', ')
- space.additionalProperties.setFeature('adjacentSurfaceIDs', adjacent_surface_ids_new)
- else
- space.additionalProperties.setFeature('adjacentSurfaceIDs', hpxml_surface.sameas_id)
- end
- end
-
# Creates a space and zone based on contents of spaces and value of location.
# Sets a "dwelling unit multiplier" equal to the number of similar units represented.
#
diff --git a/HPXMLtoOpenStudio/resources/model.rb b/HPXMLtoOpenStudio/resources/model.rb
index ca48cc006f..55983fbafb 100644
--- a/HPXMLtoOpenStudio/resources/model.rb
+++ b/HPXMLtoOpenStudio/resources/model.rb
@@ -942,19 +942,15 @@ def self.merge_unit_models(model, hpxml_osm_map)
end
model_objects.each do |obj|
- next unless obj.to_Space.is_initialized
-
- space = obj.to_Space.get
- adjacent_surface_id_str = space.additionalProperties.getFeatureAsString('adjacentSurfaceIDs')
- next unless adjacent_surface_id_str.is_initialized
-
- adjacent_surface_ids = adjacent_surface_id_str.to_s.split(', ')
- # All the surfaces that are adjacent to the space
- adjacent_surfaces = model_objects.select { |o| o.to_Surface.is_initialized && o.to_Surface.get.additionalProperties.getFeatureAsString('hpxmlID').is_initialized && (adjacent_surface_ids.include? o.to_Surface.get.additionalProperties.getFeatureAsString('hpxmlID').to_s) }
- adjacent_surfaces.each do |surface_obj|
- surface = surface_obj.to_Surface.get
- surface.createAdjacentSurface(space)
- end
+ next unless obj.to_Surface.is_initialized
+ next unless obj.to_Surface.get.additionalProperties.getFeatureAsString('hpxmlSameasID').is_initialized
+
+ surface_obj = obj.to_Surface.get
+ hpxml_sameas_id = surface_obj.additionalProperties.getFeatureAsString('hpxmlSameasID').to_s
+
+ adjacent_surface = model_objects.find { |o| o.to_Surface.is_initialized && o.to_Surface.get.additionalProperties.getFeatureAsString('hpxmlID').is_initialized && (hpxml_sameas_id == o.to_Surface.get.additionalProperties.getFeatureAsString('hpxmlID').to_s) }.to_Surface.get
+ surface_obj.setConstruction(adjacent_surface.construction.get.to_Construction.get.reverseConstruction)
+ adjacent_surface.setAdjacentSurface(surface_obj)
end
end
diff --git a/workflow/hpxml_inputs.json b/workflow/hpxml_inputs.json
index 5151befba7..827d299976 100644
--- a/workflow/hpxml_inputs.json
+++ b/workflow/hpxml_inputs.json
@@ -3608,7 +3608,8 @@
"clothes_dryer_present": false
},
"sample_files/base-bldgtype-mf-whole-building-common-spaces.xml": {
- "parent_hpxml": "sample_files/base-bldgtype-mf-whole-building.xml"
+ "parent_hpxml": "sample_files/base-bldgtype-mf-whole-building.xml",
+ "heating_system_heating_capacity": 24000
},
"sample_files/base-pv.xml": {
"parent_hpxml": "sample_files/base.xml",
diff --git a/workflow/sample_files/base-bldgtype-mf-whole-building-common-spaces.xml b/workflow/sample_files/base-bldgtype-mf-whole-building-common-spaces.xml
index b752501b74..16376daa1d 100644
--- a/workflow/sample_files/base-bldgtype-mf-whole-building-common-spaces.xml
+++ b/workflow/sample_files/base-bldgtype-mf-whole-building-common-spaces.xml
@@ -229,7 +229,7 @@
electricity
- 12000.0
+ 24000.0
Percent
1.0
@@ -1246,7 +1246,7 @@
electricity
- 12000.0
+ 24000.0
Percent
1.0
@@ -1949,7 +1949,7 @@
electricity
- 12000.0
+ 24000.0
Percent
1.0
diff --git a/workflow/tests/util.rb b/workflow/tests/util.rb
index e7635f1b67..cfc4de415b 100644
--- a/workflow/tests/util.rb
+++ b/workflow/tests/util.rb
@@ -423,7 +423,7 @@ def _verify_outputs(rundir, hpxml_path, results, hpxml, unit_multiplier)
end
assert_equal(0, num_unused_objects)
# FIXME: Common space test file has 2 unused schedule by dropping the othersidecoefficient object, which contains schedules
- assert_equal((hpxml_path.include? 'base-bldgtype-mf-whole-building-common-spaces') ? 4 : 0, num_unused_schedules)
+ assert_equal(0, num_unused_schedules) unless (hpxml_path.include? 'base-bldgtype-mf-whole-building-common-spaces')
assert_equal(0, num_unused_constructions)
# Check for Output:Meter and Output:Variable warnings