osut 0.2.2 → 0.2.6

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 +211 -271
  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: 04db3007d81904287c0c36c677270a1e1bc5e45375d3b37bbac9068d51c43754
4
+ data.tar.gz: aafa2bb60bdfaf5b5df03e99aca0cb3181f6270468a761652d2b6d0438037daa
5
5
  SHA512:
6
- metadata.gz: d1a0ecc532b9825109b8fc321721041db6da61058ed1bdc6d35736359ffade836843f8b15969d80ec9c7a8b27086ab1bd80b5bdade5b1c9129ad3a6a0c96b477
7
- data.tar.gz: '09e1f33ba5a139332ece204787fd262a3c89e641b8e4b7355a77509f49f166baace2b9686b4485f6c59bc28c1169b4b7d54bffcdfd185260dbdae066d23e0d7a'
6
+ metadata.gz: ca60fd088aa106b52bfe15fe4fd352c55a2c79083f95212d7e9e37250f1832b3b94f0dc415e664f5289e837600aad2ab936eff304e9d5cdba0c3a977a56cd7d0
7
+ data.tar.gz: 8aa96937a55520f99a9c122d0fe98f69ec97d45cbe0cac1e0db7626b48cacee5d65f801eb4a9791e3a6953d64473555b981b3bd9646d7948b6b5c26b2d02e041
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
 
@@ -312,10 +306,7 @@ module OSut
312
306
 
313
307
  unless equip.to_ZoneHVACLowTemperatureRadiantElectric.empty?
314
308
  equip = equip.to_ZoneHVACLowTemperatureRadiantElectric.get
315
-
316
- unless equip.heatingSetpointTemperatureSchedule.empty?
317
- sched = equip.heatingSetpointTemperatureSchedule.get
318
- end
309
+ sched = equip.heatingSetpointTemperatureSchedule
319
310
  end
320
311
 
321
312
  unless equip.to_ZoneHVACLowTempRadiantConstFlow.empty?
@@ -377,13 +368,12 @@ module OSut
377
368
  end
378
369
  end
379
370
 
380
- return res if res[:spt]
381
371
  return res if zone.thermostat.empty?
382
- tstat = zone.thermostat.get
372
+ tstat = zone.thermostat.get
373
+ res[:spt] = nil
383
374
 
384
375
  unless tstat.to_ThermostatSetpointDualSetpoint.empty? &&
385
376
  tstat.to_ZoneControlThermostatStagedDualSetpoint.empty?
386
- res[:dual] = true
387
377
 
388
378
  unless tstat.to_ThermostatSetpointDualSetpoint.empty?
389
379
  tstat = tstat.to_ThermostatSetpointDualSetpoint.get
@@ -392,7 +382,8 @@ module OSut
392
382
  end
393
383
 
394
384
  unless tstat.heatingSetpointTemperatureSchedule.empty?
395
- sched = tstat.heatingSetpointTemperatureSchedule.get
385
+ res[:dual] = true
386
+ sched = tstat.heatingSetpointTemperatureSchedule.get
396
387
 
397
388
  unless sched.to_ScheduleRuleset.empty?
398
389
  sched = sched.to_ScheduleRuleset.get
@@ -456,11 +447,10 @@ module OSut
456
447
  #
457
448
  # @return [Bool] true if valid heating temperature setpoints
458
449
  # @return [Bool] false if invalid input
459
- def heatingTemperatureSetpoints?(model)
450
+ def heatingTemperatureSetpoints?(model = nil)
460
451
  mth = "OSut::#{__callee__}"
461
- cl = OpenStudio::Model::Model
452
+ cl = OpenStudio::Model::Model
462
453
 
463
- return invalid("model", mth, 1, DBG, false) unless model
464
454
  return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
465
455
 
466
456
  model.getThermalZones.each do |zone|
@@ -478,7 +468,7 @@ module OSut
478
468
  #
479
469
  # @return [Hash] spt: (Float), dual: (Bool)
480
470
  # @return [Hash] spt: nil, dual: false (if invalid input)
481
- def minCoolScheduledSetpoint(zone)
471
+ def minCoolScheduledSetpoint(zone = nil)
482
472
  # Largely inspired from Parker & Marrec's "thermal_zone_cooled?" procedure.
483
473
  #
484
474
  # github.com/NREL/openstudio-standards/blob/
@@ -488,7 +478,7 @@ module OSut
488
478
  cl = OpenStudio::Model::ThermalZone
489
479
  res = { spt: nil, dual: false }
490
480
 
491
- return invalid("zone", mth, 1, DBG, res) unless zone.respond_to?(NS)
481
+ return invalid("zone", mth, 1, DBG, res) unless zone.respond_to?(NS)
492
482
  id = zone.nameString
