osut 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/osut/utils.rb +218 -268
  3. data/lib/osut/version.rb +1 -1
  4. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6694166e055d664bf9ac9b0d155874e6bbf55e9ebdce91bf9566fe8f8c5c4365
4
- data.tar.gz: be5804b79d0df25606a9d4b0a41607d405d8786c89c4695661c56bfcef4d0a93
3
+ metadata.gz: be4500de4b7207ef0f1bb50342c4f63e12714bf22a17779f928b51adba0b862f
4
+ data.tar.gz: 3b4aa51dff5771c31fe5edc89d1326555447d2eaa7844b4944ff43b84b01cad2
5
5
  SHA512:
6
- metadata.gz: d1a0ecc532b9825109b8fc321721041db6da61058ed1bdc6d35736359ffade836843f8b15969d80ec9c7a8b27086ab1bd80b5bdade5b1c9129ad3a6a0c96b477
7
- data.tar.gz: '09e1f33ba5a139332ece204787fd262a3c89e641b8e4b7355a77509f49f166baace2b9686b4485f6c59bc28c1169b4b7d54bffcdfd185260dbdae066d23e0d7a'
6
+ metadata.gz: 47c84a2d136ec1b6552436cdcc555469813987485f64a2a4ddfa99788f5ee1d5b99d4b369b50766652b028f39204d3efccf7793276400e256d81492bbb85127f
7
+ data.tar.gz: af28329f47ff40ac971ec3b9333c0e21cb0ef293f1c2697aeebe96d0009acba34696a1f43c9bf07828b854ae8bd110b95b295bcb849dc8e0ff3e21236e4de308
data/lib/osut/utils.rb CHANGED
@@ -35,12 +35,12 @@ module OSut
35
35
 
36
36
  TOL = 0.01
37
37
  TOL2 = TOL * TOL
38
- NS = "nameString" # OpenStudio IdfObject nameString method
39
38
  DBG = OSut::DEBUG # mainly to flag invalid arguments to devs (buggy code)
40
39
  INF = OSut::INFO # not currently used in OSut
41
40
  WRN = OSut::WARN # WARN users of 'iffy' .osm inputs (yet not critical)
42
41
  ERR = OSut::ERROR # flag invalid .osm inputs (then exit via 'return')
43
42
  FTL = OSut::FATAL # not currently used in OSut
43
+ NS = "nameString" # OpenStudio IdfObject nameString method
44
44
 
45
45
  # This first set of utilities (~750 lines) help distinguishing spaces that
46
46
  # are directly vs indirectly CONDITIONED, vs SEMI-HEATED. The solution here
@@ -127,7 +127,7 @@ module OSut
127
127
  #
128
128
  # @return [Hash] min: (Float), max: (Float)
129
129
  # @return [Hash] min: nil, max: nil (if invalid input)
130
- def scheduleRulesetMinMax(sched)
130
+ def scheduleRulesetMinMax(sched = nil)
131
131
  # Largely inspired from David Goldwasser's
132
132
  # "schedule_ruleset_annual_min_max_value":
133
133
  #
@@ -138,7 +138,7 @@ module OSut
138
138
  cl = OpenStudio::Model::ScheduleRuleset
139
139
  res = { min: nil, max: nil }
140
140
 
141
- return invalid("sched", mth, 1, DBG, res) unless sched.respond_to?(NS)
141
+ return invalid("sched", mth, 1, DBG, res) unless sched.respond_to?(NS)
142
142
  id = sched.nameString
143
143
  return mismatch(id, sched, cl, mth, DBG, res) unless sched.is_a?(cl)
144
144
 
@@ -148,11 +148,11 @@ module OSut
148
148
 
149
149
  profiles.each do |profile|
150
150
  id = profile.nameString
151
+
151
152
  profile.values.each do |val|
152
- unless val.is_a?(Numeric)
153
- log(WRN, "Skipping non-numeric profile values in '#{id}' (#{mth})")
154
- next
155
- end
153
+ ok = val.is_a?(Numeric)
154
+ log(WRN, "Skipping non-numeric value in '#{id}' (#{mth})") unless ok
155
+ next unless ok
156
156
 
157
157
  res[:min] = val unless res[:min]
158
158
  res[:min] = val if res[:min] > val
@@ -174,7 +174,7 @@ module OSut
174
174
  #
175
175
  # @return [Hash] min: (Float), max: (Float)
176
176
  # @return [Hash] min: nil, max: nil (if invalid input)
177
- def scheduleConstantMinMax(sched)
177
+ def scheduleConstantMinMax(sched = nil)
178
178
  # Largely inspired from David Goldwasser's
179
179
  # "schedule_constant_annual_min_max_value":
180
180
  #
@@ -185,16 +185,14 @@ module OSut
185
185
  cl = OpenStudio::Model::ScheduleConstant
186
186
  res = { min: nil, max: nil }
187
187
 
188
- return invalid("sched", mth, 1, DBG, res) unless sched.respond_to?(NS)
188
+ return invalid("sched", mth, 1, DBG, res) unless sched.respond_to?(NS)
189
189
  id = sched.nameString
190
190
  return mismatch(id, sched, cl, mth, DBG, res) unless sched.is_a?(cl)
191
191
 
192
- unless sched.value.is_a?(Numeric)
193
- return mismatch("'#{id}' value", sched.value, Numeric, mth, ERR, res)
194
- else
195
- res[:min] = sched.value
196
- res[:max] = sched.value
197
- end
192
+ valid = sched.value.is_a?(Numeric)
193
+ mismatch("'#{id}' value", sched.value, Numeric, mth, ERR, res) unless valid
194
+ res[:min] = sched.value
195
+ res[:max] = sched.value
198
196
 
199
197
  res
200
198
  end
@@ -206,7 +204,7 @@ module OSut
206
204
  #
207
205
  # @return [Hash] min: (Float), max: (Float)
208
206
  # @return [Hash] min: nil, max: nil (if invalid input)
209
- def scheduleCompactMinMax(sched)
207
+ def scheduleCompactMinMax(sched = nil)
210
208
  # Largely inspired from Andrew Parker's
211
209
  # "schedule_compact_annual_min_max_value":
212
210
  #
@@ -219,7 +217,7 @@ module OSut
219
217
  prev_str = ""
220
218
  res = { min: nil, max: nil }
221
219
 
222
- return invalid("sched", mth, 1, DBG, res) unless sched.respond_to?(NS)
220
+ return invalid("sched", mth, 1, DBG, res) unless sched.respond_to?(NS)
223
221
  id = sched.nameString
224
222
  return mismatch(id, sched, cl, mth, DBG, res) unless sched.is_a?(cl)
225
223
 
@@ -233,13 +231,11 @@ module OSut
233
231
  end
234
232
 
