tbd 3.2.1 → 3.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a5a9002ff406325bc226c157ed29ffc22ccb60ac46b9c6bf29358f04aa9d6a3
4
- data.tar.gz: 60b0a52d46be3a91a2119cb5d3875826e7b278ca9ce78a230e9575263dbb4c89
3
+ metadata.gz: 15442c735a4591e22e609b8b2f73733a583a5f6b304c3c479866d98afcf63251
4
+ data.tar.gz: c4d53db567cc419b2e0eb5b4b48f85f19e6c12745bab19ee257566c0c1b38bea
5
5
  SHA512:
6
- metadata.gz: df24c1565b802fd84c8f067d78662adf23d5fb2059145b6b22da98b8c06f70b49283ef7f28613e91c1258e3c7b865d17e81df93c2340aec431483597d8ddb847
7
- data.tar.gz: 34ea2406564f08b36a54987ab08fd5d2cb24e49bfb02640169278f1138f65f36ac2c9279abb7e8be83e1c2aec4cca639b8735ab13f54f0470fe0a8470977be86
6
+ metadata.gz: e314e00796a8d0e6e44c6ccd23d0e9accaa45be119589da431d00f45354c622bbedd964b94053fd9823c33914713b07984e0f0df44f714d4311ac16f382c0407
7
+ data.tar.gz: 7344be85007deed2d9a22d83c0cb20d4860d4e048c1bf281e7f2c8024439696b75ba6d232c9f8985253003c1295d2e537fc29a9fb9322ea6d756bcd08ec0dcf9
@@ -3,8 +3,8 @@
3
3
  <schema_version>3.0</schema_version>
4
4
  <name>tbd_measure</name>
5
5
  <uid>8890787b-8c25-4dc8-8641-b6be1b6c2357</uid>
6
- <version_id>2fc42e1d-2010-44ae-9837-6390512c41d4</version_id>
7
- <version_modified>20230214T105829Z</version_modified>
6
+ <version_id>30d7ee3a-8327-4a32-92fd-5cc0b9ab24b0</version_id>
7
+ <version_modified>20230516T180637Z</version_modified>
8
8
  <xml_checksum>99772807</xml_checksum>
9
9
  <class_name>TBDMeasure</class_name>
10
10
  <display_name>Thermal Bridging and Derating - TBD</display_name>
@@ -406,12 +406,6 @@
406
406
  <usage_type>resource</usage_type>
407
407
  <checksum>A4E8433C</checksum>
408
408
  </file>
409
- <file>
410
- <filename>utils.rb</filename>
411
- <filetype>rb</filetype>
412
- <usage_type>resource</usage_type>
413
- <checksum>283C976C</checksum>
414
- </file>
415
409
  <file>
416
410
  <version>
417
411
  <software_program>OpenStudio</software_program>
@@ -429,24 +423,12 @@
429
423
  <usage_type>test</usage_type>
430
424
  <checksum>58ED6635</checksum>
431
425
  </file>
432
- <file>
433
- <filename>geo.rb</filename>
434
- <filetype>rb</filetype>
435
- <usage_type>resource</usage_type>
436
- <checksum>F447D8CE</checksum>
437
- </file>
438
426
  <file>
439
427
  <filename>README.md</filename>
440
428
  <filetype>md</filetype>
441
429
  <usage_type>readme</usage_type>
442
430
  <checksum>B836C43A</checksum>
443
431
  </file>
444
- <file>
445
- <filename>ua.rb</filename>
446
- <filetype>rb</filetype>
447
- <usage_type>resource</usage_type>
448
- <checksum>19193778</checksum>
449
- </file>
450
432
  <file>
451
433
  <filename>psi.rb</filename>
452
434
  <filetype>rb</filetype>
@@ -465,5 +447,23 @@
465
447
  <usage_type>resource</usage_type>
466
448
  <checksum>A3BB982A</checksum>
467
449
  </file>