493
483
  return mismatch(id, zone, cl, mth, DBG, res) unless zone.is_a?(cl)
494
484
 
@@ -555,13 +545,12 @@ module OSut
555
545
  end
556
546
  end
557
547
 
558
- return res if res[:spt]
559
548
  return res if zone.thermostat.empty?
560
- tstat = zone.thermostat.get
549
+ tstat = zone.thermostat.get
550
+ res[:spt] = nil
561
551
 
562
552
  unless tstat.to_ThermostatSetpointDualSetpoint.empty? &&
563
553
  tstat.to_ZoneControlThermostatStagedDualSetpoint.empty?
564
- res[:dual] = true
565
554
 
566
555
  unless tstat.to_ThermostatSetpointDualSetpoint.empty?
567
556
  tstat = tstat.to_ThermostatSetpointDualSetpoint.get
@@ -570,7 +559,8 @@ module OSut
570
559
  end
571
560
 
572
561
  unless tstat.coolingSetpointTemperatureSchedule.empty?
573
- sched = tstat.coolingSetpointTemperatureSchedule.get
562
+ res[:dual] = true
563
+ sched = tstat.coolingSetpointTemperatureSchedule.get
574
564
 
575
565
  unless sched.to_ScheduleRuleset.empty?
576
566
  sched = sched.to_ScheduleRuleset.get
@@ -634,11 +624,10 @@ module OSut
634
624
  #
635
625
  # @return [Bool] true if valid cooling temperature setpoints
636
626
  # @return [Bool] false if invalid input
637
- def coolingTemperatureSetpoints?(model)
627
+ def coolingTemperatureSetpoints?(model = nil)
638
628
  mth = "OSut::#{__callee__}"
639
- cl = OpenStudio::Model::Model
629
+ cl = OpenStudio::Model::Model
640
630
 
641
- return invalid("model", mth, 1, DBG, false) unless model
642
631
  return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
643
632
 
644
633
  model.getThermalZones.each do |zone|
@@ -655,11 +644,10 @@ module OSut
655
644
  #
656
645
  # @return [Bool] true if model has one or more HVAC air loops
657
646
  # @return [Bool] false if invalid input
658
- def airLoopsHVAC?(model)
647
+ def airLoopsHVAC?(model = nil)
659
648
  mth = "OSut::#{__callee__}"
660
- cl = OpenStudio::Model::Model
649
+ cl = OpenStudio::Model::Model
661
650
 
662
- return invalid("model", mth, 1, DBG, false) unless model
663
651
  return mismatch("model", model, cl, mth, DBG, false) unless model.is_a?(cl)
664
652
 
665
653
  model.getThermalZones.each do |zone|
@@ -680,68 +668,59 @@ module OSut
680
668
  #
681
669
  # @return [Bool] true if should be tagged as plenum
682
670
  # @return [Bool] false if invalid input
683
- def plenum?(space, loops, setpoints)
671
+ def plenum?(space = nil, loops = nil, setpoints = nil)
684
672
  # Largely inspired from NREL's "space_plenum?" procedure:
685
673
  #
686
674
  # github.com/NREL/openstudio-standards/blob/
687
675
  # 58964222d25783e9da4ae292e375fb0d5c902aa5/lib/openstudio-standards/
688
676
  # standards/Standards.Space.rb#L1384
689
-
677
+ #
690
678
  # A space may be tagged as a plenum if:
691
679
  #
692
680
  # CASE A: its zone's "isPlenum" == true (SDK method) for a fully-developed
693
681
  # OpenStudio model (complete with HVAC air loops);
694
682
  #
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
683
+ # CASE B: (IN ABSENCE OF HVAC AIRLOOPS) if it's excluded from a building's
684
+ # total floor area yet linked to a zone holding an 'inactive'
685
+ # thermostat, i.e. can't extract valid setpoints; OR
701
686
  #
702
- # CASE D: its name string holds "plenum" (also case insensitive).
703
-
687
+ # CASE C: (IN ABSENCE OF HVAC AIRLOOPS & VALID SETPOINTS) it has "plenum"
688
+ # (case insensitive) as a spacetype (or as a spacetype's
689
+ # 'standards spacetype').
704
690
  mth = "OSut::#{__callee__}"
705
691
  cl = OpenStudio::Model::Space
706
692
 
707
- return invalid("space", mth, 1, DBG, false) unless space.respond_to?(NS)
693
+ return invalid("space", mth, 1, DBG, false) unless space.respond_to?(NS)
708
694
  id = space.nameString