235
233
  return empty("'#{id}' values", mth, ERR, res) if vals.empty?
236
-
237
- if vals.min.is_a?(Numeric) && vals.max.is_a?(Numeric)
238
- res[:min] = vals.min
239
- res[:max] = vals.max
240
- else
241
- log(ERR, "Non-numeric values in '#{id}' (#{mth})")
242
- end
234
+ ok = vals.min.is_a?(Numeric) && vals.max.is_a?(Numeric)
235
+ log(ERR, "Non-numeric values in '#{id}' (#{mth})") unless ok
236
+ return res unless ok
237
+ res[:min] = vals.min
238
+ res[:max] = vals.max
243
239
 
244
240
  res
245
241
  end
@@ -251,24 +247,22 @@ module OSut
251
247
  #
252
248
  # @return [Hash] min: (Float), max: (Float)
253
249
  # @return [Hash] min: nil, max: nil (if invalid input)
254
- def scheduleIntervalMinMax(sched)
250
+ def scheduleIntervalMinMax(sched = nil)
255
251
  mth = "OSut::#{__callee__}"
256
252
  cl = OpenStudio::Model::ScheduleInterval
257
253
  vals = []
258
254
  prev_str = ""
259
255
  res = { min: nil, max: nil }
260
256
 
261
- return invalid("sched", mth, 1, DBG, res) unless sched.respond_to?(NS)
257
+ return invalid("sched", mth, 1, DBG, res) unless sched.respond_to?(NS)
262
258
  id = sched.nameString
263
259
  return mismatch(id, sched, cl, mth, DBG, res) unless sched.is_a?(cl)
264
-
265
260
  vals = sched.timeSeries.values
266
- if vals.min.is_a?(Numeric) && vals.max.is_a?(Numeric)
267
- res[:min] = vals.min
268
- res[:max] = vals.max
269
- else
270
- log(ERR, "Non-numeric values in '#{id}' (#{mth})")
271
- end
261
+ ok = vals.min.is_a?(Numeric) && vals.max.is_a?(Numeric)
262
+ log(ERR, "Non-numeric values in '#{id}' (#{mth})") unless ok
263
+ return res unless ok
264
+ res[:min] = vals.min
265
+ res[:max] = vals.max
272
266
 
273
267
  res
274
268
  end
@@ -281,7 +275,7 @@ module OSut
281
275
  #
282
276
  # @return [Hash] spt: (Float), dual: (Bool)
283
277
  # @return [Hash] spt: nil, dual: false (if invalid input)
284
- def maxHeatScheduledSetpoint(zone)
278
+ def maxHeatScheduledSetpoint(zone = nil)
285
279
  # Largely inspired from Parker & Marrec's "thermal_zone_heated?" procedure.
286
280
  # The solution here is a tad more relaxed to encompass SEMI-HEATED zones as
287
281
  # per Canadian NECB criteria (basically any space with at least 10 W/m2 of
@@ -294,7 +288,7 @@ module OSut
294
288
  cl = OpenStudio::Model::ThermalZone
295
289
  res = { spt: nil, dual: false }
296
290
 
297
- return invalid("zone", mth, 1, DBG, res) unless zone.respond_to?(NS)
291
+ return invalid("zone", mth, 1, DBG, res) unless zone.respond_to?(NS)
298
292
  id = zone.nameString
299
293
  return mismatch(id, zone, cl, mth, DBG, res) unless zone.is_a?(cl)
300
294
 
@@ -310,9 +304,16 @@ module OSut
310
304
  end
311
305
  end
312
306
 
307
+ # unless equip.to_ZoneHVACLowTemperatureRadiantElectric.empty?
308
+ # equip = equip.to_ZoneHVACLowTemperatureRadiantElectric.get
309
+ #
310
+ # unless equip.heatingSetpointTemperatureSchedule.empty?
311
+ # sched = equip.heatingSetpointTemperatureSchedule.get
312
+ # end
313
+ # end
314
+
313
315
  unless equip.to_ZoneHVACLowTemperatureRadiantElectric.empty?
314
316
  equip = equip.to_ZoneHVACLowTemperatureRadiantElectric.get
315
-
316
317
  unless equip.heatingSetpointTemperatureSchedule.empty?
317
318
  sched = equip.heatingSetpointTemperatureSchedule.get
318
319
  end
@@ -377,13 +378,12 @@ module OSut
377
378
  end
378
379
  end
379
380
 
380
- return res if res[:spt]
381
381
  return res if zone.thermostat.empty?
382
- tstat = zone.thermostat.get
382
+ tstat = zone.thermostat.get
383
+ res[:spt] = nil
383
384
 
384
385
  unless tstat.to_ThermostatSetpointDualSetpoint.empty? &&
385
386
  tstat.to_ZoneControlThermostatStagedDualSetpoint.empty?
386
- res[:dual] = true
387
387
 
388
388
  unless tstat.to_ThermostatSetpointDualSetpoint.empty?
389
389
  tstat = tstat.to_ThermostatSetpointDualSetpoint.get
@@ -392,7 +392,8 @@ module OSut
392
392
  end
393
393
 
394
394
  unless tstat.heatingSetpointTemperatureSchedule.empty?
395
- sched = tstat.heatingSetpointTemperatureSchedule.get
395
+ res[:dual] = true
396
+ sched = tstat.heatingSetpointTemperatureSchedule.get
396
397
 
397
398
  unless sched.to_ScheduleRuleset.empty?
398
399
  sched = sched.to_ScheduleRuleset.get
@@ -456,11 +457,10 @@ module OSut
456
457
  #
457
458
  # @return [Bool] true if valid heating temperature setpoints
458
459
  # @return [Bool] false if invalid input
459
- def heatingTemperatureSetpoints?(model)
460
+ def heatingTemperatureSetpoints?(model = nil)
460
461
  mth = "OSut::#{__callee__}"
461
- cl = OpenStudio::Model::Model
462
+ cl = OpenStudio::Model::Model
462
463
 
463
- return invalid("model", mth, 1, DBG, false) unless model
464
464
  return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
465
465
 
466
466
  model.getThermalZones.each do |zone|
@@ -478,7 +478,7 @@ module OSut
478
478
  #
479
479
  # @return [Hash] spt: (Float), dual: (Bool)
480
480
  # @return [Hash] spt: nil, dual: false (if invalid input)
481
- def minCoolScheduledSetpoint(zone)
481
+ def minCoolScheduledSetpoint(zone = nil)
482
482
  # Largely inspired from Parker & Marrec's "thermal_zone_cooled?" procedure.
483
483
  #
484
484
  # github.com/NREL/openstudio-standards/blob/
@@ -488,7 +488,7 @@ module OSut
488
488
  cl = OpenStudio::Model::ThermalZone
