tbd 3.5.2 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -106,26 +106,28 @@ module OSut
106
106
  # default inside + outside air film resistances (m2.K/W)
107
107
  @@film = {
108
108
  shading: 0.000, # NA
109
- partition: 0.150, # uninsulated wood- or steel-framed wall
110
- wall: 0.150, # un/insulated wall
111
- roof: 0.140, # un/insulated roof
112
- floor: 0.190, # un/insulated (exposed) floor
113
- basement: 0.120, # un/insulated basement wall
114
- slab: 0.160, # un/insulated basement slab or slab-on-grade
109
+ ceiling: 0.266, # interzone floor/ceiling
110
+ partition: 0.239, # interzone wall partition
111
+ wall: 0.150, # exposed wall
112
+ roof: 0.135, # exposed roof
113
+ floor: 0.192, # exposed floor
114
+ basement: 0.120, # basement wall
115
+ slab: 0.162, # basement slab or slab-on-grade
115
116
  door: 0.150, # standard, 45mm insulated steel (opaque) door
116
117
  window: 0.150, # vertical fenestration, e.g. glazed doors, windows
117
- skylight: 0.140 # e.g. domed 4' x 4' skylight
118
+ skylight: 0.135 # e.g. domed 4' x 4' skylight
118
119
  }.freeze
119
120
 
120
121
  # default (~1980s) envelope Uo (W/m2•K), based on surface type
