osut 0.2.2 → 0.2.6

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.
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: []