489
489
  res = { spt: nil, dual: false }
490
490
 
491
- return invalid("zone", mth, 1, DBG, res) unless zone.respond_to?(NS)
491
+ return invalid("zone", mth, 1, DBG, res) unless zone.respond_to?(NS)
492
492
  id = zone.nameString
493
493
  return mismatch(id, zone, cl, mth, DBG, res) unless zone.is_a?(cl)
494
494
 
@@ -555,13 +555,12 @@ module OSut
555
555
  end
556
556
  end
557
557
 
558
- return res if res[:spt]
559
558
  return res if zone.thermostat.empty?
560
- tstat = zone.thermostat.get
559
+ tstat = zone.thermostat.get
560
+ res[:spt] = nil
561
561
 
562
562
  unless tstat.to_ThermostatSetpointDualSetpoint.empty? &&
563
563
  tstat.to_ZoneControlThermostatStagedDualSetpoint.empty?
564
- res[:dual] = true
565
564
 
566
565
  unless tstat.to_ThermostatSetpointDualSetpoint.empty?
567
566
  tstat = tstat.to_ThermostatSetpointDualSetpoint.get
@@ -570,7 +569,8 @@ module OSut
570
569
  end
571
570
 
572
571
  unless tstat.coolingSetpointTemperatureSchedule.empty?
573
- sched = tstat.coolingSetpointTemperatureSchedule.get
572
+ res[:dual] = true
573
+ sched = tstat.coolingSetpointTemperatureSchedule.get
574
574
 
575
575
  unless sched.to_ScheduleRuleset.empty?
576
576
  sched = sched.to_ScheduleRuleset.get
@@ -634,11 +634,10 @@ module OSut
634
634
  #
635
635
  # @return [Bool] true if valid cooling temperature setpoints
636
636
  # @return [Bool] false if invalid input
637
- def coolingTemperatureSetpoints?(model)
637
+ def coolingTemperatureSetpoints?(model = nil)
638
638
  mth = "OSut::#{__callee__}"
639
- cl = OpenStudio::Model::Model
639
+ cl = OpenStudio::Model::Model
640
640
 
641
- return invalid("model", mth, 1, DBG, false) unless model
642
641
  return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
643
642
 
644
643
  model.getThermalZones.each do |zone|
@@ -655,11 +654,10 @@ module OSut
655
654
  #
656
655
  # @return [Bool] true if model has one or more HVAC air loops
657
656
  # @return [Bool] false if invalid input
658
- def airLoopsHVAC?(model)
657
+ def airLoopsHVAC?(model = nil)
659
658
  mth = "OSut::#{__callee__}"
660
- cl = OpenStudio::Model::Model
659
+ cl = OpenStudio::Model::Model
661
660
 
662
- return invalid("model", mth, 1, DBG, false) unless model
663
661
  return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
664
662
 
665
663
  model.getThermalZones.each do |zone|
@@ -680,68 +678,59 @@ module OSut
680
678
  #
681
679
  # @return [Bool] true if should be tagged as plenum
682
680
  # @return [Bool] false if invalid input
683
- def plenum?(space, loops, setpoints)
681
+ def plenum?(space = nil, loops = nil, setpoints = nil)
684
682
  # Largely inspired from NREL's "space_plenum?" procedure:
685
683
  #
686
684
  # github.com/NREL/openstudio-standards/blob/
687
685
  # 58964222d25783e9da4ae292e375fb0d5c902aa5/lib/openstudio-standards/
688
686
  # standards/Standards.Space.rb#L1384
689
-
687
+ #
690
688
  # A space may be tagged as a plenum if:
691
689
  #
692
690
  # CASE A: its zone's "isPlenum" == true (SDK method) for a fully-developed
693
691
  # OpenStudio model (complete with HVAC air loops);
694
692
  #
695
- # CASE B: it's excluded from building's total floor area yet linked to a
696
- # zone holding an "inactive" thermostat (i.e., can't extract
697
- # valid setpoints);
698
- #
699
- # CASE C: it has a spacetype whose name holds "plenum", or a spacetype with
700
- # a 'standards spacetype' holding "plenum" (case insensitive); OR
693
+ # CASE B: (IN ABSENCE OF HVAC AIRLOOPS) if it's excluded from a building's
694
+ # total floor area yet linked to a zone holding an 'inactive'
695
+ # thermostat, i.e. can't extract valid setpoints; OR
701
696
  #
702
- # CASE D: its name string holds "plenum" (also case insensitive).
703
-
697
+ # CASE C: (IN ABSENCE OF HVAC AIRLOOPS & VALID SETPOINTS) it has "plenum"
698
+ # (case insensitive) as a spacetype (or as a spacetype's
699
+ # 'standards spacetype').
704
700
  mth = "OSut::#{__callee__}"
705
701
  cl = OpenStudio::Model::Space
706
702
 
707
- return invalid("space", mth, 1, DBG, false) unless space.respond_to?(NS)
703
+ return invalid("space", mth, 1, DBG, false) unless space.respond_to?(NS)
708
704
  id = space.nameString
709
705
  return mismatch(id, space, cl, mth, DBG, false) unless space.is_a?(cl)
710
-
711
706
  valid = loops == true || loops == false
712
- return invalid("loops", mth, 2, DBG, false) unless valid
713
-
707
+ return invalid("loops", mth, 2, DBG, false) unless valid
714
708
  valid = setpoints == true || setpoints == false
715
709
  return invalid("setpoints", mth, 3, DBG, false) unless valid
716
710
 
717
711
  unless space.thermalZone.empty?
718
712
  zone = space.thermalZone.get
719
- return true if zone.isPlenum && loops # CASE A
713
+ return zone.isPlenum if loops # A
720
714
 
721
715
  if setpoints
722
- heating = maxHeatScheduledSetpoint(zone)
723
- cooling = minCoolScheduledSetpoint(zone)
724
-
725
- return false if heating[:spt] || cooling[:spt] # directly conditioned
726
-
727
- unless space.partofTotalFloorArea
728
- return true if heating[:dual] || cooling[:dual] # CASE B
729
- end
716
+ heat = maxHeatScheduledSetpoint(zone)
717
+ cool = minCoolScheduledSetpoint(zone)
718
+ return false if heat[:spt] || cool[:spt] # directly conditioned
719
+ return heat[:dual] || cool[:dual] unless space.partofTotalFloorArea # B
720
+ return false
730
721
  end
731
722
  end
732
723
 
733
724
  unless space.spaceType.empty?
734
725
  type = space.spaceType.get
735
- return true if type.nameString.downcase.include?("plenum") # CASE C
726
+ return type.nameString.downcase == "plenum" # C
736
727
 
737
728
  unless type.standardsSpaceType.empty?
738
729
  type = type.standardsSpaceType.get