450
+ <file>
451
+ <filename>ua.rb</filename>
452
+ <filetype>rb</filetype>
453
+ <usage_type>resource</usage_type>
454
+ <checksum>EEFCA7DA</checksum>
455
+ </file>
456
+ <file>
457
+ <filename>utils.rb</filename>
458
+ <filetype>rb</filetype>
459
+ <usage_type>resource</usage_type>
460
+ <checksum>C03764A5</checksum>
461
+ </file>
462
+ <file>
463
+ <filename>geo.rb</filename>
464
+ <filetype>rb</filetype>
465
+ <usage_type>resource</usage_type>
466
+ <checksum>5F84399F</checksum>
467
+ </file>
468
468
  </files>
469
469
  </measure>
@@ -218,42 +218,6 @@ module TBD
218
218
  true
219
219
  end
220
220
 
221
- ##
222
- # Validate whether an OpenStudio planar surface is safe for TBD to process.
223
- #
224
- # @param s [OpenStudio::Model::PlanarSurface] a surface
225
- #
226
- # @return [Bool] true if valid surface
227
- def validate(s = nil)
228
- mth = "TBD::#{__callee__}"
229
- cl = OpenStudio::Model::PlanarSurface
230
-
231
- return mismatch("surface", s, cl, mth, DBG, false) unless s.is_a?(cl)
232
-
233
- id = s.nameString
234
- size = s.vertices.size
235
- last = size - 1
236
-
237
- log(ERR, "#{id} #{size} vertices? need +3 (#{mth})") unless size > 2
238
- return false unless size > 2
239
-
240
- [0, last].each do |i|
241
- v1 = s.vertices[i]
242
- v2 = s.vertices[i + 1] unless i == last
243
- v2 = s.vertices.first if i == last
244
- vector = v2 - v1
245
- bad = vector.length < TOL
246
-
247
- # As is, this comparison also catches collinear vertices (< 10mm apart)
248
- # along an edge. Should avoid red-flagging such cases. TO DO.
249
- log(ERR, "#{id}: < #{TOL}m (#{mth})") if bad
250
- return false if bad
251
- end
252
-
253
- # Add as many extra tests as needed ...
254
- true
255
- end
256
-
257
221
  ##
258
222
  # Return site-specific (or true) Topolys normal vector of OpenStudio surface.
259
223
  #
@@ -290,30 +254,33 @@ module TBD
290
254
  cl2 = OpenStudio::Model::Surface
291
255
  cl3 = OpenStudio::Model::LayeredConstruction
292
256
 
293
- return mismatch("model", model, cl1, mth) unless model.is_a?(cl1)
294
- return mismatch("surface", surface, cl2, mth) unless surface.is_a?(cl2)
295
-
296
- return nil unless validate(surface)
257
+ return mismatch("model", model, cl1, mth) unless model.is_a?(cl1)
258
+ return mismatch("surface", surface, cl2, mth) unless surface.is_a?(cl2)
259
+ return nil unless surface_valid?(surface)
297
260
 
298
261
  nom = surface.nameString
299
262
  surf = {}
300
263
  subs = {}
301
264
  fd = false
302
265
  return empty("'#{nom}' space", mth, ERR) if surface.space.empty?
266
+
303
267
  space = surface.space.get
304
268
  stype = space.spaceType
305
269
  story = space.buildingStory
306
270
  tr = transforms(model, space)
307
271
  return invalid("'#{nom}' transform", mth, 0, FTL) unless tr[:t] && tr[:r]
272
+
308
273
  t = tr[:t]
309
274
  n = trueNormal(surface, tr[:r])
310
275
  return invalid("'#{nom}' normal", mth, 0, FTL) unless n
276
+
311
277
  type = surface.surfaceType.downcase
312
278
  facing = surface.outsideBoundaryCondition
313
279
 
314
280
  if facing.downcase == "surface"
315
281
  empty = surface.adjacentSurface.empty?
316
- return invalid("'#{nom}': adjacent surface", mth, 0, ERR) if empty
282
+ return invalid("'#{nom}': adjacent surface", mth, 0, ERR) if empty
283
+
317
284
  facing = surface.adjacentSurface.get.nameString
318
285
  end
319
286
 
@@ -350,9 +317,10 @@ module TBD
350
317
  surf[:story ] = story.get unless story.empty?
351
318
  surf[:n ] = n
352
319
  surf[:gross ] = surface.grossArea
320
+ surf[:filmRSI ] = surface.filmResistance
353
321
 
354
322
  surface.subSurfaces.sort_by { |s| s.nameString }.each do |s|