709
695
  return mismatch(id, space, cl, mth, DBG, false) unless space.is_a?(cl)
710
-
711
696
  valid = loops == true || loops == false
712
- return invalid("loops", mth, 2, DBG, false) unless valid
713
-
697
+ return invalid("loops", mth, 2, DBG, false) unless valid
714
698
  valid = setpoints == true || setpoints == false
715
699
  return invalid("setpoints", mth, 3, DBG, false) unless valid
716
700
 
717
701
  unless space.thermalZone.empty?
718
702
  zone = space.thermalZone.get
719
- return true if zone.isPlenum && loops # CASE A
703
+ return zone.isPlenum if loops # A
720
704
 
721
705
  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
706
+ heat = maxHeatScheduledSetpoint(zone)
707
+ cool = minCoolScheduledSetpoint(zone)
708
+ return false if heat[:spt] || cool[:spt] # directly conditioned
709
+ return heat[:dual] || cool[:dual] unless space.partofTotalFloorArea # B
710
+ return false
730
711
  end
731
712
  end
732
713
 
733
714
  unless space.spaceType.empty?
734
715
  type = space.spaceType.get
735
- return true if type.nameString.downcase.include?("plenum") # CASE C
716
+ return type.nameString.downcase == "plenum" # C
736
717
 
737
718
  unless type.standardsSpaceType.empty?
738
719
  type = type.standardsSpaceType.get
739
- return true if type.downcase.include?("plenum") # CASE C
720
+ return type.downcase == "plenum" # C
740
721
  end
741
722
  end
742
723
 
743
- return true if space.nameString.downcase.include?("plenum")
744
-
745
724
  false
746
725
  end
747
726
 
@@ -752,17 +731,16 @@ module OSut
752
731
  # @param avl [String] seasonal availability choice (optional, default "ON")
753
732
  #
754
733
  # @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
734
+ # @return [NilClass] if invalid input
735
+ def availabilitySchedule(model = nil, avl = "")
736
+ mth = "OSut::#{__callee__}"
737
+ cl = OpenStudio::Model::Model
738
+ limits = nil
759
739
 
760
- return invalid("model", mth, 1) unless model
761
- return mismatch("model", model, cl, mth) unless model.is_a?(cl)
740
+ return mismatch("model", model, cl, mth) unless model.is_a?(cl)
741
+ return invalid("availability", avl, 2, mth) unless avl.respond_to?(:to_s)
762
742
 
763
743
  # Either fetch availability ScheduleTypeLimits object, or create one.
764
- limits = nil
765
-
766
744
  model.getScheduleTypeLimitss.each do |l|
767
745
  break if limits
768
746
  next if l.lowerLimitValue.empty?
@@ -797,7 +775,7 @@ module OSut
797
775
  may01 = year.makeDate(OpenStudio::MonthOfYear.new("May"), 1)
798
776
  oct31 = year.makeDate(OpenStudio::MonthOfYear.new("Oct"), 31)
799
777
 
800
- case avl.downcase
778
+ case avl.to_s.downcase
801
779
  when "winter" # available from November 1 to April 30 (6 months)
802
780
  val = 1
803
781
  sch = off
@@ -838,29 +816,29 @@ module OSut
838
816
  unless schedule.empty?
839
817
  schedule = schedule.get
840
818
  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
819
+ ok = ok && default.nameString == dft
820
+ ok = ok && default.times.size == 1
821
+ ok = ok && default.values.size == 1
822
+ ok = ok && default.times.first == time
823
+ ok = ok && default.values.first == val
846
824
  rules = schedule.scheduleRules
847
825
  ok = ok && (rules.size == 0 || rules.size == 1)
848
826
 
849
827
  if rules.size == 1
850
828
  rule = rules.first
851
- ok = ok && rule.nameString == tag
829
+ ok = ok && rule.nameString == tag
852
830
  ok = ok && !rule.startDate.empty?
853
831
  ok = ok && !rule.endDate.empty?
854
- ok = ok && rule.startDate.get == may01
855
- ok = ok && rule.endDate.get == oct31
832
+ ok = ok && rule.startDate.get == may01
833
+ ok = ok && rule.endDate.get == oct31
856
834
  ok = ok && rule.applyAllDays
857
835
 
858
836
  d = rule.daySchedule
859
- ok = ok && d.nameString == day
860
- ok = ok && d.times.size == 1
861
- ok = ok && d.values.size == 1
837
+ ok = ok && d.nameString == day
838
+ ok = ok && d.times.size == 1
839
+ ok = ok && d.values.size == 1
862
840
  ok = ok && d.times.first.totalSeconds == secs