739
- return true if type.downcase.include?("plenum") # CASE C
730
+ return type.downcase == "plenum" # C
740
731
  end
741
732
  end
742
733
 
743
- return true if space.nameString.downcase.include?("plenum")
744
-
745
734
  false
746
735
  end
747
736
 
@@ -752,17 +741,16 @@ module OSut
752
741
  # @param avl [String] seasonal availability choice (optional, default "ON")
753
742
  #
754
743
  # @return [OpenStudio::Model::Schedule] HVAC availability sched
755
- # @return [nil] if invalid input
756
- def availabilitySchedule(model, avl = "")
757
- mth = "OSut::#{__callee__}"
758
- cl = OpenStudio::Model::Model
744
+ # @return [NilClass] if invalid input
745
+ def availabilitySchedule(model = nil, avl = "")
746
+ mth = "OSut::#{__callee__}"
747
+ cl = OpenStudio::Model::Model
748
+ limits = nil
759
749
 
760
- return invalid("model", mth, 1) unless model
761
- return mismatch("model", model, cl, mth) unless model.is_a?(cl)
750
+ return mismatch("model", model, cl, mth) unless model.is_a?(cl)
751
+ return invalid("availability", avl, 2, mth) unless avl.respond_to?(:to_s)
762
752
 
763
753
  # Either fetch availability ScheduleTypeLimits object, or create one.
764
- limits = nil
765
-
766
754
  model.getScheduleTypeLimitss.each do |l|
767
755
  break if limits
768
756
  next if l.lowerLimitValue.empty?
@@ -797,7 +785,7 @@ module OSut
797
785
  may01 = year.makeDate(OpenStudio::MonthOfYear.new("May"), 1)
798
786
  oct31 = year.makeDate(OpenStudio::MonthOfYear.new("Oct"), 31)
799
787
 
800
- case avl.downcase
788
+ case avl.to_s.downcase
801
789
  when "winter" # available from November 1 to April 30 (6 months)
802
790
  val = 1
803
791
  sch = off
@@ -838,29 +826,29 @@ module OSut
838
826
  unless schedule.empty?
839
827
  schedule = schedule.get
840
828
  default = schedule.defaultDaySchedule
841
- ok = ok && default.nameString == dft
842
- ok = ok && default.times.size == 1
843
- ok = ok && default.values.size == 1
844
- ok = ok && default.times.first == time
845
- ok = ok && default.values.first == val
829
+ ok = ok && default.nameString == dft
830
+ ok = ok && default.times.size == 1
831
+ ok = ok && default.values.size == 1
832
+ ok = ok && default.times.first == time
833
+ ok = ok && default.values.first == val
846
834
  rules = schedule.scheduleRules
847
835
  ok = ok && (rules.size == 0 || rules.size == 1)
848
836
 
849
837
  if rules.size == 1
850
838
  rule = rules.first
851
- ok = ok && rule.nameString == tag
839
+ ok = ok && rule.nameString == tag
852
840
  ok = ok && !rule.startDate.empty?
853
841
  ok = ok && !rule.endDate.empty?
854
- ok = ok && rule.startDate.get == may01
855
- ok = ok && rule.endDate.get == oct31
842
+ ok = ok && rule.startDate.get == may01
843
+ ok = ok && rule.endDate.get == oct31
856
844
  ok = ok && rule.applyAllDays
857
845
 
858
846
  d = rule.daySchedule
859
- ok = ok && d.nameString == day
860
- ok = ok && d.times.size == 1
861
- ok = ok && d.values.size == 1
847
+ ok = ok && d.nameString == day
848
+ ok = ok && d.times.size == 1
849
+ ok = ok && d.values.size == 1
862
850
  ok = ok && d.times.first.totalSeconds == secs
863
- ok = ok && d.values.first.to_i != val
851
+ ok = ok && d.values.first.to_i != val
864
852
  end
865
853
 
866
854
  return schedule if ok
@@ -869,38 +857,26 @@ module OSut
869
857
 
870
858
  schedule = OpenStudio::Model::ScheduleRuleset.new(model)
871
859
  schedule.setName(nom)
872
-
873
- unless schedule.setScheduleTypeLimits(limits)
874
- log(ERR, "'#{nom}': Can't set schedule type limits (#{mth})")
875
- return nil
876
- end
877
-
878
- unless schedule.defaultDaySchedule.addValue(time, val)
879
- log(ERR, "'#{nom}': Can't set default day schedule (#{mth})")
880
- return nil
881
- end
882
-
860
+ ok = schedule.setScheduleTypeLimits(limits)
861
+ log(ERR, "'#{nom}': Can't set schedule type limits (#{mth})") unless ok
862
+ return nil unless ok
863
+ ok = schedule.defaultDaySchedule.addValue(time, val)
864
+ log(ERR, "'#{nom}': Can't set default day schedule (#{mth})") unless ok
865
+ return nil unless ok
883
866
  schedule.defaultDaySchedule.setName(dft)
884
867
 
885
868
  unless tag.empty?
886
869
  rule = OpenStudio::Model::ScheduleRule.new(schedule, sch)
887
870
  rule.setName(tag)
888
-
889
- unless rule.setStartDate(may01)
890
- log(ERR, "'#{tag}': Can't set start date (#{mth})")
891
- return nil
892
- end
893
-
894
- unless rule.setEndDate(oct31)
895
- log(ERR, "'#{tag}': Can't set end date (#{mth})")
896
- return nil
897
- end
898
-
899
- unless rule.setApplyAllDays(true)
900
- log(ERR, "'#{tag}': Can't apply to all days (#{mth})")
901
- return nil
902
- end
903
-
871
+ ok = rule.setStartDate(may01)
872
+ log(ERR, "'#{tag}': Can't set start date (#{mth})") unless ok
873
+ return nil unless ok
874
+ ok = rule.setEndDate(oct31)
875
+ log(ERR, "'#{tag}': Can't set end date (#{mth})") unless ok
876
+ return nil unless ok
877
+ ok = rule.setApplyAllDays(true)
878
+ log(ERR, "'#{tag}': Can't apply to all days (#{mth})") unless ok
879
+ return nil unless ok
904
880
  rule.daySchedule.setName(day)
905
881
  end
906
882
 
@@ -911,43 +887,41 @@ module OSut
911
887
  # Validate if default construction set holds a base ground construction.
912
888
  #
913
889
  # @param set [OpenStudio::Model::DefaultConstructionSet] a default set
914
- # @param base [OpensStudio::Model::ConstructionBase] a construction base
915
- # @param ground [Bool] true if ground-facing surface
916
- # @param exterior [Bool] true if exterior-facing surface
917
- # @param type [String] a surface type
890
+ # @param bse [OpensStudio::Model::ConstructionBase] a construction base
891
+ # @param gr [Bool] true if ground-facing surface
892
+ # @param ex [Bool] true if exterior-facing surface
893
+ # @param typ [String] a surface type
918
894
  #