355
- next unless validate(s)
323
+ next unless surface_valid?(s)
356
324
 
357
325
  id = s.nameString
358
326
  valid = s.vertices.size == 3 || s.vertices.size == 4
@@ -39,13 +39,14 @@ module TBD
39
39
  cl1 = OpenStudio::Model::Model
40
40
  cl2 = OpenStudio::Model::LayeredConstruction
41
41
  cl3 = Numeric
42
+ cl4 = String
42
43
 
43
- return mismatch("model", model, cl1, mth, DBG, res) unless model.is_a?(cl1)
44
- return mismatch("id", id, String, mth, DBG, res) unless id.is_a?(String)
45
- return mismatch("lc", lc, cl2, mth, DBG, res) unless lc.is_a?(cl2)
46
- return mismatch("hloss", hloss, cl3, mth, DBG, res) unless hloss.is_a?(cl3)
47
- return mismatch("film", film, cl3, mth, DBG, res) unless film.is_a?(cl3)
48
- return mismatch("Ut", ut, cl3, mth, DBG, res) unless ut.is_a?(cl3)
44
+ return mismatch("model", model, cl1, mth, DBG, res) unless model.is_a?(cl1)
45
+ return mismatch("id" , id, cl4, mth, DBG, res) unless id.is_a?(cl4)
46
+ return mismatch("lc" , lc, cl2, mth, DBG, res) unless lc.is_a?(cl2)
47
+ return mismatch("hloss", hloss, cl3, mth, DBG, res) unless hloss.is_a?(cl3)
48
+ return mismatch("film" , film, cl3, mth, DBG, res) unless film.is_a?(cl3)
49
+ return mismatch("Ut" , ut, cl3, mth, DBG, res) unless ut.is_a?(cl3)
49
50
 
50
51
  loss = 0.0 # residual heatloss (not assigned) [W/K]
51
52
  area = lc.getNetArea
@@ -54,12 +55,12 @@ module TBD
54
55
  lyr[:index] = nil unless lyr[:index] >= 0
55
56
  lyr[:index] = nil unless lyr[:index] < lc.layers.size
56
57
 
57
- return invalid("'#{id}' layer index", mth, 0, ERR, res) unless lyr[:index]
58
- return zero("'#{id}': heatloss", mth, WRN, res) unless hloss > TOL
59
- return zero("'#{id}': films", mth, WRN, res) unless film > TOL
60
- return zero("'#{id}': Ut", mth, WRN, res) unless ut > TOL
61
- return invalid("'#{id}': Ut", mth, 0, WRN, res) unless ut < 5.678
62
- return zero("'#{id}': net area (m2)", mth, ERR, res) unless area > TOL
58
+ return invalid("'#{id}' layer index", mth, 0, ERR, res) unless lyr[:index]
59
+ return zero("'#{id}': heatloss" , mth, WRN, res) unless hloss > TOL
60
+ return zero("'#{id}': films" , mth, WRN, res) unless film > TOL
61
+ return zero("'#{id}': Ut" , mth, WRN, res) unless ut > TOL
62
+ return invalid("'#{id}': Ut" , mth, 0, WRN, res) unless ut < 5.678
63
+ return zero("'#{id}': net area (m2)", mth, ERR, res) unless area > TOL
63
64
 
64
65
  # First, calculate initial layer RSi to initially meet Ut target.
65
66
  rt = 1 / ut # target construction Rt
@@ -76,7 +77,7 @@ module TBD
76
77
 
77
78
  if lyr[:type] == :massless
78
79
  m = lc.getLayer(lyr[:index]).to_MasslessOpaqueMaterial
79
- return invalid("'#{id}' massless layer?", mth, 0) if m.empty?
80
+ return invalid("'#{id}' massless layer?", mth, 0, DBG, res) if m.empty?
80
81
 
81
82
  m = m.get.clone(model).to_MasslessOpaqueMaterial.get
82
83
  m.setName("#{id} uprated")
@@ -85,7 +86,7 @@ module TBD
85
86
  m.setThermalResistance(new_r)
86
87
  else # type == :standard
87
88
  m = lc.getLayer(lyr[:index]).to_StandardOpaqueMaterial