863
- ok = ok && d.values.first.to_i != val
841
+ ok = ok && d.values.first.to_i != val
864
842
  end
865
843
 
866
844
  return schedule if ok
@@ -869,38 +847,26 @@ module OSut
869
847
 
870
848
  schedule = OpenStudio::Model::ScheduleRuleset.new(model)
871
849
  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
-
850
+ ok = schedule.setScheduleTypeLimits(limits)
851
+ log(ERR, "'#{nom}': Can't set schedule type limits (#{mth})") unless ok
852
+ return nil unless ok
853
+ ok = schedule.defaultDaySchedule.addValue(time, val)
854
+ log(ERR, "'#{nom}': Can't set default day schedule (#{mth})") unless ok
855
+ return nil unless ok
883
856
  schedule.defaultDaySchedule.setName(dft)
884
857
 
885
858
  unless tag.empty?
886
859
  rule = OpenStudio::Model::ScheduleRule.new(schedule, sch)
887
860
  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
-
861
+ ok = rule.setStartDate(may01)
862
+ log(ERR, "'#{tag}': Can't set start date (#{mth})") unless ok
863
+ return nil unless ok
864
+ ok = rule.setEndDate(oct31)
865
+ log(ERR, "'#{tag}': Can't set end date (#{mth})") unless ok
866
+ return nil unless ok
867
+ ok = rule.setApplyAllDays(true)
868
+ log(ERR, "'#{tag}': Can't apply to all days (#{mth})") unless ok
869
+ return nil unless ok
904
870
  rule.daySchedule.setName(day)
905
871
  end
906
872
 
@@ -911,43 +877,41 @@ module OSut
911
877
  # Validate if default construction set holds a base ground construction.
912
878
  #
913
879
  # @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
880
+ # @param bse [OpensStudio::Model::ConstructionBase] a construction base
881
+ # @param gr [Bool] true if ground-facing surface
882
+ # @param ex [Bool] true if exterior-facing surface
883
+ # @param typ [String] a surface type
918
884
  #
919
885
  # @return [Bool] true if default construction set holds construction
920
886
  # @return [Bool] false if invalid input
921
- def holdsConstruction?(set, base, ground = false, exterior = false, type = "")
887
+ def holdsConstruction?(set = nil, bse = nil, gr = false, ex = false, typ = "")
922
888
  mth = "OSut::#{__callee__}"
923
889
  cl1 = OpenStudio::Model::DefaultConstructionSet
924
890
  cl2 = OpenStudio::Model::ConstructionBase
925
891
 
926
- return invalid("set", mth, 1, DBG, false) unless set.respond_to?(NS)
892
+ return invalid("set", mth, 1, DBG, false) unless set.respond_to?(NS)
927
893
  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"
894
+ return mismatch(id, set, cl1, mth, DBG, false) unless set.is_a?(cl1)
895
+ return invalid("base", mth, 2, DBG, false) unless bse.respond_to?(NS)
896
+ id = bse.nameString
897
+ return mismatch(id, bse, cl2, mth, DBG, false) unless bse.is_a?(cl2)
898
+ valid = gr == true || gr == false
899
+ return invalid("ground", mth, 3, DBG, false) unless valid
900
+ valid = ex == true || ex == false
901
+ return invalid("exterior", mth, 4, DBG, false) unless valid
902
+ valid = typ.respond_to?(:to_s)
903
+ return invalid("surface typ", mth, 4, DBG, false) unless valid
904
+ type = typ.to_s.downcase
905
+ valid = type == "floor" || type == "wall" || type == "roofceiling"
942
906
  return invalid("surface type", mth, 5, DBG, false) unless valid
943
907
 
944
908
  constructions = nil
945
909
 
946
- if ground
910
+ if gr
947
911
  unless set.defaultGroundContactSurfaceConstructions.empty?
948
912
  constructions = set.defaultGroundContactSurfaceConstructions.get
949
913
  end
950
- elsif exterior
914
+ elsif ex
951
915
  unless set.defaultExteriorSurfaceConstructions.empty?
952
916
  constructions = set.defaultExteriorSurfaceConstructions.get
953
917
  end
@@ -959,21 +923,21 @@ module OSut
959
923
 
960
924
  return false unless constructions
961
925
 
962
- case typ
926
+ case type
963
927
  when "roofceiling"
964
928
  unless constructions.roofCeilingConstruction.empty?
965
929
  construction = constructions.roofCeilingConstruction.get
966
- return true if construction == base
930
+ return true if construction == bse
967
931
  end
968
932
  when "floor"
969
933
  unless constructions.floorConstruction.empty?
970
934
  construction = constructions.floorConstruction.get
971
- return true if construction == base
935
+ return true if construction == bse
972
936
  end
