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

Initializes nrcan_446 branch: skylights & wells #1854

Open
wants to merge 10 commits into
base: nrcan
Choose a base branch
from
Open

Conversation

brgix
Copy link
Collaborator

@brgix brgix commented Nov 25, 2024

Pull request overview

Description

BTAP is currently limited in how/where it adds NECB-required skylights in OpenStudio models (e.g. SRR 5% for NECB2011). Outstanding limitations include sloped roofs, narrow/concave roof surfaces, plenum/attic cases (requiring skylight wells). These initial limitations are understandable, given the challenges of dealing with odd or finely-grained geometry - no walk in the park. Yet without a general solution, BTAP remains unable to quantify (to the full extent) energy/carbon savings (or more commonly penalties) from code-required skylights in NECB reference buildings.

Approach

The solution is part of the OSut gem, a TBD dependency (TBD is now a standard OpenStudio gem, and is distributed with BTAP/OpenStudio-Standards). OSut's addSkylights method tackles the above limitations. It can fail with invalid geometry (e.g. some of the DND models!), or if model geometry is, let's say, unreasonable (e.g. just a bunch of closets, or where all roof surfaces are heavily tessellated or subdivided as strips the width of a desk). This unfortunately may happen with 3rd-party models! Easiest to consult related OSut PRs, here & here.

Testing Plan

OSut's addSkylights is regularly tested in isolation: e.g. OSut RSpec, and recently stress-tested on ALL DND projects (private repo, some BTAP staff have access). Specific BTAP unit tests are nonetheless needed. Existing regression tests will need to be updated, as the implications of a general solution are somewhat far reaching (e.g. automatic photocell placement, heating/cooling sizing, resulting energy use + carbon emissions).

Related issues

This work may solve the concerns raised here & here - not sure.

Pull Request Author

  • Method changes or additions
  • Data changes or additions
  • Added tests for added methods
  • If methods have been deprecated, update rest of code to use the new methods
  • Documented new methods using yard syntax
  • Resolved yard documentation errors for new code (ran bundle exec rake doc)
  • Resolved rubocop syntax errors for new code (ran bundle exec rake rubocop)
  • All new and existing tests passes
  • If the code adds new require statements, ensure these are in core ruby or add to the gemspec

Review Checklist

This will not be exhaustively relevant to every PR.

  • Perform a code review on GitHub
  • All related changes have been implemented: method additions, changes, tests
  • If fixing a defect, verify by running develop branch and reproducing defect, then running PR and reproducing fix
  • If a new feature, test the new feature and try creative ways to break it
  • CI status: all green or justified

@brgix brgix requested a review from ckirney November 25, 2024 17:03
@brgix brgix self-assigned this Nov 25, 2024
@@ -1502,7 +1502,7 @@ def initialize(model = nil, argh = {})
next unless construction[:stypes ] == stypes
next if construction[:surfaces].empty?

construction[:surfaces].values.each { |surface| surface.setConstruction(construction[:uo]) }
# construction[:surfaces].values.each { |surface| surface.setConstruction(construction[:uo]) }
Copy link
Collaborator Author

@brgix brgix Dec 6, 2024

Choose a reason for hiding this comment

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

Not sure what this call is attempting, but commenting it out allows BTAP thermal bridging tests to pass. A revised skylight/well distribution would affect (linear) thermal bridging calculations down the line ...

@@ -51,7 +51,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'rubyXL', '~> 3.4'
spec.add_development_dependency 'simplecov', '0.22.0'
spec.add_development_dependency 'yard', '~> 0.9'
spec.add_runtime_dependency 'tbd'
spec.add_runtime_dependency 'tbd', '~> 3.4.4'
Copy link
Collaborator Author

@brgix brgix Dec 10, 2024

Choose a reason for hiding this comment

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

The autozone sizing runs rely on an older OpenStudio linking to a much older OSut gem:

osut (0.2.8) ':/ruby/2.7.0/gems/osut-0.2.8'

Updating the gemspec and Gemfile.lock files to set the minimum TBD & OSut gem versions. This is not fixing the observed issue, yet keeping it here as a reminder.


EDIT: mainly a local gem management issue - fixed. That said, OpenStudio & Standards would be better off with:

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

See here- good!

# It distinguishes between plenums and other conditioned spaces. It uses only the non-plenum roof area to
# calculate the maximum skylight area to be applied to the building.
#
# OPTION B: Selected if srr_opt == 'osut'. OSut's 'addSkylights' attempts to meet the requested SRR% target, even if
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Method apply_max_srr_nrcan now offers two options. By default, OSut's addSkylights is not called (srr_opt: is defaulted to an empty string). If instead set to 'osut', BTAP would rely on addSkylights to match the requested :srr_lim, either to match NECB fixed SRR% requirements, or some user-set SRR%.

