openstudio-extension 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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