88
- return invalid("'#{id}' standard layer?", mth, 0) if m.empty?
89
+ return invalid("'#{id}' standard layer?", mth, 0, DBG, res) if m.empty?
89
90
 
90
91
  m = m.get.clone(model).to_StandardOpaqueMaterial.get
91
92
  m.setName("#{id} uprated")
@@ -141,11 +142,12 @@ module TBD
141
142
  mth = "TBD::#{__callee__}"
142
143
  cl1 = OpenStudio::Model::Model
143
144
  cl2 = Hash
145
+ cl3 = OpenStudio::Model::LayeredConstruction
144
146
  a = false
145
147
 
146
- return mismatch("model", model, cl1, mth, DBG, a) unless model.is_a?(cl1)
147
- return mismatch("surfaces", s, cl2, mth, DBG, a) unless s.is_a?(cl2)
148
- return mismatch("argh", model, cl1, mth, DBG, a) unless argh.is_a?(cl2)
148
+ return mismatch("model" , model, cl1, mth, DBG, a) unless model.is_a?(cl1)
149
+ return mismatch("surfaces", s, cl2, mth, DBG, a) unless s.is_a?(cl2)
150
+ return mismatch("argh" , model, cl1, mth, DBG, a) unless argh.is_a?(cl2)
149
151
 
150
152
  argh[:uprate_walls ] = false unless argh.key?(:uprate_walls )
151
153
  argh[:uprate_roofs ] = false unless argh.key?(:uprate_roofs )
@@ -168,11 +170,13 @@ module TBD
168
170
  groups[:roof ][:op] = argh[:roof_option ]
169
171
  groups[:floor][:op] = argh[:floor_option ]
170
172
 
171
- groups.each do |label, g|
173
+ groups.each do |type, g|
172
174
  next unless g[:up]
173
175
  next unless g[:ut].is_a?(Numeric)
174
176
  next unless g[:ut] < 5.678
175
177
 
178
+ typ = type
179
+ typ = :ceiling if typ == :roof # fix in future revision. TO-DO.
176
180
  coll = {}
177
181
  area = 0
178
182
  film = 100000000000000
@@ -183,86 +187,81 @@ module TBD
183
187
  g[:op].downcase == "all floor constructions"
184
188
 
185
189
  if g[:op].empty?
186
- log(ERR, "Construction to uprate? (#{mth})")
190
+ log(ERR, "Construction (#{type}) to uprate? (#{mth})")
187
191
  elsif all
188
- model.getSurfaces.each do |sss|
189
- next unless sss.surfaceType.downcase.include?(label.to_s)
190
- next unless sss.outsideBoundaryCondition.downcase == "outdoors"
191
- next if sss.construction.empty?
192
- next if sss.construction.get.to_LayeredConstruction.empty?
193
-
194
- c = sss.construction.get.to_LayeredConstruction.get
195
- i = c.nameString
196
-
197
- # Reliable unless referenced by other surface types e.g. floor vs wall.
198
- if c.getNetArea > area
199
- area = c.getNetArea
192
+ s.each do |nom, surface|
193
+ next unless surface.key?(:deratable )
194
+ next unless surface.key?(:type )
195
+ next unless surface.key?(:construction)
196
+ next unless surface.key?(:filmRSI )
197
+ next unless surface.key?(:index )
198
+ next unless surface.key?(:ltype )
199
+ next unless surface.key?(:r )
200
+ next unless surface[:deratable ]
201
+ next unless surface[:type ] == typ
202
+ next unless surface[:construction].is_a?(cl3)
203
+ next if surface[:index ].nil?
204
+
205
+ # Retain lowest surface film resistance (e.g. tilted surfaces).
206
+ c = surface[:construction]
207
+ i = c.nameString
208
+ aire = c.getNetArea
209
+ film = surface[:filmRSI] if surface[:filmRSI] < film
210
+
211
+ # Retain construction covering largest area. The following conditional
212
+ # is reliable UNLESS linked to other deratable surface types e.g. both
213
+ # floors AND walls (see "elsif lc" corrections below).
214
+ if aire > area
200
215
  lc = c
216
+ area = aire
201
217
  id = i
202
218
  end
203
219
 
