Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 68 additions & 57 deletions lib/osut/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,14 @@ def genConstruction(model = nil, specs = {})
chk = @@uo.keys.include?(specs[:type])
return invalid("surface type", mth, 2, ERR) unless chk

specs[:uo] = @@uo[ specs[:type] ] unless specs.key?(:uo)
specs[:uo] = @@uo[ specs[:type] ] unless specs.key?(:uo) # can be nil
u = specs[:uo]

if u
return mismatch("#{id} Uo", u, Numeric, mth) unless u.is_a?(Numeric)
return invalid("#{id} Uo (> 5.678)", mth, 2, ERR) if u > 5.678
return negative("#{id} Uo" , mth, ERR) if u < 0
return mismatch("#{id} Uo", u, Numeric, mth) unless u.is_a?(Numeric)
return invalid("#{id} Uo (> 5.678)", mth, 2, ERR) if u > 5.678
return zero("#{id} Uo", mth, ERR) if u.round(2) == 0.00
return negative("#{id} Uo", mth, ERR) if u < 0
end

# Optional specs. Log/reset if invalid.
Expand Down Expand Up @@ -466,14 +467,14 @@ def genConstruction(model = nil, specs = {})
a[:compo ][:d ] = d
a[:compo ][:id ] = "OSut|#{mt}|#{format('%03d', d*1000)[-3..-1]}"
when :window
a[:glazing][:u ] = specs[:uo ]
a[:glazing][:u ] = u ? u : @@uo[:window]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

A user could set a glazing assembly's specs["uo"] to nil. This is caught and reset here.

a[:glazing][:shgc] = 0.450
a[:glazing][:shgc] = specs[:shgc] if specs.key?(:shgc)
a[:glazing][:id ] = "OSut|window"
a[:glazing][:id ] += "|U#{format('%.1f', a[:glazing][:u])}"
a[:glazing][:id ] += "|SHGC#{format('%d', a[:glazing][:shgc]*100)}"
when :skylight
a[:glazing][:u ] = specs[:uo ]
a[:glazing][:u ] = u ? u : @@uo[:skylight]
a[:glazing][:shgc] = 0.450
a[:glazing][:shgc] = specs[:shgc] if specs.key?(:shgc)
a[:glazing][:id ] = "OSut|skylight"
Expand All @@ -482,25 +483,11 @@ def genConstruction(model = nil, specs = {})
end

# Initiate layers.
glazed = true
glazed = false if a[:glazing].empty?
layers = OpenStudio::Model::OpaqueMaterialVector.new unless glazed
layers = OpenStudio::Model::FenestrationMaterialVector.new if glazed
unglazed = a[:glazing].empty? ? true : false

if glazed
u = a[:glazing][:u ]
shgc = a[:glazing][:shgc]
lyr = model.getSimpleGlazingByName(a[:glazing][:id])

if lyr.empty?
lyr = OpenStudio::Model::SimpleGlazing.new(model, u, shgc)
lyr.setName(a[:glazing][:id])
else
lyr = lyr.get
end
if unglazed
layers = OpenStudio::Model::OpaqueMaterialVector.new

layers << lyr
else
# Loop through each layer spec, and generate construction.
a.each do |i, l|
next if l.empty?
Expand All @@ -524,44 +511,68 @@ def genConstruction(model = nil, specs = {})

layers << lyr
end
else
layers = OpenStudio::Model::FenestrationMaterialVector.new

u0 = a[:glazing][:u ]
shgc = a[:glazing][:shgc]
lyr = model.getSimpleGlazingByName(a[:glazing][:id])

if lyr.empty?
lyr = OpenStudio::Model::SimpleGlazing.new(model, u0, shgc)
lyr.setName(a[:glazing][:id])
else
lyr = lyr.get
end

layers << lyr
end

c = OpenStudio::Model::Construction.new(layers)
c.setName(id)

# Adjust insulating layer thickness or conductivity to match requested Uo.
unless glazed
ro = 0
ro = 1 / specs[:uo] - @@film[ specs[:type] ] if specs[:uo]

if specs[:type] == :door # 1x layer, adjust conductivity
layer = c.getLayer(0).to_StandardOpaqueMaterial
return invalid("#{id} standard material?", mth, 0) if layer.empty?

layer = layer.get
k = layer.thickness / ro
layer.setConductivity(k)
elsif ro > 0 # multiple layers, adjust insulating layer thickness
lyr = insulatingLayer(c)
return invalid("#{id} construction", mth, 0) if lyr[:index].nil?
return invalid("#{id} construction", mth, 0) if lyr[:type ].nil?
return invalid("#{id} construction", mth, 0) if lyr[:r ].zero?

index = lyr[:index]
layer = c.getLayer(index).to_StandardOpaqueMaterial
return invalid("#{id} material @#{index}", mth, 0) if layer.empty?

layer = layer.get
k = layer.conductivity
d = (ro - rsi(c) + lyr[:r]) * k
return invalid("#{id} adjusted m", mth, 0) if d < 0.03

nom = "OSut|"
nom += layer.nameString.gsub(/[^a-z]/i, "").gsub("OSut", "")
nom += "|"
nom += format("%03d", d*1000)[-3..-1]
layer.setName(nom) if model.getStandardOpaqueMaterialByName(nom).empty?
layer.setThickness(d)
if u and unglazed
ro = 1 / u - film

if ro > 0

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For opaque construction, adjusting insulating layer thickness (to meet a requested assembly Uo factor) is skipped entirely if the requested Uo isn't:

  • Numeric
  • within postulated (acceptable) range

if specs[:type] == :door # 1x layer, adjust conductivity
layer = c.getLayer(0).to_StandardOpaqueMaterial
return invalid("#{id} standard material?", mth, 0) if layer.empty?