# the requested skylight area, often by 10% to 15%. This makes it unfair
# for NECBs, and more challenging when dealing with skylight wells. This
# issue only applies with attics - not plenums. Trim down SRR if required.
target = (srr_lim * graX / gra0) * graX
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OSut's addSkylights yields better results if smaller utility/technical rooms (e.g. narrow corridors, WCs) are first set aside. If feasible, addSkylights prioritizes larger spaces that aren't daylit.

hospital

Hospital prototype

For this reason, it is preferable to send as argument the actual SRR-based skylight area (addSkyLights(spaces, {area: target})), rather than the initial SRR (addSkyLights(spaces, {srr: 0.05)). In both example addSkylights calls here, spaces is a subset of model.getSpaces.

Gross roof area (GRA) calculations should exclude (unconditioned) attic roof overhangs, such as in the SmallOffice prototype model. The calculated skylight area target is adjusted if attic overhangs are detected. No overhangs (i.e. graX/gra0 equals unity)? No adjustment.

types = types.reject { |tp| tp.nameString.downcase.include?("shower" ) }
types = types.reject { |tp| tp.nameString.downcase.include?("washroom" ) }
types = types.reject { |tp| tp.nameString.downcase.include?("corr" ) }
types = types.reject { |tp| tp.nameString.downcase.include?("stair" ) }
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Spacetype string patterns that are currently proposed to filter out utility/technical rooms.

skm2 = skys.sum(&:grossArea)

unless skm2.round == target.round
msg = "Skylights m2: failed to meet #{target.round} (vs #{skm2.round})"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Challenging model geometry (e.g. invalid geometry, tiny rooms, heavily tessellated roof surfaces) is usually behind the rare cases where addSkylights fails to meet the requested target area. Skylights (and if required, skylight wells) may still be added - yet possibly insufficient in number to reach the target.

neduc_roof

NorthernEducation BTAP prototype, meeting the requested building 5% SRR.

neduc_nroof

Same NorthernEducation BTAP prototype, yet without rendering plenum roof surfaces - only skylight well walls

srr = case template
when 'NECB2020' then 0.02
when 'NECB2017' then 0.02
else 0.05 # e.g. NECB2011, NECB2015
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For now, only testing NECB2011 5% SRR (toughest target to meet).

# SRR% of 4.5%. The effective 'ratio' would vary based on geometry,
# e.g. larger building footprint, wider overhangs.
ratio = gra0.round > graX.round ? skm2 / graX : skm2 / gra0
assert(ratio.round(2) == srr, "BTAP/OSut: Incorrect SRR (#{cas})?")
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Although the requested SRR-based target area is reduced when dealing with (unconditioned) attic roof overhangs (e.g. 4.5% instead of the requested 5%), it's always possible to validate whether a requested SRR of 5% is met by dividing skylight area by the effective gross roof area.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

todo : ratio = gra0.round > graX.round ? skm2 / graX : skm2 / gra0 should of course boil down to ratio = skm2 / graX.

assert(log.key?(:level), "BTAP/OSut: log level (#{cas})?")
assert(log.key?(:message), "BTAP/OSut: log message (#{cas})?")
next if log[:level] < 1 # 'INFO'
next unless log.include?("(OSut::addSkylights)")
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Internal OSut logs can be accessed, ideally by filtering for appended (OSut::addSkylights) in the log messages.

# 'NorthernEducation',
# 'QuickServiceRestaurant',
'QuickServiceRestaurant',
# 'SmallOffice'
Copy link
Collaborator Author

@brgix brgix Dec 11, 2024

Choose a reason for hiding this comment

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

Currently having an issue with reported skylight area when dealing with the SmallOffice (attic roof overhangs). This works when tested in isolation (i.e. outside of OpenStudio-Standards/BTAP), so likely an unintended bug somewhere - my bad.


EDIT: Yup - error fixed.

# 'SmallOffice'
]

# NOTE: Skipping NorthernEducation for now:
# - Standards.Model.rb:5432: warning: instance variable @space_type_map not initialized
# - model works in isolation (e.g. SDK 3.6.1, 3.7.0, 3.8.0)
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 NorthernEducation model works with addSkylights just fine in isolation. This is likely more of a BTAP prototype family thing. Setting aside for now.

