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 building MF Common Spaces #1850

Draft
wants to merge 22 commits into
base: master
Choose a base branch
from
Draft

Conversation

yzhou601
Copy link
Collaborator

@yzhou601 yzhou601 commented Oct 4, 2024

Pull Request Description

  • Allow specifying unconditioned common spaces in a multi-family building as separate Building elements.
  • Allow modeling interzonal surface heat transfer between conditioned and unconditioned spaces using sameas attribute.
  • Figure out solution for sameas attribute -- SystemIdentifier attributes hpxmlwg/hpxml#399

Checklist

Not all may apply:

  • Schematron validator (EPvalidator.xml) has been updated
  • Sample files have been added/updated (openstudio tasks.rb update_hpxmls)
  • Tests have been added/updated (e.g., HPXMLtoOpenStudio/tests/test*.rb and/or workflow/tests/test*.rb)
  • Documentation has been updated
  • Changelog has been updated
  • openstudio tasks.rb update_measures has been run
  • No unexpected changes to simulation results of sample files

@yzhou601 yzhou601 self-assigned this Oct 4, 2024
@@ -779,21 +779,38 @@ def self.merge_unit_models(model, hpxml_osm_map)
end
end

hpxml_osm_map.values.each_with_index do |unit_model, unit_number|
unit_surface_to_obj_index_map = {} # map of unit model surface handle to whole building model object index
Copy link
Collaborator Author

@yzhou601 yzhou601 Oct 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shorowit Sadly that there's not a better solution to keep the surface-to-surface mapping information while knowing the handles after merging unit models (and I cannot do it in front, by assigning a surface in another model space as adjacent surface). The only solution I can think of here is to use the indexes after calling model.addObjects. OS source codes specifically call out that the order will be kept, so it's fine for now, but we need to be careful if this is called more than once in the future.
Tried a hundred times and this one seems to be working, still pretty promising!

Copy link
Contributor

@shorowit shorowit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great to see some progress! A couple initial questions/thoughts.

HPXMLtoOpenStudio/resources/geometry.rb Outdated Show resolved Hide resolved
HPXMLtoOpenStudio/resources/model.rb Outdated Show resolved Hide resolved
@yzhou601
Copy link
Collaborator Author

yzhou601 commented Oct 4, 2024

The approach seems working! 🎉 Going to make this PR more concrete next.

Comment on lines 698 to 702
<sch:rule context='/h:HPXML/h:Building/h:BuildingDetails/h:Enclosure/h:RimJoists/h:RimJoist[h:SystemIdentifier/@sameas]'>
<sch:assert role='ERROR' test='count(h:ExteriorAdjacentTo) = 0'>Expected 0 element(s) for xpath: ExteriorAdjacentTo</sch:assert>
<sch:assert role='ERROR' test='count(h:InteriorAdjacentTo) = 0'>Expected 0 element(s) for xpath: InteriorAdjacentTo</sch:assert>
<sch:assert role='ERROR' test='count(h:Area) = 0'>Expected 0 element(s) for xpath: Area</sch:assert>
<sch:assert role='ERROR' test='count(h:Insulation/h:AssemblyEffectiveRValue) = 0'>Expected 0 element(s) for xpath: Insulation/AssemblyEffectiveRValue</sch:assert>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we simply use count(*) or similar to prevent any detailed inputs? We'll also want a test_validation.rb test that shows these Schematron errors getting hit.

@@ -941,6 +940,21 @@ def self.merge_unit_models(model, hpxml_osm_map)
end
end
end

model_objects.each do |obj|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently it feels a bit more complicated than I would have expected just because you're operating on the objects of the final merged model, so you have to know which surfaces are linked and what space to attach them to. I wonder if this can be simplified.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest a few commit tried to simplify it a bit further (first one makes it working). I removed two methods in the hpxml.rb and only keeps the sameas which returns the fully described object. I stored the adjacent surface ids directly to the OS:Space additional properties, and I stored all the HPXML ids to OS:Surface additional properties. In this way, after merging, I can use OS:Space to find the OS:Surface. It's more straightforward and less coding compared to previous approach. However, the adjacency still happens after merging, I think this execution point is determined in the first place, mainly because that the OS:Surface and the OS:Space is not in the same workspace before merging, so it's not able to create adjacent surface using OS:Space outside the scope. Let me know if you have any thoughts.

@yzhou601
Copy link
Collaborator Author

yzhou601 commented Oct 24, 2024

One more remaining question related to calling individual dwelling unit (wrote down in case I forgot): Should we enforce the surfaces with full descriptions to be specified in conditioned dwelling units so that they can be called alone? Technically for surfaces connecting two Building elements, users can specify the surface full description in either Building element, which will make the call of running individual dwelling unit error-prone. We can either:

  1. Only allow whole building simulation when sameas is provided, then the surface inputs can be specified in either Building element, or:
  2. Only allow surface full descriptions all in conditioned units, and the user can call building ids of conditioned units to simulate individual dwelling units

Honestly I was implementing assuming the former in the first place, considering how MulTEA uses this feature (and floors are always specified as floors(vs. celing) in MulTEA inputs, so the full descriptions can be in the unconditioned Building element), but I'm fine with the second for more flexibility.