973
937
  else
974
938
  unless constructions.wallConstruction.empty?
975
939
  construction = constructions.wallConstruction.get
976
- return true if construction == base
940
+ return true if construction == bse
977
941
  end
978
942
  end
979
943
 
@@ -987,30 +951,25 @@ module OSut
987
951
  # @param s [OpenStudio::Model::Surface] a surface
988
952
  #
989
953
  # @return [OpenStudio::Model::DefaultConstructionSet] default set
990
- # @return [nil] if invalid input
991
- def defaultConstructionSet(model, s)
954
+ # @return [NilClass] if invalid input
955
+ def defaultConstructionSet(model = nil, s = nil)
992
956
  mth = "OSut::#{__callee__}"
993
957
  cl1 = OpenStudio::Model::Model
994
958
  cl2 = OpenStudio::Model::Surface
995
959
 
996
- return invalid("model", mth, 1) unless model
997
960
  return mismatch("model", model, cl1, mth) unless model.is_a?(cl1)
998
-
999
- return invalid("s", mth, 2) unless s.respond_to?(NS)
961
+ return invalid("s", mth, 2) unless s.respond_to?(NS)
1000
962
  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
963
+ return mismatch(id, s, cl2, mth) unless s.is_a?(cl2)
1007
964
 
965
+ ok = s.isConstructionDefaulted
966
+ log(ERR, "'#{id}' construction not defaulted (#{mth})") unless ok
967
+ return nil unless ok
1008
968
  return empty("'#{id}' construction", mth, ERR) if s.construction.empty?
1009
969
  base = s.construction.get
1010
970
  return empty("'#{id}' space", mth, ERR) if s.space.empty?
1011
971
  space = s.space.get
1012
972
  type = s.surfaceType
1013
-
1014
973
  ground = false
1015
974
  exterior = false
1016
975
 
@@ -1060,7 +1019,7 @@ module OSut
1060
1019
  #
1061
1020
  # @return [Bool] true if all layers are valid
1062
1021
  # @return [Bool] false if invalid input
1063
- def standardOpaqueLayers?(lc)
1022
+ def standardOpaqueLayers?(lc = nil)
1064
1023
  mth = "OSut::#{__callee__}"
1065
1024
  cl = OpenStudio::Model::LayeredConstruction
1066
1025
 
@@ -1068,6 +1027,7 @@ module OSut
1068
1027
  return mismatch(lc.nameString, lc, cl, mth, DBG, false) unless lc.is_a?(cl)
1069
1028
 
1070
1029
  lc.layers.each { |m| return false if m.to_StandardOpaqueMaterial.empty? }
1030
+
1071
1031
  true
1072
1032
  end
1073
1033
 
@@ -1078,21 +1038,20 @@ module OSut
1078
1038
  #
1079
1039
  # @return [Double] total layered construction thickness
1080
1040
  # @return [Double] 0 if invalid input
1081
- def thickness(lc)
1041
+ def thickness(lc = nil)
1082
1042
  mth = "OSut::#{__callee__}"
1083
1043
  cl = OpenStudio::Model::LayeredConstruction
1084
1044
 
1085
- return invalid("lc", mth, 1, DBG, 0) unless lc.respond_to?(NS)
1045
+ return invalid("lc", mth, 1, DBG, 0.0) unless lc.respond_to?(NS)
1086
1046
  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
1047
+ return mismatch(id, lc, cl, mth, DBG, 0.0) unless lc.is_a?(cl)
1093
1048
 
1049
+ ok = standardOpaqueLayers?(lc)
1050
+ log(ERR, "'#{id}' holds non-StandardOpaqueMaterial(s) (#{mth})") unless ok
1051
+ return 0.0 unless ok
1094
1052
  thickness = 0.0
1095
1053
  lc.layers.each { |m| thickness += m.thickness }
1054
+
1096
1055
  thickness
1097
1056
  end
1098
1057
 
@@ -1122,11 +1081,12 @@ module OSut
1122
1081
  # The EnergyPlus Engineering calculations were designed for vertical windows
1123
1082
  # - not horizontal, slanted or domed surfaces - use with caution.
1124
1083
  mth = "OSut::#{__callee__}"
1125
- cl = Numeric
1084
+ cl = Numeric
1126
1085
 
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
1086
+ return mismatch("usi", usi, cl, mth, DBG, 0.1216) unless usi.is_a?(cl)
1087
+ return invalid("usi", mth, 1, WRN, 0.1216) if usi > 8.0
1088
+ return negative("usi", mth, WRN, 0.1216) if usi < 0
1089
+ return zero("usi", mth, WRN, 0.1216) if usi.abs < TOL
1130
1090
 
