Skip to content

Commit

Permalink
change the approach to create surfaces at unit models to avoid compon…
Browse files Browse the repository at this point in the history
…ent load error (not sum to total delivered) due to missing surfaces. Increase heating capacity to avoid unmet load
  • Loading branch information
yzhou601 committed Nov 21, 2024
1 parent 5d6080a commit 0872523
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 58 deletions.
8 changes: 4 additions & 4 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>2bb7d5cd-06e3-406b-9eb0-96dfe7e2c6d2</version_id>
<version_modified>2024-11-19T01:12:59Z</version_modified>
<version_id>d26078da-8691-4396-af39-a5cb563e1407</version_id>
<version_modified>2024-11-21T02:54:20Z</version_modified>
<xml_checksum>D8922A73</xml_checksum>
<class_name>HPXMLtoOpenStudio</class_name>
<display_name>HPXML to OpenStudio Translator</display_name>
Expand Down Expand Up @@ -345,7 +345,7 @@
<filename>geometry.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>945B3E0D</checksum>
<checksum>3DBAF60E</checksum>
</file>
<file>
<filename>hotwater_appliances.rb</filename>
Expand Down Expand Up @@ -447,7 +447,7 @@
<filename>model.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>ECCE7792</checksum>
<checksum>ED1985F0</checksum>
</file>
<file>
<filename>output.rb</filename>
Expand Down
88 changes: 52 additions & 36 deletions HPXMLtoOpenStudio/resources/geometry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand All @@ -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 = []

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 = []

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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')
Expand All @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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.
#
Expand Down
22 changes: 9 additions & 13 deletions HPXMLtoOpenStudio/resources/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion workflow/hpxml_inputs.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
<ElectricResistance/>
</HeatingSystemType>
<HeatingSystemFuel>electricity</HeatingSystemFuel>
<HeatingCapacity>12000.0</HeatingCapacity>
<HeatingCapacity>24000.0</HeatingCapacity>
<AnnualHeatingEfficiency>
<Units>Percent</Units>
<Value>1.0</Value>
Expand Down Expand Up @@ -1246,7 +1246,7 @@
<ElectricResistance/>
</HeatingSystemType>
<HeatingSystemFuel>electricity</HeatingSystemFuel>
<HeatingCapacity>12000.0</HeatingCapacity>
<HeatingCapacity>24000.0</HeatingCapacity>
<AnnualHeatingEfficiency>
<Units>Percent</Units>
<Value>1.0</Value>
Expand Down Expand Up @@ -1949,7 +1949,7 @@
<ElectricResistance/>
</HeatingSystemType>
<HeatingSystemFuel>electricity</HeatingSystemFuel>
<HeatingCapacity>12000.0</HeatingCapacity>
<HeatingCapacity>24000.0</HeatingCapacity>
<AnnualHeatingEfficiency>
<Units>Percent</Units>
<Value>1.0</Value>
Expand Down
2 changes: 1 addition & 1 deletion workflow/tests/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 0872523

Please sign in to comment.