919
895
  # @return [Bool] true if default construction set holds construction
920
896
  # @return [Bool] false if invalid input
921
- def holdsConstruction?(set, base, ground = false, exterior = false, type = "")
897
+ def holdsConstruction?(set = nil, bse = nil, gr = false, ex = false, typ = "")
922
898
  mth = "OSut::#{__callee__}"
923
899
  cl1 = OpenStudio::Model::DefaultConstructionSet
924
900
  cl2 = OpenStudio::Model::ConstructionBase
925
901
 
926
- return invalid("set", mth, 1, DBG, false) unless set.respond_to?(NS)
902
+ return invalid("set", mth, 1, DBG, false) unless set.respond_to?(NS)
927
903
  id = set.nameString
928
- return mismatch(id, set, cl1, mth, DBG, false) unless set.is_a?(cl1)
929
-
930
- return invalid("base", mth, 2, DBG, false) unless base.respond_to?(NS)
931
- id = base.nameString
932
- return mismatch(id, base, cl2, mth, DBG, false) unless base.is_a?(cl2)
933
-
934
- valid = ground == true || ground == false
935
- return invalid("ground", mth, 3, DBG, false) unless valid
936
-
937
- valid = exterior == true || exterior == false
938
- return invalid("exterior", mth, 4, DBG, false) unless valid
939
-
940
- typ = type.to_s.downcase
941
- valid = typ == "floor" || typ == "wall" || typ == "roofceiling"
904
+ return mismatch(id, set, cl1, mth, DBG, false) unless set.is_a?(cl1)
905
+ return invalid("base", mth, 2, DBG, false) unless bse.respond_to?(NS)
906
+ id = bse.nameString
907
+ return mismatch(id, bse, cl2, mth, DBG, false) unless bse.is_a?(cl2)
908
+ valid = gr == true || gr == false
909
+ return invalid("ground", mth, 3, DBG, false) unless valid
910
+ valid = ex == true || ex == false
911
+ return invalid("exterior", mth, 4, DBG, false) unless valid
912
+ valid = typ.respond_to?(:to_s)
913
+ return invalid("surface typ", mth, 4, DBG, false) unless valid
914
+ type = typ.to_s.downcase
915
+ valid = type == "floor" || type == "wall" || type == "roofceiling"
942
916
  return invalid("surface type", mth, 5, DBG, false) unless valid
943
917
 
944
918
  constructions = nil
945
919
 
946
- if ground
920
+ if gr
947
921
  unless set.defaultGroundContactSurfaceConstructions.empty?
948
922
  constructions = set.defaultGroundContactSurfaceConstructions.get
949
923
  end
950
- elsif exterior
924
+ elsif ex
951
925
  unless set.defaultExteriorSurfaceConstructions.empty?
952
926
  constructions = set.defaultExteriorSurfaceConstructions.get
953
927
  end
@@ -959,21 +933,21 @@ module OSut
959
933
 
960
934
  return false unless constructions
961
935
 
962
- case typ
936
+ case type
963
937
  when "roofceiling"
964
938
  unless constructions.roofCeilingConstruction.empty?
965
939
  construction = constructions.roofCeilingConstruction.get
966
- return true if construction == base
940
+ return true if construction == bse
967
941
  end
968
942
  when "floor"
969
943
  unless constructions.floorConstruction.empty?
970
944
  construction = constructions.floorConstruction.get
971
- return true if construction == base
945
+ return true if construction == bse
972
946
  end
973
947
  else
974
948
  unless constructions.wallConstruction.empty?
975
949
  construction = constructions.wallConstruction.get
976
- return true if construction == base
950
+ return true if construction == bse
977
951
  end
978
952
  end
979
953
 
@@ -987,30 +961,25 @@ module OSut
987
961
  # @param s [OpenStudio::Model::Surface] a surface
988
962
  #
989
963
  # @return [OpenStudio::Model::DefaultConstructionSet] default set
990
- # @return [nil] if invalid input
991
- def defaultConstructionSet(model, s)
964
+ # @return [NilClass] if invalid input
965
+ def defaultConstructionSet(model = nil, s = nil)
992
966
  mth = "OSut::#{__callee__}"
993
967
  cl1 = OpenStudio::Model::Model
994
968
  cl2 = OpenStudio::Model::Surface
995
969
 
996
- return invalid("model", mth, 1) unless model
997
970
  return mismatch("model", model, cl1, mth) unless model.is_a?(cl1)
998
-
999
- return invalid("s", mth, 2) unless s.respond_to?(NS)
971
+ return invalid("s", mth, 2) unless s.respond_to?(NS)
1000
972
  id = s.nameString
1001
- return mismatch(id, s, cl2, mth) unless s.is_a?(cl2)
1002
-
1003
- unless s.isConstructionDefaulted
1004
- log(ERR, "'#{id}' construction not defaulted (#{mth})")
1005
- return nil
1006
- end
973
+ return mismatch(id, s, cl2, mth) unless s.is_a?(cl2)
1007
974
 
975
+ ok = s.isConstructionDefaulted
976
+ log(ERR, "'#{id}' construction not defaulted (#{mth})") unless ok
977
+ return nil unless ok
1008
978
  return empty("'#{id}' construction", mth, ERR) if s.construction.empty?
1009
979
  base = s.construction.get
1010
980
  return empty("'#{id}' space", mth, ERR) if s.space.empty?
1011
981
  space = s.space.get
1012
982
  type = s.surfaceType
1013
-
1014
983
  ground = false
1015
984
  exterior = false
1016
985
 
@@ -1060,7 +1029,7 @@ module OSut
1060
1029
  #
1061
1030
  # @return [Bool] true if all layers are valid
1062
1031
  # @return [Bool] false if invalid input
1063
- def standardOpaqueLayers?(lc)
1032
+ def standardOpaqueLayers?(lc = nil)
1064
1033
  mth = "OSut::#{__callee__}"
1065
1034
  cl = OpenStudio::Model::LayeredConstruction
1066
1035
 
@@ -1068,6 +1037,7 @@ module OSut
1068
1037
  return mismatch(lc.nameString, lc, cl, mth, DBG, false) unless lc.is_a?(cl)
1069
1038
 
1070
1039
  lc.layers.each { |m| return false if m.to_StandardOpaqueMaterial.empty? }
1040
+
1071
1041
  true
1072
1042
  end
1073
1043
 
@@ -1078,21 +1048,20 @@ module OSut
1078
1048
  #
1079
1049
  # @return [Double] total layered construction thickness
1080
1050
  # @return [Double] 0 if invalid input