1131
1091
  rsi = 1 / (0.025342 * usi + 29.163853) # exterior film, next interior film
1132
1092
 
@@ -1142,64 +1102,49 @@ module OSut
1142
1102
  # @param t [Float] gas temperature (°C) (optional)
1143
1103
  #
1144
1104
  # @return [Float] calculated RSi at standard conditions (0 if error)
1145
- def rsi(lc, film, t = 0.0)
1105
+ def rsi(lc = nil, film = 0.0, t = 0.0)
1146
1106
  # This is adapted from BTAP's Material Module's "get_conductance" (P. Lopez)
1147
1107
  #
1148
1108
  # https://github.com/NREL/OpenStudio-Prototype-Buildings/blob/
1149
1109
  # c3d5021d8b7aef43e560544699fb5c559e6b721d/lib/btap/measures/
1150
1110
  # btap_equest_converter/envelope.rb#L122
1151
-
1152
1111
  mth = "OSut::#{__callee__}"
1153
1112
  cl1 = OpenStudio::Model::LayeredConstruction
1154
1113
  cl2 = Numeric
1155
1114
 
1156
- return invalid("lc", mth, 1, DBG, 0) unless lc.respond_to?(NS)
1115
+ return invalid("lc", mth, 1, DBG, 0.0) unless lc.respond_to?(NS)
1157
1116
  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
1117
+ return mismatch(id, lc, cl1, mth, DBG, 0.0) unless lc.is_a?(cl1)
1118
+ return mismatch("film", film, cl2, mth, DBG, 0.0) unless film.is_a?(cl2)
1119
+ return mismatch("temp K", t, cl2, mth, DBG, 0.0) unless t.is_a?(cl2)
1120
+ t += 273.0 # °C to K
1121
+ return negative("temp K", mth, DBG, 0.0) if t < 0
1122
+ return negative("film", mth, DBG, 0.0) if film < 0
1169
1123
 
1170
1124
  rsi = film
1171
1125
 
1172
1126
  lc.layers.each do |m|
1173
1127
  # 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
1128
+ empty = m.to_SimpleGlazing.empty?
1129
+ return 1 / m.to_SimpleGlazing.get.uFactor unless empty
1130
+ empty = m.to_StandardGlazing.empty?
1131
+ rsi += m.to_StandardGlazing.get.thermalResistance unless empty
1132
+ empty = m.to_RefractionExtinctionGlazing.empty?
1133
+ rsi += m.to_RefractionExtinctionGlazing.get.thermalResistance unless empty
1134
+ empty = m.to_Gas.empty?
1135
+ rsi += m.to_Gas.get.getThermalResistance(t) unless empty
1136
+ empty = m.to_GasMixture.empty?
1137
+ rsi += m.to_GasMixture.get.getThermalResistance(t) unless empty
1189
1138
 
1190
1139
  # 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
1140
+ empty = m.to_StandardOpaqueMaterial.empty?
1141
+ rsi += m.to_StandardOpaqueMaterial.get.thermalResistance unless empty
1142
+ empty = m.to_MasslessOpaqueMaterial.empty?
1143
+ rsi += m.to_MasslessOpaqueMaterial.get.thermalResistance unless empty
1144
+ empty = m.to_RoofVegetation.empty?
1145
+ rsi += m.to_RoofVegetation.get.thermalResistance unless empty
1146
+ empty = m.to_AirGap.empty?
1147
+ rsi += m.to_AirGap.get.thermalResistance unless empty
1203
1148
  end
1204
1149
 
1205
1150
  rsi
@@ -1215,18 +1160,17 @@ module OSut
1215
1160
  #
1216
1161
  # @return [Hash] index: (Integer), type: (:standard or :massless), r: (Float)
1217
1162
  # @return [Hash] index: nil, type: nil, r: 0 (if invalid input)
1218
- def insulatingLayer(lc)
1163
+ def insulatingLayer(lc = nil)
1219
1164
  mth = "OSut::#{__callee__}"
1220
1165
  cl = OpenStudio::Model::LayeredConstruction
1221
1166
  res = { index: nil, type: nil, r: 0.0 }
1222
1167
  i = 0 # iterator
1223
1168
 
1224
- return invalid("lc", mth, 1, DBG, res) unless lc.respond_to?(NS)
1169
+ return invalid("lc", mth, 1, DBG, res) unless lc.respond_to?(NS)
1225
1170
  id = lc.nameString
1226
1171
  return mismatch(id, lc, cl1, mth, DBG, res) unless lc.is_a?(cl)
1227
1172
 
1228
1173
  lc.layers.each do |m|