model.getSubSurfaces.each do |sub|
next unless sub.subSurfaceType.downcase == "skylight"

skm2 += sub.grossArea
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's more reasonable - slaps forehead.

@@ -776,6 +776,7 @@ def apply_max_srr_nrcan(model:, srr_lim:, srr_opt: '')
sub_surface_create_scaled_subsurfaces_from_surface(surface: roof, area_fraction: srr_lim, construction: skylight_construct_set)
end
else # OPTION B
TBD.clean!
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

OSut piggy-backs on top of TBD. It shares the same internal logger. Clean reset.

@@ -810,7 +811,7 @@ def apply_max_srr_nrcan(model:, srr_lim:, srr_opt: '')
# the requested skylight area, often by 10% to 15%. This makes it unfair
# for NECBs, and more challenging when dealing with skylight wells. This
# issue only applies with attics - not plenums. Trim down SRR if required.
target = (srr_lim * graX / gra0) * graX
target = (srr_lim * graX / gra0) * gra0
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Silly mistake.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

todo: target = (srr_lim * graX / gra0) * gra0 should of course be simply target = srr_lim * graX.

@@ -827,6 +828,7 @@ def apply_max_srr_nrcan(model:, srr_lim:, srr_opt: '')
spaces = spaces.select { |sp| types.include?(sp.spaceType.get) }

TBD.addSkyLights(spaces, {area: target})
self.osut[:logs] = TBD.logs
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hold on to OSut logged messages, in case.

# - Standards.Model.rb:5432: warning: instance variable @space_type_map not initialized
# - model works in isolation (e.g. SDK 3.6.1, 3.7.0, 3.8.0)
# Minitest::UnexpectedError: RuntimeError: validation of model failed.
# ... /openstudio-standards/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb:714:in `apply_loads'
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Keep (invalid?) NorthernEducation model aside.


ratio = skm2 / graX
Copy link
Collaborator Author

@brgix brgix Jan 14, 2025

Choose a reason for hiding this comment

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

Reducing equation - simpler to communicate (see here). No change in results.

@@ -811,7 +811,7 @@ def apply_max_srr_nrcan(model:, srr_lim:, srr_opt: '')
# the requested skylight area, often by 10% to 15%. This makes it unfair
# for NECBs, and more challenging when dealing with skylight wells. This
# issue only applies with attics - not plenums. Trim down SRR if required.
target = (srr_lim * graX / gra0) * gra0
target = srr_lim * graX
Copy link
Collaborator Author

@brgix brgix Jan 14, 2025

Choose a reason for hiding this comment

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

Reducing equation - oversight (see here). No change in results.

ckirney and others added 2 commits January 14, 2025 11:34
@@ -1502,7 +1502,8 @@ def initialize(model = nil, argh = {})
next unless construction[:stypes ] == stypes
next if construction[:surfaces].empty?

# construction[:surfaces].values.each { |surface| surface.setConstruction(construction[:uo]) }
BTAP::Geometry::Surfaces.set_surfaces_construction_conductance(construction[:surfaces].values, construction[:uo])
# construction[:surfaces].values.each { |surface| surface.setConstruction(construction[:uo]) }
Copy link
Collaborator Author

@brgix brgix Jan 15, 2025

Choose a reason for hiding this comment

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

Reintroducing the BTAP::Geometry::Surfaces.set_surfaces_construction_conductance call. This had changed about a year ago with this commit. However, the change couldn't have worked given that construction[:uo] is simply a U-factor (in W/K.m2). Reverting to the original solution for now.

TBD doesn't need to have uprated, clear-field U-factors reset for constructions this way. At the time, keeping track (within the BTAP-generated OSM) of the uprated construction U-factors was deemed useful for documentation/costing purposes. This can be achieved otherwise within BTAP. Other changes are expected over the next weeks, given the upcoming structure/construction changes to BTAP. So this may change.

Note that this is unrelated to skylight wells, and the change should be introduced under a new (or another existing) pull request. TODO.

@@data[MASS2][ :bad][:jamb ] = { psi: 0.350 }
@@data[MASS2][ :bad][:sill ] = { psi: 0.350 }
@@data[MASS2][ :bad][:fenestration] = { psi: 0.350 }
@@data[MASS2][ :bad][:door ] = { psi: 0.000 }
Copy link
Collaborator Author

@brgix brgix Jan 21, 2025

Choose a reason for hiding this comment

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

The need to distinguish fenestrated vs opaque subsurfaces is described in this PR. Committed changes in related branch nrcan_372 have largely been merged here. The initial PR and branch nrcan_372 will likely be closed without merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants