tbd 3.0.2 → 3.0.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: a5922a435bc3f8d97b6aa78588346e59108b6455d27c72e0aeecdd792079f33f
4
- data.tar.gz: 5f683e96a3f1c33e0bb782bbb545b713d9d55beaf2359d3983f9b4d010716ce0
3
+ metadata.gz: fff7235e8a345417b5d6885b6b131f8a3ec91cdcad6cf06a691a5455ad04c842
4
+ data.tar.gz: 89b326a95347bcaf4415b6c5643e2ae0841aae2e238cdfc6326bc069fdff1134
5
5
  SHA512:
6
- metadata.gz: 7284058dc4ec681c102435484b8af994fd027ea666aa4ed760f0e178cfcef297de033454c0a2c7cd60c95f13b71308bdcc6c4fa0a846819ec92edaa46007af08
7
- data.tar.gz: 78e74d0942d5ad735c9b696c5cdaa72b5ad8a92fbf34a3c8843e41dce41f1de0eddfda3b53ef8db08c3f9c87a73b92b3ad8bb89400a58283c1805d7d660d83f7
6
+ metadata.gz: c38bdf41f39fccaaed05642f7d4b8e24f13833a120c9b4af152399a9b796a3594574961745de4728e4f4261a75567fb46b4e953ef061453cec94f413ec26583f
7
+ data.tar.gz: f4844f96093b176796d1c89c3af61dd8473c6417d1305a64b84abf96eee1328357ff6d34cd2726a4e6bd3617f45ca80c4a6af2309f63772f1acd2dc4dbfc15cb
@@ -3,11 +3,11 @@ name: Pull Request CI
3
3
  on:
4
4
  pull_request:
5
5
  branches:
6
- - master
6
+ - develop
7
7
 
8
8
  jobs:
9
9
  test_300x:
10
- runs-on: ubuntu-20.04
10
+ runs-on: ubuntu-22.04
11
11
  steps:
12
12
  - name: Check out repository
13
13
  uses: actions/checkout@v2
@@ -23,7 +23,7 @@ jobs:
23
23
  docker exec -t test bundle exec rake
24
24
  docker kill test
25
25
  test_321x:
26
- runs-on: ubuntu-20.04
26
+ runs-on: ubuntu-22.04
27
27
  steps:
28
28
  - name: Check out repository
29
29
  uses: actions/checkout@v2
@@ -39,7 +39,7 @@ jobs:
39
39
  docker exec -t test bundle exec rake
40
40
  docker kill test
41
41
  test_330x:
42
- runs-on: ubuntu-20.04
42
+ runs-on: ubuntu-22.04
43
43
  steps:
44
44
  - name: Check out repository
45
45
  uses: actions/checkout@v2
@@ -55,7 +55,7 @@ jobs:
55
55
  docker exec -t test bundle exec rake
56
56
  docker kill test
57
57
  test_340x:
58
- runs-on: ubuntu-20.04
58
+ runs-on: ubuntu-22.04
59
59
  steps:
60
60
  - name: Check out repository
61
61
  uses: actions/checkout@v2
@@ -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>65ac80d9-ef1e-4c41-9a62-68e2e1e6c5db</version_id>
7
- <version_modified>20220918T225251Z</version_modified>
6
+ <version_id>3e0bc642-b37c-488f-96d5-e73f4c6d432b</version_id>
7
+ <version_modified>20221020T205858Z</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>
@@ -432,12 +432,6 @@
432
432
  <usage_type>test</usage_type>
433
433
  <checksum>6ED9AF88</checksum>
434
434
  </file>
435
- <file>
436
- <filename>psi.rb</filename>
437
- <filetype>rb</filetype>
438
- <usage_type>resource</usage_type>
439
- <checksum>655E8232</checksum>
440
- </file>
441
435
  <file>
442
436
  <filename>README.md</filename>
443
437
  <filetype>md</filetype>
@@ -445,16 +439,22 @@
445
439
  <checksum>36FA11E3</checksum>
446
440
  </file>
447
441
  <file>
448
- <filename>geo.rb</filename>
442
+ <filename>ua.rb</filename>
449
443
  <filetype>rb</filetype>
450
444
  <usage_type>resource</usage_type>
451
- <checksum>8A794826</checksum>
445
+ <checksum>7264CA34</checksum>
452
446
  </file>
453
447
  <file>
454
- <filename>ua.rb</filename>
448
+ <filename>psi.rb</filename>
449
+ <filetype>rb</filetype>
450
+ <usage_type>resource</usage_type>
451
+ <checksum>F31F865A</checksum>
452
+ </file>
453
+ <file>
454
+ <filename>geo.rb</filename>
455
455
  <filetype>rb</filetype>
456
456
  <usage_type>resource</usage_type>
457
- <checksum>7873E62A</checksum>
457
+ <checksum>D999D942</checksum>
458
458
  </file>
459
459
  </files>