1229
-
1230
1174
  unless m.to_MasslessOpaqueMaterial.empty?
1231
1175
  m = m.to_MasslessOpaqueMaterial.get
1232
1176
 
@@ -1269,15 +1213,13 @@ module OSut
1269
1213
  #
1270
1214
  # @return [Hash] t: (OpenStudio::Transformation), r: Float
1271
1215
  # @return [Hash] t: nil, r: nil (if invalid input)
1272
- def transforms(model, group)
1216
+ def transforms(model = nil, group = nil)
1273
1217
  mth = "OSut::#{__callee__}"
1274
1218
  cl1 = OpenStudio::Model::Model
1275
1219
  cl2 = OpenStudio::Model::PlanarSurfaceGroup
1276
1220
  res = { t: nil, r: nil }
1277
1221
 
1278
- return invalid("model", mth, 1, DBG, res) unless model
1279
1222
  return mismatch("model", model, cl1, mth, DBG, res) unless model.is_a?(cl1)
1280
-
1281
1223
  return invalid("group", mth, 2, DBG, res) unless group.respond_to?(NS)
1282
1224
  id = group.nameString
1283
1225
  return mismatch(id, group, cl2, mth, DBG, res) unless group.is_a?(cl2)
@@ -1294,16 +1236,14 @@ module OSut
1294
1236
  # @param pts [Array] an OpenStudio Point3D array/vector
1295
1237
  #
1296
1238
  # @return [Array] flattened OpenStudio 3D points
1297
- def flatZ(pts)
1239
+ def flatZ(pts = nil)
1298
1240
  mth = "OSut::#{__callee__}"
1299
1241
  cl1 = OpenStudio::Point3dVector
1300
1242
  cl2 = OpenStudio::Point3d
1301
- v = OpenStudio::Point3dVector.new
1243
+ v = OpenStudio::Point3dVector.new
1302
1244
 
1303
- return invalid("points", mth, 1, DBG, v) unless pts
1304
1245
  valid = pts.is_a?(cl1) || pts.is_a?(Array)
1305
- return mismatch("points", pts, cl1, mth, DBG, v) unless valid
1306
-
1246
+ return mismatch("points", pts, cl1, mth, DBG, v) unless valid
1307
1247
  pts.each { |pt| mismatch("pt", pt, cl2, mth, ERR, v) unless pt.is_a?(cl2) }
1308
1248
  pts.each { |pt| v << OpenStudio::Point3d.new(pt.x, pt.y, 0) }
1309
1249
 
@@ -1320,53 +1260,49 @@ module OSut
1320
1260
  #
1321
1261
  # @return [Bool] true if 1st polygon fits entirely within the 2nd polygon
1322
1262
  # @return [Bool] false if invalid input
1323
- def fits?(p1, p2, id1 = "", id2 = "")
1263
+ def fits?(p1 = nil, p2 = nil, id1 = "", id2 = "")
1324
1264
  mth = "OSut::#{__callee__}"
1325
1265
  cl1 = OpenStudio::Point3dVector
1326
1266
  cl2 = OpenStudio::Point3d
1327
1267
  a = false
1268
+
1269
+ return invalid("id1", mth, 3, DBG, a) unless id1.respond_to?(:to_s)
1270
+ return invalid("id2", mth, 4, DBG, a) unless id2.respond_to?(:to_s)
1328
1271
  i1 = id1.to_s
1329
1272
  i2 = id2.to_s
1330
1273
  i1 = "poly1" if i1.empty?
1331
1274
  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
1275
+ valid1 = p1.is_a?(cl1) || p1.is_a?(Array)
1276
+ valid2 = p2.is_a?(cl1) || p2.is_a?(Array)
1277
+ return mismatch(i1, p1, cl1, mth, DBG, a) unless valid1
1278
+ return mismatch(i2, p2, cl1, mth, DBG, a) unless valid2
1336
1279
  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
1280
  return empty(i2, mth, ERR, a) if p2.empty?