121
122
  @@uo = {
122
123
  shading: nil, # N/A
124
+ ceiling: nil, # N/A
123
125
  partition: nil, # N/A
124
126
  wall: 0.384, # rated R14.8 hr•ft2F/Btu
125
127
  roof: 0.327, # rated R17.6 hr•ft2F/Btu
126
128
  floor: 0.317, # rated R17.9 hr•ft2F/Btu (exposed floor)
127
- basement: nil,
128
- slab: nil,
129
+ basement: nil, # N/A
130
+ slab: nil, # N/A
129
131
  door: 1.800, # insulated, unglazed steel door (single layer)
130
132
  window: 2.800, # e.g. patio doors (simple glazing)
131
133
  skylight: 3.500 # all skylight technologies
@@ -197,6 +199,61 @@ module OSut
197
199
  @@mats[:door ][:rho] = 600.000
198
200
  @@mats[:door ][:cp ] = 1000.000
199
201
 
202
+ ##
203
+ # Returns surface air film resistance(s). Surface tilt-dependent values are
204
+ # returned if a valid surface tilt [0, PI] is provided. Otherwise, generic
205
+ # tilt-independent air film resistances are returned instead.
206
+ #
207
+ # @param [:to_sym] surface type, e.g. :roof, :wall, :partition, :ceiling
208
+ # @param [Numeric] surface tilt (in rad), optional
209
+ #
210
+ # @return [Float] surface air film resistance(s)
211
+ # @return [0.0] if invalid input (see logs)
212
+ def filmResistances(type = :wall, tilt = 2 * Math::PI)
213
+ mth = "OSut::#{__callee__}"
214
+
215
+ unless tilt.is_a?(Numeric)
216
+ return mismatch("tilt", tilt, Float, mth, DBG, 0.0)
217
+ end
218
+
219
+ unless type.respond_to?(:to_sym)
220
+ return mismatch("type", type, Symbol, mth, DBG, 0.0)
221
+ end
222
+
223
+ type = type.to_s.downcase.to_sym
224
+
225
+ unless @@film.key?(type)
226
+ return invalid("type", mth, 1, DBG, 0.0)
227
+ end
228
+
229
+ # Generic, tilt-independent values.
230
+ r = @@film[type]
231
+ return r if type == :shading
232
+
233
+ # Valid tilt?
234
+ if tilt.between?(0, Math::PI)
235
+ r = OpenStudio::Model::PlanarSurface.stillAirFilmResistance(tilt)
236
+ return r if type == :basement || type == :slab
237
+
238
+ if type == :ceiling || type == :partition
239
+ # Interzone. Fetch reciprocal tilt, e.g. if tilt == 0°, tiltx = 180°
240
+ tiltx = tilt + Math::PI
241
+
242
+ # Assuming tilt is contrained [0°, 180°] - constrain tiltx [0° 180°]:
243
+ # e.g. tiltx == 210° if tilt == 30°, so convert tiltx to 150°
244
+ # e.g. tiltx == 330° if tilt == 150°, so convert tiltx to 30°
245
+ # e.g. tiltx == 275° if tilt == 95°, so convert tiltx to 85°
246
+ tiltx = Math::PI - tilt if tiltx > Math::PI
247
+
248
+ r += OpenStudio::Model::PlanarSurface.stillAirFilmResistance(tiltx)
249
+ else
250
+ r += 0.03 # "MOVINGAIR_15MPH"
251
+ end
252
+ end
253
+
254
+ r
255
+ end
256
+
200
257
  ##
201
258
  # Validates if every material in a layered construction is standard & opaque.
202
259
  #
@@ -470,7 +527,7 @@ module OSut
470
527
  ##
471
528
  # Resets a construction's Uo factor by adjusting its insulating layer
472
529
  # thermal conductivity, then if needed its thickness (or its RSi value if
473
- # massless). Unless material uniquness is requested, a matching material is
530
+ # massless). Unless material uniqueness is requested, a matching material is
474
531
  # recovered instead of instantiating a new one. The latter is renamed
475
532
  # according to its adjusted conductivity/thickness (or RSi value).
476
533
  #
@@ -600,10 +657,6 @@ module OSut
600
657
  return mismatch("model", model, cl1, mth) unless model.is_a?(cl1)
601
658
  return mismatch("specs", specs, cl2, mth) unless specs.is_a?(cl2)
602
659
 
603
- specs[:id] = "" unless specs.key?(:id)
604
- id = trim(specs[:id])
605
- id = "OSut:CON:#{specs[:type]}" if id.empty?
606
-
607
660
  if specs.key?(:type)
608
661
  unless @@uo.keys.include?(specs[:type])
609
662
  return invalid("surface type", mth, 2, ERR)
@@ -612,6 +665,10 @@ module OSut
612
665
  specs[:type] = :wall
613
666
  end
614
667
 
668
+ specs[:id] = "" unless specs.key?(:id)
669
+ id = trim(specs[:id])
670
+ id = "OSut:CON:#{specs[:type]}" if id.empty?
671
+
615
672
  specs[:uo] = @@uo[ specs[:type] ] unless specs.key?(:uo) # can be nil
616
673
  u = specs[:uo]
617
674
 
@@ -652,6 +709,35 @@ module OSut
652
709
  a[:compo][:mat] = @@mats[mt]
653
710
  a[:compo][:d ] = d
654
711
  a[:compo][:id ] = "OSut:#{mt}:#{format('%03d', d*1000)[-3..-1]}"
712
+ when :ceiling
713
+ unless specs[:clad] == :none
714
+ mt = :concrete
715
+ mt = :material if specs[:clad] == :light
716
+ d = 0.015
717
+ d = 0.100 if specs[:clad] == :medium
718
+ d = 0.200 if specs[:clad] == :heavy
719
+ a[:clad][:mat] = @@mats[mt]
720
+ a[:clad][:d ] = d
721
+ a[:clad][:id ] = "OSut:#{mt}:#{format('%03d', d*1000)[-3..-1]}"
722
+ end
723
+
724
+ mt = :mineral
725
+ mt = :polyiso if specs[:frame] == :medium
726
+ mt = :cellulose if specs[:frame] == :heavy
727
+ mt = :material unless u
728
+ d = 0.100
729
+ d = 0.015 unless u
730
+ a[:compo][:mat] = @@mats[mt]
731
+ a[:compo][:d ] = d
732
+ a[:compo][:id ] = "OSut:#{mt}:#{format('%03d', d*1000)[-3..-1]}"
733
+
734
+ unless specs[:finish] == :none
735
+ mt = :material
736
+ d = 0.015
737
+ a[:finish][:mat] = @@mats[mt]
738
+ a[:finish][:d ] = d
739
+ a[:finish][:id ] = "OSut:#{mt}:#{format('%03d', d*1000)[-3..-1]}"
740
+ end
655
741
  when :partition
656
742
  unless specs[:clad] == :none
657
743
  d = 0.015
data/lib/tbd/geo.rb CHANGED
@@ -299,14 +299,16 @@ module TBD
299
299
  return invalid("#{nom} normal", mth, 0, ERR) unless n
300
300
 
301
301
  type = surface.surfaceType.downcase
302
- facing = surface.outsideBoundaryCondition
302
+ facing = surface.outsideBoundaryCondition.downcase
303
+ interz = false
303
304
  setpts = setpoints(space)
304
305
 
305
- if facing.downcase == "surface"
306
- empty = surface.adjacentSurface.empty?
307
- return invalid("#{nom}: adjacent surface", mth, 0, ERR) if empty
306
+ if facing == "surface"
307
+ adj = surface.adjacentSurface
308
+ return invalid("#{nom}: adjacent surface", mth, 0, ERR) if adj.empty?
308
309
 
309
- facing = surface.adjacentSurface.get.nameString
310
+ facing = adj.get.nameString
311
+ interz = true
310
312
  end
311
313
 
312
314
  unless surface.construction.empty?
@@ -315,8 +317,9 @@ module TBD
315
317
  unless lc.empty?
316
318
  lc = lc.get
317
319
  lyr = insulatingLayer(lc)
320
+ idx = lyr[:index]
318
321
 
319
- if lyr[:index].is_a?(Integer) && lyr[:index].between?(0, lc.numLayers - 1)
322
+ if idx.is_a?(Integer) && idx.between?(0, lc.numLayers - 1)
320
323
  surf[:construction] = lc
321
324
  # index: ... of layer/material (to derate) within construction
322
325
  # ltype: either :massless (RSi) or :standard (k + d)
@@ -358,8 +361,14 @@ module TBD
358
361
  surf[:story ] = story.get unless story.empty?
359
362
  surf[:n ] = n
360
363
  surf[:gross ] = surface.grossArea
361
- surf[:filmRSI ] = surface.filmResistance
362
364
  surf[:spandrel ] = spandrel?(surface)
365
+ surf[:filmRSI ] = surface.filmResistance
366
+
367
+ if interz
368
+ typ = :ceiling # interzone roof or ceiling
369
+ typ = :partition if surf[:type] == :wall
370
+ surf[:filmRSI] = TBD.filmResistances(typ, surface.tilt)
371
+ end
363
372
 
364
373
  surface.subSurfaces.sort_by { |s| s.nameString }.each do |s|
365
374
  next if poly(s).empty?
@@ -508,7 +517,7 @@ module TBD
508
517
  end
509
518
 
510
519
  unless u.is_a?(Numeric)
511
- r = rsi(c, surface.filmResistance)
520
+ r = rsi(c, surf[:filmRSI])
512
521
 
513
522
  if r < TOL
514
523
  log(ERR, "Skipping '#{id}': U-factor unavailable (#{mth})")
@@ -831,7 +840,7 @@ module TBD
831
840
  edge[:surfaces].keys.each do |id|
832
841
  next unless floors.key?(id)
833
842
 
834
- next unless floors[id][:boundary].downcase == "foundation"
843
+ next unless floors[id][:boundary] == "foundation"
835
844
  next if floors[id].key?(:kiva)
836
845
 
837
846
  # Initially set as slab-on-grade. Track 'exposed foundation perimeter'.
@@ -845,7 +854,7 @@ module TBD
845
854
  edge[:surfaces].keys.each do |i|
846
855
  next if i == id
847
856
  next unless walls.key?(i)
848
- next unless walls[i][:boundary].downcase == "foundation"
857
+ next unless walls[i][:boundary] == "foundation"
849
858
  next if walls[i].key?(:kiva)
850
859
 
851
860
  floors[id][:kiva ] = :basement
@@ -857,7 +866,7 @@ module TBD
857
866
  edge[:surfaces].keys.each do |i|
858
867
  next if i == id
859
868
  next unless walls.key?(i)
860
- next unless walls[i][:boundary].downcase == "outdoors"
869
+ next unless walls[i][:boundary] == "outdoors"
861
870
 
862
871
  floors[id][:exposed] += edge[:length]
863
872
  end
@@ -872,7 +881,7 @@ module TBD
872
881
  e[:surfaces].keys.each do |ii|
873
882
  next if i == ii
874
883
  next unless walls.key?(ii)
875
- next unless walls[ii][:boundary].downcase == "foundation"
884
+ next unless walls[ii][:boundary] == "foundation"
876
885
  next if walls[ii].key?(:kiva)
877
886
 
878
887
  floors[id][:kiva ] = :basement
@@ -883,7 +892,7 @@ module TBD
883
892
  e[:surfaces].keys.each do |ii|
884
893
  next if i == ii
885
894
  next unless walls.key?(ii)
886
- next unless walls[ii][:boundary].downcase == "outdoors"
895
+ next unless walls[ii][:boundary] == "outdoors"
887
896
 
888
897
  floors[id][:exposed] += e[:length]
889
898
  end
data/lib/tbd/psi.rb CHANGED
@@ -1424,8 +1424,10 @@ module TBD
1424
1424
  m = m.clone(model).to_MasslessOpaqueMaterial.get
1425
1425
  m.setName("#{id} #{up}m tbd")
1426
1426
 
1427
- de_r = RMIN unless de_r > RMIN
1428
- loss = (de_u - 1 / de_r) * net unless de_r > RMIN
1427
+ if de_r < RMIN
1428
+ de_r = RMIN
1429
+ loss = (de_u - 1 / de_r) * net
1430
+ end
1429
1431
 
1430
1432
  unless m.setThermalResistance(de_r)
1431
1433
  return invalid("Can't derate #{id}: RSi#{de_r.round(2)}", mth)
@@ -1555,7 +1557,7 @@ module TBD
1555
1557
  next unless surface[:conditioned]
1556
1558
  next if surface[:ground ]
1557
1559
 
1558
- unless surface[:boundary].downcase == "outdoors"
1560
+ unless surface[:boundary] == "outdoors"
1559
1561
  next unless tbd[:surfaces].key?(surface[:boundary])
1560
1562
  next if tbd[:surfaces][surface[:boundary]][:conditioned]
1561
1563
  end
@@ -2202,7 +2204,7 @@ module TBD
2202
2204
  next if holes.key?(i)
2203
2205
  next if shades.key?(i)
2204
2206
 
2205
- facing = tbd[:surfaces][i][:boundary].downcase
2207
+ facing = tbd[:surfaces][i][:boundary]
2206
2208
  next unless facing == "othersidecoefficients"
2207
2209
 
2208
2210
  s1 = edge[:surfaces][id]
@@ -2971,6 +2973,7 @@ module TBD
2971
2973
  # derate a construction/material pair having " tbd" in their OpenStudio name.
2972
2974
  tbd[:surfaces].each do |id, surface|
2973
2975
  next unless surface.key?(:construction)
2976
+ next unless surface.key?(:filmRSI)
2974
2977
  next unless surface.key?(:index)
2975
2978
  next unless surface.key?(:ltype)
2976
2979
  next unless surface.key?(:r)
@@ -2981,8 +2984,7 @@ module TBD
2981
2984
  s = model.getSurfaceByName(id)
2982
2985
  next if s.empty?
2983
2986
 
2984
- s = s.get
2985
-
2987
+ s = s.get
2986
2988
  index = surface[:index ]
2987
2989
  current_c = surface[:construction]
2988
2990
  c = current_c.clone(model).to_LayeredConstruction.get
@@ -2995,7 +2997,7 @@ module TBD
2995
2997
  if m
2996
2998
  c.setLayer(index, m)
2997
2999
  c.setName("#{id} c tbd")
2998
- current_R = rsi(current_c, s.filmResistance)
3000
+ current_R = rsi(current_c, surface[:filmRSI])
2999
3001
 
3000
3002
  # In principle, the derated "ratio" could be calculated simply by
3001
3003
  # accessing a surface's uFactor. Yet air layers within constructions
@@ -3035,7 +3037,7 @@ module TBD
3035
3037
 
3036
3038
  # Compute updated RSi value from layers.
3037
3039
  updated_c = s.construction.get.to_LayeredConstruction.get
3038
- updated_R = rsi(updated_c, s.filmResistance)
3040
+ updated_R = rsi(updated_c, surface[:filmRSI])
3039
3041
  ratio = -(current_R - updated_R) * 100 / current_R
3040
3042
 
3041
3043
  surface[:ratio] = ratio if ratio.abs > TOL
@@ -3047,14 +3049,10 @@ module TBD
3047
3049
  tbd[:surfaces].each do |id, surface|
3048
3050
  next unless surface[:deratable]
3049
3051
  next unless surface.key?(:construction)
3052
+ next unless surface.key?(:filmRSI)
3050
3053
  next if surface.key?(:u)
3051
3054
 
3052
- s = model.getSurfaceByName(id)
3053
- msg = "Skipping missing surface '#{id}' (#{mth})"
3054
- log(ERR, msg) if s.empty?
3055
- next if s.empty?
3056
-
3057
- surface[:u] = 1.0 / rsi(surface[:construction], s.get.filmResistance)
3055
+ surface[:u] = 1.0 / rsi(surface[:construction], surface[:filmRSI])
3058
3056
  end
3059
3057
 
3060
3058
  json[:io][:edges] = []
@@ -3204,8 +3202,8 @@ module TBD
3204
3202
 
3205
3203
  uo = format("%.3f", g[:uo])
3206
3204
  ut = format("%.3f", g[:ut])
3207
- output = "An initial #{label.to_s} Uo of #{uo} W/m2•K is required to " \
3208
- "achieve an overall Ut of #{ut} W/m2•K for #{g[:op]}"
3205
+ output = "An area-weighted #{label.to_s} Uo of #{uo} W/m2•K is " \
3206
+ "required to meet an overall Ut of #{ut} W/m2•K for #{g[:op]}"
3209
3207
  u_t << output
3210
3208
  runner.registerInfo(output)
3211
3209
  end