1081
- def thickness(lc)
1051
+ def thickness(lc = nil)
1082
1052
  mth = "OSut::#{__callee__}"
1083
1053
  cl = OpenStudio::Model::LayeredConstruction
1084
1054
 
1085
- return invalid("lc", mth, 1, DBG, 0) unless lc.respond_to?(NS)
1055
+ return invalid("lc", mth, 1, DBG, 0.0) unless lc.respond_to?(NS)
1086
1056
  id = lc.nameString
1087
- return mismatch(id, lc, cl, mth, DBG, 0) unless lc.is_a?(cl)
1088
-
1089
- unless standardOpaqueLayers?(lc)
1090
- log(ERR, "'#{id}' holds non-StandardOpaqueMaterial(s) (#{mth})")
1091
- return 0
1092
- end
1057
+ return mismatch(id, lc, cl, mth, DBG, 0.0) unless lc.is_a?(cl)
1093
1058
 
1059
+ ok = standardOpaqueLayers?(lc)
1060
+ log(ERR, "'#{id}' holds non-StandardOpaqueMaterial(s) (#{mth})") unless ok
1061
+ return 0.0 unless ok
1094
1062
  thickness = 0.0
1095
1063
  lc.layers.each { |m| thickness += m.thickness }
1064
+
1096
1065
  thickness
1097
1066
  end
1098
1067
 
@@ -1122,11 +1091,12 @@ module OSut
1122
1091
  # The EnergyPlus Engineering calculations were designed for vertical windows
1123
1092
  # - not horizontal, slanted or domed surfaces - use with caution.
1124
1093
  mth = "OSut::#{__callee__}"
1125
- cl = Numeric
1094
+ cl = Numeric
1126
1095
 
1127
- return invalid("usi", mth, 1, DBG, 0.1216) unless usi
1128
- return mismatch("usi", usi, cl, mth, DBG, 0.1216) unless usi.is_a?(cl)
1129
- return invalid("usi", mth, 1, WRN, 0.1216) if usi > 8.0
1096
+ return mismatch("usi", usi, cl, mth, DBG, 0.1216) unless usi.is_a?(cl)
1097
+ return invalid("usi", mth, 1, WRN, 0.1216) if usi > 8.0
1098
+ return negative("usi", mth, WRN, 0.1216) if usi < 0
1099
+ return zero("usi", mth, WRN, 0.1216) if usi.abs < TOL
1130
1100
 
1131
1101
  rsi = 1 / (0.025342 * usi + 29.163853) # exterior film, next interior film
1132
1102
 
@@ -1142,64 +1112,49 @@ module OSut
1142
1112
  # @param t [Float] gas temperature (°C) (optional)
1143
1113
  #
1144
1114
  # @return [Float] calculated RSi at standard conditions (0 if error)
1145
- def rsi(lc, film, t = 0.0)
1115
+ def rsi(lc = nil, film = 0.0, t = 0.0)
1146
1116
  # This is adapted from BTAP's Material Module's "get_conductance" (P. Lopez)
1147
1117
  #
1148
1118
  # https://github.com/NREL/OpenStudio-Prototype-Buildings/blob/
1149
1119
  # c3d5021d8b7aef43e560544699fb5c559e6b721d/lib/btap/measures/
1150
1120
  # btap_equest_converter/envelope.rb#L122
1151
-
1152
1121
  mth = "OSut::#{__callee__}"
1153
1122
  cl1 = OpenStudio::Model::LayeredConstruction
1154
1123
  cl2 = Numeric
1155
1124
 
1156
- return invalid("lc", mth, 1, DBG, 0) unless lc.respond_to?(NS)
1125
+ return invalid("lc", mth, 1, DBG, 0.0) unless lc.respond_to?(NS)
1157
1126
  id = lc.nameString
1158
- return mismatch(id, lc, cl1, mth, DBG, 0) unless lc.is_a?(cl1)
1159
-
1160
- return invalid("film", mth, 2, DBG, 0) unless film
1161
- return invalid("temperature", mth, 3, DBG, 0) unless t
1162
-
1163
- return mismatch("film", film, cl2, mth, DBG, 0) unless film.is_a?(cl2)
1164
- return mismatch("temperature", t, cl2, mth, DBG, 0) unless t.is_a?(cl2)
1165
-
1166
- tt = t + 273.0 # °C to K
1167
- return negative("temp K", mth, DBG, 0) if tt < 0
1168
- return negative("film", mth, DBG, 0) if film < 0
1127
+ return mismatch(id, lc, cl1, mth, DBG, 0.0) unless lc.is_a?(cl1)
1128
+ return mismatch("film", film, cl2, mth, DBG, 0.0) unless film.is_a?(cl2)
1129
+ return mismatch("temp K", t, cl2, mth, DBG, 0.0) unless t.is_a?(cl2)
1130
+ t += 273.0 # °C to K
1131
+ return negative("temp K", mth, DBG, 0.0) if t < 0
1132
+ return negative("film", mth, DBG, 0.0) if film < 0
1169
1133
 
1170
1134
  rsi = film
1171
1135
 
1172
1136
  lc.layers.each do |m|
1173
1137
  # Fenestration materials first (ignoring shades, screens, etc.)
1174
- unless m.to_SimpleGlazing.empty?
1175
- return 1 / m.to_SimpleGlazing.get.uFactor # no need to loop
1176
- end
1177
- unless m.to_StandardGlazing.empty?
1178
- rsi += m.to_StandardGlazing.get.thermalResistance
1179
- end
1180
- unless m.to_RefractionExtinctionGlazing.empty?
1181
- rsi += m.to_RefractionExtinctionGlazing.get.thermalResistance
1182
- end
1183
- unless m.to_Gas.empty?
1184
- rsi += m.to_Gas.get.getThermalResistance(tt)
1185
- end
1186
- unless m.to_GasMixture.empty?
1187
- rsi += m.to_GasMixture.get.getThermalResistance(tt)
1188
- end
1138
+ empty = m.to_SimpleGlazing.empty?
1139
+ return 1 / m.to_SimpleGlazing.get.uFactor unless empty
1140
+ empty = m.to_StandardGlazing.empty?
1141
+ rsi += m.to_StandardGlazing.get.thermalResistance unless empty
1142
+ empty = m.to_RefractionExtinctionGlazing.empty?
1143
+ rsi += m.to_RefractionExtinctionGlazing.get.thermalResistance unless empty
1144
+ empty = m.to_Gas.empty?
1145
+ rsi += m.to_Gas.get.getThermalResistance(t) unless empty
1146
+ empty = m.to_GasMixture.empty?
1147
+ rsi += m.to_GasMixture.get.getThermalResistance(t) unless empty
1189
1148
 
1190
1149
  # Opaque materials next.