460
460
  </measure>
@@ -84,20 +84,21 @@ module TBD
84
84
  # vertices and wire.
85
85
  #
86
86
  # @param model [Topolys::Model] a model
87
- # @param pts [Array] a 1D array of 3D Topolys points (min 2x)
87
+ # @param pts [Array] a 1D array of 3D Topolys points (min 3x)
88
88
  #
89
89
  # @return [Hash] vx: 3D Topolys vertices Array; w: corresponding Topolys::Wire
90
90
  # @return [Hash] vx: nil; w: nil (if invalid input)
91
91
  def objects(model = nil, pts = [])
92
- mth = "OSut::#{__callee__}"
92
+ mth = "TBD::#{__callee__}"
93
93
  cl = Topolys::Model
94
94
  obj = { vx: nil, w: nil }
95
95
 
96
96
  return mismatch("model", model, cl, mth, DBG, obj) unless model.is_a?(cl)
97
97
  return mismatch("points", pts, Array, mth, DBG, obj) unless pts.is_a?(Array)
98
98
 
99
- log(DBG, "#{pts.size}? need +2 Topolys points (#{mth})") unless pts.size > 2
99
+ log(DBG, "#{pts.size}? need +3 Topolys points (#{mth})") unless pts.size > 2
100
100
  return obj unless pts.size > 2
101
+
101
102
  obj[:vx] = model.get_vertices(pts)
102
103
  obj[:w ] = model.get_wire(obj[:vx])
103
104
 
@@ -109,14 +110,14 @@ module TBD
109
110
  # As a side effect, it will - if successful - also populate a Topolys 'model'
110
111
  # with Topolys vertices, wires, holes. In rare cases such as domes of tubular
111
112
  # daylighting devices (TDDs), kids may be 'unhinged', i.e. not on same 3D
112
- # plane as 'dad(s)' - TBD corrects auch cases elsewhere.
113
+ # plane as 'dad(s)' - TBD corrects such cases elsewhere.
113
114
  #
114
115
  # @param model [Topolys::Model] a model
115
116
  # @param boys [Hash] a collection of TBD subsurfaces
116
117
  #
117
118
  # @return [Array] 3D Topolys wires of 'holes' (made by kids)
118
119
  def kids(model = nil, boys = {})
119
- mth = "OSut::#{__callee__}"
120
+ mth = "TBD::#{__callee__}"
120
121
  cl = Topolys::Model
121
122
  holes = []
122
123
 
@@ -146,7 +147,7 @@ module TBD
146
147
  #
147
148
  # @return [Array] 3D Topolys wires of 'holes' (made by kids)
148
149
  def dads(model = nil, pops = {})
149
- mth = "OSut::#{__callee__}"
150
+ mth = "TBD::#{__callee__}"
150
151
  cl = Topolys::Model
151
152
  holes = {}
152
153
 
@@ -183,7 +184,7 @@ module TBD
183
184
  # @return [Bool] true if successful
184
185
  # @return [Bool] false if invalid input
185
186
  def faces(s = {}, e = {})
186
- mth = "OSut::#{__callee__}"
187
+ mth = "TBD::#{__callee__}"
187
188
 
188
189
  return mismatch("surfaces", s, Hash, mth, DBG, false) unless s.is_a?(Hash)
189
190
  return mismatch("edges", e, Hash, mth, DBG, false) unless e.is_a?(Hash)
@@ -211,6 +212,42 @@ module TBD
211
212
  true
212
213
  end
213
214
 
215
+ ##
216
+ # Validate whether an OpenStudio planar surface is safe for TBD to process.
217
+ #
218
+ # @param s [OpenStudio::Model::PlanarSurface] a surface
219
+ #
220
+ # @return [Bool] true if valid surface
221
+ def validate(s = nil)
222
+ mth = "TBD::#{__callee__}"
223
+ cl = OpenStudio::Model::PlanarSurface
224
+
225
+ return mismatch("surface", s, cl, mth, DBG, false) unless s.is_a?(cl)
226
+
227
+ id = s.nameString
228
+ size = s.vertices.size
229
+ last = size - 1
230
+
231
+ log(ERR, "#{id} #{size} vertices? need +3 (#{mth})") unless size > 2
232
+ return false unless size > 2
233
+
234
+ [0, last].each do |i|
235
+ v1 = s.vertices[i]
236
+ v2 = s.vertices[i + 1] unless i == last
237
+ v2 = s.vertices.first if i == last
238
+ vector = v2 - v1
239
+ bad = vector.length < TOL
240
+
241
+ # As is, this comparison also catches collinear vertices (< 10mm apart)
242
+ # along an edge. Should avoid red-flagging such cases. TO DO.
243
+ log(ERR, "#{id}: < #{TOL}m (#{mth})") if bad
244
+ return false if bad
245
+ end
246
+
247
+ # Add as many extra tests as needed ...
248
+ true
249
+ end
250
+
214
251
  ##
