diff --git a/lib/osut/utils.rb b/lib/osut/utils.rb index c9148b9..17eea0f 100644 --- a/lib/osut/utils.rb +++ b/lib/osut/utils.rb @@ -1028,7 +1028,7 @@ def rsi(lc = nil, film = 0.0, t = 0.0) end ## - # Identify a layered construction's (opaque) insulating layer. The method + # Identifies a layered construction's (opaque) insulating layer. The method # returns a 3-keyed hash :index, the insulating layer index [0, n layers) # within the layered construction; :type, either :standard or :massless; and # :r, material thermal resistance in m2•K/W. @@ -1082,6 +1082,36 @@ def insulatingLayer(lc = nil) res end + ## + # Validates whether opaque surface can be considered as a curtain wall (or + # similar technology) spandrel, regardless of construction layers, by looking + # up AdditionalProperties or its identifier. + # + # @param s [OpenStudio::Model::Surface] an opaque surface + # + # @return [Bool] whether surface can be considered 'spandrel' + # @return [false] if invalid input (see logs) + def spandrel?(s = nil) + mth = "OSut::#{__callee__}" + cl = OpenStudio::Model::Surface + return invalid("surface", mth, 1, DBG, false) unless s.respond_to?(NS) + + id = s.nameString + m1 = "#{id}:spandrel" + m2 = "#{id}:spandrel:boolean" + + if s.additionalProperties.hasFeature("spandrel") + val = s.additionalProperties.getFeatureAsBoolean("spandrel") + return invalid(m1, mth, 1, ERR, false) if val.empty? + + val = val.get + return invalid(m2, mth, 1, ERR, false) unless [true, false].include?(val) + return val + end + + id.downcase.include?("spandrel") + end + # ---- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---- # # ---- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---- # # This next set of utilities (~850 lines) help distinguish spaces that are @@ -1546,8 +1576,8 @@ def maxHeatScheduledSetpoint(zone = nil) # # @param model [OpenStudio::Model::Model] a model # - # @return [Bool] wether model holds valid heating temperature setpoints - # @return [false] false if invalid input (see logs) + # @return [Bool] whether model holds valid heating temperature setpoints + # @return [false] if invalid input (see logs) def heatingTemperatureSetpoints?(model = nil) mth = "OSut::#{__callee__}" cl = OpenStudio::Model::Model @@ -1784,7 +1814,7 @@ def vestibule?(space = nil) id = space.nameString m1 = "#{id}:vestibule" - m1 = "#{id}:vestibule boolean" + m2 = "#{id}:vestibule:boolean" if space.additionalProperties.hasFeature("vestibule") val = space.additionalProperties.getFeatureAsBoolean("vestibule") @@ -2814,7 +2844,7 @@ def height(pts = nil) # @param flat [Bool] whether points are to be pre-flattened (Z=0) # # @return [Bool] whether 1st polygon fits within the 2nd polygon - # @return [false] false if invalid input (see logs) + # @return [false] if invalid input (see logs) def fits?(p1 = nil, p2 = nil, flat = true) mth = "OSut::#{__callee__}" p1 = poly(p1, false, true, false) diff --git a/spec/osut_tests_spec.rb b/spec/osut_tests_spec.rb index c6a6382..58149a5 100644 --- a/spec/osut_tests_spec.rb +++ b/spec/osut_tests_spec.rb @@ -678,6 +678,57 @@ expect(mod1.logs.first[:message]).to eq(m1) end + it "checks for spandrels" do + translator = OpenStudio::OSVersion::VersionTranslator.new + expect(mod1.clean!).to eq(DBG) + + file = File.join(__dir__, "files/osms/in/seb.osm") + path = OpenStudio::Path.new(file) + model = translator.loadModel(path) + expect(model).to_not be_empty + model = model.get + + office_walls = [] + # Smalloffice 1 Wall 1 + # Smalloffice 1 Wall 2 + # Smalloffice 1 Wall 6 + plenum_walls = [] + # Level0 Small office 1 Ceiling Plenum AbvClgPlnmWall 6 + # Level0 Small office 1 Ceiling Plenum AbvClgPlnmWall 2 + # Level0 Small office 1 Ceiling Plenum AbvClgPlnmWall 1 + + model.getSurfaces.each do |s| + next unless s.outsideBoundaryCondition.downcase == "outdoors" + next unless s.surfaceType.downcase == "wall" + + expect(mod1.spandrel?(s)).to be false + + if s.nameString.downcase.include?("smalloffice 1") + office_walls << s + elsif s.nameString.downcase.include?("small office 1 ceiling plenum") + plenum_walls << s + end + end + + expect(office_walls.size).to eq(3) + expect(plenum_walls.size).to eq(3) + expect(mod1.status).to be_zero + + # Tag Small Office walls (& plenum walls) in SEB as 'spandrels'. + tag = "spandrel" + + (office_walls + plenum_walls).each do |wall| + expect(wall.additionalProperties.setFeature(tag, true)).to be true + expect(wall.additionalProperties.hasFeature(tag)).to be true + prop = wall.additionalProperties.getFeatureAsBoolean(tag) + expect(prop).to_not be_empty + expect(prop.get).to be true + expect(mod1.spandrel?(wall)).to be true + end + + expect(mod1.status).to be_zero + end + it "checks scheduleRulesetMinMax (from within class instances)" do translator = OpenStudio::OSVersion::VersionTranslator.new expect(cls1.level).to eq(DBG)