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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +14 -0
- data/LICENSE.md +1 -1
- data/README.md +2 -0
- data/lib/openstudio/extension/runner.rb +12 -8
- data/lib/openstudio/extension/runner_config.rb +33 -6
- data/lib/openstudio/extension/version.rb +1 -1
- data/openstudio-extension.gemspec +6 -6
- metadata +15 -66
- data/lib/openstudio/extension/core/CreateResults.rb +0 -1033
- data/lib/openstudio/extension/core/check_air_sys_temps.rb +0 -160
- data/lib/openstudio/extension/core/check_calibration.rb +0 -125
- data/lib/openstudio/extension/core/check_cond_zns.rb +0 -54
- data/lib/openstudio/extension/core/check_domestic_hot_water.rb +0 -304
- data/lib/openstudio/extension/core/check_envelope_conductance.rb +0 -423
- data/lib/openstudio/extension/core/check_eui_by_end_use.rb +0 -132
- data/lib/openstudio/extension/core/check_eui_reasonableness.rb +0 -105
- data/lib/openstudio/extension/core/check_fan_pwr.rb +0 -68
- data/lib/openstudio/extension/core/check_internal_loads.rb +0 -363
- data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +0 -196
- data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +0 -296
- data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +0 -434
- data/lib/openstudio/extension/core/check_mech_sys_type.rb +0 -109
- data/lib/openstudio/extension/core/check_part_loads.rb +0 -421
- data/lib/openstudio/extension/core/check_placeholder.rb +0 -45
- data/lib/openstudio/extension/core/check_plant_cap.rb +0 -93
- data/lib/openstudio/extension/core/check_plant_temps.rb +0 -129
- data/lib/openstudio/extension/core/check_plenum_loads.rb +0 -57
- data/lib/openstudio/extension/core/check_pump_pwr.rb +0 -78
- data/lib/openstudio/extension/core/check_sch_coord.rb +0 -211
- data/lib/openstudio/extension/core/check_schedules.rb +0 -281
- data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +0 -128
- data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +0 -118
- data/lib/openstudio/extension/core/check_weather_files.rb +0 -102
- data/lib/openstudio/extension/core/deer_vintages.rb +0 -281
- data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +0 -461
- data/lib/openstudio/extension/core/os_lib_constructions.rb +0 -353
- data/lib/openstudio/extension/core/os_lib_geometry.rb +0 -1169
- data/lib/openstudio/extension/core/os_lib_helper_methods.rb +0 -383
- data/lib/openstudio/extension/core/os_lib_hvac.rb +0 -2163
- data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +0 -184
- data/lib/openstudio/extension/core/os_lib_model_generation.rb +0 -3584
- data/lib/openstudio/extension/core/os_lib_model_simplification.rb +0 -1019
- data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +0 -135
- data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +0 -170
- 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
|