1191
- unless m.to_StandardOpaqueMaterial.empty?
1192
- rsi += m.to_StandardOpaqueMaterial.get.thermalResistance
1193
- end
1194
- unless m.to_MasslessOpaqueMaterial.empty?
1195
- rsi += m.to_MasslessOpaqueMaterial.get.thermalResistance
1196
- end
1197
- unless m.to_RoofVegetation.empty?
1198
- rsi += m.to_RoofVegetation.get.thermalResistance
1199
- end
1200
- unless m.to_AirGap.empty?
1201
- rsi += m.to_AirGap.get.thermalResistance
1202
- end
1150
+ empty = m.to_StandardOpaqueMaterial.empty?
1151
+ rsi += m.to_StandardOpaqueMaterial.get.thermalResistance unless empty
1152
+ empty = m.to_MasslessOpaqueMaterial.empty?
1153
+ rsi += m.to_MasslessOpaqueMaterial.get.thermalResistance unless empty
1154
+ empty = m.to_RoofVegetation.empty?
1155
+ rsi += m.to_RoofVegetation.get.thermalResistance unless empty
1156
+ empty = m.to_AirGap.empty?
1157
+ rsi += m.to_AirGap.get.thermalResistance unless empty
1203
1158
  end
1204
1159
 
1205
1160
  rsi
@@ -1215,18 +1170,17 @@ module OSut
1215
1170
  #
1216
1171
  # @return [Hash] index: (Integer), type: (:standard or :massless), r: (Float)
1217
1172
  # @return [Hash] index: nil, type: nil, r: 0 (if invalid input)
1218
- def insulatingLayer(lc)
1173
+ def insulatingLayer(lc = nil)
1219
1174
  mth = "OSut::#{__callee__}"
1220
1175
  cl = OpenStudio::Model::LayeredConstruction
1221
1176
  res = { index: nil, type: nil, r: 0.0 }
1222
1177
  i = 0 # iterator
1223
1178
 
1224
- return invalid("lc", mth, 1, DBG, res) unless lc.respond_to?(NS)
1179
+ return invalid("lc", mth, 1, DBG, res) unless lc.respond_to?(NS)
1225
1180
  id = lc.nameString
1226
1181
  return mismatch(id, lc, cl1, mth, DBG, res) unless lc.is_a?(cl)
1227
1182
 
1228
1183
  lc.layers.each do |m|
1229
-
1230
1184
  unless m.to_MasslessOpaqueMaterial.empty?
1231
1185
  m = m.to_MasslessOpaqueMaterial.get
1232
1186
 
@@ -1269,15 +1223,13 @@ module OSut
1269
1223
  #
1270
1224
  # @return [Hash] t: (OpenStudio::Transformation), r: Float
1271
1225
  # @return [Hash] t: nil, r: nil (if invalid input)
1272
- def transforms(model, group)
1226
+ def transforms(model = nil, group = nil)
1273
1227
  mth = "OSut::#{__callee__}"
1274
1228
  cl1 = OpenStudio::Model::Model
1275
1229
  cl2 = OpenStudio::Model::PlanarSurfaceGroup
1276
1230
  res = { t: nil, r: nil }
1277
1231
 
1278
- return invalid("model", mth, 1, DBG, res) unless model
1279
1232
  return mismatch("model", model, cl1, mth, DBG, res) unless model.is_a?(cl1)
1280
-
1281
1233
  return invalid("group", mth, 2, DBG, res) unless group.respond_to?(NS)
1282
1234
  id = group.nameString
1283
1235
  return mismatch(id, group, cl2, mth, DBG, res) unless group.is_a?(cl2)
@@ -1294,16 +1246,14 @@ module OSut
1294
1246
  # @param pts [Array] an OpenStudio Point3D array/vector
1295
1247
  #
1296
1248
  # @return [Array] flattened OpenStudio 3D points
1297
- def flatZ(pts)
1249
+ def flatZ(pts = nil)
1298
1250
  mth = "OSut::#{__callee__}"
1299
1251
  cl1 = OpenStudio::Point3dVector
1300
1252
  cl2 = OpenStudio::Point3d
1301
- v = OpenStudio::Point3dVector.new
1253
+ v = OpenStudio::Point3dVector.new
1302
1254
 
1303
- return invalid("points", mth, 1, DBG, v) unless pts
1304
1255
  valid = pts.is_a?(cl1) || pts.is_a?(Array)
1305
- return mismatch("points", pts, cl1, mth, DBG, v) unless valid
1306
-
1256
+ return mismatch("points", pts, cl1, mth, DBG, v) unless valid
1307
1257
  pts.each { |pt| mismatch("pt", pt, cl2, mth, ERR, v) unless pt.is_a?(cl2) }
1308
1258
  pts.each { |pt| v << OpenStudio::Point3d.new(pt.x, pt.y, 0) }
1309
1259
 
@@ -1320,53 +1270,49 @@ module OSut
1320
1270
  #
1321
1271
  # @return [Bool] true if 1st polygon fits entirely within the 2nd polygon
1322
1272
  # @return [Bool] false if invalid input
1323
- def fits?(p1, p2, id1 = "", id2 = "")
1273
+ def fits?(p1 = nil, p2 = nil, id1 = "", id2 = "")
1324
1274
  mth = "OSut::#{__callee__}"
1325
1275
  cl1 = OpenStudio::Point3dVector
1326
1276
  cl2 = OpenStudio::Point3d
1327
1277
  a = false
1278
+
1279
+ return invalid("id1", mth, 3, DBG, a) unless id1.respond_to?(:to_s)
1280
+ return invalid("id2", mth, 4, DBG, a) unless id2.respond_to?(:to_s)
1328
1281
  i1 = id1.to_s
1329
1282
  i2 = id2.to_s
1330
1283
  i1 = "poly1" if i1.empty?
1331
1284
  i2 = "poly2" if i2.empty?
1332
-
1333
- return invalid(i1, mth, 1, DBG, a) unless p1
1334
- valid = p1.is_a?(cl1) || p1.is_a?(Array)
1335
- return mismatch(i1, p1, cl1, mth, DBG, a) unless valid
1285
+ valid1 = p1.is_a?(cl1) || p1.is_a?(Array)
1286
+ valid2 = p2.is_a?(cl1) || p2.is_a?(Array)
1287
+ return mismatch(i1, p1, cl1, mth, DBG, a) unless valid1
1288
+ return mismatch(i2, p2, cl1, mth, DBG, a) unless valid2
1336
1289
  return empty(i1, mth, ERR, a) if p1.empty?
1337
-
1338
- return invalid(i2, mth, 2, DBG, a) unless p2
1339
- valid = p2.is_a?(cl1) || p2.is_a?(Array)
1340
- return mismatch(i2, p2, cl1, mth, DBG, a) unless valid
1341
1290
  return empty(i2, mth, ERR, a) if p2.empty?