<sch:assert role='ERROR' test='count(h:Roofs/h:Roof[h:InteriorAdjacentTo="conditioned space"]) + count(h:Floors/h:Floor[h:InteriorAdjacentTo="conditioned space" and (h:ExteriorAdjacentTo="attic - vented" or h:ExteriorAdjacentTo="attic - unvented" or ((h:ExteriorAdjacentTo="other housing unit" or h:ExteriorAdjacentTo="other heated space" or h:ExteriorAdjacentTo="other multifamily buffer space" or h:ExteriorAdjacentTo="other non-freezing space") and h:FloorOrCeiling="ceiling"))]) &gt;= 1'>There must be at least one ceiling or roof adjacent to conditioned space.</sch:assert>
<sch:assert role='ERROR' test='count(h:Walls/h:Wall[h:InteriorAdjacentTo="conditioned space" and h:ExteriorAdjacentTo="outside"]) &gt;= 1'>There must be at least one exterior wall adjacent to conditioned space.</sch:assert>
<sch:assert role='ERROR' test='count(h:Slabs/h:Slab[contains(h:InteriorAdjacentTo, "conditioned")]) + count(h:Floors/h:Floor[h:InteriorAdjacentTo="conditioned space" and not(h:ExteriorAdjacentTo="attic - vented" or h:ExteriorAdjacentTo="attic - unvented" or ((h:ExteriorAdjacentTo="other housing unit" or h:ExteriorAdjacentTo="other heated space" or h:ExteriorAdjacentTo="other multifamily buffer space" or h:ExteriorAdjacentTo="other non-freezing space") and h:FloorOrCeiling="ceiling"))]) &gt;= 1'>There must be at least one floor or slab adjacent to conditioned space.</sch:assert>
<sch:assert role='ERROR' test='count(h:Roofs/h:Roof[h:InteriorAdjacentTo="conditioned space"]) + count(h:Floors/h:Floor[h:InteriorAdjacentTo="conditioned space" and (h:ExteriorAdjacentTo="attic - vented" or h:ExteriorAdjacentTo="attic - unvented" or ((h:ExteriorAdjacentTo="other housing unit" or h:ExteriorAdjacentTo="other heated space" or h:ExteriorAdjacentTo="other multifamily buffer space" or h:ExteriorAdjacentTo="other non-freezing space") and h:FloorOrCeiling="ceiling"))]) + count(h:Floors/h:Floor[h:SystemIdentifier/@sameas]) &gt;= 1'>There must be at least one ceiling or roof adjacent to conditioned space.</sch:assert>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I relaxed this rule to count "sameas" floor also as a ceiling/floor(two lines below), but we have no better knowledge of whether it's a floor or ceiling, or if user only specifies one of them. Let me know if any better thought.

<sch:assert role='ERROR' test='count(h:Walls/h:Wall[h:InteriorAdjacentTo="conditioned space" and h:ExteriorAdjacentTo="outside"]) &gt;= 1'>There must be at least one exterior wall adjacent to conditioned space.</sch:assert>
<sch:assert role='ERROR' test='count(h:Slabs/h:Slab[contains(h:InteriorAdjacentTo, "conditioned")]) + count(h:Floors/h:Floor[h:InteriorAdjacentTo="conditioned space" and not(h:ExteriorAdjacentTo="attic - vented" or h:ExteriorAdjacentTo="attic - unvented" or ((h:ExteriorAdjacentTo="other housing unit" or h:ExteriorAdjacentTo="other heated space" or h:ExteriorAdjacentTo="other multifamily buffer space" or h:ExteriorAdjacentTo="other non-freezing space") and h:FloorOrCeiling="ceiling"))]) &gt;= 1'>There must be at least one floor or slab adjacent to conditioned space.</sch:assert>
<sch:assert role='ERROR' test='count(h:Roofs/h:Roof[h:InteriorAdjacentTo="conditioned space"]) + count(h:Floors/h:Floor[h:InteriorAdjacentTo="conditioned space" and (h:ExteriorAdjacentTo="attic - vented" or h:ExteriorAdjacentTo="attic - unvented" or ((h:ExteriorAdjacentTo="other housing unit" or h:ExteriorAdjacentTo="other heated space" or h:ExteriorAdjacentTo="other multifamily buffer space" or h:ExteriorAdjacentTo="other non-freezing space") and h:FloorOrCeiling="ceiling"))]) + count(h:Floors/h:Floor[h:SystemIdentifier/@sameas]) &gt;= 1'>There must be at least one ceiling or roof adjacent to conditioned space.</sch:assert>
<sch:assert role='ERROR' test='count(h:Walls/h:Wall[h:InteriorAdjacentTo="conditioned space" and h:ExteriorAdjacentTo="outside"]) + count(h:FoundationWalls/h:FoundationWall[h:InteriorAdjacentTo="conditioned space" and h:ExteriorAdjacentTo="ground"]) + count(h:Walls/h:Wall[h:SystemIdentifier/@sameas]) + count(h:FoundationWalls/h:FoundationWall[h:SystemIdentifier/@sameas]) &gt;= 1'>There must be at least one exterior wall or foundation wall adjacent to conditioned space.</sch:assert> <!-- FIXME: Should we relax this restriction for internal zones? -->
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A basement can be a separate Building element, So I added foundation wall here too.

@shorowit shorowit changed the title Whole building Common Spaces Whole building MF Common Spaces Nov 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In progress
Development

Successfully merging this pull request may close these issues.

2 participants