215
252
  # Return site-specific (or true) Topolys normal vector of OpenStudio surface.
216
253
  #
@@ -250,6 +287,8 @@ module TBD
250
287
  return mismatch("model", model, cl1, mth) unless model.is_a?(cl1)
251
288
  return mismatch("surface", surface, cl2, mth) unless surface.is_a?(cl2)
252
289
 
290
+ return nil unless validate(surface)
291
+
253
292
  nom = surface.nameString
254
293
  surf = {}
255
294
  subs = {}
@@ -307,6 +346,8 @@ module TBD
307
346
  surf[:gross ] = surface.grossArea
308
347
 
309
348
  surface.subSurfaces.sort_by { |s| s.nameString }.each do |s|
349
+ next unless validate(s)
350
+
310
351
  id = s.nameString
311
352
  valid = s.vertices.size == 3 || s.vertices.size == 4
312
353
  log(ERR, "Skipping '#{id}': vertex # 3 or 4 (#{mth})") unless valid
@@ -334,7 +375,7 @@ module TBD
334
375
  log(ERR, "Skipping '#{id}': missing construction (#{mth})") if c.empty?
335
376
  next if c.empty?
336
377
  c = c.get.to_LayeredConstruction
337
- log(ERR, "Skipping '#{id}': must be a #{cl3} (#{mth})") if c.empty?
378
+ log(WRN, "Skipping '#{id}': subs limited to #{cl3} (#{mth})") if c.empty?
338
379
  next if c.empty?
339
380
  c = c.get
340
381
 
@@ -630,7 +671,7 @@ module TBD
630
671
  end
631
672
 
632
673
  foundation = OpenStudio::Model::FoundationKiva.new(model)
633
- foundation.setName("KIVA Foundation Floor '#{id}'")
674
+ foundation.setName("KIVA Foundation Floor #{id}")
634
675
 
635
676
  floor = model.getSurfaceByName(id)
636
677
  kiva = false if floor.empty?
@@ -844,7 +844,7 @@ module TBD
844
844
  up = ""
845
845
  up = "uprated " if m.nameString.include?(" uprated")
846
846
  m = m.clone(model).to_MasslessOpaqueMaterial.get
847
- m.setName("'#{id}' #{up}m tbd")
847
+ m.setName("#{id} #{up}m tbd")
848
848
  de_r = 0.001 unless de_r > 0.001
849
849
  loss = (de_u - 1 / de_r) * s[:net] unless de_r > 0.001
850
850
  m.setThermalResistance(de_r)
@@ -855,7 +855,7 @@ module TBD
855
855
  up = ""
856
856
  up = "uprated " if m.nameString.include?(" uprated")
857
857
  m = m.clone(model).to_StandardOpaqueMaterial.get
858
- m.setName("'#{id}' #{up}m tbd")
858
+ m.setName("#{id} #{up}m tbd")
859
859
  k = m.thermalConductivity
860
860
 
861
861
  if de_r > 0.001
@@ -1105,6 +1105,10 @@ module TBD
1105
1105
  horizontal = dz.abs < TOL
1106
1106
  vertical = dx < TOL && dy < TOL
1107
1107
  edge_V = terminal - origin
1108
+
1109
+ invalid("1x edge length < TOL", mth, 0, ERROR) if edge_V.magnitude < TOL
1110
+ next if edge_V.magnitude < TOL
1111
+
1108
1112
  edge_plane = Topolys::Plane3D.new(origin, edge_V)
1109
1113
 
1110
1114
  if vertical
@@ -1120,71 +1124,77 @@ module TBD
1120
1124
  # Loop through each linked wire and determine farthest point from
1121
1125
  # edge while ensuring candidate point is not aligned with edge.
1122
1126
  t_model.wires.each do |wire|