204
- film = sss.filmResistance if sss.filmResistance < film
205
- nom = sss.nameString
206
- coll[i] = { area: c.getNetArea, lc: c, s: {} } unless coll.key?(i)
207
- coll[i][:s][nom] = { a: sss.netArea } unless coll[i][:s].key?(nom)
220
+ coll[i] = { area: aire, lc: c, s: {} } unless coll.key?(i)
221
+ coll[i][:s][nom] = { a: surface[:net] } unless coll[i][:s].key?(nom)
208
222
  end
209
223
  else
210
224
  id = g[:op]
211
- c = model.getConstructionByName(id)
212
-
213
- if c.empty?
214
- log(ERR, "Construction '#{id}'? (#{mth})")
215
- else
216
- c = c.get.to_LayeredConstruction
217
-
218
- if c.empty?
219
- log(ERR, "'#{id}' layered construction? (#{mth})")
220
- else
221
- lc = c.get
222
- area = lc.getNetArea
223
- coll[id] = { area: area, lc: lc, s: {} }
224
-
225
- model.getSurfaces.each do |sss|
226
- next unless sss.surfaceType.downcase.include?(label.to_s)
227
- next unless sss.outsideBoundaryCondition.downcase == "outdoors"
228
- next if sss.construction.empty?
229
- next if sss.construction.get.to_LayeredConstruction.empty?
230
- lc = sss.construction.get.to_LayeredConstruction.get
231
- next unless id == lc.nameString
232
- nom = sss.nameString
233
- film = sss.filmResistance if sss.filmResistance < film
234
- ok = coll[id][:s].key?(nom)
235
- coll[id][:s][nom] = { a: sss.netArea } unless ok
236
- end
237
- end
225
+ lc = model.getConstructionByName(id)
226
+ log(ERR, "Construction '#{id}'? (#{mth})") if lc.empty?
227
+ next if lc.empty?
228
+
229
+ lc = lc.get.to_LayeredConstruction
230
+ log(ERR, "'#{id}' layered construction? (#{mth})") if lc.empty?
231
+ next if lc.empty?
232
+
233
+ lc = lc.get
234
+ area = lc.getNetArea
235
+ coll[id] = { area: area, lc: lc, s: {} }
236
+
237
+ s.each do |nom, surface|
238
+ next unless surface.key?(:deratable )
239
+ next unless surface.key?(:type )
240
+ next unless surface.key?(:construction)
241
+ next unless surface.key?(:filmRSI )
242
+ next unless surface.key?(:index )
243
+ next unless surface.key?(:ltype )
244
+ next unless surface.key?(:r )
245
+ next unless surface[:deratable ]
246
+ next unless surface[:type ] == typ
247
+ next unless surface[:construction].is_a?(cl3)
248
+ next if surface[:index ].nil?
249
+
250
+ i = surface[:construction].nameString
251
+ next unless i == id
252
+
253
+ # Retain lowest surface film resistance (e.g. tilted surfaces).
254
+ film = surface[:filmRSI] if surface[:filmRSI] < film
255
+
256
+ coll[i][:s][nom] = { a: surface[:net] } unless coll[i][:s].key?(nom)
238
257
  end
239
258
  end
240
259
 
241
260
  if coll.empty?
242
- log(ERR, "No #{label} construction to uprate - skipping (#{mth})")
261
+ log(ERR, "No #{type} construction to uprate - skipping (#{mth})")
243
262
  next
244
- elsif lc # valid layered construction - good to uprate!
245
- # Ensure lc is referenced by surface types == label.
246
- model.getSurfaces.each do |sss|
247
- next if sss.construction.empty?
248
- next if sss.construction.get.to_LayeredConstruction.empty?
249
- c = sss.construction.get.to_LayeredConstruction.get
250
- i = c.nameString
251
- next unless coll.key?(i)
252
-
253
- unless sss.surfaceType.downcase.include?(label.to_s)
254
- log(ERR, "Uprating #{label.to_s}, not '#{sss.nameString}' (#{mth})")
255
- cloned = c.clone(model).to_LayeredConstruction.get
256
- cloned.setName("'#{i}' cloned")
257
- sss.setConstruction(cloned)
258
- ok = s.key?(sss.nameString)
259
- s[sss.nameString][:construction] = cloned if ok
260
- coll[i][:s].delete(sss.nameString)
261
- coll[i][:area] = c.getNetArea
262
- next
263
- end
264
- end
265
-
263
+ elsif lc
264
+ # Valid layered construction - good to uprate!
266
265
  lyr = insulatingLayer(lc)