1342
-
1343
1281
  p1.each { |v| return mismatch(i1, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1344
1282
  p2.each { |v| return mismatch(i2, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1345
1283
 
1346
1284
  ft = OpenStudio::Transformation::alignFace(p1).inverse
1347
-
1348
1285
  ft_p1 = flatZ( (ft * p1).reverse )
1349
1286
  return false if ft_p1.empty?
1350
1287
  area1 = OpenStudio::getArea(ft_p1)
1351
- return empty(i1, mth, ERR, a) if area1.empty?
1288
+ return empty("#{i1} area", mth, ERR, a) if area1.empty?
1352
1289
  area1 = area1.get
1353
-
1354
1290
  ft_p2 = flatZ( (ft * p2).reverse )
1355
1291
  return false if ft_p2.empty?
1356
1292
  area2 = OpenStudio::getArea(ft_p2)
1357
- return empty(i2, mth, ERR, a) if area2.empty?
1293
+ return empty("#{i2} area", mth, ERR, a) if area2.empty?
1358
1294
  area2 = area2.get
1359
-
1360
1295
  union = OpenStudio::join(ft_p1, ft_p2, TOL2)
1361
1296
  return false if union.empty?
1362
1297
  union = union.get
1363
1298
  area = OpenStudio::getArea(union)
1364
- return empty("union", mth, ERR, a) if area.empty?
1299
+ return empty("#{i1}:#{i2} union area", mth, ERR, a) if area.empty?
1365
1300
  area = area.get
1366
1301
 
1367
1302
  return false if area < TOL
1368
1303
  return true if (area - area2).abs < TOL
1369
1304
  return false if (area - area2).abs > TOL
1305
+
1370
1306
  true
1371
1307
  end
1372
1308
 
@@ -1380,51 +1316,55 @@ module OSut
1380
1316
  #
1381
1317
  # @return Returns true if polygons overlaps (or either fits into the other)
1382
1318
  # @return [Bool] false if invalid input
1383
- def overlaps?(p1, p2, id1 = "", id2 = "")
1319
+ def overlaps?(p1 = nil, p2 = nil, id1 = "", id2 = "")
1384
1320
  mth = "OSut::#{__callee__}"
1385
1321
  cl1 = OpenStudio::Point3dVector
1386
1322
  cl2 = OpenStudio::Point3d
1387
1323
  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
1324
 
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
1325
+ return invalid("id1", mth, 3, DBG, a) unless id1.respond_to?(:to_s)
1326
+ return invalid("id2", mth, 4, DBG, a) unless id2.respond_to?(:to_s)
1327
+ i1 = id1.to_s
1328
+ i2 = id2.to_s
1329
+ i1 = "poly1" if i1.empty?
1330
+ i2 = "poly2" if i2.empty?
1331
+ valid1 = p1.is_a?(cl1) || p1.is_a?(Array)
1332
+ valid2 = p2.is_a?(cl1) || p2.is_a?(Array)
1333
+ return mismatch(i1, p1, cl1, mth, DBG, a) unless valid1
1334
+ return mismatch(i2, p2, cl1, mth, DBG, a) unless valid2
1396
1335
  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
1336
  return empty(i2, mth, ERR, a) if p2.empty?
1402
-
1403
1337
  p1.each { |v| return mismatch(i1, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1404
1338
  p2.each { |v| return mismatch(i2, v, cl2, mth, ERR, a) unless v.is_a?(cl2) }
1405
1339
 
1406
1340
  ft = OpenStudio::Transformation::alignFace(p1).inverse
1407
-
1408
1341
  ft_p1 = flatZ( (ft * p1).reverse )
1409
1342
  return false if ft_p1.empty?
1410
1343
  area1 = OpenStudio::getArea(ft_p1)
1411
- return empty(i1, mth, ERR, a) if area1.empty?
1344
+ return empty("#{i1} area", mth, ERR, a) if area1.empty?
1412
1345
  area1 = area1.get
1413
-
1414
1346
  ft_p2 = flatZ( (ft * p2).reverse )
1415
1347
  return false if ft_p2.empty?
1416
1348
  area2 = OpenStudio::getArea(ft_p2)
1417
- return empty(i2, mth, ERR, a) if area2.empty?
1349
+ return empty("#{i2} area", mth, ERR, a) if area2.empty?
1418
1350
  area2 = area2.get
1419
-
1420
1351
  union = OpenStudio::join(ft_p1, ft_p2, TOL2)
1421
1352
  return false if union.empty?
1422
1353
  union = union.get
1423
1354
  area = OpenStudio::getArea(union)
1424
- return empty("union", mth, ERR, a) if area.empty?
1355
+ return empty("#{i1}:#{i2} union area", mth, ERR, a) if area.empty?
1425
1356
  area = area.get
1426
1357
 
1427
1358
  return false if area < TOL
1359
+
1428
1360
  true
1429
1361
  end
1362
+
1363
+ ##
1364
+ # Callback when other modules extend OSlg
1365
+ #
1366
+ # @param base [Object] instance or class object
1367
+ def self.extended(base)
1368
+ base.send(:include, self)
1369
+ end
1430
1370
  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.6".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.6
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.6
94
94
  bug_tracker_uri: https://github.com/rd2/osut/issues
95
95
  post_install_message:
96
96
  rdoc_options: []