1123
- if surface[:wire] == wire.id # there should be a unique match
1124
- normal = tbd[:surfaces][id][:n] if tbd[:surfaces].key?(id)
1125
- normal = holes[id].attributes[:n] if holes.key?(id)
1126
- normal = shades[id][:n] if shades.key?(id)
1127
- farthest = Topolys::Point3D.new(origin.x, origin.y, origin.z)
1128
- farthest_V = farthest - origin # zero magnitude, initially
1129
- inverted = false
1130
- i_origin = wire.points.index(origin)
1131
- i_terminal = wire.points.index(terminal)
1132
- i_last = wire.points.size - 1
1133
-
1134
- if i_terminal == 0
1135
- inverted = true unless i_origin == i_last
1136
- elsif i_origin == i_last
1137
- inverted = true unless i_terminal == 0
1127
+ next unless surface[:wire] == wire.id # should be a unique match
1128
+ normal = tbd[:surfaces][id][:n] if tbd[:surfaces].key?(id)
1129
+ normal = holes[id].attributes[:n] if holes.key?(id)
1130
+ normal = shades[id][:n] if shades.key?(id)
1131
+ farthest = Topolys::Point3D.new(origin.x, origin.y, origin.z)
1132
+ farthest_V = farthest - origin # zero magnitude, initially
1133
+ inverted = false
1134
+ i_origin = wire.points.index(origin)
1135
+ i_terminal = wire.points.index(terminal)
1136
+ i_last = wire.points.size - 1
1137
+
1138
+ if i_terminal == 0
1139
+ inverted = true unless i_origin == i_last
1140
+ elsif i_origin == i_last
1141
+ inverted = true unless i_terminal == 0
1142
+ else
1143
+ inverted = true unless i_terminal - i_origin == 1
1144
+ end
1145
+
1146
+ wire.points.each do |point|
1147
+ next if point == origin
1148
+ next if point == terminal
1149
+
1150
+ point_on_plane = edge_plane.project(point)
1151
+ origin_point_V = point_on_plane - origin
1152
+ point_V_magnitude = origin_point_V.magnitude
1153
+ next unless point_V_magnitude > TOL
1154
+
1155
+ # Generate a plane between origin, terminal & point. Only consider
1156
+ # planes that share the same normal as wire.
1157
+ if inverted
1158
+ plane = Topolys::Plane3D.from_points(terminal, origin, point)
1138
1159
  else
1139
- inverted = true unless i_terminal - i_origin == 1
1160
+ plane = Topolys::Plane3D.from_points(origin, terminal, point)
1140
1161
  end
1141
1162
 
1142
- wire.points.each do |point|
1143
- next if point == origin
1144
- next if point == terminal
1145
- point_on_plane = edge_plane.project(point)
1146
- origin_point_V = point_on_plane - origin
1147
- point_V_magnitude = origin_point_V.magnitude
1148
- next unless point_V_magnitude > TOL
1149
-
1150
- # Generate a plane between origin, terminal & point. Only consider
1151
- # planes that share the same normal as wire.
1152
- if inverted
1153
- plane = Topolys::Plane3D.from_points(terminal, origin, point)
1154
- else
1155
- plane = Topolys::Plane3D.from_points(origin, terminal, point)
1156
- end
1163
+ next unless (normal.x - plane.normal.x).abs < TOL &&
1164
+ (normal.y - plane.normal.y).abs < TOL &&
1165
+ (normal.z - plane.normal.z).abs < TOL
1157
1166
 
1158
- next unless (normal.x - plane.normal.x).abs < TOL &&
1159
- (normal.y - plane.normal.y).abs < TOL &&
1160
- (normal.z - plane.normal.z).abs < TOL
1167
+ farther = point_V_magnitude > farthest_V.magnitude
1168
+ farthest = point if farther
1169
+ farthest_V = origin_point_V if farther
1170
+ end
1161
1171
 
1162
- farther = point_V_magnitude > farthest_V.magnitude
1163
- farthest = point if farther
1164
- farthest_V = origin_point_V if farther
1165
- end
1172
+ puts "ADDITION!!" if id == "ADDITION"
1173
+ puts "#{reference_V} vs #{farthest_V}" if id == "ADDITION"
1174
+
1175
+ angle = reference_V.angle(farthest_V)
1176
+ invalid("#{id} polar angle", mth, 0, ERROR, 0) if angle.nil?
1177
+ angle = 0 if angle.nil?
1166
1178
 
1167
- angle = reference_V.angle(farthest_V)
1168
- adjust = false # adjust angle [180°, 360°] if necessary
1179
+ adjust = false # adjust angle [180°, 360°] if necessary
1169
1180
 
1170
- if vertical
1181
+ if vertical
1182
+ adjust = true if east.dot(farthest_V) < -TOL
1183
+ else
1184
+ if north.dot(farthest_V).abs < TOL ||
1185
+ (north.dot(farthest_V).abs - 1).abs < TOL
1171
1186
  adjust = true if east.dot(farthest_V) < -TOL
1172
1187
  else
1173
- if north.dot(farthest_V).abs < TOL ||
1174
- (north.dot(farthest_V).abs - 1).abs < TOL
1175
- adjust = true if east.dot(farthest_V) < -TOL
1176
- else
1177
- adjust = true if north.dot(farthest_V) < -TOL
1178
- end
1188
+ adjust = true if north.dot(farthest_V) < -TOL
1179
1189
  end
1180
-
1181
- angle = 2 * Math::PI - angle if adjust
1182
- angle -= 2 * Math::PI if (angle - 2 * Math::PI).abs < TOL
1183
- surface[:angle] = angle
1184
- farthest_V.normalize!
1185
- surface[:polar] = farthest_V
1186
- surface[:normal] = normal
1187
1190
  end