267
266
  lyr[:index] = nil unless lyr[:index].is_a?(Numeric)
268
267
  lyr[:index] = nil unless lyr[:index] >= 0
@@ -270,64 +269,81 @@ module TBD
270
269
 
271
270
  log(ERR, "Insulation index for '#{id}'? (#{mth})") unless lyr[:index]
272
271
  next unless lyr[:index]
273
- hloss = 0 # sum of applicable psi & khi effects [W/K]
274
272
 
275
- coll.each do |i, col|
276
- next unless col.key?(:s)
277
- next unless col.is_a?(Hash)
273
+ # Ensure lc is exclusively linked to deratable surfaces of right type.
274
+ # If not, assign new lc clone to non-targeted surfaces.
275
+ s.each do |nom, surface|
276
+ next unless surface.key?(:type )
277
+ next unless surface.key?(:deratable )
278
+ next unless surface.key?(:construction)
279
+ next unless surface[:construction].is_a?(cl3)
280
+ next unless surface[:construction] == lc
281
+
282
+ ok = true
283
+ ok = false unless surface[:type ] == typ
284
+ ok = false unless surface[:deratable]
285
+ ok = false unless coll.key?(id)
286
+ ok = false unless coll[id][:s].key?(nom)
287
+
288
+ unless ok
289
+ log(WRN, "Cloning '#{nom}' construction - not '#{id}' (#{mth})")
290
+ sss = model.getSurfaceByName(nom)
291
+ next if sss.empty?
292
+
293
+ sss = sss.get
294
+ cloned = lc.clone(model).to_LayeredConstruction.get
295
+ cloned.setName("#{nom} - cloned")
296
+ sss.setConstruction(cloned)
297
+ surface[:construction] = cloned
298
+ coll[id][:s].delete(nom)
299
+ end
300
+ end
301
+
302
+ hloss = 0 # sum of applicable psi & khi effects [W/K]
278
303
 
304
+ # Tally applicable psi + khi losses. Possible construction reassignment.
305
+ coll.each do |i, col|
279
306
  col[:s].keys.each do |nom|
280
307
  next unless s.key?(nom)
281
- next unless s[nom].key?(:deratable )
282
308
  next unless s[nom].key?(:construction)
283
309
  next unless s[nom].key?(:index )
284
310
  next unless s[nom].key?(:ltype )
285
311
  next unless s[nom].key?(:r )
286
- next unless s[nom].key?(:type )
287
-
288
- next unless s[nom][:deratable]
289
- type = s[nom][:type].to_s.downcase
290
- type = "roof" if type == "ceiling"
291
- next unless type.include?(label.to_s)
292
312
 
293
313
  # Tally applicable psi + khi.
294
- hloss += s[nom][:heatloss] if s[nom].key?(:heatloss)
295
-
296
- # Skip construction reassignment if already referencing right one.
297
- unless s[nom][:construction] == lc
298
- sss = model.getSurfaceByName(nom)
299
- next if sss.empty?
300
- sss = sss.get
301
-
302
- if sss.isConstructionDefaulted
303
- set = defaultConstructionSet(model, sss)
304
- constructions = set.defaultExteriorSurfaceConstructions.get
305
-
306
- case sss.surfaceType.downcase
307
- when "roofceiling"
308
- constructions.setRoofCeilingConstruction(lc)
309
- when "floor"
310
- constructions.setFloorConstruction(lc)
311
- else
312
- constructions.setWallConstruction(lc)
313
- end
314
- else
315
- sss.setConstruction(lc)
316
- end
314
+ hloss += s[nom][:heatloss ] if s[nom].key?(:heatloss)
315
+ next if s[nom][:construction] == lc
316
+
317
+ # Reassign construction unless referencing lc.
318
+ sss = model.getSurfaceByName(nom)
319
+ next if sss.empty?
317
320
 