layer = layer.get
k = layer.thickness / ro
layer.setConductivity(k)
else # multiple layers, adjust insulating layer thickness
lyr = insulatingLayer(c)
return invalid("#{id} construction", mth, 0) if lyr[:index].nil?
return invalid("#{id} construction", mth, 0) if lyr[:type ].nil?
return invalid("#{id} construction", mth, 0) if lyr[:r ].zero?

index = lyr[:index]
layer = c.getLayer(index).to_StandardOpaqueMaterial
return invalid("#{id} material @#{index}", mth, 0) if layer.empty?

layer = layer.get
k = layer.conductivity
d = (ro - rsi(c) + lyr[:r]) * k
return invalid("#{id} adjusted m", mth, 0) if d < 0.03

nom = "OSut|"
nom += layer.nameString.gsub(/[^a-z]/i, "").gsub("OSut", "")
nom += "|"
nom += format("%03d", d*1000)[-3..-1]

lyr = model.getStandardOpaqueMaterialByName(nom)

if lyr.empty?
layer.setName(nom)
layer.setThickness(d)
else
omat = lyr.get
c.setLayer(index, omat)
end
end
end
end

Expand Down Expand Up @@ -746,7 +757,7 @@ def genMass(sps = OpenStudio::Model::SpaceVector.new, ratio = 2.0)
# Validates if a default construction set holds a base construction.
#
# @param set [OpenStudio::Model::DefaultConstructionSet] a default set
# @param bse [OpensStudio::Model::ConstructionBase] a construction base
# @param bse [OpenStudio::Model::ConstructionBase] a construction base
# @param gr [Bool] if ground-facing surface
# @param ex [Bool] if exterior-facing surface
# @param tp [#to_s] a surface type
Expand Down Expand Up @@ -1048,7 +1059,7 @@ def insulatingLayer(lc = nil)
return invalid("lc", mth, 1, DBG, res) unless lc.respond_to?(NS)

id = lc.nameString
return mismatch(id, lc, cl1, mth, DBG, res) unless lc.is_a?(cl)
return mismatch(id, lc, cl, mth, DBG, res) unless lc.is_a?(cl)

lc.layers.each do |m|
unless m.to_MasslessOpaqueMaterial.empty?
Expand Down
40 changes: 35 additions & 5 deletions spec/osut_tests_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,22 @@
u = 1 / cls1::rsi(surface)
expect(u).to be_within(TOL).of(uo9)

# Invalid Uo (here, skylights and windows inherit default Uo values)
specs = {type: :skylight, uo: nil}
surface = cls1::genConstruction(model, specs)
expect(surface).to be_a(OpenStudio::Model::LayeredConstruction)
expect(surface.layers.size).to eq(1)
u = 1 / cls1::rsi(surface)
expect(u).to be_within(TOL).of(uo[:skylight])

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In the case of fenestrated assemblies, an invalid Uo request is ignored and the glazing assembly inherits a default OSut Uo (here 3.5 W/m2.K for a skylight).


# Invalid Uo (here, Uo-adjustments are ignored altogether)
specs = {type: :wall, uo: nil}
surface = cls1::genConstruction(model, specs)
expect(surface).to be_a(OpenStudio::Model::LayeredConstruction)
expect(surface.layers.size).to eq(4)
u = 1 / cls1::rsi(surface)
expect(u).to be_within(TOL).of(2.23) # not matching any defaults

expect(cls1.status.zero?).to be true
end

Expand Down Expand Up @@ -790,8 +806,12 @@
expect(model).to_not be_empty
model = model.get

m = "OSut::insulatingLayer"
m1 = "Invalid 'lc' arg #1 (#{m})"
m = "OSut::insulatingLayer"
cl1 = OpenStudio::Model::Surface
cl2 = OpenStudio::Model::LayeredConstruction
n1 = "Entryway Wall 1"
m1 = "Invalid 'lc' arg #1 (#{m})"
m2 = "'#{n1}' #{cl1}? expecting #{cl2} (#{m})"

model.getLayeredConstructions.each do |lc|
lyr = mod1.insulatingLayer(lc)
Expand Down Expand Up @@ -838,7 +858,6 @@
end

lyr = mod1.insulatingLayer(nil)
expect(mod1.debug?).to be true
expect(lyr[:index]).to be_nil
expect(lyr[:type ]).to be_nil
expect(lyr[:r ]).to be_zero
Expand All @@ -848,7 +867,6 @@

expect(mod1.clean!).to eq(DBG)
lyr = mod1.insulatingLayer("")
expect(mod1.debug?).to be true
expect(lyr[:index]).to be_nil
expect(lyr[:type ]).to be_nil
expect(lyr[:r ]).to be_zero
Expand All @@ -858,13 +876,25 @@

expect(mod1.clean!).to eq(DBG)
lyr = mod1.insulatingLayer(model)
expect(mod1.debug?).to be true
expect(lyr[:index]).to be_nil
expect(lyr[:type ]).to be_nil
expect(lyr[:r ]).to be_zero
expect(mod1.debug?).to be true
expect(mod1.logs.size).to eq(1)
expect(mod1.logs.first[:message]).to eq(m1)

eWall1 = model.getSurfaceByName(n1)
expect(eWall1).to_not be_empty
eWall1 = eWall1.get

expect(mod1.clean!).to eq(DBG)
lyr = mod1.insulatingLayer(eWall1)
expect(lyr[:index]).to be_nil
expect(lyr[:type ]).to be_nil
expect(lyr[:r ]).to be_zero
expect(mod1.debug?).to be true
expect(mod1.logs.size).to eq(1)
expect(mod1.logs.first[:message]).to eq(m2)
end

it "checks for spandrels" do
Expand Down