1191
+
1192
+ angle = 2 * Math::PI - angle if adjust
1193
+ angle -= 2 * Math::PI if (angle - 2 * Math::PI).abs < TOL
1194
+ surface[:angle ] = angle
1195
+ farthest_V.normalize!
1196
+ surface[:polar ] = farthest_V
1197
+ surface[:normal] = normal
1188
1198
  end # end of edge-linked, surface-to-wire loop
1189
1199
  end # end of edge-linked surface loop
1190
1200
 
@@ -1993,8 +2003,6 @@ module TBD
1993
2003
  #
1994
2004
  # @return [Bool] true if TBD Measure is successful
1995
2005
  def exit(runner = nil, argh = {})
1996
- mth = "TBD::#{__callee__}"
1997
-
1998
2006
  # Generated files target a design context ( >= WARN ) ... change TBD log
1999
2007
  # level for debugging purposes. By default, log status is set < DBG
2000
2008
  # while log level is set @INF.
@@ -174,7 +174,6 @@ module TBD
174
174
  area = 0
175
175
  film = 100000000000000
176
176
  lc = nil
177
- uo = nil
178
177
  id = ""
179
178
  all = g[:op].downcase == "all wall constructions" ||
180
179
  g[:op].downcase == "all roof constructions" ||
@@ -236,7 +235,7 @@ module TBD
236
235
  end
237
236
 
238
237
  if coll.empty?
239
- log(ERR, "No construction to uprate - skipping (#{mth})")
238
+ log(ERR, "No #{label} construction to uprate - skipping (#{mth})")
240
239
  next
241
240
  elsif lc # valid layered construction - good to uprate!
242
241
  # Ensure lc is referenced by surface types == label.
@@ -948,7 +947,7 @@ module TBD
948
947
  model = "* modèle : #{ua[:file]}" if ua.key?(:file) && lang == :fr
949
948
  model += " (v#{ua[:version]})" if ua.key?(:version)
950
949
  report << model unless model.empty?
951
- report << "* TBD : v3.0.2"
950
+ report << "* TBD : v3.0.3"
952
951
  report << "* date : #{ua[:date]}"
953
952
 
954
953
  if lang == :en
data/lib/tbd/geo.rb CHANGED
@@ -84,20 +84,21 @@ module TBD
84
84
  # vertices and wire.
85
85
  #
86
86
  # @param model [Topolys::Model] a model
87
- # @param pts [Array] a 1D array of 3D Topolys points (min 2x)
87
+ # @param pts [Array] a 1D array of 3D Topolys points (min 3x)
88
88
  #
89
89
  # @return [Hash] vx: 3D Topolys vertices Array; w: corresponding Topolys::Wire
90
90
  # @return [Hash] vx: nil; w: nil (if invalid input)
91
91
  def objects(model = nil, pts = [])
92
- mth = "OSut::#{__callee__}"
92
+ mth = "TBD::#{__callee__}"
93
93
  cl = Topolys::Model
94
94
  obj = { vx: nil, w: nil }
95
95
 
96
96
  return mismatch("model", model, cl, mth, DBG, obj) unless model.is_a?(cl)
97
97
  return mismatch("points", pts, Array, mth, DBG, obj) unless pts.is_a?(Array)
98
98
 
99
- log(DBG, "#{pts.size}? need +2 Topolys points (#{mth})") unless pts.size > 2
99
+ log(DBG, "#{pts.size}? need +3 Topolys points (#{mth})") unless pts.size > 2
100
100
  return obj unless pts.size > 2
101
+
101
102
  obj[:vx] = model.get_vertices(pts)
102
103
  obj[:w ] = model.get_wire(obj[:vx])
103
104
 
@@ -109,14 +110,14 @@ module TBD
109
110
  # As a side effect, it will - if successful - also populate a Topolys 'model'
110
111
  # with Topolys vertices, wires, holes. In rare cases such as domes of tubular
111
112
  # daylighting devices (TDDs), kids may be 'unhinged', i.e. not on same 3D
112
- # plane as 'dad(s)' - TBD corrects auch cases elsewhere.
113
+ # plane as 'dad(s)' - TBD corrects such cases elsewhere.
113
114
  #
114
115
  # @param model [Topolys::Model] a model
115
116
  # @param boys [Hash] a collection of TBD subsurfaces
116
117
  #
117
118
  # @return [Array] 3D Topolys wires of 'holes' (made by kids)
118
119
  def kids(model = nil, boys = {})
119
- mth = "OSut::#{__callee__}"
120
+ mth = "TBD::#{__callee__}"
120
121
  cl = Topolys::Model
121
122
  holes = []
122
123
 
@@ -146,7 +147,7 @@ module TBD
146
147
  #
147
148
  # @return [Array] 3D Topolys wires of 'holes' (made by kids)
148
149
  def dads(model = nil, pops = {})
149
- mth = "OSut::#{__callee__}"
150
+ mth = "TBD::#{__callee__}"
150
151
  cl = Topolys::Model
151
152
  holes = {}
152
153
 
@@ -183,7 +184,7 @@ module TBD
183
184
  # @return [Bool] true if successful