318
- s[nom][:construction] = lc # reset TBD attributes
319
- s[nom][:index ] = lyr[:index]
320
- s[nom][:ltype ] = lyr[:type ]
321
- s[nom][:r ] = lyr[:r ] # temporary
321
+ sss = sss.get
322
+
323
+ if sss.isConstructionDefaulted
324
+ set = defaultConstructionSet(model, sss) # building? story?
325
+ constructions = set.defaultExteriorSurfaceConstructions
326
+
327
+ unless constructions.empty?
328
+ constructions = constructions.get
329
+ constructions.setWallConstruction(lc) if typ == :wall
330
+ constructions.setFloorConstruction(lc) if typ == :floor
331
+ constructions.setRoofCeilingConstruction(lc) if typ == :ceiling
332
+ end
333
+ else
334
+ sss.setConstruction(lc)
322
335
  end
336
+
337
+ s[nom][:construction] = lc # reset TBD attributes
338
+ s[nom][:index ] = lyr[:index]
339
+ s[nom][:ltype ] = lyr[:type ]
340
+ s[nom][:r ] = lyr[:r ] # temporary
323
341
  end
324
342
  end
325
343
 
326
344
  # Merge to ensure a single entry for coll Hash.
327
345
  coll.each do |i, col|
328
346
  next if i == id
329
- next unless coll.key?(id)
330
- coll[id][:area] += col[:area]
331
347
 
332
348
  col[:s].each do |nom, sss|
333
349
  coll[id][:s][nom] = sss unless coll[id][:s].key?(nom)
@@ -338,6 +354,8 @@ module TBD
338
354
  log(DBG, "Collection == 1? for '#{id}' (#{mth})") unless coll.size == 1
339
355
  next unless coll.size == 1
340
356
 
357
+ area = lc.getNetArea
358
+ coll[id][:area] = area
341
359
  res = uo(model, lc, id, hloss, film, g[:ut])
342
360
  log(ERR, "Unable to uprate '#{id}' (#{mth})") unless res[:uo] && res[:m]
343
361
  next unless res[:uo] && res[:m]
@@ -347,27 +365,18 @@ module TBD
347
365
  # Loop through coll :s, and reset :r - likely modified by uo().
348
366
  coll.values.first[:s].keys.each do |nom|
349
367
  next unless s.key?(nom)
350
- next unless s[nom].key?(:deratable )
351
- next unless s[nom].key?(:construction)
352
- next unless s[nom].key?(:index )
353
- next unless s[nom].key?(:ltype )
354
- next unless s[nom].key?(:type )
355
-
356
- next unless s[nom][:deratable ]
357
- next unless s[nom][:construction] == lc
358
- next unless s[nom][:index ] == lyr[:index]
359
- next unless s[nom][:ltype ] == lyr[:type]
360
-
361
- type = s[nom][:type].to_s.downcase
362
- type = "roof" if type == "ceiling"
363
- next unless type.include?(label.to_s)
364
- next unless s[nom].key?(:r)
365
- s[nom][:r] = lyr[:r] # final
368
+ next unless s[nom].key?(:index)
369
+ next unless s[nom].key?(:ltype)
370
+ next unless s[nom].key?(:r )
371
+ next unless s[nom][:index] == lyr[:index]
372
+ next unless s[nom][:ltype] == lyr[:type ]
373
+
374
+ s[nom][:r] = lyr[:r] # uprated insulating RSi factor, before derating
366
375
  end
367
376
 
368
- argh[:wall_uo ] = res[:uo] if label == :wall
369
- argh[:roof_uo ] = res[:uo] if label == :roof
370
- argh[:floor_uo] = res[:uo] if label == :floor
377
+ argh[:wall_uo ] = res[:uo] if typ == :wall
378
+ argh[:roof_uo ] = res[:uo] if typ == :ceiling
379
+ argh[:floor_uo] = res[:uo] if typ == :floor
371
380
  else
372
381
  log(ERR, "Nilled construction to uprate - (#{mth})")
373
382
  return false
@@ -920,7 +929,7 @@ module TBD
920
929
  model = "* modèle : #{ua[:file]}" if ua.key?(:file) && lang == :fr
921
930
  model += " (v#{ua[:version]})" if ua.key?(:version)
922
931
  report << model unless model.empty?
923
- report << "* TBD : v3.2.1"
932
+ report << "* TBD : v3.2.3"
924
933
  report << "* date : #{ua[:date]}"
925
934
 
926
935
  if lang == :en