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.
- checksums.yaml +4 -4
- data/lib/measures/tbd/measure.xml +6 -6
- data/lib/measures/tbd/resources/geo.rb +22 -13
- data/lib/measures/tbd/resources/psi.rb +14 -16
- data/lib/measures/tbd/resources/ua.rb +258 -256
- data/lib/measures/tbd/resources/utils.rb +100 -14
- data/lib/tbd/geo.rb +22 -13
- data/lib/tbd/psi.rb +14 -16
- data/lib/tbd/ua.rb +258 -256
- data/lib/tbd/version.rb +1 -1
- metadata +3 -3
|
@@ -25,103 +25,113 @@ module TBD
|
|
|
25
25
|
# Calculates construction Uo (including surface film resistances) to meet Ut.
|
|
26
26
|
#
|
|
27
27
|
# @param model [OpenStudio::Model::Model] a model
|
|
28
|
-
# @param lc [OpenStudio::Model::LayeredConstruction] a layered construction
|
|
29
28
|
# @param id [#to_s] layered construction identifier
|
|
30
|
-
# @param
|
|
29
|
+
# @param lc [OpenStudio::Model::LayeredConstruction] a layered construction
|
|
30
|
+
# @param area [Numeric] net surface area covered by layered construction
|
|
31
31
|
# @param film [Numeric] target surface film resistance, in m2•K/W
|
|
32
|
+
# @param hloss [Numeric] heat loss from major thermal bridging, in W/K
|
|
32
33
|
# @param ut [Numeric] target overall Ut for lc, in W/m2•K
|
|
33
34
|
#
|
|
34
|
-
# @return [
|
|
35
|
-
|
|
36
|
-
def uo(model = nil, lc = nil, id = "", hloss = 0.0, film = 0.0, ut = 0.0)
|
|
35
|
+
# @return [Float] Uo [W/m2•K] required to meet Ut (see logs if 0 or UMIN)
|
|
36
|
+
def uo(id = "", lc = nil, area = 0, film = 0, hloss = 0, ut = 0)
|
|
37
37
|
mth = "TBD::#{__callee__}"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
cl3 = Numeric
|
|
42
|
-
cl4 = String
|
|
38
|
+
cl1 = OpenStudio::Model::LayeredConstruction
|
|
39
|
+
cl2 = Numeric
|
|
40
|
+
cl3 = String
|
|
43
41
|
id = trim(id)
|
|
44
|
-
return mismatch("
|
|
45
|
-
return mismatch("
|
|
46
|
-
return mismatch("
|
|
47
|
-
return mismatch("
|
|
48
|
-
return mismatch("
|
|
49
|
-
return mismatch("Ut" , ut,
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
return mismatch("id" , id, cl3, mth, DBG, 0) if id.empty?
|
|
43
|
+
return mismatch("lc" , lc, cl1, mth, DBG, 0) unless lc.is_a?(cl1)
|
|
44
|
+
return mismatch("area" , area, cl2, mth, DBG, 0) unless area.is_a?(cl2)
|
|
45
|
+
return mismatch("film" , film, cl2, mth, DBG, 0) unless film.is_a?(cl2)
|
|
46
|
+
return mismatch("hloss", hloss, cl2, mth, DBG, 0) unless hloss.is_a?(cl2)
|
|
47
|
+
return mismatch("Ut" , ut, cl2, mth, DBG, 0) unless ut.is_a?(cl2)
|
|
48
|
+
|
|
49
|
+
# Residual heatloss (not assigned) [W/K].
|
|
50
|
+
model = lc.model
|
|
51
|
+
loss = 0
|
|
52
|
+
lyr = insulatingLayer(lc)
|
|
53
|
+
|
|
54
|
+
# Validate insulating layer.
|
|
54
55
|
lyr[:index] = nil unless lyr[:index].is_a?(Numeric)
|
|
55
56
|
lyr[:index] = nil unless lyr[:index] >= 0
|
|
56
57
|
lyr[:index] = nil unless lyr[:index] < lc.layers.size
|
|
57
|
-
return invalid("#{id} layer index", mth, 3,
|
|
58
|
-
return zero("#{id}:
|
|
59
|
-
return
|
|
60
|
-
return zero("#{id}:
|
|
61
|
-
return
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
#
|
|
65
|
-
rt
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
58
|
+
return invalid("#{id} layer index", mth, 3, DBG, 0) unless lyr[:index]
|
|
59
|
+
return zero("#{id}: net area (m2)", mth, DBG, 0) unless area > TOL
|
|
60
|
+
return negative("#{id}: film RSI" , mth, DBG, 0) if film < 0
|
|
61
|
+
return zero("#{id}: heatloss" , mth, DBG, 0) if hloss < TOL
|
|
62
|
+
return zero("#{id}: Ut" , mth, DBG, 0) unless ut > UMIN
|
|
63
|
+
return invalid("#{id}: Ut" , mth, 4, DBG, 0) unless ut < UMAX
|
|
64
|
+
|
|
65
|
+
# Calculate initial layer RSi to initially meet Ut target.
|
|
66
|
+
rt = 1 / ut # target construction Rt
|
|
67
|
+
r0 = rsi(lc, film) # current construction R0
|
|
68
|
+
r = lyr[:r] + rt - r0 # new, un-derated layer RSi
|
|
69
|
+
|
|
70
|
+
# Adjust if below admissible threshold.
|
|
71
|
+
if r < 0
|
|
72
|
+
zero("#{id}: layer RSI", mth, INF)
|
|
73
|
+
r = RMIN
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Uprate to counter heat loss from thermal bridging.
|
|
77
|
+
u = 1 / r
|
|
78
|
+
u -= (hloss / area)
|
|
79
|
+
|
|
80
|
+
# Adjust if beyond admissible range.
|
|
81
|
+
if u < UMIN
|
|
82
|
+
negative("#{id}: new Uo", mth, INF)
|
|
83
|
+
u = UMIN
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
r = 1 / u
|
|
75
87
|
|
|
76
88
|
if lyr[:type] == :massless
|
|
77
89
|
m = lc.getLayer(lyr[:index]).to_MasslessOpaqueMaterial
|
|
78
|
-
return invalid("#{id} massless layer?", mth, 0, DBG,
|
|
90
|
+
return invalid("#{id} massless layer?", mth, 0, DBG, 0) if m.empty?
|
|
79
91
|
|
|
80
92
|
m = m.get.clone(model).to_MasslessOpaqueMaterial.get
|
|
81
93
|
m.setName("#{id} uprated")
|
|
82
94
|
|
|
83
|
-
|
|
84
|
-
|
|
95
|
+
if r < RMIN
|
|
96
|
+
r = RMIN
|
|
97
|
+
loss = (u - 1 / r) * area
|
|
98
|
+
end
|
|
85
99
|
|
|
86
|
-
unless m.setThermalResistance(
|
|
87
|
-
return invalid("Can't uprate #{id}: RSi#{
|
|
100
|
+
unless m.setThermalResistance(r)
|
|
101
|
+
return invalid("Can't uprate #{id}: RSi#{r.round(2)}", mth, 0, DBG, 0)
|
|
88
102
|
end
|
|
89
103
|
else
|
|
90
104
|
m = lc.getLayer(lyr[:index]).to_StandardOpaqueMaterial
|
|
91
|
-
return invalid("#{id} standard layer?", mth, 0, DBG,
|
|
105
|
+
return invalid("#{id} standard layer?", mth, 0, DBG, 0) if m.empty?
|
|
92
106
|
|
|
93
107
|
m = m.get.clone(model).to_StandardOpaqueMaterial.get
|
|
94
108
|
m.setName("#{id} uprated")
|
|
95
109
|
|
|
96
110
|
d = m.thickness
|
|
97
|
-
k = (d /
|
|
98
|
-
d = (k *
|
|
111
|
+
k = (d / r).clamp(KMIN, KMAX)
|
|
112
|
+
d = (k * r).clamp(DMIN, DMAX)
|
|
99
113
|
|
|
100
|
-
loss = (
|
|
114
|
+
loss = (u - k / d) * area if d / k < RMIN
|
|
101
115
|
|
|
102
116
|
unless m.setThermalConductivity(k)
|
|
103
|
-
return invalid("Can't uprate #{id}: K#{k.round(3)}", mth, 0, DBG,
|
|
117
|
+
return invalid("Can't uprate #{id}: K#{k.round(3)}", mth, 0, DBG, 0)
|
|
104
118
|
end
|
|
105
119
|
|
|
106
120
|
unless m.setThickness(d)
|
|
107
|
-
return invalid("Can't uprate #{id}: #{(d*1000).to_i}mm", mth, 0, DBG,
|
|
121
|
+
return invalid("Can't uprate #{id}: #{(d*1000).to_i}mm", mth, 0, DBG, 0)
|
|
108
122
|
end
|
|
109
123
|
end
|
|
110
124
|
|
|
111
|
-
return invalid("Can't ID insulating layer", mth, 0, DBG,
|
|
125
|
+
return invalid("Can't ID insulating layer", mth, 0, DBG, 0) unless m
|
|
112
126
|
|
|
113
127
|
lc.setLayer(lyr[:index], m)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if loss > TOL
|
|
117
|
-
h_loss = format "%.3f", loss
|
|
118
|
-
return invalid("Can't assign #{h_loss} W/K to #{id}", mth, 0, DBG, res)
|
|
119
|
-
end
|
|
128
|
+
ro = rsi(lc, film)
|
|
129
|
+
uo = ro < RMIN ? UMIN : 1 / ro
|
|
120
130
|
|
|
121
|
-
|
|
122
|
-
|
|
131
|
+
h = format "%.3f", loss
|
|
132
|
+
log(INF, "Can't set #{h} W/K to #{id} #{mth}") if loss > TOL
|
|
123
133
|
|
|
124
|
-
|
|
134
|
+
uo
|
|
125
135
|
end
|
|
126
136
|
|
|
127
137
|
##
|
|
@@ -185,230 +195,219 @@ module TBD
|
|
|
185
195
|
groups[:roof ][:ut] = argh[:roof_ut ]
|
|
186
196
|
groups[:floor][:ut] = argh[:floor_ut ]
|
|
187
197
|
|
|
188
|
-
groups[:wall ][:op] = trim(argh[:wall_option
|
|
189
|
-
groups[:roof ][:op] = trim(argh[:roof_option
|
|
190
|
-
groups[:floor][:op] = trim(argh[:floor_option
|
|
198
|
+
groups[:wall ][:op] = trim(argh[:wall_option ])
|
|
199
|
+
groups[:roof ][:op] = trim(argh[:roof_option ])
|
|
200
|
+
groups[:floor][:op] = trim(argh[:floor_option])
|
|
191
201
|
|
|
202
|
+
# Group and process walls, roofs and floors sequentially/independently.
|
|
192
203
|
groups.each do |type, g|
|
|
193
204
|
next unless g[:up]
|
|
194
205
|
next unless g[:ut].is_a?(Numeric)
|
|
195
206
|
next unless g[:ut] < UMAX
|
|
196
|
-
next
|
|
207
|
+
next unless g[:ut] > UMIN
|
|
197
208
|
|
|
198
|
-
typ
|
|
199
|
-
typ
|
|
209
|
+
typ = type
|
|
210
|
+
typ = :ceiling if typ == :roof
|
|
211
|
+
|
|
212
|
+
# Collection of one or several constructions to uprate.
|
|
200
213
|
coll = {}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
op = g[:op].downcase
|
|
206
|
-
all = tout.include?(op)
|
|
207
|
-
|
|
208
|
-
if g[:op].empty?
|
|
209
|
-
log(WRN, "Construction (#{type}) to uprate? (#{mth})")
|
|
210
|
-
elsif all
|
|
214
|
+
op = g[:op]
|
|
215
|
+
|
|
216
|
+
# Uprate ALL constructions of same type, e.g. walls.
|
|
217
|
+
if tout.include?(op.downcase)
|
|
211
218
|
s.each do |nom, surface|
|
|
212
|
-
next unless surface.key?(:deratable
|
|
213
|
-
next unless surface.key?(:type
|
|
219
|
+
next unless surface.key?(:deratable)
|
|
220
|
+
next unless surface.key?(:type)
|
|
214
221
|
next unless surface.key?(:construction)
|
|
215
|
-
next unless surface.key?(:filmRSI
|
|
216
|
-
next unless surface.key?(:
|
|
217
|
-
next unless surface.key?(:
|
|
218
|
-
next unless surface.key?(:
|
|
222
|
+
next unless surface.key?(:filmRSI)
|
|
223
|
+
next unless surface.key?(:ltype)
|
|
224
|
+
next unless surface.key?(:r)
|
|
225
|
+
next unless surface.key?(:index)
|
|
226
|
+
next unless surface.key?(:net)
|
|
219
227
|
next unless surface[:deratable ]
|
|
220
228
|
next unless surface[:type ] == typ
|
|
221
229
|
next unless surface[:construction].is_a?(cl3)
|
|
222
230
|
next if surface[:index ].nil?
|
|
223
231
|
|
|
224
|
-
#
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
id =
|
|
232
|
+
# Collect constructions to uprate.
|
|
233
|
+
lc = surface[:construction]
|
|
234
|
+
id = lc.nameString
|
|
235
|
+
|
|
236
|
+
# Track construction-specific parameters.
|
|
237
|
+
unless coll.key?(id)
|
|
238
|
+
coll[id] = {}
|
|
239
|
+
coll[id][:lc ] = lc
|
|
240
|
+
coll[id][:s ] = {}
|
|
241
|
+
coll[id][:hloss] = 0
|
|
242
|
+
coll[id][:area ] = 0
|
|
243
|
+
coll[id][:film ] = 0
|
|
244
|
+
coll[id][:fA ] = 0
|
|
245
|
+
coll[id][:uA ] = 0
|
|
246
|
+
coll[id][:u0 ] = 0
|
|
237
247
|
end
|
|
238
248
|
|
|
239
|
-
coll[
|
|
240
|
-
coll[
|
|
249
|
+
coll[id][:idx] = surface[:index] unless coll[id].key?(:idx)
|
|
250
|
+
coll[id][:ltp] = surface[:ltype] unless coll[id].key?(:ltp)
|
|
251
|
+
|
|
252
|
+
# Track surface-specific parameters.
|
|
253
|
+
unless coll[id][:s].key?(nom)
|
|
254
|
+
coll[id][:s][nom] = {}
|
|
255
|
+
coll[id][:s][nom][:a] = surface[:net]
|
|
256
|
+
coll[id][:s][nom][:f] = surface[:filmRSI]
|
|
257
|
+
coll[id][:s][nom][:h] = 0
|
|
258
|
+
next unless surface.key?(:heatloss)
|
|
259
|
+
next unless surface[:heatloss].abs > TOL
|
|
260
|
+
|
|
261
|
+
coll[id][:s][nom][:h] = surface[:heatloss]
|
|
262
|
+
end
|
|
241
263
|
end
|
|
242
264
|
else
|
|
243
|
-
id =
|
|
265
|
+
id = op # single, user-selected construction
|
|
244
266
|
lc = model.getConstructionByName(id)
|
|
245
|
-
|
|
246
|
-
|
|
267
|
+
|
|
268
|
+
if lc.empty?
|
|
269
|
+
log(WRN, "Construction '#{id}'? (#{mth})")
|
|
270
|
+
next
|
|
271
|
+
end
|
|
247
272
|
|
|
248
273
|
lc = lc.get.to_LayeredConstruction
|
|
249
|
-
log(WRN, "'#{id}' layered construction? (#{mth})") if lc.empty?
|
|
250
|
-
next if lc.empty?
|
|
251
274
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
275
|
+
if lc.empty?
|
|
276
|
+
log(WRN, "'#{id}' layered construction? (#{mth})")
|
|
277
|
+
next
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
lc = lc.get
|
|
281
|
+
|
|
282
|
+
coll[id] = {}
|
|
283
|
+
coll[id][:lc ] = lc
|
|
284
|
+
coll[id][:s ] = {}
|
|
285
|
+
coll[id][:hloss] = 0
|
|
286
|
+
coll[id][:area ] = 0
|
|
287
|
+
coll[id][:film ] = 0
|
|
288
|
+
coll[id][:fA ] = 0
|
|
289
|
+
coll[id][:uA ] = 0
|
|
290
|
+
coll[id][:u0 ] = 0
|
|
255
291
|
|
|
256
292
|
s.each do |nom, surface|
|
|
257
|
-
next unless surface.key?(:deratable
|
|
258
|
-
next unless surface.key?(:type
|
|
293
|
+
next unless surface.key?(:deratable)
|
|
294
|
+
next unless surface.key?(:type)
|
|
259
295
|
next unless surface.key?(:construction)
|
|
260
|
-
next unless surface.key?(:filmRSI
|
|
261
|
-
next unless surface.key?(:
|
|
262
|
-
next unless surface.key?(:
|
|
263
|
-
next unless surface.key?(:
|
|
296
|
+
next unless surface.key?(:filmRSI)
|
|
297
|
+
next unless surface.key?(:ltype)
|
|
298
|
+
next unless surface.key?(:r)
|
|
299
|
+
next unless surface.key?(:index)
|
|
300
|
+
next unless surface.key?(:net)
|
|
264
301
|
next unless surface[:deratable ]
|
|
265
302
|
next unless surface[:type ] == typ
|
|
266
303
|
next unless surface[:construction].is_a?(cl3)
|
|
304
|
+
next unless surface[:construction].nameString == id
|
|
267
305
|
next if surface[:index ].nil?
|
|
268
306
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
#
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
307
|
+
coll[id][:idx] = surface[:index] unless coll[id].key?(:idx)
|
|
308
|
+
coll[id][:ltp] = surface[:ltype] unless coll[id].key?(:ltp)
|
|
309
|
+
|
|
310
|
+
# Track (for surfaces of targeted type):
|
|
311
|
+
# - net area
|
|
312
|
+
# - air film resistances
|
|
313
|
+
unless coll[id][:s].key?(nom)
|
|
314
|
+
coll[id][:s][nom] = {}
|
|
315
|
+
coll[id][:s][nom][:a] = surface[:net]
|
|
316
|
+
coll[id][:s][nom][:f] = surface[:filmRSI]
|
|
317
|
+
coll[id][:s][nom][:h] = 0
|
|
318
|
+
next unless surface.key?(:heatloss)
|
|
319
|
+
next unless surface[:heatloss].abs > TOL
|
|
320
|
+
|
|
321
|
+
coll[id][:s][nom][:h] = surface[:heatloss]
|
|
322
|
+
end
|
|
276
323
|
end
|
|
277
324
|
end
|
|
278
325
|
|
|
279
326
|
if coll.empty?
|
|
280
|
-
log(WRN, "
|
|
327
|
+
log(WRN, "Unable to uprate #{type} construction - skipping (#{mth})")
|
|
281
328
|
next
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
sss = model.getSurfaceByName(nom)
|
|
310
|
-
next if sss.empty?
|
|
311
|
-
|
|
312
|
-
sss = sss.get
|
|
329
|
+
else
|
|
330
|
+
coll.each do |id, col|
|
|
331
|
+
lc = col[:lc]
|
|
332
|
+
|
|
333
|
+
# Ensure lc is exclusively linked to deratable surfaces of targeted
|
|
334
|
+
# type. If not, assign new lc clone to non-targeted surfaces.
|
|
335
|
+
s.each do |nom, surface|
|
|
336
|
+
next unless surface.key?(:deratable)
|
|
337
|
+
next unless surface.key?(:type)
|
|
338
|
+
next unless surface.key?(:construction)
|
|
339
|
+
next unless surface.key?(:filmRSI)
|
|
340
|
+
next unless surface.key?(:ltype)
|
|
341
|
+
next unless surface.key?(:r)
|
|
342
|
+
next unless surface.key?(:index)
|
|
343
|
+
next unless surface.key?(:net)
|
|
344
|
+
next unless surface[:deratable]
|
|
345
|
+
next unless surface[:construction].is_a?(cl3)
|
|
346
|
+
next unless surface[:construction] == lc
|
|
347
|
+
next if surface[:index ].nil?
|
|
348
|
+
next if surface[:type ] == typ
|
|
349
|
+
next if coll[id][:s].key?(nom)
|
|
350
|
+
|
|
351
|
+
log(INF, "Cloning '#{nom}' construction - not '#{id}' (#{mth})")
|
|
352
|
+
srf = model.getSurfaceByName(nom)
|
|
353
|
+
next if srf.empty?
|
|
354
|
+
|
|
355
|
+
srf = srf.get
|
|
313
356
|
cloned = lc.clone(model).to_LayeredConstruction.get
|
|
314
357
|
cloned.setName("#{nom} - cloned")
|
|
315
|
-
|
|
358
|
+
srf.setConstruction(cloned)
|
|
316
359
|
surface[:construction] = cloned
|
|
317
|
-
coll[id][:s].delete(nom)
|
|
318
360
|
end
|
|
319
361
|
end
|
|
320
362
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
next unless s[nom].key?(:construction)
|
|
328
|
-
next unless s[nom].key?(:index)
|
|
329
|
-
next unless s[nom].key?(:ltype)
|
|
330
|
-
next unless s[nom].key?(:r)
|
|
331
|
-
|
|
332
|
-
# Tally applicable psi+khi.
|
|
333
|
-
hloss += s[nom][:heatloss ] if s[nom].key?(:heatloss)
|
|
334
|
-
next if s[nom][:construction] == lc
|
|
335
|
-
|
|
336
|
-
# Reassign construction unless referencing lc.
|
|
337
|
-
sss = model.getSurfaceByName(nom)
|
|
338
|
-
next if sss.empty?
|
|
339
|
-
|
|
340
|
-
sss = sss.get
|
|
341
|
-
|
|
342
|
-
if sss.isConstructionDefaulted
|
|
343
|
-
set = defaultConstructionSet(sss) # building? story?
|
|
344
|
-
|
|
345
|
-
if set.nil?
|
|
346
|
-
sss.setConstruction(lc)
|
|
347
|
-
else
|
|
348
|
-
constructions = set.defaultExteriorSurfaceConstructions
|
|
349
|
-
|
|
350
|
-
unless constructions.empty?
|
|
351
|
-
constructions = constructions.get
|
|
352
|
-
constructions.setWallConstruction(lc) if typ == :wall
|
|
353
|
-
constructions.setFloorConstruction(lc) if typ == :floor
|
|
354
|
-
constructions.setRoofCeilingConstruction(lc) if typ == :ceiling
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
else
|
|
358
|
-
sss.setConstruction(lc)
|
|
359
|
-
end
|
|
363
|
+
coll.each do |id, col|
|
|
364
|
+
col[:s].values.each do |item|
|
|
365
|
+
col[:hloss] += item[:h]
|
|
366
|
+
col[:area ] += item[:a]
|
|
367
|
+
col[:fA ] += item[:a] / item[:f] unless item[:f] < 0
|
|
368
|
+
end
|
|
360
369
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
s[nom][:r ] = lyr[:r ] # temporary
|
|
370
|
+
if col[:area] < TOL
|
|
371
|
+
empty("#{id} area", mth, WRN)
|
|
372
|
+
next
|
|
365
373
|
end
|
|
366
|
-
end
|
|
367
374
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
next if i == id
|
|
375
|
+
# Area-weighted surface air film resistances.
|
|
376
|
+
col[:film] = 1 / (col[:fA] / col[:area])
|
|
371
377
|
|
|
372
|
-
|
|
373
|
-
|
|
378
|
+
# Fetch required, uprated Uo.
|
|
379
|
+
u = uo(id, col[:lc], col[:area], col[:film], col[:hloss], g[:ut])
|
|
380
|
+
|
|
381
|
+
unless u > UMIN
|
|
382
|
+
log(WRN, "Unable to completely uprate '#{id}' (#{mth})")
|
|
383
|
+
u = UMIN
|
|
374
384
|
end
|
|
375
|
-
end
|
|
376
385
|
|
|
377
|
-
|
|
386
|
+
col[:u ] = u
|
|
387
|
+
col[:uA] = u * col[:area]
|
|
378
388
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
end
|
|
389
|
+
# Recoup uprated construction and insulating layer.
|
|
390
|
+
lc = col[:lc]
|
|
391
|
+
lyr = insulatingLayer(lc)
|
|
383
392
|
|
|
384
|
-
|
|
385
|
-
|
|
393
|
+
# Reset surface :r (uprated RSi of insulation, before derating).
|
|
394
|
+
col[:s].keys.each do |nom|
|
|
395
|
+
next unless s.key?(nom)
|
|
396
|
+
next unless s[nom].key?(:r)
|
|
386
397
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
next
|
|
398
|
+
s[nom][:r] = lyr[:r]
|
|
399
|
+
end
|
|
390
400
|
end
|
|
391
401
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
coll.values.first[:s].keys.each do |nom|
|
|
396
|
-
next unless s.key?(nom)
|
|
397
|
-
next unless s[nom].key?(:index)
|
|
398
|
-
next unless s[nom].key?(:ltype)
|
|
399
|
-
next unless s[nom].key?(:r )
|
|
400
|
-
next unless s[nom][:index] == lyr[:index]
|
|
401
|
-
next unless s[nom][:ltype] == lyr[:type ]
|
|
402
|
+
# Store UA-averaged, upgraded Uo-factor per type.
|
|
403
|
+
area = coll.values.sum { |col| col[:area] }
|
|
404
|
+
uA = coll.values.sum { |col| col[:uA ] }
|
|
402
405
|
|
|
403
|
-
|
|
406
|
+
if area > TOL
|
|
407
|
+
argh[:wall_uo ] = uA / area if typ == :wall
|
|
408
|
+
argh[:roof_uo ] = uA / area if typ == :ceiling
|
|
409
|
+
argh[:floor_uo] = uA / area if typ == :floor
|
|
404
410
|
end
|
|
405
|
-
|
|
406
|
-
argh[:wall_uo ] = res[:uo] if typ == :wall
|
|
407
|
-
argh[:roof_uo ] = res[:uo] if typ == :ceiling
|
|
408
|
-
argh[:floor_uo] = res[:uo] if typ == :floor
|
|
409
|
-
else
|
|
410
|
-
log(WRN, "Nilled construction to uprate - (#{mth})")
|
|
411
|
-
return false
|
|
412
411
|
end
|
|
413
412
|
end
|
|
414
413
|
|
|
@@ -442,54 +441,57 @@ module TBD
|
|
|
442
441
|
return mismatch("sets", sets, cl1, mth, DBG, false) unless sets.is_a?(cl2)
|
|
443
442
|
|
|
444
443
|
shorts = sets.shorthands("code (Quebec)")
|
|
445
|
-
empty = shorts[:has].empty? || shorts[:val].empty?
|
|
446
|
-
log(DBG, "Missing QC PSI set for 3.3 UA' tradeoff (#{mth})") if empty
|
|
447
|
-
return false if empty
|
|
448
444
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
445
|
+
if shorts[:has].empty? || shorts[:val].empty?
|
|
446
|
+
log(DBG, "Missing QC PSI set for 3.3 UA' tradeoff (#{mth})")
|
|
447
|
+
return false
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
unless [true, false].include?(spts)
|
|
451
|
+
log(DBG, "setpoints must be true or false for 3.3 UA' tradeoff")
|
|
452
|
+
return false
|
|
453
|
+
end
|
|
452
454
|
|
|
453
455
|
s.each do |id, surface|
|
|
454
456
|
next unless surface.key?(:deratable)
|
|
455
457
|
next unless surface[:deratable]
|
|
456
458
|
next unless surface.key?(:type)
|
|
457
459
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
heating = surface[:heating] if surface.key?(:heating)
|
|
463
|
-
cooling = surface[:cooling] if surface.key?(:cooling)
|
|
460
|
+
htng = spts ? -24 : 21
|
|
461
|
+
clng = spts ? 50 : 24
|
|
462
|
+
htng = surface[:heating] if surface.key?(:heating)
|
|
463
|
+
clng = surface[:cooling] if surface.key?(:cooling)
|
|
464
464
|
|
|
465
|
-
#
|
|
466
|
-
|
|
467
|
-
ref = 1 / 3.60 if surface[:type] == :wall
|
|
465
|
+
# Avoid 'divide by zero' case.
|
|
466
|
+
htng = -24 if htng < -24
|
|
468
467
|
|
|
469
|
-
# Adjust for lower heating setpoint
|
|
470
|
-
|
|
468
|
+
# Start with surface U-factors. Adjust for lower heating setpoint.
|
|
469
|
+
# Assumes -25C design conditions.
|
|
470
|
+
ref = ( surface[:type] == :wall ) ? (1 / 3.60) : (1 / 5.46)
|
|
471
|
+
ref *= 43 / (htng + 25) if htng > -25 && htng < 18 && clng > 40
|
|
471
472
|
|
|
472
473
|
surface[:ref] = ref
|
|
473
474
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
ref
|
|
475
|
+
# Loop through subsurfaces.
|
|
476
|
+
if surface.key?(:skylights)
|
|
477
|
+
ref = 2.85
|
|
478
|
+
ref *= 43 / (htng + 25) if htng > -25 && htng < 18 && clng > 40
|
|
477
479
|
|
|
478
480
|
surface[:skylights].values.map { |skylight| skylight[:ref] = ref }
|
|
479
481
|
end
|
|
480
482
|
|
|
481
483
|
if surface.key?(:windows)
|
|
482
|
-
ref
|
|
483
|
-
ref *= 43 / (
|
|
484
|
+
ref = 2.0
|
|
485
|
+
ref *= 43 / (htng + 25) if htng > -25 && htng < 18 && clng > 40
|
|
484
486
|
|
|
485
487
|
surface[:windows].values.map { |window| window[:ref] = ref }
|
|
486
488
|
end
|
|
487
489
|
|
|
488
490
|
if surface.key?(:doors)
|
|
489
|
-
surface[:doors].each do |
|
|
490
|
-
ref
|
|
491
|
-
ref
|
|
492
|
-
|
|
491
|
+
surface[:doors].values.each do |door|
|
|
492
|
+
ref = ( door.key?(:glazed) && door[:glazed] ) ? 2.0 : 0.9
|
|
493
|
+
ref *= 43 / (htng + 25) if htng > -25 && htng < 18 && clng > 40
|
|
494
|
+
|
|
493
495
|
door[:ref] = ref
|
|
494
496
|
end
|
|
495
497
|
end
|
|
@@ -1000,7 +1002,7 @@ module TBD
|
|
|
1000
1002
|
model = "* modèle : #{ua[:file]}" if ua.key?(:file) && lang == :fr
|
|
1001
1003
|
model += " (v#{ua[:version]})" if ua.key?(:version)
|
|
1002
1004
|
report << model unless model.empty?
|
|
1003
|
-
report << "* TBD : v3.
|
|
1005
|
+
report << "* TBD : v3.6.0"
|
|
1004
1006
|
report << "* date : #{ua[:date]}"
|
|
1005
1007
|
|
|
1006
1008
|
if lang == :en
|