184
185
  # @return [Bool] false if invalid input
185
186
  def faces(s = {}, e = {})
186
- mth = "OSut::#{__callee__}"
187
+ mth = "TBD::#{__callee__}"
187
188
 
188
189
  return mismatch("surfaces", s, Hash, mth, DBG, false) unless s.is_a?(Hash)
189
190
  return mismatch("edges", e, Hash, mth, DBG, false) unless e.is_a?(Hash)
@@ -211,6 +212,42 @@ module TBD
211
212
  true
212
213
  end
213
214
 
215
+ ##
216
+ # Validate whether an OpenStudio planar surface is safe for TBD to process.
217
+ #
218
+ # @param s [OpenStudio::Model::PlanarSurface] a surface
219
+ #
220
+ # @return [Bool] true if valid surface
221
+ def validate(s = nil)
222
+ mth = "TBD::#{__callee__}"
223
+ cl = OpenStudio::Model::PlanarSurface
224
+
225
+ return mismatch("surface", s, cl, mth, DBG, false) unless s.is_a?(cl)
226
+
227
+ id = s.nameString
228
+ size = s.vertices.size
229
+ last = size - 1
230
+
231
+ log(ERR, "#{id} #{size} vertices? need +3 (#{mth})") unless size > 2
232
+ return false unless size > 2
233
+
234
+ [0, last].each do |i|
235
+ v1 = s.vertices[i]
236
+ v2 = s.vertices[i + 1] unless i == last
237
+ v2 = s.vertices.first if i == last
238
+ vector = v2 - v1
239
+ bad = vector.length < TOL
240
+
241
+ # As is, this comparison also catches collinear vertices (< 10mm apart)
242
+ # along an edge. Should avoid red-flagging such cases. TO DO.
243
+ log(ERR, "#{id}: < #{TOL}m (#{mth})") if bad
244
+ return false if bad
245
+ end
246
+
247
+ # Add as many extra tests as needed ...
248
+ true
249
+ end
250
+
214
251
  ##
215
252
  # Return site-specific (or true) Topolys normal vector of OpenStudio surface.
216
253
  #
@@ -250,6 +287,8 @@ module TBD
250
287
  return mismatch("model", model, cl1, mth) unless model.is_a?(cl1)
251
288
  return mismatch("surface", surface, cl2, mth) unless surface.is_a?(cl2)
252
289
 
290
+ return nil unless validate(surface)
291
+
253
292
  nom = surface.nameString
254
293
  surf = {}
255
294
  subs = {}
@@ -307,6 +346,8 @@ module TBD
307
346
  surf[:gross ] = surface.grossArea
308
347
 
309
348
  surface.subSurfaces.sort_by { |s| s.nameString }.each do |s|
349
+ next unless validate(s)
350
+
310
351
  id = s.nameString
311
352
  valid = s.vertices.size == 3 || s.vertices.size == 4
312
353
  log(ERR, "Skipping '#{id}': vertex # 3 or 4 (#{mth})") unless valid
@@ -334,7 +375,7 @@ module TBD
334
375
  log(ERR, "Skipping '#{id}': missing construction (#{mth})") if c.empty?
335
376
  next if c.empty?
336
377
  c = c.get.to_LayeredConstruction
337
- log(ERR, "Skipping '#{id}': must be a #{cl3} (#{mth})") if c.empty?
378
+ log(WRN, "Skipping '#{id}': subs limited to #{cl3} (#{mth})") if c.empty?
338
379
  next if c.empty?
339
380
  c = c.get
340
381
 
@@ -630,7 +671,7 @@ module TBD
630
671
  end
631
672
 
632
673
  foundation = OpenStudio::Model::FoundationKiva.new(model)
633
- foundation.setName("KIVA Foundation Floor '#{id}'")
674
+ foundation.setName("KIVA Foundation Floor #{id}")
634
675
 
635
676
  floor = model.getSurfaceByName(id)
636
677
  kiva = false if floor.empty?
data/lib/tbd/psi.rb CHANGED
@@ -844,7 +844,7 @@ module TBD
844
844
  up = ""
845
845
  up = "uprated " if m.nameString.include?(" uprated")
846
846
  m = m.clone(model).to_MasslessOpaqueMaterial.get
847
- m.setName("'#{id}' #{up}m tbd")
847
+ m.setName("#{id} #{up}m tbd")
848
848
  de_r = 0.001 unless de_r > 0.001
849
849
  loss = (de_u - 1 / de_r) * s[:net] unless de_r > 0.001
850
850
  m.setThermalResistance(de_r)
@@ -855,7 +855,7 @@ module TBD
855
855
  up = ""
856
856
  up = "uprated " if m.nameString.include?(" uprated")
857
857
  m = m.clone(model).to_StandardOpaqueMaterial.get
