tbd 3.0.2 → 3.0.3
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/.github/workflows/pull_request.yml +5 -5
- data/lib/measures/tbd/measure.xml +12 -12
- data/lib/measures/tbd/resources/geo.rb +50 -9
- data/lib/measures/tbd/resources/psi.rb +66 -58
- data/lib/measures/tbd/resources/ua.rb +2 -3
- data/lib/tbd/geo.rb +50 -9
- data/lib/tbd/psi.rb +66 -58
- data/lib/tbd/ua.rb +2 -3
- data/lib/tbd/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fff7235e8a345417b5d6885b6b131f8a3ec91cdcad6cf06a691a5455ad04c842
|
4
|
+
data.tar.gz: 89b326a95347bcaf4415b6c5643e2ae0841aae2e238cdfc6326bc069fdff1134
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
-
|
6
|
+
- develop
|
7
7
|
|
8
8
|
jobs:
|
9
9
|
test_300x:
|
10
|
-
runs-on: ubuntu-
|
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-
|
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-
|
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-
|
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>
|
7
|
-
<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>
|
442
|
+
<filename>ua.rb</filename>
|
449
443
|
<filetype>rb</filetype>
|
450
444
|
<usage_type>resource</usage_type>
|
451
|
-
<checksum>
|
445
|
+
<checksum>7264CA34</checksum>
|
452
446
|
</file>
|
453
447
|
<file>
|
454
|
-
<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>
|
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
|
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 = "
|
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 +
|
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
|
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 = "
|
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 = "
|
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 = "
|
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(
|
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
|
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("
|
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("
|
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
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
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
|
-
|
1160
|
+
plane = Topolys::Plane3D.from_points(origin, terminal, point)
|
1140
1161
|
end
|
1141
1162
|
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
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
|
-
|
1159
|
-
|
1160
|
-
|
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
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
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
|
-
|
1168
|
-
adjust = false # adjust angle [180°, 360°] if necessary
|
1179
|
+
adjust = false # adjust angle [180°, 360°] if necessary
|
1169
1180
|
|
1170
|
-
|
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)
|
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.
|
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
|
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 = "
|
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 +
|
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
|
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 = "
|
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 = "
|
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 = "
|
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(
|
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
|
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("
|
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("
|
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
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
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
|
-
|
1160
|
+
plane = Topolys::Plane3D.from_points(origin, terminal, point)
|
1140
1161
|
end
|
1141
1162
|
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
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
|
-
|
1159
|
-
|
1160
|
-
|
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
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
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
|
-
|
1168
|
-
adjust = false # adjust angle [180°, 360°] if necessary
|
1179
|
+
adjust = false # adjust angle [180°, 360°] if necessary
|
1169
1180
|
|
1170
|
-
|
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)
|
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.
|
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
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.
|
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-
|
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.
|
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: []
|