openstudio-extension 0.7.1 → 0.8.0

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +14 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +2 -0
  6. data/lib/openstudio/extension/runner.rb +12 -8
  7. data/lib/openstudio/extension/runner_config.rb +33 -6
  8. data/lib/openstudio/extension/version.rb +1 -1
  9. data/openstudio-extension.gemspec +6 -6
  10. metadata +15 -66
  11. data/lib/openstudio/extension/core/CreateResults.rb +0 -1033
  12. data/lib/openstudio/extension/core/check_air_sys_temps.rb +0 -160
  13. data/lib/openstudio/extension/core/check_calibration.rb +0 -125
  14. data/lib/openstudio/extension/core/check_cond_zns.rb +0 -54
  15. data/lib/openstudio/extension/core/check_domestic_hot_water.rb +0 -304
  16. data/lib/openstudio/extension/core/check_envelope_conductance.rb +0 -423
  17. data/lib/openstudio/extension/core/check_eui_by_end_use.rb +0 -132
  18. data/lib/openstudio/extension/core/check_eui_reasonableness.rb +0 -105
  19. data/lib/openstudio/extension/core/check_fan_pwr.rb +0 -68
  20. data/lib/openstudio/extension/core/check_internal_loads.rb +0 -363
  21. data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +0 -196
  22. data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +0 -296
  23. data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +0 -434
  24. data/lib/openstudio/extension/core/check_mech_sys_type.rb +0 -109
  25. data/lib/openstudio/extension/core/check_part_loads.rb +0 -421
  26. data/lib/openstudio/extension/core/check_placeholder.rb +0 -45
  27. data/lib/openstudio/extension/core/check_plant_cap.rb +0 -93
  28. data/lib/openstudio/extension/core/check_plant_temps.rb +0 -129
  29. data/lib/openstudio/extension/core/check_plenum_loads.rb +0 -57
  30. data/lib/openstudio/extension/core/check_pump_pwr.rb +0 -78
  31. data/lib/openstudio/extension/core/check_sch_coord.rb +0 -211
  32. data/lib/openstudio/extension/core/check_schedules.rb +0 -281
  33. data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +0 -128
  34. data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +0 -118
  35. data/lib/openstudio/extension/core/check_weather_files.rb +0 -102
  36. data/lib/openstudio/extension/core/deer_vintages.rb +0 -281
  37. data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +0 -461
  38. data/lib/openstudio/extension/core/os_lib_constructions.rb +0 -353
  39. data/lib/openstudio/extension/core/os_lib_geometry.rb +0 -1169
  40. data/lib/openstudio/extension/core/os_lib_helper_methods.rb +0 -383
  41. data/lib/openstudio/extension/core/os_lib_hvac.rb +0 -2163
  42. data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +0 -184
  43. data/lib/openstudio/extension/core/os_lib_model_generation.rb +0 -3584
  44. data/lib/openstudio/extension/core/os_lib_model_simplification.rb +0 -1019
  45. data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +0 -135
  46. data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +0 -170
  47. data/lib/openstudio/extension/core/os_lib_schedules.rb +0 -933