858
- m.setName("'#{id}' #{up}m tbd")
858
+ m.setName("#{id} #{up}m tbd")
859
859
  k = m.thermalConductivity
860
860
 
861
861
  if de_r > 0.001
@@ -1105,6 +1105,10 @@ module TBD
1105
1105
  horizontal = dz.abs < TOL
1106
1106
  vertical = dx < TOL && dy < TOL
1107
1107
  edge_V = terminal - origin
1108
+
1109
+ invalid("1x edge length < TOL", mth, 0, ERROR) if edge_V.magnitude < TOL
1110
+ next if edge_V.magnitude < TOL
1111
+
1108
1112
  edge_plane = Topolys::Plane3D.new(origin, edge_V)
1109
1113
 
1110
1114
  if vertical
@@ -1120,71 +1124,77 @@ module TBD
1120
1124
  # Loop through each linked wire and determine farthest point from
1121
1125
  # edge while ensuring candidate point is not aligned with edge.
1122
1126
  t_model.wires.each do |wire|
1123
- if surface[:wire] == wire.id # there should be a unique match
1124
- normal = tbd[:surfaces][id][:n] if tbd[:surfaces].key?(id)
1125
- normal = holes[id].attributes[:n] if holes.key?(id)
1126
- normal = shades[id][:n] if shades.key?(id)
1127
- farthest = Topolys::Point3D.new(origin.x, origin.y, origin.z)
1128
- farthest_V = farthest - origin # zero magnitude, initially
1129
- inverted = false
1130
- i_origin = wire.points.index(origin)
1131
- i_terminal = wire.points.index(terminal)
1132
- i_last = wire.points.size - 1
1133
-
1134
- if i_terminal == 0
1135
- inverted = true unless i_origin == i_last
1136
- elsif i_origin == i_last
1137
- inverted = true unless i_terminal == 0
1127
+ next unless surface[:wire] == wire.id # should be a unique match
1128
+ normal = tbd[:surfaces][id][:n] if tbd[:surfaces].key?(id)
1129
+ normal = holes[id].attributes[:n] if holes.key?(id)
1130
+ normal = shades[id][:n] if shades.key?(id)
1131
+ farthest = Topolys::Point3D.new(origin.x, origin.y, origin.z)
1132
+ farthest_V = farthest - origin # zero magnitude, initially
1133
+ inverted = false
1134
+ i_origin = wire.points.index(origin)
1135
+ i_terminal = wire.points.index(terminal)
1136
+ i_last = wire.points.size - 1
1137
+
1138
+ if i_terminal == 0
1139
+ inverted = true unless i_origin == i_last
1140
+ elsif i_origin == i_last
1141
+ inverted = true unless i_terminal == 0
1142
+ else
1143
+ inverted = true unless i_terminal - i_origin == 1
1144
+ end
1145
+
1146
+ wire.points.each do |point|
1147
+ next if point == origin
1148
+ next if point == terminal
1149
+
1150
+ point_on_plane = edge_plane.project(point)
1151
+ origin_point_V = point_on_plane - origin
1152
+ point_V_magnitude = origin_point_V.magnitude
1153
+ next unless point_V_magnitude > TOL
1154
+
1155
+ # Generate a plane between origin, terminal & point. Only consider
1156
+ # planes that share the same normal as wire.
1157
+ if inverted
1158
+ plane = Topolys::Plane3D.from_points(terminal, origin, point)
1138
1159
  else
1139
- inverted = true unless i_terminal - i_origin == 1
1160
+ plane = Topolys::Plane3D.from_points(origin, terminal, point)
1140
1161
  end
1141
1162
 
1142
- wire.points.each do |point|
1143
- next if point == origin
1144
- next if point == terminal
1145
- point_on_plane = edge_plane.project(point)
1146
- origin_point_V = point_on_plane - origin
1147
- point_V_magnitude = origin_point_V.magnitude
1148
- next unless point_V_magnitude > TOL
1149
-
1150
- # Generate a plane between origin, terminal & point. Only consider
1151
- # planes that share the same normal as wire.
1152
- if inverted
1153
- plane = Topolys::Plane3D.from_points(terminal, origin, point)
1154
- else
1155
- plane = Topolys::Plane3D.from_points(origin, terminal, point)
1156
- end
1163
+ next unless (normal.x - plane.normal.x).abs < TOL &&
1164
+ (normal.y - plane.normal.y).abs < TOL &&
1165
+ (normal.z - plane.normal.z).abs < TOL
1157
1166
 
1158
- next unless (normal.x - plane.normal.x).abs < TOL &&
1159
- (normal.y - plane.normal.y).abs < TOL &&
1160
- (normal.z - plane.normal.z).abs < TOL
1167
+ farther = point_V_magnitude > farthest_V.magnitude
1168
+ farthest = point if farther
1169
+ farthest_V = origin_point_V if farther
1170
+ end
1161
1171
 