1342
-
1343
1291
  p1.each { |v| return mismatch(i1, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1344
1292
  p2.each { |v| return mismatch(i2, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1345
1293
 
1346
1294
  ft = OpenStudio::Transformation::alignFace(p1).inverse
1347
-
1348
1295
  ft_p1 = flatZ( (ft * p1).reverse )
1349
1296
  return false if ft_p1.empty?
1350
1297
  area1 = OpenStudio::getArea(ft_p1)
1351
- return empty(i1, mth, ERR, a) if area1.empty?
1298
+ return empty("#{i1} area", mth, ERR, a) if area1.empty?
1352
1299
  area1 = area1.get
1353
-
1354
1300
  ft_p2 = flatZ( (ft * p2).reverse )
1355
1301
  return false if ft_p2.empty?
1356
1302
  area2 = OpenStudio::getArea(ft_p2)
1357
- return empty(i2, mth, ERR, a) if area2.empty?
1303
+ return empty("#{i2} area", mth, ERR, a) if area2.empty?
1358
1304
  area2 = area2.get
1359
-
1360
1305
  union = OpenStudio::join(ft_p1, ft_p2, TOL2)
1361
1306
  return false if union.empty?
1362
1307
  union = union.get
1363
1308
  area = OpenStudio::getArea(union)
1364
- return empty("union", mth, ERR, a) if area.empty?
1309
+ return empty("#{i1}:#{i2} union area", mth, ERR, a) if area.empty?
1365
1310
  area = area.get
1366
1311
 
1367
1312
  return false if area < TOL
1368
1313
  return true if (area - area2).abs < TOL
1369
1314
  return false if (area - area2).abs > TOL
1315
+
1370
1316
  true
1371
1317
  end
1372
1318
 
@@ -1380,51 +1326,55 @@ module OSut
1380
1326
  #
1381
1327
  # @return Returns true if polygons overlaps (or either fits into the other)
1382
1328
  # @return [Bool] false if invalid input
1383
- def overlaps?(p1, p2, id1 = "", id2 = "")
1329
+ def overlaps?(p1 = nil, p2 = nil, id1 = "", id2 = "")
1384
1330
  mth = "OSut::#{__callee__}"
1385
1331
  cl1 = OpenStudio::Point3dVector
1386
1332
  cl2 = OpenStudio::Point3d
1387
1333
  a = false
1388
- i1 = id1.to_s
1389
- i2 = id2.to_s
1390
- i1 = "poly1" if i1.empty?
1391
- i2 = "poly2" if i2.empty?
1392
1334
 
1393
- return invalid(i1, mth, 1, DBG, a) unless p1
1394
- valid = p1.is_a?(cl1) || p1.is_a?(Array)
1395
- return mismatch(i1, p1, cl1, mth, DBG, a) unless valid
1335
+ return invalid("id1", mth, 3, DBG, a) unless id1.respond_to?(:to_s)
1336
+ return invalid("id2", mth, 4, DBG, a) unless id2.respond_to?(:to_s)
1337
+ i1 = id1.to_s
1338
+ i2 = id2.to_s
1339
+ i1 = "poly1" if i1.empty?
1340
+ i2 = "poly2" if i2.empty?
1341
+ valid1 = p1.is_a?(cl1) || p1.is_a?(Array)
1342
+ valid2 = p2.is_a?(cl1) || p2.is_a?(Array)
1343
+ return mismatch(i1, p1, cl1, mth, DBG, a) unless valid1
1344
+ return mismatch(i2, p2, cl1, mth, DBG, a) unless valid2
1396
1345
  return empty(i1, mth, ERR, a) if p1.empty?
1397
-
1398
- return invalid(i2, mth, 2, DBG, a) unless p2
1399
- valid = p2.is_a?(cl1) || p2.is_a?(Array)
1400
- return mismatch(i2, p2, cl1, mth, DBG, a) unless valid
1401
1346
  return empty(i2, mth, ERR, a) if p2.empty?
1402
-
1403
1347
  p1.each { |v| return mismatch(i1, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1404
1348
  p2.each { |v| return mismatch(i2, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1405
1349
 
1406
1350
  ft = OpenStudio::Transformation::alignFace(p1).inverse
1407
-
1408
1351
  ft_p1 = flatZ( (ft * p1).reverse )
1409
1352
  return false if ft_p1.empty?
1410
1353
  area1 = OpenStudio::getArea(ft_p1)
1411
- return empty(i1, mth, ERR, a) if area1.empty?
1354
+ return empty("#{i1} area", mth, ERR, a) if area1.empty?
1412
1355
  area1 = area1.get
1413
-
1414
1356
  ft_p2 = flatZ( (ft * p2).reverse )
1415
1357
  return false if ft_p2.empty?
1416
1358
  area2 = OpenStudio::getArea(ft_p2)
1417
- return empty(i2, mth, ERR, a) if area2.empty?
1359
+ return empty("#{i2} area", mth, ERR, a) if area2.empty?
1418
1360
  area2 = area2.get
1419
-
1420
1361
  union = OpenStudio::join(ft_p1, ft_p2, TOL2)
1421
1362
  return false if union.empty?
1422
1363
  union = union.get
1423
1364
  area = OpenStudio::getArea(union)
1424
- return empty("union", mth, ERR, a) if area.empty?
1365
+ return empty("#{i1}:#{i2} union area", mth, ERR, a) if area.empty?
1425
1366
  area = area.get
1426
1367
 
1427
1368
  return false if area < TOL
1369
+
1428
1370
  true
1429
1371
  end
1372
+
1373
+ ##
1374
+ # Callback when other modules extend OSlg
1375
+ #
1376
+ # @param base [Object] instance or class object
1377
+ def self.extended(base)
1378
+ base.send(:include, self)
1379
+ end
1430
1380
  end
data/lib/osut/version.rb CHANGED
@@ -29,5 +29,5 @@
29
29
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
30
 
31
31
  module OSut
32
- VERSION = "0.2.2".freeze
32
+ VERSION = "0.2.3".freeze
33
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osut
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Bourgeois
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-29 00:00:00.000000000 Z
11
+ date: 2022-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oslg
@@ -90,7 +90,7 @@ licenses:
90
90
  - BSD-3-Clause
91
91
  metadata:
92
92
  homepage_uri: https://github.com/rd2/osut
93
- source_code_uri: https://github.com/rd2/osut/tree/v0.2.2
93
+ source_code_uri: https://github.com/rd2/osut/tree/v0.2.3
94
94
  bug_tracker_uri: https://github.com/rd2/osut/issues
95
95
  post_install_message:
96
96
  rdoc_options: []