@@ -1,933 +0,0 @@
1
- # *******************************************************************************
2
- # OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
3
- # See also https://openstudio.net/license
4
- # *******************************************************************************
5
-
6
- module OsLib_Schedules
7
- # create a ruleset schedule with a basic profile
8
- def self.createSimpleSchedule(model, options = {})
9
- defaults = {
10
- 'name' => nil,
11
- 'winterTimeValuePairs' => { 24.0 => 0.0 },
12
- 'summerTimeValuePairs' => { 24.0 => 1.0 },
13
- 'defaultTimeValuePairs' => { 24.0 => 1.0 }
14
- }
15
-
16
- # merge user inputs with defaults
17
- options = defaults.merge(options)
18
-
19
- # ScheduleRuleset
20
- sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
21
- if name
22
- sch_ruleset.setName(options['name'])
23
- end
24
-
25
- # Winter Design Day
26
- winter_dsn_day = OpenStudio::Model::ScheduleDay.new(model)
27
- sch_ruleset.setWinterDesignDaySchedule(winter_dsn_day)
28
- winter_dsn_day = sch_ruleset.winterDesignDaySchedule
29
- winter_dsn_day.setName("#{sch_ruleset.name} Winter Design Day")
30
- options['winterTimeValuePairs'].each do |k, v|
31
- hour = k.truncate
32
- min = ((k - hour) * 60).to_i
33
- winter_dsn_day.addValue(OpenStudio::Time.new(0, hour, min, 0), v)
34
- end
35
-
36
- # Summer Design Day
37
- summer_dsn_day = OpenStudio::Model::ScheduleDay.new(model)
38
- sch_ruleset.setSummerDesignDaySchedule(summer_dsn_day)
39
- summer_dsn_day = sch_ruleset.summerDesignDaySchedule
40
- summer_dsn_day.setName("#{sch_ruleset.name} Summer Design Day")
41
- options['summerTimeValuePairs'].each do |k, v|
42
- hour = k.truncate
43
- min = ((k - hour) * 60).to_i
44
- summer_dsn_day.addValue(OpenStudio::Time.new(0, hour, min, 0), v)
45
- end
46
-
47
- # All Days
48
- default_day = sch_ruleset.defaultDaySchedule
49
- default_day.setName("#{sch_ruleset.name} Schedule Week Day")
50
- options['defaultTimeValuePairs'].each do |k, v|
51
- hour = k.truncate
52
- min = ((k - hour) * 60).to_i
53
- default_day.addValue(OpenStudio::Time.new(0, hour, min, 0), v)
54
- end
55
-
56
- result = sch_ruleset
57
- return result
58
- end
59
-
60
- # find the maximum profile value for a schedule
61
- def self.getMinMaxAnnualProfileValue(model, schedule)
62
- # validate schedule
63
- if schedule.to_ScheduleRuleset.is_initialized
64
- schedule = schedule.to_ScheduleRuleset.get
65
-
66
- # gather profiles
67
- profiles = []
68
- defaultProfile = schedule.to_ScheduleRuleset.get.defaultDaySchedule
69
- profiles << defaultProfile
70
- rules = schedule.scheduleRules
71
- rules.each do |rule|
72
- profiles << rule.daySchedule
73
- end
74
-
75
- # test profiles
76
- min = nil
77
- max = nil
78
- profiles.each do |profile|
79
- profile.values.each do |value|
80
- if min.nil?
81
- min = value
82
- else
83
- if min > value then min = value end
84
- end
85
- if max.nil?
86
- max = value
87
- else
88
- if max < value then max = value end
89
- end
90
- end
91
- end
92
- result = { 'min' => min, 'max' => max } # this doesn't include summer and winter design day
93
- else
94
- result = nil
95
- end
96
-
97
- return result
98
- end
99
-
100
- # find the maximum profile value for a schedule
101
- def self.simpleScheduleValueAdjust(model, schedule, double, modificationType = 'Multiplier') # can increase/decrease by percentage or static value
102
- # TODO: - add in design days, maybe as optional argument
103
-
104
- # give option to clone or not
105
-
106
- # gather profiles
107
- profiles = []
108
- defaultProfile = schedule.to_ScheduleRuleset.get.defaultDaySchedule
109
- profiles << defaultProfile
110
- rules = schedule.scheduleRules
111
- rules.each do |rule|
112
- profiles << rule.daySchedule
113
- end
114
-
115
- # alter profiles
116
- profiles.each do |profile|
117
- times = profile.times
118
- i = 0
119
- profile.values.each do |value|
120
- if modificationType == 'Multiplier' || modificationType == 'Percentage' # percentage was used early on but Multiplier is preferable
121
- profile.addValue(times[i], value * double)
122
- end
123
- if modificationType == 'Sum' || modificationType == 'Value' # value was used early on but Sum is preferable
124
- profile.addValue(times[i], value + double)
125
- end
126
- i += 1
127
- end
128
- end
129
-
130
- result = schedule
131
- return result
132
- end
133
-
134
- # change value when value passes/fails test
135
- def self.conditionalScheduleValueAdjust(model, schedule, valueTestDouble, passDouble, failDouble, floorDouble, modificationType = 'Multiplier') # can increase/decrease by percentage or static value
136
- # TODO: - add in design days, maybe as optional argument
137
-
138
- # give option to clone or not
139
-
140
- # gather profiles
141
- profiles = []
142
- defaultProfile = schedule.to_ScheduleRuleset.get.defaultDaySchedule
143
- profiles << defaultProfile
144
- rules = schedule.scheduleRules
145
- rules.each do |rule|
146
- profiles << rule.daySchedule
147
- end
148
-
149
- # alter profiles
150
- profiles.each do |profile|
151
- times = profile.times
152
- i = 0
153
-
154
- profile.values.each do |value|
155
- # run test on this value
156
- if value < valueTestDouble
157
- double = passDouble
158
- else
159
- double = failDouble
160
- end
161
-
162
- # skip if value is floor or less
163
- next if value <= floorDouble
164
-
165
- if modificationType == 'Multiplier'
166
- profile.addValue(times[i], [value * double, floorDouble].max) # take the max of the floor or resulting value
167
- end
168
- if modificationType == 'Sum'
169
- profile.addValue(times[i], [value + double, floorDouble].max) # take the max of the floor or resulting value
170
- end
171
- i += 1
172
- end
173
- end
174
-
175
- result = schedule
176
- return result
177
- end
178
-
179
- # change value when time passes test
180
- def self.timeConditionalScheduleValueAdjust(model, schedule, hhmm_before, hhmm__after, inside_double, outside_double, modificationType = 'Multiplier') # can increase/decrease by percentage or static value
181
- # setup variables
182
- array = hhmm_before.to_s.split('')
183
- before_hour = "#{array[0]}#{array[1]}".to_i
184
- before_min = "#{array[2]}#{array[3]}".to_i
185
- array = hhmm__after.to_s.split('')
186
- after_hour = "#{array[0]}#{array[1]}".to_i
187
- after_min = "#{array[2]}#{array[3]}".to_i
188
-
189
- # gather profiles
190
- profiles = []
191
- schedule = schedule.to_ScheduleRuleset.get
192
- defaultProfile = schedule.defaultDaySchedule
193
- profiles << defaultProfile
194
- rules = schedule.scheduleRules
195
- rules.each do |rule|
196
- profiles << rule.daySchedule
197
- end
198
-
199
- # alter profiles
200
- profiles.each do |day_sch|
201
- times = day_sch.times
202
- i = 0
203
-
204
- # set times special times needed for methods below
205
- before_time = OpenStudio::Time.new(0, before_hour, before_min, 0)
206
- after_time = OpenStudio::Time.new(0, after_hour, after_min, 0)
207
- # day_end_time = OpenStudio::Time.new(0, 24, 0, 0)
208
-
209
- # add datapoint at before and after time
210
- original_value_at_before_time = day_sch.getValue(before_time)
211
- original_value_at_after_time = day_sch.getValue(after_time)
212
- day_sch.addValue(before_time, original_value_at_before_time)
213
- day_sch.addValue(after_time, original_value_at_after_time)
214
-
215
- # make arrays for original times and values
216
- times = day_sch.times
217
- values = day_sch.values
218
- day_sch.clearValues
219
-
220
- # make arrays for new values
221
- new_times = []
222
- new_values = []
223
-
224
- # loop through original time/value pairs to populate new array
225
- for i in 0..(values.length - 1)
226
- new_times << times[i]
227
-
228
- if times[i] > before_time && times[i] <= after_time # updated this so times[i] == before_time goes into the else
229
- if inside_double.nil?
230
- new_values << values[i]
231
- elsif modificationType == 'Sum'
232
- new_values << inside_double + values[i]
233
- elsif modificationType == 'Replace'
234
- new_values << inside_double
235
- else # should be Multiplier
236
- new_values << inside_double * values[i]
237
- end
238
- else
239
- if outside_double.nil?
240
- new_values << values[i]
241
- elsif modificationType == 'Sum'
242
- new_values << outside_double + values[i]
243
- elsif modificationType == 'Replace'
244
- new_values << outside_double
245
- else # should be Multiplier
246
- new_values << outside_double * values[i]
247
- end
248
- end
249
-
250
- end
251
-
252
- # generate new day_sch values
253
- for i in 0..(new_values.length - 1)
254
- day_sch.addValue(new_times[i], new_values[i])
255
- end
256
- end
257
-
258
- result = schedule
259
- return result
260
- end
261
-
262
- # merge multiple schedules into one using load or other value to weight each schedules influence on the merge
263
- def self.weightedMergeScheduleRulesets(model, scheduleWeighHash)
264
- # WARNING NOT READY FOR GENERAL USE YET - this doesn't do anything with rules yet, just winter, summer, and default profile
265
-
266
- # get denominator for weight
267
- denominator = 0
268
- scheduleWeighHash.each do |schedule, weight|
269
- denominator += weight
270
- end
271
-
272
- # create new schedule
273
- sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
274
- sch_ruleset.setName('Merged Schedule') # TODO: - make this optional user argument
275
-
276
- # create winter design day profile
277
- winter_dsn_day = OpenStudio::Model::ScheduleDay.new(model)
278
- sch_ruleset.setWinterDesignDaySchedule(winter_dsn_day)
279
- winter_dsn_day = sch_ruleset.winterDesignDaySchedule
280
- winter_dsn_day.setName("#{sch_ruleset.name} Winter Design Day")
281
-
282
- # create summer design day profile
283
- summer_dsn_day = OpenStudio::Model::ScheduleDay.new(model)
284
- sch_ruleset.setSummerDesignDaySchedule(summer_dsn_day)
285
- summer_dsn_day = sch_ruleset.summerDesignDaySchedule
286
- summer_dsn_day.setName("#{sch_ruleset.name} Summer Design Day")
287
-
288
- # create default profile
289
- default_day = sch_ruleset.defaultDaySchedule
290
- default_day.setName("#{sch_ruleset.name} Schedule Week Day")
291
-
292
- # hash of schedule rules
293
- rulesHash = {} # mon, tue, wed, thur, fri, sat, sun, startDate, endDate
294
- # to avoid stacking order issues across schedules, I may need to make a rule for each day of the week for each date range
295
-
296
- scheduleWeighHash.each do |schedule, weight|
297
- # populate winter design day profile
298
- oldWinterProfile = schedule.to_ScheduleRuleset.get.winterDesignDaySchedule
299
- times_final = summer_dsn_day.times
300
- i = 0
301
- valueUpdatedArray = []
302
- # loop through times already in profile and update values
303
- until i > times_final.size - 1
304
- value = oldWinterProfile.getValue(times_final[i]) * weight / denominator
305
- starting_value = winter_dsn_day.getValue(times_final[i])
306
- winter_dsn_day.addValue(times_final[i], value + starting_value)
307
- valueUpdatedArray << times_final[i]
308
- i += 1
309
- end
310
- # loop through any new times unique to the current old profile to be merged
311
- j = 0
312
- times = oldWinterProfile.times
313
- values = oldWinterProfile.values
314
- until j > times.size - 1
315
- unless valueUpdatedArray.include? times[j]
316
- value = values[j] * weight / denominator
317
- starting_value = winter_dsn_day.getValue(times[j])
318
- winter_dsn_day.addValue(times[j], value + starting_value)
319
- end
320
- j += 1
321
- end
322
-
323
- # populate summer design day profile
324
- oldSummerProfile = schedule.to_ScheduleRuleset.get.summerDesignDaySchedule
325
- times_final = summer_dsn_day.times
326
- i = 0
327
- valueUpdatedArray = []
328
- # loop through times already in profile and update values
329
- until i > times_final.size - 1
330
- value = oldSummerProfile.getValue(times_final[i]) * weight / denominator
331
- starting_value = summer_dsn_day.getValue(times_final[i])
332
- summer_dsn_day.addValue(times_final[i], value + starting_value)
333
- valueUpdatedArray << times_final[i]
334
- i += 1
335
- end
336
- # loop through any new times unique to the current old profile to be merged
337
- j = 0
338
- times = oldSummerProfile.times
339
- values = oldSummerProfile.values
340
- until j > times.size - 1
341
- unless valueUpdatedArray.include? times[j]
342
- value = values[j] * weight / denominator
343
- starting_value = summer_dsn_day.getValue(times[j])
344
- summer_dsn_day.addValue(times[j], value + starting_value)
345
- end
346
- j += 1
347
- end
348
-
349
- # populate default profile
350
- oldDefaultProfile = schedule.to_ScheduleRuleset.get.defaultDaySchedule
351
- times_final = default_day.times
352
- i = 0
353
- valueUpdatedArray = []
354
- # loop through times already in profile and update values
355
- until i > times_final.size - 1
356
- value = oldDefaultProfile.getValue(times_final[i]) * weight / denominator
357
- starting_value = default_day.getValue(times_final[i])
358
- default_day.addValue(times_final[i], value + starting_value)
359
- valueUpdatedArray << times_final[i]
360
- i += 1
361
- end
362
- # loop through any new times unique to the current old profile to be merged
363
- j = 0
364
- times = oldDefaultProfile.times
365
- values = oldDefaultProfile.values
366
- until j > times.size - 1
367
- unless valueUpdatedArray.include? times[j]
368
- value = values[j] * weight / denominator
369
- starting_value = default_day.getValue(times[j])
370
- default_day.addValue(times[j], value + starting_value)
371
- end
372
- j += 1
373
- end
374
-
375
- # create rules
376
-
377
- # gather data for rule profiles
378
-
379
- # populate rule profiles
380
- end
381
-
382
- result = { 'mergedSchedule' => sch_ruleset, 'denominator' => denominator }
383
- return result
384
- end
385
-
386
- # create a new schedule using absolute velocity of existing schedule
387
- def self.scheduleFromRateOfChange(model, schedule)
388
- # clone source schedule
389
- newSchedule = schedule.clone(model)
390
- newSchedule.setName("#{schedule.name} - Rate of Change")
391
- newSchedule = newSchedule.to_ScheduleRuleset.get
392
-
393
- # create array of all profiles to change. This includes summer, winter, default, and rules
394
- profiles = []
395
- profiles << newSchedule.winterDesignDaySchedule
396
- profiles << newSchedule.summerDesignDaySchedule
397
- profiles << newSchedule.defaultDaySchedule
398
-
399
- # time values may need
400
- endProfileTime = OpenStudio::Time.new(0, 24, 0, 0)
401
- hourBumpTime = OpenStudio::Time.new(0, 1, 0, 0)
402
- oneHourLeftTime = OpenStudio::Time.new(0, 23, 0, 0)
403
-
404
- rules = newSchedule.scheduleRules
405
- rules.each do |rule|
406
- profiles << rule.daySchedule
407
- end
408
-
409
- profiles.uniq.each do |profile|
410
- times = profile.times
411
- values = profile.values
412
-
413
- i = 0
414
- valuesIntermediate = []
415
- timesIntermediate = []
416
- until i == values.size
417
- if i == 0
418
- valuesIntermediate << 0.0
419
- if times[i] > hourBumpTime
420
- timesIntermediate << times[i] - hourBumpTime
421
- if times[i + 1].nil?
422
- timeStepValue = endProfileTime.hours + endProfileTime.minutes / 60 - times[i].hours - times[i].minutes / 60
423
- else
424
- timeStepValue = times[i + 1].hours + times[i + 1].minutes / 60 - times[i].hours - times[i].minutes / 60
425
- end
426
- valuesIntermediate << (values[i + 1].to_f - values[i].to_f).abs / (timeStepValue * 2)
427
- end
428
- timesIntermediate << times[i]
429
- elsif i == (values.size - 1)
430
- if times[times.size - 2] < oneHourLeftTime
431
- timesIntermediate << times[times.size - 2] + hourBumpTime # this should be the second to last time
432
- timeStepValue = times[i - 1].hours + times[i - 1].minutes / 60 - times[i - 2].hours - times[i - 2].minutes / 60
433
- valuesIntermediate << (values[i - 1].to_f - values[i - 2].to_f).abs / (timeStepValue * 2)
434
- end
435
- valuesIntermediate << 0.0
436
- timesIntermediate << times[i] # this should be the last time
437
- else
438
- # get value multiplier based on how many hours it is spread over
439
- timeStepValue = times[i].hours + times[i].minutes / 60 - times[i - 1].hours - times[i - 1].minutes / 60
440
- valuesIntermediate << (values[i].to_f - values[i - 1].to_f).abs / timeStepValue
441
- timesIntermediate << times[i]
442
- end
443
- i += 1
444
- end
445
-
446
- # delete all profile values
447
- profile.clearValues
448
-
449
- i = 0
450
- until i == timesIntermediate.size
451
- if i == (timesIntermediate.size - 1)
452
- profile.addValue(timesIntermediate[i], valuesIntermediate[i].to_f)
453
- else
454
- profile.addValue(timesIntermediate[i], valuesIntermediate[i].to_f)
455
- end
456
- i += 1
457
- end
458
- end
459
-
460
- # fix velocity so it isn't fraction change per step, but per hour (I need to count hours between times and divide value by this)
461
-
462
- result = newSchedule
463
- return result
464
- end
465
-
466
- # create a complex ruleset schedule
467
- def self.createComplexSchedule(model, options = {})
468
- defaults = {
469
- 'name' => nil,
470
- 'default_day' => ['always_on', [24.0, 1.0]]
471
- }
472
-
473
- # merge user inputs with defaults
474
- options = defaults.merge(options)
475
-
476
- # ScheduleRuleset
477
- sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
478
- if name
479
- sch_ruleset.setName(options['name'])
480
- end
481
-
482
- # Winter Design Day
483
- unless options['winter_design_day'].nil?
484
- winter_dsn_day = OpenStudio::Model::ScheduleDay.new(model)
485
- sch_ruleset.setWinterDesignDaySchedule(winter_dsn_day)
486
- winter_dsn_day = sch_ruleset.winterDesignDaySchedule
487
- winter_dsn_day.setName("#{sch_ruleset.name} Winter Design Day")
488
- options['winter_design_day'].each do |data_pair|
489
- hour = data_pair[0].truncate
490
- min = ((data_pair[0] - hour) * 60).to_i
491
- winter_dsn_day.addValue(OpenStudio::Time.new(0, hour, min, 0), data_pair[1])
492
- end
493
- end
494
-
495
- # Summer Design Day
496
- unless options['summer_design_day'].nil?
497
- summer_dsn_day = OpenStudio::Model::ScheduleDay.new(model)
498
- sch_ruleset.setSummerDesignDaySchedule(summer_dsn_day)
499
- summer_dsn_day = sch_ruleset.summerDesignDaySchedule
500
- summer_dsn_day.setName("#{sch_ruleset.name} Summer Design Day")
501
- options['summer_design_day'].each do |data_pair|
502
- hour = data_pair[0].truncate
503
- min = ((data_pair[0] - hour) * 60).to_i
504
- summer_dsn_day.addValue(OpenStudio::Time.new(0, hour, min, 0), data_pair[1])
505
- end
506
- end
507
-
508
- # Default Day
509
- default_day = sch_ruleset.defaultDaySchedule
510
- default_day.setName("#{sch_ruleset.name} #{options['default_day'][0]}")
511
- default_data_array = options['default_day']
512
- default_data_array.delete_at(0)
513
- default_data_array.each do |data_pair|
514
- hour = data_pair[0].truncate
515
- min = ((data_pair[0] - hour) * 60).to_i
516
- default_day.addValue(OpenStudio::Time.new(0, hour, min, 0), data_pair[1])
517
- end
518
-
519
- # Rules
520
- unless options['rules'].nil?
521
- options['rules'].each do |data_array|
522
- rule = OpenStudio::Model::ScheduleRule.new(sch_ruleset)
523
- rule.setName("#{sch_ruleset.name} #{data_array[0]} Rule")
524
- date_range = data_array[1].split('-')
525
- start_date = date_range[0].split('/')
526
- end_date = date_range[1].split('/')
527
- rule.setStartDate(model.getYearDescription.makeDate(start_date[0].to_i, start_date[1].to_i))
528
- rule.setEndDate(model.getYearDescription.makeDate(end_date[0].to_i, end_date[1].to_i))
529
- days = data_array[2].split('/')
530
- rule.setApplySunday(true) if days.include? 'Sun'
531
- rule.setApplyMonday(true) if days.include? 'Mon'
532
- rule.setApplyTuesday(true) if days.include? 'Tue'
533
- rule.setApplyWednesday(true) if days.include? 'Wed'
534
- rule.setApplyThursday(true) if days.include? 'Thu'
535
- rule.setApplyFriday(true) if days.include? 'Fri'
536
- rule.setApplySaturday(true) if days.include? 'Sat'
537
- day_schedule = rule.daySchedule
538
- day_schedule.setName("#{sch_ruleset.name} #{data_array[0]}")
539
- data_array.delete_at(0)
540
- data_array.delete_at(0)
541
- data_array.delete_at(0)
542
- data_array.each do |data_pair|
543
- hour = data_pair[0].truncate
544
- min = ((data_pair[0] - hour) * 60).to_i
545
- day_schedule.addValue(OpenStudio::Time.new(0, hour, min, 0), data_pair[1])
546
- end
547
- end
548
- end
549
-
550
- result = sch_ruleset
551
- return result
552
- end
553
-
554
- def self.addScheduleTypeLimits(model) # TODO: - make sure to add this new method to cofee when done
555
- type_limits = {}
556
-
557
- lightsScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
558
- lightsScheduleTypeLimits.setName('Lights Schedule Type Limits')
559
- lightsScheduleTypeLimits.setLowerLimitValue(0.0)
560
- lightsScheduleTypeLimits.setUpperLimitValue(1.0)
561
- lightsScheduleTypeLimits.setNumericType('Continuous')
562
- lightsScheduleTypeLimits.setUnitType('Dimensionless')
563
- type_limits['Lights'] = lightsScheduleTypeLimits
564
-
565
- occupancyScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
566
- occupancyScheduleTypeLimits.setName('Occupancy Schedule Type Limits')
567
- occupancyScheduleTypeLimits.setLowerLimitValue(0.0)
568
- occupancyScheduleTypeLimits.setUpperLimitValue(1.0)
569
- occupancyScheduleTypeLimits.setNumericType('Continuous')
570
- occupancyScheduleTypeLimits.setUnitType('Dimensionless')
571
- type_limits['Occupancy'] = occupancyScheduleTypeLimits
572
-
573
- peopleActivityScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
574
- peopleActivityScheduleTypeLimits.setName('People Activity Type Limits')
575
- peopleActivityScheduleTypeLimits.setLowerLimitValue(0.0)
576
- # peopleActivityScheduleTypeLimits.setUpperLimitValue(1500.0)
577
- peopleActivityScheduleTypeLimits.setNumericType('Continuous')
578
- peopleActivityScheduleTypeLimits.setUnitType('ActivityLevel')
579
- type_limits['People Activity'] = peopleActivityScheduleTypeLimits
580
-
581
- equipmentScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
582
- equipmentScheduleTypeLimits.setName('Equipment Schedule Type Limits')
583
- equipmentScheduleTypeLimits.setLowerLimitValue(0.0)
584
- equipmentScheduleTypeLimits.setUpperLimitValue(1.0)
585
- equipmentScheduleTypeLimits.setNumericType('Continuous')
586
- equipmentScheduleTypeLimits.setUnitType('Dimensionless')
587
- type_limits['Equipment'] = equipmentScheduleTypeLimits
588
-
589
- waterUseScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
590
- waterUseScheduleTypeLimits.setName('Water Use Schedule Type Limits')
591
- waterUseScheduleTypeLimits.setLowerLimitValue(0.0)
592
- waterUseScheduleTypeLimits.setUpperLimitValue(1.0)
593
- waterUseScheduleTypeLimits.setNumericType('Continuous')
594
- waterUseScheduleTypeLimits.setUnitType('Dimensionless')
595
- type_limits['Water Use'] = waterUseScheduleTypeLimits
596
-
597
- elevatorsScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
598
- elevatorsScheduleTypeLimits.setName('Elevators Schedule Type Limits')
599
- elevatorsScheduleTypeLimits.setLowerLimitValue(0.0)
600
- elevatorsScheduleTypeLimits.setUpperLimitValue(1.0)
601
- elevatorsScheduleTypeLimits.setNumericType('Continuous')
602
- elevatorsScheduleTypeLimits.setUnitType('Dimensionless')
603
- type_limits['Elevators'] = elevatorsScheduleTypeLimits
604
-
605
- processLoadsScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
606
- processLoadsScheduleTypeLimits.setName('Process Loads Schedule Type Limits')
607
- processLoadsScheduleTypeLimits.setLowerLimitValue(0.0)
608
- processLoadsScheduleTypeLimits.setUpperLimitValue(1.0)
609
- processLoadsScheduleTypeLimits.setNumericType('Continuous')
610
- processLoadsScheduleTypeLimits.setUnitType('Dimensionless')
611
- type_limits['Process Load'] = elevatorsScheduleTypeLimits
612
-
613
- thermostatHeatingScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
614
- thermostatHeatingScheduleTypeLimits.setName('Thermostat Heating Setpoint Schedule Type Limits')
615
- thermostatHeatingScheduleTypeLimits.setLowerLimitValue(0.0)
616
- thermostatHeatingScheduleTypeLimits.setUpperLimitValue(100.0)
617
- thermostatHeatingScheduleTypeLimits.setNumericType('Continuous')
618
- thermostatHeatingScheduleTypeLimits.setUnitType('Temperature')
619
- type_limits['Thermostat Heating Setpoint'] = thermostatHeatingScheduleTypeLimits
620
-
621
- temperatureScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
622
- temperatureScheduleTypeLimits.setName('Thermostat Cooling Setpoint Schedule Type Limits')
623
- temperatureScheduleTypeLimits.setLowerLimitValue(0.0)
624
- temperatureScheduleTypeLimits.setUpperLimitValue(100.0)
625
- temperatureScheduleTypeLimits.setNumericType('Continuous')
626
- temperatureScheduleTypeLimits.setUnitType('Temperature')
627
- type_limits['Thermostat Cooling Setpoint'] = temperatureScheduleTypeLimits
628
-
629
- hvacOperationScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
630
- hvacOperationScheduleTypeLimits.setName('HVAC Operation Schedule Type Limits')
631
- hvacOperationScheduleTypeLimits.setLowerLimitValue(0)
632
- hvacOperationScheduleTypeLimits.setUpperLimitValue(1)
633
- hvacOperationScheduleTypeLimits.setNumericType('Discrete')
634
- hvacOperationScheduleTypeLimits.setUnitType('Availability')
635
- type_limits['HVAC Operation'] = hvacOperationScheduleTypeLimits
636
-
637
- temperatureScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
638
- temperatureScheduleTypeLimits.setName('Temperature Schedule Type Limits')
639
- temperatureScheduleTypeLimits.setNumericType('Continuous')
640
- temperatureScheduleTypeLimits.setUnitType('Temperature')
641
- type_limits['Temperature'] = temperatureScheduleTypeLimits
642
-
643
- fractionScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
644
- fractionScheduleTypeLimits.setName('Fraction Schedule Type Limits')
645
- fractionScheduleTypeLimits.setLowerLimitValue(0.0)
646
- fractionScheduleTypeLimits.setUpperLimitValue(1.0)
647
- fractionScheduleTypeLimits.setNumericType('Continuous')
648
- fractionScheduleTypeLimits.setUnitType('Dimensionless')
649
- type_limits['Fraction'] = fractionScheduleTypeLimits
650
-
651
- dimensionlessScheduleTypeLimits = OpenStudio::Model::ScheduleTypeLimits.new(model)
652
- dimensionlessScheduleTypeLimits.setName('Dimensionless Schedule Type Limits')
653
- dimensionlessScheduleTypeLimits.setNumericType('Continuous')
654
- dimensionlessScheduleTypeLimits.setUnitType('Dimensionless')
655
- type_limits['Dimensionless'] = dimensionlessScheduleTypeLimits
656
-
657
- return type_limits
658
- end
659
-
660
- # create TimeSeries from ScheduleRuleset
661
- def self.create_timeseries_from_schedule_ruleset(model, schedule_ruleset)
662
- yd = model.getYearDescription
663
- start_date = yd.makeDate(1, 1)
664
- end_date = yd.makeDate(12, 31)
665
-
666
- values = OpenStudio::DoubleVector.new
667
- day = OpenStudio::Time.new(1.0)
668
- interval = OpenStudio::Time.new(1.0 / 48.0)
669
- day_schedules = schedule_ruleset.to_ScheduleRuleset.get.getDaySchedules(start_date, end_date)
670
- day_schedules.each do |day_schedule|
671
- time = interval
672
- while time < day
673
- values << day_schedule.getValue(time)
674
- time += interval
675
- end
676
- end
677
- time_series = OpenStudio::TimeSeries.new(start_date, interval, OpenStudio.createVector(values), '')
678
- end
679
-
680
- # create ScheduleVariableInterval from TimeSeries
681
- def self.create_schedule_variable_interval_from_time_series(model, time_series)
682
- result = OpenStudio::Model::ScheduleInterval.fromTimeSeries(time_series, model).get
683
- end
684
-
685
- def self.adjust_hours_of_operation_for_schedule_ruleset(runner, model, schedule, options = {})
686
- defaults = {
687
- 'base_start_hoo' => 8.0, # may not be good idea to have default
688
- 'base_finish_hoo' => 18.0, # may not be good idea to have default
689
- 'delta_length_hoo' => 0.0,
690
- 'shift_hoo' => 0.0,
691
- 'default' => true,
692
- 'mon' => true,
693
- 'tue' => true,
694
- 'wed' => true,
695
- 'thur' => true,
696
- 'fri' => true,
697
- 'sat' => true,
698
- 'sun' => true,
699
- 'summer' => false,
700
- 'winter' => false
701
- }
702
-
703
- # merge user inputs with defaults
704
- options = defaults.merge(options)
705
-
706
- # grab schedule out of argument
707
- if schedule.to_ScheduleRuleset.is_initialized
708
- schedule = schedule.to_ScheduleRuleset.get
709
- else
710
- runner.registerWarning("you should only pass ruleset schedules into this method. skipping #{schedule.name}")
711
- return nil
712
- end
713
-
714
- # array of all profiles to change
715
- profiles = []
716
-
717
- # push default profiles to array
718
- if options['default']
719
- default_rule = schedule.defaultDaySchedule
720
- profiles << default_rule
721
- end
722
-
723
- # push profiles to array
724
- rules = schedule.scheduleRules
725
- rules.each do |rule|
726
- day_sch = rule.daySchedule
727
-
728
- # if any day requested also exists in the rule, then it will be altered
729
- alter_rule = false
730
- if rule.applyMonday && rule.applyMonday == options['mon'] then alter_rule = true end
731
- if rule.applyTuesday && rule.applyTuesday == options['tue'] then alter_rule = true end
732
- if rule.applyWednesday && rule.applyWednesday == options['wed'] then alter_rule = true end
733
- if rule.applyThursday && rule.applyThursday == options['thur'] then alter_rule = true end
734
- if rule.applyFriday && rule.applyFriday == options['fri'] then alter_rule = true end
735
- if rule.applySaturday && rule.applySaturday == options['sat'] then alter_rule = true end
736
- if rule.applySunday && rule.applySunday == options['sun'] then alter_rule = true end
737
-
738
- # TODO: - add in logic to warn user about conflicts where a single rule has conflicting tests
739
-
740
- if alter_rule
741
- profiles << day_sch
742
- end
743
- end
744
-
745
- # add design days to array
746
- if options['summer']
747
- summer_design = schedule.summerDesignDaySchedule
748
- profiles << summer_design
749
- end
750
- if options['winter']
751
- winter_design = schedule.winterDesignDaySchedule
752
- profiles << winter_design
753
- end
754
-
755
- # give info messages as I change specific profiles
756
- runner.registerInfo("Adjusting #{schedule.name}")
757
-
758
- # rename schedule
759
- schedule.setName("#{schedule.name} - extend #{options['delta_length_hoo']} shift #{options['shift_hoo']}") # if I put inputs here name will get long
760
-
761
- # break time args into hours and minutes
762
- start_hoo_hours = (options['base_start_hoo']).to_i
763
- start_hoo_minutes = (((options['base_start_hoo']) - (options['base_start_hoo']).to_i) * 60).to_i
764
- finish_hoo_hours = (options['base_finish_hoo']).to_i
765
- finish_hoo_minutes = (((options['base_finish_hoo']) - (options['base_finish_hoo']).to_i) * 60).to_i
766
- delta_hours = (options['delta_length_hoo']).to_i
767
- delta_minutes = (((options['delta_length_hoo']) - (options['delta_length_hoo']).to_i) * 60).to_i
768
- shift_hours = (options['shift_hoo']).to_i
769
- shift_minutes = (((options['shift_hoo']) - (options['shift_hoo']).to_i) * 60).to_i
770
-
771
- # time objects to use in measure
772
- time_0 = OpenStudio::Time.new(0, 0, 0, 0)
773
- time_1_min = OpenStudio::Time.new(0, 0, 1, 0) # add this to avoid times in day profile less than this
774
- time_12 = OpenStudio::Time.new(0, 12, 0, 0)
775
- time_24 = OpenStudio::Time.new(0, 24, 0, 0)
776
- start_hoo_time = OpenStudio::Time.new(0, start_hoo_hours, start_hoo_minutes, 0)
777
- finish_hoo_time = OpenStudio::Time.new(0, finish_hoo_hours, finish_hoo_minutes, 0)
778
- delta_time = OpenStudio::Time.new(0, delta_hours, delta_minutes, 0) # not used
779
- shift_time = OpenStudio::Time.new(0, shift_hours, shift_minutes, 0)
780
-
781
- # calculations
782
- if options['base_start_hoo'] <= options['base_finish_hoo']
783
- base_opp_day_length = options['base_finish_hoo'] - options['base_start_hoo']
784
- mid_hoo = start_hoo_time + (finish_hoo_time - start_hoo_time) / 2
785
- mid_non_hoo = mid_hoo + time_12
786
- if mid_non_hoo > time_24 then mid_non_hoo -= time_24 end
787
- else
788
- base_opp_day_length = options['base_finish_hoo'] - options['base_start_hoo'] + 24
789
- mid_non_hoo = finish_hoo_time + (start_hoo_time - finish_hoo_time) / 2
790
- mid_hoo = mid_non_hoo + time_12
791
- if mid_non_hoo > time_24 then mid_non_hoo -= time_24 end
792
- end
793
- adjusted_opp_day_length = base_opp_day_length + options['delta_length_hoo']
794
- hoo_time_multiplier = adjusted_opp_day_length / base_opp_day_length
795
- non_hoo_time_multiplier = (24 - adjusted_opp_day_length) / (24 - base_opp_day_length)
796
-
797
- # check for invalid input
798
- if adjusted_opp_day_length < 0
799
- runner.registerError('Requested hours of operation adjustment results in an invalid negative hours of operation')
800
- return false
801
- end
802
- # check for invalid input
803
- if adjusted_opp_day_length > 24
804
- runner.registerError('Requested hours of operation adjustment results in more than 24 hours of operation')
805
- return false
806
- end
807
-
808
- # making some temp objects to avoid having to deal with wrap around for change of hoo times
809
- mid_hoo < start_hoo_time ? (adj_mid_hoo = mid_hoo + time_24) : (adj_mid_hoo = mid_hoo)
810
- finish_hoo_time < adj_mid_hoo ? (adj_finish_hoo_time = finish_hoo_time + time_24) : (adj_finish_hoo_time = finish_hoo_time)
811
- mid_non_hoo < adj_finish_hoo_time ? (adj_mid_non_hoo = mid_non_hoo + time_24) : (adj_mid_non_hoo = mid_non_hoo)
812
- adj_start = start_hoo_time + time_24 # not used
813
-
814
- # edit profiles
815
- profiles.each do |day_sch|
816
- times = day_sch.times
817
- values = day_sch.values
818
-
819
- # in this case delete all values outside of
820
- # todo - may need similar logic if exactly 0 hours
821
- if adjusted_opp_day_length == 24
822
- start_val = day_sch.getValue(start_hoo_time)
823
- finish_val = day_sch.getValue(finish_hoo_time)
824
-
825
- # remove times out of range that should not be reference or compressed
826
- if start_hoo_time < finish_hoo_time
827
- times.each do |time|
828
- if time <= start_hoo_time || time > finish_hoo_time
829
- day_sch.removeValue(time)
830
- end
831
- end
832
- # add in values
833
- day_sch.addValue(start_hoo_time,start_val)
834
- day_sch.addValue(finish_hoo_time,finish_val)
835
- day_sch.addValue(time_24,[start_val,finish_val].max)
836
- else
837
- times.each do |time|
838
- if time > start_hoo_time && time <= finish_hoo_time
839
- day_sch.removeValue(time)
840
- end
841
- end
842
- # add in values
843
- day_sch.addValue(finish_hoo_time,finish_val)
844
- day_sch.addValue(start_hoo_time,start_val)
845
- day_sch.addValue(time_24,[values.first,values.last].max)
846
- end
847
-
848
- end
849
-
850
- times = day_sch.times
851
- values = day_sch.values
852
-
853
- # arrays for values to avoid overlap conflict of times
854
- new_times = []
855
- new_values = []
856
-
857
- # this is to store what datapoint will be first after midnight, and what the value at that time should be
858
- min_time_new = time_24
859
- min_time_value = nil
860
-
861
- # flag if found time at 24
862
- found_24_or_0 = false
863
-
864
- # push times to array
865
- times.each do |time|
866
- # create logic for four possible quadrants. Assume any quadrant can pass over 24/0 threshold
867
- time < start_hoo_time ? (temp_time = time + time_24) : (temp_time = time)
868
-
869
- # calculate change in time do to hoo delta
870
- if temp_time <= adj_finish_hoo_time
871
- expand_time = (temp_time - adj_mid_hoo) * hoo_time_multiplier - (temp_time - adj_mid_hoo)
872
- else
873
- expand_time = (temp_time - adj_mid_non_hoo) * non_hoo_time_multiplier - (temp_time - adj_mid_non_hoo)
874
- end
875
-
876
- new_time = time + shift_time + expand_time
877
-
878
- # adjust wrap around times
879
- if new_time < time_0
880
- new_time += time_24
881
- elsif new_time > time_24
882
- new_time -= time_24
883
- end
884
- new_times << new_time
885
-
886
- # see which new_time has the lowest value. Then add a value at 24 equal to that
887
- if !found_24_or_0 && new_time <= min_time_new
888
- min_time_new = new_time
889
- min_time_value = day_sch.getValue(time)
890
- elsif new_time == time_24 # this was added to address time exactly at 24
891
- min_time_new = new_time
892
- min_time_value = day_sch.getValue(time)
893
- found_24_or_0 = true
894
- elsif new_time == time_0
895
- min_time_new = new_time
896
- min_time_value = day_sch.getValue(time_0)
897
- found_24_or_0 = true
898
- end
899
- end
900
-
901
- # push values to array
902
- values.each do |value|
903
- new_values << value
904
- end
905
-
906
- # add value for what will be 24
907
- new_times << time_24
908
- new_values << min_time_value
909
-
910
- new_time_val_hash = {}
911
- new_times.each_with_index do |time,i|
912
- new_time_val_hash[time.totalHours] = {:time => time, :value => new_values[i]}
913
- end
914
-
915
- # clear values
916
- day_sch.clearValues
917
-
918
- new_time_val_hash = Hash[new_time_val_hash.sort]
919
- prev_time = nil
920
- new_time_val_hash.sort.each do |hours,time_val|
921
- if prev_time.nil? || time_val[:time] - prev_time > time_1_min
922
- day_sch.addValue(time_val[:time], time_val[:value])
923
- prev_time = time_val[:time]
924
- else
925
- puts "time step in #{day_sch.name} between #{prev_time.toString} and #{time_val[:time].toString} is too small to support, not adding value"
926
- end
927
- end
928
-
929
- end
930
-
931
- return schedule
932
- end
933
- end