Skip to content
12 changes: 6 additions & 6 deletions lib/measures/tbd/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>tbd_measure</name>
<uid>8890787b-8c25-4dc8-8641-b6be1b6c2357</uid>
<version_id>833dbf17-e51f-43c7-9c8b-a79ef0e6bd3b</version_id>
<version_modified>2026-02-03T13:53:54Z</version_modified>
<version_id>4934dc58-443d-436f-9358-2d221b4de65a</version_id>
<version_modified>2026-04-17T10:47:02Z</version_modified>
<xml_checksum>99772807</xml_checksum>
<class_name>TBDMeasure</class_name>
<display_name>Thermal Bridging and Derating - TBD</display_name>
Expand Down Expand Up @@ -499,7 +499,7 @@
<filename>geo.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>9CA80CEB</checksum>
<checksum>AD9546E1</checksum>
</file>
<file>
<filename>geometry.rb</filename>
Expand All @@ -523,7 +523,7 @@
<filename>psi.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>B9FB5E02</checksum>
<checksum>9F7B97ED</checksum>
</file>
<file>
<filename>tbd.rb</filename>
Expand All @@ -541,13 +541,13 @@
<filename>ua.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>D3A2A391</checksum>
<checksum>0FB3F654</checksum>
</file>
<file>
<filename>utils.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>118F3A32</checksum>
<checksum>26EC8C4F</checksum>
</file>
<file>
<filename>version.rb</filename>
Expand Down
35 changes: 22 additions & 13 deletions lib/measures/tbd/resources/geo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,16 @@ def properties(surface = nil, argh = {})
return invalid("#{nom} normal", mth, 0, ERR) unless n

type = surface.surfaceType.downcase
facing = surface.outsideBoundaryCondition
facing = surface.outsideBoundaryCondition.downcase
interz = false
setpts = setpoints(space)

if facing.downcase == "surface"
empty = surface.adjacentSurface.empty?
return invalid("#{nom}: adjacent surface", mth, 0, ERR) if empty
if facing == "surface"
adj = surface.adjacentSurface
return invalid("#{nom}: adjacent surface", mth, 0, ERR) if adj.empty?

facing = surface.adjacentSurface.get.nameString
facing = adj.get.nameString
interz = true
end

unless surface.construction.empty?
Expand All @@ -315,8 +317,9 @@ def properties(surface = nil, argh = {})
unless lc.empty?
lc = lc.get
lyr = insulatingLayer(lc)
idx = lyr[:index]

if lyr[:index].is_a?(Integer) && lyr[:index].between?(0, lc.numLayers - 1)
if idx.is_a?(Integer) && idx.between?(0, lc.numLayers - 1)
surf[:construction] = lc
# index: ... of layer/material (to derate) within construction
# ltype: either :massless (RSi) or :standard (k + d)
Expand Down Expand Up @@ -358,8 +361,14 @@ def properties(surface = nil, argh = {})
surf[:story ] = story.get unless story.empty?
surf[:n ] = n
surf[:gross ] = surface.grossArea
surf[:filmRSI ] = surface.filmResistance
surf[:spandrel ] = spandrel?(surface)
surf[:filmRSI ] = surface.filmResistance

if interz
typ = :ceiling # interzone roof or ceiling
typ = :partition if surf[:type] == :wall
surf[:filmRSI] = TBD.filmResistances(typ, surface.tilt)
end

surface.subSurfaces.sort_by { |s| s.nameString }.each do |s|
next if poly(s).empty?
Expand Down Expand Up @@ -508,7 +517,7 @@ def properties(surface = nil, argh = {})
end

unless u.is_a?(Numeric)
r = rsi(c, surface.filmResistance)
r = rsi(c, surf[:filmRSI])

if r < TOL
log(ERR, "Skipping '#{id}': U-factor unavailable (#{mth})")
Expand Down Expand Up @@ -831,7 +840,7 @@ def kiva(model = nil, walls = {}, floors = {}, edges = {})
edge[:surfaces].keys.each do |id|
next unless floors.key?(id)

next unless floors[id][:boundary].downcase == "foundation"
next unless floors[id][:boundary] == "foundation"
next if floors[id].key?(:kiva)

# Initially set as slab-on-grade. Track 'exposed foundation perimeter'.
Expand All @@ -845,7 +854,7 @@ def kiva(model = nil, walls = {}, floors = {}, edges = {})
edge[:surfaces].keys.each do |i|
next if i == id
next unless walls.key?(i)
next unless walls[i][:boundary].downcase == "foundation"
next unless walls[i][:boundary] == "foundation"
next if walls[i].key?(:kiva)

floors[id][:kiva ] = :basement
Expand All @@ -857,7 +866,7 @@ def kiva(model = nil, walls = {}, floors = {}, edges = {})
edge[:surfaces].keys.each do |i|
next if i == id
next unless walls.key?(i)
next unless walls[i][:boundary].downcase == "outdoors"
next unless walls[i][:boundary] == "outdoors"