1162
- farther = point_V_magnitude > farthest_V.magnitude
1163
- farthest = point if farther
1164
- farthest_V = origin_point_V if farther
1165
- end
1172
+ puts "ADDITION!!" if id == "ADDITION"
1173
+ puts "#{reference_V} vs #{farthest_V}" if id == "ADDITION"
1174
+
1175
+ angle = reference_V.angle(farthest_V)
1176
+ invalid("#{id} polar angle", mth, 0, ERROR, 0) if angle.nil?
1177
+ angle = 0 if angle.nil?
1166
1178
 
1167
- angle = reference_V.angle(farthest_V)
1168
- adjust = false # adjust angle [180°, 360°] if necessary
1179
+ adjust = false # adjust angle [180°, 360°] if necessary
1169
1180
 
1170
- if vertical
1181
+ if vertical
1182
+ adjust = true if east.dot(farthest_V) < -TOL
1183
+ else
1184
+ if north.dot(farthest_V).abs < TOL ||
1185
+ (north.dot(farthest_V).abs - 1).abs < TOL
1171
1186
  adjust = true if east.dot(farthest_V) < -TOL
1172
1187
  else
1173
- if north.dot(farthest_V).abs < TOL ||
1174
- (north.dot(farthest_V).abs - 1).abs < TOL
1175
- adjust = true if east.dot(farthest_V) < -TOL
1176
- else
1177
- adjust = true if north.dot(farthest_V) < -TOL
1178
- end
1188
+ adjust = true if north.dot(farthest_V) < -TOL
1179
1189
  end
1180
-
1181
- angle = 2 * Math::PI - angle if adjust
1182
- angle -= 2 * Math::PI if (angle - 2 * Math::PI).abs < TOL
1183
- surface[:angle] = angle
1184
- farthest_V.normalize!
1185
- surface[:polar] = farthest_V
1186
- surface[:normal] = normal
1187
1190
  end
1191
+
1192
+ angle = 2 * Math::PI - angle if adjust
1193
+ angle -= 2 * Math::PI if (angle - 2 * Math::PI).abs < TOL
1194
+ surface[:angle ] = angle
1195
+ farthest_V.normalize!
1196
+ surface[:polar ] = farthest_V
1197
+ surface[:normal] = normal
1188
1198
  end # end of edge-linked, surface-to-wire loop
1189
1199
  end # end of edge-linked surface loop
1190
1200
 
@@ -1993,8 +2003,6 @@ module TBD
1993
2003
  #
1994
2004
  # @return [Bool] true if TBD Measure is successful
1995
2005
  def exit(runner = nil, argh = {})
1996
- mth = "TBD::#{__callee__}"
1997
-
1998
2006
  # Generated files target a design context ( >= WARN ) ... change TBD log
1999
2007
  # level for debugging purposes. By default, log status is set < DBG
2000
2008
  # while log level is set @INF.
data/lib/tbd/ua.rb CHANGED
@@ -174,7 +174,6 @@ module TBD
174
174
  area = 0
175
175
  film = 100000000000000
176
176
  lc = nil
177
- uo = nil
178
177
  id = ""
179
178
  all = g[:op].downcase == "all wall constructions" ||
180
179
  g[:op].downcase == "all roof constructions" ||
@@ -236,7 +235,7 @@ module TBD
236
235
  end
237
236
 
238
237
  if coll.empty?
239
- log(ERR, "No construction to uprate - skipping (#{mth})")
238
+ log(ERR, "No #{label} construction to uprate - skipping (#{mth})")
240
239
  next
241
240
  elsif lc # valid layered construction - good to uprate!
242
241
  # Ensure lc is referenced by surface types == label.
@@ -948,7 +947,7 @@ module TBD
948
947
  model = "* modèle : #{ua[:file]}" if ua.key?(:file) && lang == :fr
949
948
  model += " (v#{ua[:version]})" if ua.key?(:version)
950
949
  report << model unless model.empty?
951
- report << "* TBD : v3.0.2"
950
+ report << "* TBD : v3.0.3"
952
951
  report << "* date : #{ua[:date]}"
953
952
 
954
953
  if lang == :en
data/lib/tbd/version.rb CHANGED
@@ -21,5 +21,5 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  module TBD
24
- VERSION = "3.0.2".freeze
24
+ VERSION = "3.0.3".freeze
25
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tbd
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Bourgeois & Dan Macumber
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-09-19 00:00:00.000000000 Z
11
+ date: 2022-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: topolys
@@ -163,7 +163,7 @@ licenses:
163
163
  - MIT
164
164
  metadata:
165
165
  homepage_uri: https://github.com/rd2/tbd
166
- source_code_uri: https://github.com/rd2/tbd/tree/v3.0.2
166
+ source_code_uri: https://github.com/rd2/tbd/tree/v3.0.3
167
167
  bug_tracker_uri: https://github.com/rd2/tbd/issues
168
168
  post_install_message:
169
169
  rdoc_options: []