floors[id][:exposed] += edge[:length]
end
Expand All @@ -872,7 +881,7 @@ def kiva(model = nil, walls = {}, floors = {}, edges = {})
e[:surfaces].keys.each do |ii|
next if i == ii
next unless walls.key?(ii)
next unless walls[ii][:boundary].downcase == "foundation"
next unless walls[ii][:boundary] == "foundation"
next if walls[ii].key?(:kiva)

floors[id][:kiva ] = :basement
Expand All @@ -883,7 +892,7 @@ def kiva(model = nil, walls = {}, floors = {}, edges = {})
e[:surfaces].keys.each do |ii|
next if i == ii
next unless walls.key?(ii)
next unless walls[ii][:boundary].downcase == "outdoors"
next unless walls[ii][:boundary] == "outdoors"

floors[id][:exposed] += e[:length]
end
Expand Down
30 changes: 14 additions & 16 deletions lib/measures/tbd/resources/psi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1424,8 +1424,10 @@ def derate(id = "", s = {}, lc = nil)
m = m.clone(model).to_MasslessOpaqueMaterial.get
m.setName("#{id} #{up}m tbd")

de_r = RMIN unless de_r > RMIN
loss = (de_u - 1 / de_r) * net unless de_r > RMIN
if de_r < RMIN
de_r = RMIN
loss = (de_u - 1 / de_r) * net
end

unless m.setThermalResistance(de_r)
return invalid("Can't derate #{id}: RSi#{de_r.round(2)}", mth)
Expand Down Expand Up @@ -1555,7 +1557,7 @@ def process(model = nil, argh = {})
next unless surface[:conditioned]
next if surface[:ground ]

unless surface[:boundary].downcase == "outdoors"
unless surface[:boundary] == "outdoors"
next unless tbd[:surfaces].key?(surface[:boundary])
next if tbd[:surfaces][surface[:boundary]][:conditioned]
end
Expand Down Expand Up @@ -2202,7 +2204,7 @@ def process(model = nil, argh = {})
next if holes.key?(i)
next if shades.key?(i)

facing = tbd[:surfaces][i][:boundary].downcase
facing = tbd[:surfaces][i][:boundary]
next unless facing == "othersidecoefficients"

s1 = edge[:surfaces][id]
Expand Down Expand Up @@ -2971,6 +2973,7 @@ def process(model = nil, argh = {})
# derate a construction/material pair having " tbd" in their OpenStudio name.
tbd[:surfaces].each do |id, surface|
next unless surface.key?(:construction)
next unless surface.key?(:filmRSI)
next unless surface.key?(:index)
next unless surface.key?(:ltype)
next unless surface.key?(:r)
Expand All @@ -2981,8 +2984,7 @@ def process(model = nil, argh = {})
s = model.getSurfaceByName(id)
next if s.empty?

s = s.get

s = s.get
index = surface[:index ]
current_c = surface[:construction]
c = current_c.clone(model).to_LayeredConstruction.get
Expand All @@ -2995,7 +2997,7 @@ def process(model = nil, argh = {})
if m
c.setLayer(index, m)
c.setName("#{id} c tbd")
current_R = rsi(current_c, s.filmResistance)
current_R = rsi(current_c, surface[:filmRSI])

# In principle, the derated "ratio" could be calculated simply by
# accessing a surface's uFactor. Yet air layers within constructions
Expand Down Expand Up @@ -3035,7 +3037,7 @@ def process(model = nil, argh = {})

# Compute updated RSi value from layers.
updated_c = s.construction.get.to_LayeredConstruction.get
updated_R = rsi(updated_c, s.filmResistance)
updated_R = rsi(updated_c, surface[:filmRSI])
ratio = -(current_R - updated_R) * 100 / current_R

surface[:ratio] = ratio if ratio.abs > TOL
Expand All @@ -3047,14 +3049,10 @@ def process(model = nil, argh = {})
tbd[:surfaces].each do |id, surface|
next unless surface[:deratable]
next unless surface.key?(:construction)
next unless surface.key?(:filmRSI)
next if surface.key?(:u)

s = model.getSurfaceByName(id)
msg = "Skipping missing surface '#{id}' (#{mth})"
log(ERR, msg) if s.empty?
next if s.empty?

surface[:u] = 1.0 / rsi(surface[:construction], s.get.filmResistance)
surface[:u] = 1.0 / rsi(surface[:construction], surface[:filmRSI])
end

json[:io][:edges] = []
Expand Down Expand Up @@ -3204,8 +3202,8 @@ def exit(runner = nil, argh = {})

uo = format("%.3f", g[:uo])
ut = format("%.3f", g[:ut])
output = "An initial #{label.to_s} Uo of #{uo} W/m2•K is required to " \
"achieve an overall Ut of #{ut} W/m2•K for #{g[:op]}"
output = "An area-weighted #{label.to_s} Uo of #{uo} W/m2•K is " \
"required to meet an overall Ut of #{ut} W/m2•K for #{g[:op]}"
u_t << output
runner.registerInfo(output)
end
Expand Down
Loading
Loading