openstudio-geb 0.5.0 → 0.6.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 +7 -3
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -12
- data/files/seasonal_shedding_peak_hours.json +1 -0
- data/files/seasonal_shifting_peak_hours.json +1 -0
- data/files/seasonal_shifting_take_hours.json +1 -0
- data/lib/measures/AdjustThermostatSetpointsByDegreesForPeakHours/measure.rb +963 -442
- data/lib/measures/add_ceiling_fan/measure.rb +2 -2
- data/lib/measures/add_rooftop_pv_simple/measure.rb +1 -1
- data/lib/measures/apply_dynamic_coating_to_roof_wall/measure.rb +2 -2
- data/lib/measures/precooling/measure.rb +964 -354
- data/lib/measures/preheating/measure.rb +940 -356
- data/lib/measures/reduce_epd_by_percentage_for_peak_hours/measure.rb +509 -300
- data/lib/measures/reduce_lpd_by_percentage_for_peak_hours/measure.rb +534 -309
- data/lib/openstudio/geb/version.rb +1 -1
- data/openstudio-geb.gemspec +4 -6
- metadata +17 -42
@@ -9,6 +9,7 @@
|
|
9
9
|
# http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/
|
10
10
|
|
11
11
|
# start the measure
|
12
|
+
require 'json'
|
12
13
|
class ReduceLPDByPercentageForPeakHours < OpenStudio::Measure::ModelMeasure
|
13
14
|
# human readable name
|
14
15
|
def name
|
@@ -23,7 +24,7 @@ class ReduceLPDByPercentageForPeakHours < OpenStudio::Measure::ModelMeasure
|
|
23
24
|
|
24
25
|
# human readable description of modeling approach
|
25
26
|
def modeler_description
|
26
|
-
return 'This measure will clone all of the lighting schedules for each zone. Then the schedules are adjusted by the specified percentage during the specified time period.'
|
27
|
+
return 'This measure will clone all of the lighting schedules for each zone. Then the schedules are adjusted by the specified percentage during the specified time period. Only schedules defined in scheduleRuleSet format will be modified.'
|
27
28
|
end
|
28
29
|
|
29
30
|
# define the arguments that the user will input
|
@@ -32,73 +33,138 @@ class ReduceLPDByPercentageForPeakHours < OpenStudio::Measure::ModelMeasure
|
|
32
33
|
lpd_reduce_percent = OpenStudio::Measure::OSArgument.makeDoubleArgument('lpd_reduce_percent', true)
|
33
34
|
lpd_reduce_percent.setDisplayName('Percentage Reduction of Lighting Power (%)')
|
34
35
|
lpd_reduce_percent.setDescription('Enter a value between 0 and 100')
|
35
|
-
lpd_reduce_percent.setDefaultValue(
|
36
|
+
lpd_reduce_percent.setDefaultValue(30.0)
|
36
37
|
args << lpd_reduce_percent
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
args << start_time
|
39
|
+
lpd_reduce_percent_unoccupied = OpenStudio::Measure::OSArgument.makeDoubleArgument('lpd_reduce_percent_unoccupied', false)
|
40
|
+
lpd_reduce_percent_unoccupied.setDisplayName('Percentage Reduction of Lighting Power (%) for Unoccupied Spaces (if different, optional)')
|
41
|
+
lpd_reduce_percent_unoccupied.setDescription('Enter a value between 0 and 100')
|
42
|
+
lpd_reduce_percent_unoccupied.setDefaultValue(60.0)
|
43
|
+
args << lpd_reduce_percent_unoccupied
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
end_time.setDisplayName('End Time for the Reduction')
|
48
|
-
end_time.setDescription('In HH:MM:SS format')
|
49
|
-
end_time.setDefaultValue('21:00:00')
|
50
|
-
args << end_time
|
51
|
-
|
52
|
-
# Use alternative default start and end time for different climate zone
|
53
|
-
alt_periods = OpenStudio::Measure::OSArgument.makeBoolArgument('alt_periods', false)
|
54
|
-
alt_periods.setDisplayName('Use alternative default start and end time based on the climate zone of the model?')
|
55
|
-
alt_periods.setDescription('This will overwrite the star and end time you input')
|
56
|
-
alt_periods.setDefaultValue(false)
|
57
|
-
args << alt_periods
|
58
|
-
|
59
|
-
# make an argument for the start date of the reduction
|
60
|
-
start_date1 = OpenStudio::Ruleset::OSArgument.makeStringArgument('start_date1', false)
|
61
|
-
start_date1.setDisplayName('First start date for the Reduction')
|
45
|
+
start_date1 = OpenStudio::Ruleset::OSArgument.makeStringArgument('start_date1', true)
|
46
|
+
start_date1.setDisplayName('First start date for the reduction')
|
62
47
|
start_date1.setDescription('In MM-DD format')
|
63
48
|
start_date1.setDefaultValue('06-01')
|
64
49
|
args << start_date1
|
65
|
-
|
66
|
-
|
67
|
-
end_date1 = OpenStudio::Ruleset::OSArgument.makeStringArgument('end_date1', false)
|
68
|
-
end_date1.setDisplayName('First end date for the Reduction')
|
50
|
+
end_date1 = OpenStudio::Ruleset::OSArgument.makeStringArgument('end_date1', true)
|
51
|
+
end_date1.setDisplayName('First end date for the reduction')
|
69
52
|
end_date1.setDescription('In MM-DD format')
|
70
53
|
end_date1.setDefaultValue('09-30')
|
71
54
|
args << end_date1
|
72
55
|
|
73
|
-
|
74
|
-
# make an argument for the second start date of the reduction
|
75
56
|
start_date2 = OpenStudio::Ruleset::OSArgument.makeStringArgument('start_date2', false)
|
76
|
-
start_date2.setDisplayName('Second start date for the
|
77
|
-
start_date2.setDescription('Specify a date in MM-DD format if you want a second
|
57
|
+
start_date2.setDisplayName('Second start date for the reduction (optional)')
|
58
|
+
start_date2.setDescription('Specify a date in MM-DD format if you want a second season of reduction; leave blank if not needed.')
|
78
59
|
start_date2.setDefaultValue('')
|
79
60
|
args << start_date2
|
80
|
-
|
81
|
-
# make an argument for the second end date of the reduction
|
82
61
|
end_date2 = OpenStudio::Ruleset::OSArgument.makeStringArgument('end_date2', false)
|
83
|
-
end_date2.setDisplayName('Second end date for the
|
84
|
-
end_date2.setDescription('Specify a date in MM-DD format if you want a second
|
62
|
+
end_date2.setDisplayName('Second end date for the reduction')
|
63
|
+
end_date2.setDescription('Specify a date in MM-DD format if you want a second season of reduction; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
|
85
64
|
end_date2.setDefaultValue('')
|
86
65
|
args << end_date2
|
87
66
|
|
88
|
-
|
67
|
+
|
89
68
|
start_date3 = OpenStudio::Ruleset::OSArgument.makeStringArgument('start_date3', false)
|
90
|
-
start_date3.setDisplayName('
|
91
|
-
start_date3.setDescription('Specify a date in MM-DD format if you want a third
|
69
|
+
start_date3.setDisplayName('Third start date for the reduction (optional)')
|
70
|
+
start_date3.setDescription('Specify a date in MM-DD format if you want a third season of reduction; leave blank if not needed.')
|
92
71
|
start_date3.setDefaultValue('')
|
93
72
|
args << start_date3
|
94
|
-
|
95
|
-
# make an argument for the third end date of the reduction
|
96
73
|
end_date3 = OpenStudio::Ruleset::OSArgument.makeStringArgument('end_date3', false)
|
97
|
-
end_date3.setDisplayName('
|
98
|
-
end_date3.setDescription('Specify a date in MM-DD format if you want a third
|
74
|
+
end_date3.setDisplayName('Third end date for the reduction')
|
75
|
+
end_date3.setDescription('Specify a date in MM-DD format if you want a third season of reduction; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
|
99
76
|
end_date3.setDefaultValue('')
|
100
77
|
args << end_date3
|
101
78
|
|
79
|
+
start_date4 = OpenStudio::Ruleset::OSArgument.makeStringArgument('start_date4', false)
|
80
|
+
start_date4.setDisplayName('Fourth start date for the reduction (optional)')
|
81
|
+
start_date4.setDescription('Specify a date in MM-DD format if you want a fourth season of reduction; leave blank if not needed.')
|
82
|
+
start_date4.setDefaultValue('')
|
83
|
+
args << start_date4
|
84
|
+
end_date4 = OpenStudio::Ruleset::OSArgument.makeStringArgument('end_date4', false)
|
85
|
+
end_date4.setDisplayName('Fourth end date for the reduction')
|
86
|
+
end_date4.setDescription('Specify a date in MM-DD format if you want a fourth season of reduction; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
|
87
|
+
end_date4.setDefaultValue('')
|
88
|
+
args << end_date4
|
89
|
+
|
90
|
+
|
91
|
+
start_date5 = OpenStudio::Ruleset::OSArgument.makeStringArgument('start_date5', false)
|
92
|
+
start_date5.setDisplayName('Fifth start date for the reduction (optional)')
|
93
|
+
start_date5.setDescription('Specify a date in MM-DD format if you want a fifth season of reduction; leave blank if not needed.')
|
94
|
+
start_date5.setDefaultValue('')
|
95
|
+
args << start_date5
|
96
|
+
end_date5 = OpenStudio::Ruleset::OSArgument.makeStringArgument('end_date5', false)
|
97
|
+
end_date5.setDisplayName('Fifth end date for the reduction')
|
98
|
+
end_date5.setDescription('Specify a date in MM-DD format if you want a fifth season of reduction; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
|
99
|
+
end_date5.setDefaultValue('')
|
100
|
+
args << end_date5
|
101
|
+
|
102
|
+
start_time1 = OpenStudio::Measure::OSArgument.makeStringArgument('start_time1', true)
|
103
|
+
start_time1.setDisplayName('Start time of the reduction for the first season')
|
104
|
+
start_time1.setDescription('In HH:MM:SS format')
|
105
|
+
start_time1.setDefaultValue('17:00:00')
|
106
|
+
args << start_time1
|
107
|
+
end_time1 = OpenStudio::Measure::OSArgument.makeStringArgument('end_time1', true)
|
108
|
+
end_time1.setDisplayName('End time of the reduction for the first season')
|
109
|
+
end_time1.setDescription('In HH:MM:SS format')
|
110
|
+
end_time1.setDefaultValue('21:00:00')
|
111
|
+
args << end_time1
|
112
|
+
|
113
|
+
|
114
|
+
start_time2 = OpenStudio::Measure::OSArgument.makeStringArgument('start_time2', false)
|
115
|
+
start_time2.setDisplayName('Start time of the reduction for the second season (optional)')
|
116
|
+
start_time2.setDescription('In HH:MM:SS format')
|
117
|
+
start_time2.setDefaultValue('')
|
118
|
+
args << start_time2
|
119
|
+
end_time2 = OpenStudio::Measure::OSArgument.makeStringArgument('end_time2', false)
|
120
|
+
end_time2.setDisplayName('End time of the reduction for the second season (optional)')
|
121
|
+
end_time2.setDescription('In HH:MM:SS format')
|
122
|
+
end_time2.setDefaultValue('')
|
123
|
+
args << end_time2
|
124
|
+
|
125
|
+
|
126
|
+
start_time3 = OpenStudio::Measure::OSArgument.makeStringArgument('start_time3', false)
|
127
|
+
start_time3.setDisplayName('Start time of the reduction for the third season (optional)')
|
128
|
+
start_time3.setDescription('In HH:MM:SS format')
|
129
|
+
start_time3.setDefaultValue('')
|
130
|
+
args << start_time3
|
131
|
+
end_time3 = OpenStudio::Measure::OSArgument.makeStringArgument('end_time3', false)
|
132
|
+
end_time3.setDisplayName('End time of the reduction for the third season (optional)')
|
133
|
+
end_time3.setDescription('In HH:MM:SS format')
|
134
|
+
end_time3.setDefaultValue('')
|
135
|
+
args << end_time3
|
136
|
+
|
137
|
+
|
138
|
+
start_time4 = OpenStudio::Measure::OSArgument.makeStringArgument('start_time4', false)
|
139
|
+
start_time4.setDisplayName('Start time of the reduction for the fourth season (optional)')
|
140
|
+
start_time4.setDescription('In HH:MM:SS format')
|
141
|
+
start_time4.setDefaultValue('')
|
142
|
+
args << start_time4
|
143
|
+
end_time4 = OpenStudio::Measure::OSArgument.makeStringArgument('end_time4', false)
|
144
|
+
end_time4.setDisplayName('End time of the reduction for the fourth season (optional)')
|
145
|
+
end_time4.setDescription('In HH:MM:SS format')
|
146
|
+
end_time4.setDefaultValue('')
|
147
|
+
args << end_time4
|
148
|
+
|
149
|
+
|
150
|
+
start_time5 = OpenStudio::Measure::OSArgument.makeStringArgument('start_time5', false)
|
151
|
+
start_time5.setDisplayName('Start time of the reduction for the fifth season (optional)')
|
152
|
+
start_time5.setDescription('In HH:MM:SS format')
|
153
|
+
start_time5.setDefaultValue('')
|
154
|
+
args << start_time5
|
155
|
+
end_time5 = OpenStudio::Measure::OSArgument.makeStringArgument('end_time5', false)
|
156
|
+
end_time5.setDisplayName('End time of the reduction for the fifth season (optional)')
|
157
|
+
end_time5.setDescription('In HH:MM:SS format')
|
158
|
+
end_time5.setDefaultValue('')
|
159
|
+
args << end_time5
|
160
|
+
|
161
|
+
|
162
|
+
# Use alternative default start and end time for different climate zone
|
163
|
+
alt_periods = OpenStudio::Measure::OSArgument.makeBoolArgument('alt_periods', true)
|
164
|
+
alt_periods.setDisplayName('Use alternative default start and end time based on the state of the model from the Cambium load profile peak period?')
|
165
|
+
alt_periods.setDescription('This will overwrite the start and end time and date provided by the user')
|
166
|
+
alt_periods.setDefaultValue(false)
|
167
|
+
args << alt_periods
|
102
168
|
|
103
169
|
return args
|
104
170
|
end
|
@@ -112,14 +178,27 @@ class ReduceLPDByPercentageForPeakHours < OpenStudio::Measure::ModelMeasure
|
|
112
178
|
return false
|
113
179
|
end
|
114
180
|
lpd_reduce_percent = runner.getDoubleArgumentValue('lpd_reduce_percent', user_arguments)
|
115
|
-
|
116
|
-
|
181
|
+
lpd_reduce_percent_unoccupied = runner.getOptionalDoubleArgumentValue('lpd_reduce_percent_unoccupied', user_arguments)
|
182
|
+
start_time1 = runner.getStringArgumentValue('start_time1', user_arguments)
|
183
|
+
end_time1 = runner.getStringArgumentValue('end_time1', user_arguments)
|
184
|
+
start_time2 = runner.getStringArgumentValue('start_time2', user_arguments)
|
185
|
+
end_time2 = runner.getStringArgumentValue('end_time2', user_arguments)
|
186
|
+
start_time3 = runner.getStringArgumentValue('start_time3', user_arguments)
|
187
|
+
end_time3 = runner.getStringArgumentValue('end_time3', user_arguments)
|
188
|
+
start_time4 = runner.getStringArgumentValue('start_time4', user_arguments)
|
189
|
+
end_time4 = runner.getStringArgumentValue('end_time4', user_arguments)
|
190
|
+
start_time5 = runner.getStringArgumentValue('start_time5', user_arguments)
|
191
|
+
end_time5 = runner.getStringArgumentValue('end_time5', user_arguments)
|
117
192
|
start_date1 = runner.getStringArgumentValue('start_date1', user_arguments)
|
118
193
|
end_date1 = runner.getStringArgumentValue('end_date1', user_arguments)
|
119
194
|
start_date2 = runner.getStringArgumentValue('start_date2', user_arguments)
|
120
195
|
end_date2 = runner.getStringArgumentValue('end_date2', user_arguments)
|
121
196
|
start_date3 = runner.getStringArgumentValue('start_date3', user_arguments)
|
122
197
|
end_date3 = runner.getStringArgumentValue('end_date3', user_arguments)
|
198
|
+
start_date4 = runner.getStringArgumentValue('start_date4', user_arguments)
|
199
|
+
end_date4 = runner.getStringArgumentValue('end_date4', user_arguments)
|
200
|
+
start_date5 = runner.getStringArgumentValue('start_date5', user_arguments)
|
201
|
+
end_date5 = runner.getStringArgumentValue('end_date5', user_arguments)
|
123
202
|
alt_periods = runner.getBoolArgumentValue('alt_periods', user_arguments)
|
124
203
|
|
125
204
|
# validate the percentage
|
@@ -130,192 +209,172 @@ class ReduceLPDByPercentageForPeakHours < OpenStudio::Measure::ModelMeasure
|
|
130
209
|
runner.registerWarning('The percentage reduction of lighting power is negative. This will increase the lighting power.')
|
131
210
|
end
|
132
211
|
|
133
|
-
|
212
|
+
if lpd_reduce_percent_unoccupied.empty?
|
213
|
+
runner.registerInfo("Unoccupied percentage reduction is not provided. Use the same value for all spaces")
|
214
|
+
lpd_reduce_percent_unoccupied = lpd_reduce_percent
|
215
|
+
elsif lpd_reduce_percent_unoccupied.to_f > 100
|
216
|
+
runner.registerError('The percentage reduction of lighting power cannot be larger than 100.')
|
217
|
+
return false
|
218
|
+
elsif lpd_reduce_percent_unoccupied.to_f < 0
|
219
|
+
runner.registerWarning('The percentage reduction of lighting power is negative. This will increase the lighting power.')
|
220
|
+
else
|
221
|
+
lpd_reduce_percent_unoccupied = lpd_reduce_percent_unoccupied.to_f
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
# set the default start and end time based on state
|
134
226
|
if alt_periods
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
if climateZone.institution == 'ASHRAE'
|
140
|
-
ashraeClimateZone = climateZone.value
|
141
|
-
runner.registerInfo("Using ASHRAE Climate zone #{ashraeClimateZone}.")
|
142
|
-
end
|
227
|
+
state = model.getWeatherFile.stateProvinceRegion
|
228
|
+
if state == ''
|
229
|
+
runner.registerError('Unable to find state in model WeatherFile. The measure cannot be applied.')
|
230
|
+
return false
|
143
231
|
end
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
232
|
+
file = File.open(File.join(File.dirname(__FILE__), "../../../files/seasonal_shedding_peak_hours.json"))
|
233
|
+
default_peak_periods = JSON.load(file)
|
234
|
+
file.close
|
235
|
+
unless default_peak_periods.key?state
|
236
|
+
runner.registerAsNotApplicable("No default inputs for the state of the WeatherFile #{state}")
|
237
|
+
return false
|
148
238
|
end
|
239
|
+
peak_periods = default_peak_periods[state]
|
240
|
+
start_time1 = peak_periods["winter_peak_start"].split[1]
|
241
|
+
end_time1 = peak_periods["winter_peak_end"].split[1]
|
242
|
+
start_time2 = peak_periods["intermediate_peak_start"].split[1]
|
243
|
+
end_time2 = peak_periods["intermediate_peak_end"].split[1]
|
244
|
+
start_time3 = peak_periods["summer_peak_start"].split[1]
|
245
|
+
end_time3 = peak_periods["summer_peak_end"].split[1]
|
246
|
+
start_time4 = peak_periods["intermediate_peak_start"].split[1]
|
247
|
+
end_time4 = peak_periods["intermediate_peak_end"].split[1]
|
248
|
+
start_time5 = peak_periods["winter_peak_start"].split[1]
|
249
|
+
end_time5 = peak_periods["winter_peak_end"].split[1]
|
250
|
+
start_date1 = '01-01'
|
251
|
+
end_date1 = '03-31'
|
252
|
+
start_date2 = '04-01'
|
253
|
+
end_date2 = '05-31'
|
254
|
+
start_date3 = '06-01'
|
255
|
+
end_date3 = '09-30'
|
256
|
+
start_date4 = '10-01'
|
257
|
+
end_date4 = '11-30'
|
258
|
+
start_date5 = '12-01'
|
259
|
+
end_date5 = '12-31'
|
260
|
+
end
|
149
261
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
end_time = '16:59:00'
|
163
|
-
when '2A'
|
164
|
-
start_time = '17:01:00'
|
165
|
-
end_time = '20:59:00'
|
166
|
-
when '2B'
|
167
|
-
start_time = '17:01:00'
|
168
|
-
end_time = '20:59:00'
|
169
|
-
when '3A'
|
170
|
-
start_time = '19:01:00'
|
171
|
-
end_time = '22:59:00'
|
172
|
-
when '3B'
|
173
|
-
start_time = '18:01:00'
|
174
|
-
end_time = '21:59:00'
|
175
|
-
when '3C'
|
176
|
-
start_time = '19:01:00'
|
177
|
-
end_time = '22:59:00'
|
178
|
-
when '4A'
|
179
|
-
start_time = '12:01:00'
|
180
|
-
end_time = '15:59:00'
|
181
|
-
when '4B'
|
182
|
-
start_time = '17:01:00'
|
183
|
-
end_time = '20:59:00'
|
184
|
-
when '4C'
|
185
|
-
start_time = '17:01:00'
|
186
|
-
end_time = '20:59:00'
|
187
|
-
when '5A'
|
188
|
-
start_time = '20:01:00'
|
189
|
-
end_time = '23:59:00'
|
190
|
-
when '5B'
|
191
|
-
start_time = '17:01:00'
|
192
|
-
end_time = '20:59:00'
|
193
|
-
when '5C'
|
194
|
-
start_time = '17:01:00'
|
195
|
-
end_time = '20:59:00'
|
196
|
-
when '6A'
|
197
|
-
start_time = '16:01:00'
|
198
|
-
end_time = '19:59:00'
|
199
|
-
when '6B'
|
200
|
-
start_time = '17:01:00'
|
201
|
-
end_time = '20:59:00'
|
202
|
-
when '7A'
|
203
|
-
start_time = '16:01:00'
|
204
|
-
end_time = '19:59:00'
|
262
|
+
def validate_time_format(star_time, end_time, runner)
|
263
|
+
time1 = /(\d\d):(\d\d):(\d\d)/.match(star_time)
|
264
|
+
time2 = /(\d\d):(\d\d):(\d\d)/.match(end_time)
|
265
|
+
if time1 and time2
|
266
|
+
os_starttime = OpenStudio::Time.new(star_time)
|
267
|
+
os_endtime = OpenStudio::Time.new(end_time)
|
268
|
+
if star_time >= end_time
|
269
|
+
runner.registerError('The start time needs to be earlier than the end time.')
|
270
|
+
return false
|
271
|
+
else
|
272
|
+
return os_starttime, os_endtime
|
273
|
+
end
|
205
274
|
else
|
206
|
-
runner.registerError('
|
275
|
+
runner.registerError('The provided time is not in HH-MM-SS format.')
|
207
276
|
return false
|
208
277
|
end
|
209
278
|
end
|
210
279
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
280
|
+
def validate_date_format(start_date1, end_date1, runner, model)
|
281
|
+
smd = /(\d\d)-(\d\d)/.match(start_date1)
|
282
|
+
emd = /(\d\d)-(\d\d)/.match(end_date1)
|
283
|
+
if smd.nil? or emd.nil?
|
284
|
+
runner.registerError('The provided date is not in MM-DD format.')
|
285
|
+
return false
|
286
|
+
else
|
287
|
+
start_month = smd[1].to_i
|
288
|
+
start_day = smd[2].to_i
|
289
|
+
end_month = emd[1].to_i
|
290
|
+
end_day = emd[2].to_i
|
291
|
+
if start_date1 > end_date1
|
292
|
+
runner.registerError('The start date cannot be later date the end time.')
|
293
|
+
return false
|
294
|
+
else
|
295
|
+
# os_start_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(start_month), start_day)
|
296
|
+
# os_end_date = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(end_month), end_day)
|
297
|
+
os_start_date = model.getYearDescription.makeDate(start_month, start_day)
|
298
|
+
os_end_date = model.getYearDescription.makeDate(end_month, end_day)
|
299
|
+
return os_start_date, os_end_date
|
300
|
+
end
|
301
|
+
end
|
216
302
|
end
|
217
303
|
|
218
|
-
|
219
|
-
|
304
|
+
# First time period
|
305
|
+
time_result1 = validate_time_format(start_time1, end_time1, runner)
|
306
|
+
if time_result1
|
307
|
+
shift_time_start1, shift_time_end1 = time_result1
|
220
308
|
else
|
221
|
-
runner.registerError('
|
309
|
+
runner.registerError('The required time period for the reduction is not in correct format!')
|
222
310
|
return false
|
223
311
|
end
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
312
|
+
# The other optional time periods
|
313
|
+
shift_time_start2,shift_time_end2,shift_time_start3,shift_time_end3,shift_time_start4,shift_time_end4,shift_time_start5,shift_time_end5 = [nil]*8
|
314
|
+
if (not start_time2.empty?) and (not end_time2.empty?)
|
315
|
+
time_result2 = validate_time_format(start_time2, end_time2, runner)
|
316
|
+
if time_result2
|
317
|
+
shift_time_start2, shift_time_end2 = time_result2
|
318
|
+
end
|
319
|
+
end
|
320
|
+
if (not start_time3.empty?) and (not end_time3.empty?)
|
321
|
+
time_result3 = validate_time_format(start_time3, end_time3, runner)
|
322
|
+
if time_result3
|
323
|
+
shift_time_start3, shift_time_end3 = time_result3
|
324
|
+
end
|
325
|
+
end
|
326
|
+
if (not start_time4.empty?) and (not end_time4.empty?)
|
327
|
+
time_result4 = validate_time_format(start_time4, end_time4, runner)
|
328
|
+
if time_result4
|
329
|
+
shift_time_start4, shift_time_end4 = time_result4
|
330
|
+
end
|
331
|
+
end
|
332
|
+
if (not start_time5.empty?) and (not end_time5.empty?)
|
333
|
+
time_result5 = validate_time_format(start_time5, end_time5, runner)
|
334
|
+
if time_result5
|
335
|
+
shift_time_start5, shift_time_end5 = time_result5
|
336
|
+
end
|
228
337
|
end
|
229
338
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
start_month1 = md[1].to_i
|
235
|
-
start_day1 = md[2].to_i
|
339
|
+
# First date period
|
340
|
+
date_result1 = validate_date_format(start_date1, end_date1, runner, model)
|
341
|
+
if date_result1
|
342
|
+
os_start_date1, os_end_date1 = date_result1
|
236
343
|
else
|
237
|
-
runner.registerError('
|
344
|
+
runner.registerError('The required date period for the reduction is not in correct format!')
|
238
345
|
return false
|
239
346
|
end
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
runner.registerError('End date must be in MM-DD format.')
|
248
|
-
return false
|
347
|
+
# Other optional date period
|
348
|
+
os_start_date2, os_end_date2, os_start_date3, os_end_date3, os_start_date4, os_end_date4, os_start_date5, os_end_date5 = [nil]*8
|
349
|
+
if (not start_date2.empty?) and (not end_date2.empty?)
|
350
|
+
date_result2 = validate_date_format(start_date2, end_date2, runner, model)
|
351
|
+
if date_result2
|
352
|
+
os_start_date2, os_end_date2 = date_result2
|
353
|
+
end
|
249
354
|
end
|
250
355
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
if (start_date2.empty? and not end_date2.empty?) or (end_date2.empty? and not start_date2.empty?)
|
256
|
-
runner.registerWarning("Either start date or end date for the second period of reduction is not specified, so the second period of reduction will not be used.")
|
257
|
-
elsif not start_date2.empty? and (not end_date2.empty?)
|
258
|
-
smd = /(\d\d)-(\d\d)/.match(start_date2)
|
259
|
-
if smd
|
260
|
-
start_month2 = smd[1].to_i
|
261
|
-
start_day2 = smd[2].to_i
|
262
|
-
else
|
263
|
-
runner.registerError('Start date must be in MM-DD format. If you do not want a second period, leave both the start and end date blank.')
|
264
|
-
return false
|
265
|
-
end
|
266
|
-
emd = /(\d\d)-(\d\d)/.match(end_date2)
|
267
|
-
if emd
|
268
|
-
end_month2 = emd[1].to_i
|
269
|
-
end_day2 = emd[2].to_i
|
270
|
-
else
|
271
|
-
runner.registerError('End date must be in MM-DD format. If you do not want a second period, leave both the start and end date blank.')
|
272
|
-
return false
|
356
|
+
if (not start_date3.empty?) and (not end_date3.empty?)
|
357
|
+
date_result3 = validate_date_format(start_date3, end_date3, runner, model)
|
358
|
+
if date_result3
|
359
|
+
os_start_date3, os_end_date3 = date_result3
|
273
360
|
end
|
274
|
-
else
|
275
|
-
|
276
361
|
end
|
277
362
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
end_day3 = nil
|
283
|
-
if (start_date3.empty? and not end_date3.empty?) or (end_date3.empty? and not start_date3.empty?)
|
284
|
-
runner.registerWarning("Either start date or end date for the third period of reduction is not specified, so the second period of reduction will not be used.")
|
285
|
-
elsif not start_date3.empty? and (not end_date3.empty?)
|
286
|
-
smd = /(\d\d)-(\d\d)/.match(start_date3)
|
287
|
-
if smd
|
288
|
-
start_month3 = smd[1].to_i
|
289
|
-
start_day3 = smd[2].to_i
|
290
|
-
else
|
291
|
-
runner.registerError('Start date must be in MM-DD format. If you do not want a third period, leave both the start and end date blank.')
|
292
|
-
return false
|
293
|
-
end
|
294
|
-
emd = /(\d\d)-(\d\d)/.match(end_date3)
|
295
|
-
if emd
|
296
|
-
end_month3 = emd[1].to_i
|
297
|
-
end_day3 = emd[2].to_i
|
298
|
-
else
|
299
|
-
runner.registerError('End date must be in MM-DD format. If you do not want a third period, leave both the start and end date blank.')
|
300
|
-
return false
|
363
|
+
if (not start_date4.empty?) and (not end_date4.empty?)
|
364
|
+
date_result4 = validate_date_format(start_date4, end_date4, runner, model)
|
365
|
+
if date_result4
|
366
|
+
os_start_date4, os_end_date4 = date_result4
|
301
367
|
end
|
302
368
|
end
|
303
369
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
os_start_date2 = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(start_month2), start_day2)
|
310
|
-
os_end_date2 = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(end_month2), end_day2)
|
311
|
-
end
|
312
|
-
os_start_date3 = nil
|
313
|
-
os_end_date3 = nil
|
314
|
-
if [start_month3, start_day3, end_month3, end_day3].all?
|
315
|
-
os_start_date3 = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(start_month3), start_day3)
|
316
|
-
os_end_date3 = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(end_month3), end_day3)
|
370
|
+
if (not start_date5.empty?) and (not end_date5.empty?)
|
371
|
+
date_result5 = validate_date_format(start_date5, end_date5, runner, model)
|
372
|
+
if date_result5
|
373
|
+
os_start_date5, os_end_date5 = date_result5
|
374
|
+
end
|
317
375
|
end
|
318
376
|
|
377
|
+
|
319
378
|
# daylightsaving adjustment added in visualization, so deprecated here
|
320
379
|
# # Check model's daylight saving period, if cooling period is within daylight saving period, shift the cooling start time and end time by one hour later
|
321
380
|
# # only check the first period now, assuming majority will only modify for a single period
|
@@ -329,145 +388,220 @@ class ReduceLPDByPercentageForPeakHours < OpenStudio::Measure::ModelMeasure
|
|
329
388
|
# end
|
330
389
|
# end
|
331
390
|
|
332
|
-
|
391
|
+
adjust_period_inputs = { "period1" => {"date_start"=>os_start_date1, "date_end"=>os_end_date1,
|
392
|
+
"time_start"=>shift_time_start1, "time_end"=>shift_time_end1},
|
393
|
+
"period2" => {"date_start"=>os_start_date2, "date_end"=>os_end_date2,
|
394
|
+
"time_start"=>shift_time_start2, "time_end"=>shift_time_end2},
|
395
|
+
"period3" => {"date_start"=>os_start_date3, "date_end"=>os_end_date3,
|
396
|
+
"time_start"=>shift_time_start3, "time_end"=>shift_time_end3},
|
397
|
+
"period4" => {"date_start"=>os_start_date4, "date_end"=>os_end_date4,
|
398
|
+
"time_start"=>shift_time_start4, "time_end"=>shift_time_end4},
|
399
|
+
"period5" => {"date_start"=>os_start_date5, "date_end"=>os_end_date5,
|
400
|
+
"time_start"=>shift_time_start5, "time_end"=>shift_time_end5} }
|
401
|
+
|
402
|
+
|
403
|
+
exclude_space_types = ["ER_Exam", "ER_NurseStn", "ER_Trauma", "ER_Triage", "ICU_NurseStn", "ICU_Open", "ICU_PatRm", "Lab",
|
404
|
+
"OR", "Anesthesia", "BioHazard", "Exam", "MedGas", "OR", "PACU", "PreOp", "ProcedureRoom", "Lab with fume hood", 'HspSurgOutptLab']
|
405
|
+
|
333
406
|
applicable = false
|
334
|
-
lights = model.getLightss
|
335
407
|
# create a hash to map the old schedule name to the new schedule
|
336
408
|
light_schedules = {}
|
337
|
-
|
338
|
-
puts "light: #{light.name}"
|
409
|
+
model.getLightss.each do |light|
|
339
410
|
light_sch = light.schedule
|
340
|
-
puts "light_sch: #{light_sch}"
|
341
411
|
if light_sch.empty?
|
342
412
|
runner.registerWarning("#{light.name} doesn't have a schedule.")
|
343
413
|
else
|
414
|
+
occupied = false
|
415
|
+
if light.spaceType.is_initialized
|
416
|
+
space_type = light.spaceType.get
|
417
|
+
peoples = space_type.people
|
418
|
+
peoples.each do |people|
|
419
|
+
if people.numberOfPeople.is_initialized and people.numberOfPeople.get > 0
|
420
|
+
occupied = true
|
421
|
+
break
|
422
|
+
elsif people. peoplePerFloorArea.is_initialized and people.peoplePerFloorArea.get > 0
|
423
|
+
occupied = true
|
424
|
+
break
|
425
|
+
elsif people. spaceFloorAreaPerPerson.is_initialized and people.spaceFloorAreaPerPerson.get > 0
|
426
|
+
occupied = true
|
427
|
+
break
|
428
|
+
end
|
429
|
+
end
|
430
|
+
elsif light.space.is_initialized
|
431
|
+
space = light.space.get
|
432
|
+
# Any of the peoplePerFloorArea, numberOfPeople or floorAreaPerPerson will calculate the people automatically
|
433
|
+
# despite the Number of People Calculation Method specified
|
434
|
+
if space.peoplePerFloorArea > 0
|
435
|
+
occupied = true
|
436
|
+
end
|
437
|
+
end
|
438
|
+
if occupied
|
439
|
+
lpd_factor = 1 - (lpd_reduce_percent/100)
|
440
|
+
runner.registerInfo("The space/space type associated with the light #{light.name.to_s} is occupied, so the LPD is reduced for #{lpd_reduce_percent}%")
|
441
|
+
else
|
442
|
+
lpd_factor = 1 - (lpd_reduce_percent_unoccupied/100)
|
443
|
+
runner.registerInfo("The space/space type associated with the light #{light.name.to_s} is unoccupied, so the LPD is reduced for #{lpd_reduce_percent_unoccupied}%")
|
444
|
+
end
|
445
|
+
|
344
446
|
if light_schedules.key?(light_sch.get.name.to_s)
|
345
|
-
new_light_sch = light_schedules[light_sch.get.name.to_s]
|
447
|
+
new_light_sch = light_schedules[light_sch.get.name.to_s]["schedule"]
|
346
448
|
else
|
347
449
|
new_light_sch = light_sch.get.clone(model)
|
348
450
|
new_light_sch = new_light_sch.to_Schedule.get
|
349
451
|
new_light_sch.setName("#{light_sch.get.name.to_s} adjusted #{lpd_factor}")
|
350
452
|
# add to the hash
|
351
|
-
light_schedules[light_sch.get.name.to_s] = new_light_sch
|
453
|
+
light_schedules[light_sch.get.name.to_s] = {"schedule"=>new_light_sch, "lpd_factor" => lpd_factor}
|
352
454
|
end
|
353
455
|
light.setSchedule(new_light_sch)
|
354
456
|
end
|
457
|
+
|
458
|
+
|
355
459
|
end
|
356
460
|
|
357
|
-
light_schedules.each do |old_name,
|
358
|
-
|
359
|
-
|
461
|
+
light_schedules.each do |old_name, sch_info|
|
462
|
+
cloned_light_sch = sch_info["schedule"]
|
463
|
+
lpd_factor = sch_info["lpd_factor"]
|
464
|
+
if cloned_light_sch.to_ScheduleRuleset.empty?
|
360
465
|
runner.registerWarning("Schedule #{old_name} isn't a ScheduleRuleset object and won't be altered by this measure.")
|
361
|
-
|
466
|
+
cloned_light_sch.remove # remove un-used cloned schedule
|
362
467
|
else
|
363
|
-
schedule_set =
|
364
|
-
default_rule = schedule_set.defaultDaySchedule
|
468
|
+
schedule_set = cloned_light_sch.to_ScheduleRuleset.get
|
365
469
|
rules = schedule_set.scheduleRules
|
366
470
|
days_covered = Array.new(7, false)
|
367
471
|
original_rule_number = rules.length
|
472
|
+
current_index = 0
|
368
473
|
if original_rule_number > 0
|
369
|
-
runner.registerInfo("
|
370
|
-
current_index = 0
|
474
|
+
runner.registerInfo("schedule rule set #{old_name} has #{original_rule_number} rules.")
|
371
475
|
# rules are in order of priority
|
372
476
|
rules.each do |rule|
|
373
|
-
runner.registerInfo("
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
477
|
+
runner.registerInfo("---- Rule No.#{rule.ruleIndex}: #{rule.name.to_s}")
|
478
|
+
if rule.dateSpecificationType == "SpecificDates"
|
479
|
+
## if the rule applies to SpecificDates, collect the dates that fall into each adjustment date period,
|
480
|
+
## and create a new rule for each date period with covered specific dates
|
481
|
+
runner.registerInfo("======= The rule #{rule.name.to_s} only covers specific dates.")
|
482
|
+
## the specificDates cannot be modified in place because it's a frozen array
|
483
|
+
all_specific_dates = []
|
484
|
+
rule.specificDates.each { |date| all_specific_dates << date }
|
485
|
+
adjust_period_inputs.each do |period, period_inputs|
|
486
|
+
period_inputs["specific_dates"] = []
|
487
|
+
os_start_date = period_inputs["date_start"]
|
488
|
+
os_end_date = period_inputs["date_end"]
|
489
|
+
shift_time_start = period_inputs["time_start"]
|
490
|
+
shift_time_end = period_inputs["time_end"]
|
491
|
+
if [os_start_date, os_end_date, shift_time_start, shift_time_end].all?
|
492
|
+
rule.specificDates.each do |covered_date|
|
493
|
+
if covered_date >= os_start_date and covered_date <= os_end_date
|
494
|
+
period_inputs["specific_dates"] << covered_date
|
495
|
+
all_specific_dates.delete(covered_date)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
runner.registerInfo("========= Specific dates within date range #{os_start_date.to_s} to #{os_end_date.to_s}: #{period_inputs["specific_dates"].map(&:to_s)}")
|
499
|
+
runner.registerInfo("!!! Specific dates haven't been covered: #{all_specific_dates.map(&:to_s)}")
|
500
|
+
next if period_inputs["specific_dates"].empty?
|
501
|
+
rule_period = modify_rule_for_specific_dates(rule, os_start_date, os_end_date, shift_time_start, shift_time_end,
|
502
|
+
lpd_factor, period_inputs["specific_dates"])
|
503
|
+
if rule_period
|
504
|
+
applicable = true
|
505
|
+
if schedule_set.setScheduleRuleIndex(rule_period, current_index)
|
506
|
+
current_index += 1
|
507
|
+
runner.registerInfo("-------- The rule #{rule_period.name.to_s} for #{rule_period.dateSpecificationType} is added as priority #{current_index}")
|
508
|
+
else
|
509
|
+
runner.registerError("Fail to set rule index for #{rule_period.name.to_s}.")
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
405
513
|
end
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
514
|
+
if all_specific_dates.empty?
|
515
|
+
## if all specific dates have been covered by new rules for each adjustment date period, remove the original rule
|
516
|
+
runner.registerInfo("The original rule is removed since no specific date left")
|
517
|
+
else
|
518
|
+
## if there's still dates left to be covered, modify the original rule to only cover these dates
|
519
|
+
## (this is just in case that the rule order was not set correctly, and the original rule is still applied to all specific dates;
|
520
|
+
## also to make the logic in OSM more clearer)
|
521
|
+
## the specificDates cannot be modified in place, so create a new rule with the left dates to replace the original rule
|
522
|
+
original_rule_update = clone_rule_with_new_dayschedule(rule, rule.name.to_s + " - dates left")
|
523
|
+
schedule_set.setScheduleRuleIndex(original_rule_update, current_index)
|
524
|
+
current_index += 1
|
525
|
+
all_specific_dates.each do |date|
|
526
|
+
original_rule_update.addSpecificDate(date)
|
527
|
+
end
|
528
|
+
runner.registerInfo("-------- The original rule #{rule.name.to_s} is modified to only cover the rest of the dates: #{all_specific_dates.map(&:to_s)}")
|
529
|
+
runner.registerInfo("-------- and is shifted to priority #{current_index}")
|
530
|
+
end
|
531
|
+
rule.remove
|
532
|
+
|
533
|
+
else
|
534
|
+
## If the rule applies to a DateRange, check if the DateRange overlaps with each adjustment date period
|
535
|
+
## if so, create a new rule for that adjustment date period
|
536
|
+
runner.registerInfo("******* The rule #{rule.name.to_s} covers date range #{rule.startDate.get} - #{rule.endDate.get}.")
|
537
|
+
# runner.registerInfo("Before adjustment, the rule #{rule.name.to_s} is #{rule.daySchedule.values}")
|
538
|
+
adjust_period_inputs.each do |period, period_inputs|
|
539
|
+
os_start_date = period_inputs["date_start"]
|
540
|
+
os_end_date = period_inputs["date_end"]
|
541
|
+
shift_time_start = period_inputs["time_start"]
|
542
|
+
shift_time_end = period_inputs["time_end"]
|
543
|
+
if [os_start_date, os_end_date, shift_time_start, shift_time_end].all?
|
544
|
+
overlapped, new_start_dates, new_end_dates = check_date_ranges_overlap(rule, os_start_date, os_end_date)
|
545
|
+
if overlapped
|
546
|
+
new_start_dates.each_with_index do |start_date, i|
|
547
|
+
rule_period = modify_rule_for_date_period(rule, start_date, new_end_dates[i], shift_time_start, shift_time_end, lpd_factor)
|
548
|
+
if rule_period
|
549
|
+
# runner.registerInfo("After adjustment, the rule #{rule.name.to_s} becomes #{rule.daySchedule.values}")
|
550
|
+
# runner.registerInfo("After adjustment, the new rule is #{rule_period.daySchedule.values}")
|
551
|
+
applicable = true
|
552
|
+
if period == "period1"
|
553
|
+
checkDaysCovered(rule_period, days_covered)
|
554
|
+
end
|
555
|
+
if schedule_set.setScheduleRuleIndex(rule_period, current_index)
|
556
|
+
runner.registerInfo("-------- The rule #{rule_period.name.to_s} is added as priority #{current_index}")
|
557
|
+
current_index += 1
|
558
|
+
else
|
559
|
+
runner.registerError("-------- Fail to set rule index for #{rule_period.name.to_s}.")
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
## The original rule will be shifted to the currently lowest priority
|
567
|
+
## Setting the rule to an existing index will automatically push all other rules after it down
|
568
|
+
if schedule_set.setScheduleRuleIndex(rule, current_index)
|
569
|
+
runner.registerInfo("-------- The original rule #{rule.name.to_s} is shifted to priority #{current_index}")
|
570
|
+
current_index += 1
|
571
|
+
else
|
572
|
+
runner.registerError("Fail to set rule index for #{rule.name.to_s}.")
|
415
573
|
end
|
416
|
-
current_index += 1
|
417
|
-
runner.registerInfo(" ------------ schedule updated for #{rule_period3.startDate.get} to #{rule_period3.endDate.get}")
|
418
|
-
end
|
419
|
-
|
420
|
-
# The original rule will be shifted to have the currently lowest priority
|
421
|
-
unless schedule_set.setScheduleRuleIndex(rule, original_rule_number + current_index - 1)
|
422
|
-
runner.registerError("Fail to set rule index for #{rule.daySchedule.name.to_s}.")
|
423
574
|
end
|
424
|
-
# runner.registerInfo("--------------- Current sule index of the original rule: #{rule.ruleIndex}")
|
425
575
|
end
|
426
576
|
else
|
427
577
|
runner.registerWarning("Lighting schedule #{old_name} is a ScheduleRuleSet, but has no ScheduleRules associated. It won't be altered by this measure.")
|
428
578
|
end
|
429
579
|
|
580
|
+
# if all rules not covered all days in a week, then the rest of days use defaultDaySchedule
|
581
|
+
# defaultDaySchedule cannot specify date range, so clone it to a new rule to set date range and cover the rest of days
|
582
|
+
default_day = schedule_set.defaultDaySchedule
|
430
583
|
if days_covered.include?(false)
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
new_default_day.clearValues
|
444
|
-
new_default_day = updateDaySchedule(new_default_day, day_time_vector, day_value_vector, shift_time1, shift_time2, lpd_factor)
|
445
|
-
if os_start_date2 and os_end_date2
|
446
|
-
copy_sch_rule_for_period(model, new_default_rule, new_default_day, os_start_date2, os_end_date2)
|
447
|
-
end
|
448
|
-
if os_start_date3 and os_end_date3
|
449
|
-
copy_sch_rule_for_period(model, new_default_rule, new_default_day, os_start_date3, os_end_date3)
|
584
|
+
runner.registerInfo("-------- Some days use default day. Adding new scheduleRule from defaultDaySchedule for applicable date period.")
|
585
|
+
adjust_period_inputs.each do |period, period_inputs|
|
586
|
+
runner.registerInfo("******** current covered days: #{days_covered}")
|
587
|
+
os_start_date = period_inputs["date_start"]
|
588
|
+
os_end_date = period_inputs["date_end"]
|
589
|
+
shift_time_start = period_inputs["time_start"]
|
590
|
+
shift_time_end = period_inputs["time_end"]
|
591
|
+
if [os_start_date, os_end_date, shift_time_start, shift_time_end].all?
|
592
|
+
new_default_rule_period = modify_default_day_for_date_period(schedule_set, default_day, days_covered, os_start_date, os_end_date,
|
593
|
+
shift_time_start, shift_time_end, lpd_factor)
|
594
|
+
schedule_set.setScheduleRuleIndex(new_default_rule_period, current_index)
|
595
|
+
end
|
450
596
|
end
|
451
|
-
|
452
597
|
end
|
453
|
-
|
598
|
+
# schedule_set.scheduleRules.each do |final_rule|
|
599
|
+
# runner.registerInfo("Finally rules: #{final_rule.name.to_s} is in priority #{final_rule.ruleIndex}")
|
600
|
+
# end
|
454
601
|
end
|
455
602
|
|
456
603
|
end
|
457
604
|
|
458
|
-
# # Check the final rules within each schedule
|
459
|
-
# runner.registerInfo("------------------------FINAL--------------------")
|
460
|
-
# space_type.lights.each do |light|
|
461
|
-
# lgt_schedule_set = light.schedule
|
462
|
-
# unless lgt_schedule_set.empty?
|
463
|
-
# runner.registerInfo("Schedule #{lgt_schedule_set.get.name.to_s}:")
|
464
|
-
# sch_set = lgt_schedule_set.get.to_Schedule.get
|
465
|
-
# sch_set.to_ScheduleRuleset.get.scheduleRules.each do |rule|
|
466
|
-
# runner.registerInfo(" rule #{rule.ruleIndex}: #{rule.daySchedule.name.to_s} from #{rule.startDate.get} to #{rule.endDate.get}")
|
467
|
-
# end
|
468
|
-
# end
|
469
|
-
# end
|
470
|
-
|
471
605
|
unless applicable
|
472
606
|
runner.registerAsNotApplicable('No lighting schedule in the model could be altered.')
|
473
607
|
end
|
@@ -475,15 +609,107 @@ class ReduceLPDByPercentageForPeakHours < OpenStudio::Measure::ModelMeasure
|
|
475
609
|
return true
|
476
610
|
end
|
477
611
|
|
478
|
-
def
|
479
|
-
|
480
|
-
|
481
|
-
|
612
|
+
def check_date_ranges_overlap(rule, adjust_start_date, adjust_end_date)
|
613
|
+
## check if the original rule applied DateRange overlaps with the adjustment date period
|
614
|
+
overlapped = false
|
615
|
+
new_start_dates = []
|
616
|
+
new_end_dates = []
|
617
|
+
if rule.endDate.get >= rule.startDate.get and rule.startDate.get <= adjust_end_date and rule.endDate.get >= adjust_start_date
|
618
|
+
overlapped = true
|
619
|
+
new_start_dates << [adjust_start_date, rule.startDate.get].max
|
620
|
+
new_end_dates << [adjust_end_date, rule.endDate.get].min
|
621
|
+
elsif rule.endDate.get < rule.startDate.get
|
622
|
+
## If the DateRange has a endDate < startDate, the range wraps around the year.
|
623
|
+
if rule.endDate.get >= adjust_start_date
|
624
|
+
overlapped = true
|
625
|
+
new_start_dates << adjust_start_date
|
626
|
+
new_end_dates << rule.endDate.get
|
627
|
+
end
|
628
|
+
if rule.startDate.get <= adjust_end_date
|
629
|
+
overlapped = true
|
630
|
+
new_start_dates << rule.startDate.get
|
631
|
+
new_end_dates << adjust_end_date
|
632
|
+
end
|
633
|
+
end
|
634
|
+
return overlapped, new_start_dates, new_end_dates
|
635
|
+
end
|
636
|
+
|
637
|
+
def clone_rule_with_new_dayschedule(original_rule, new_rule_name)
|
638
|
+
## Cloning a scheduleRule will automatically clone the daySchedule associated with it, but it's a shallow copy,
|
639
|
+
## because the daySchedule is a resource that can be used by many scheduleRule
|
640
|
+
## Therefore, once the daySchedule is modified for the cloned scheduleRule, the original daySchedule is also changed
|
641
|
+
## Also, there's no function to assign a new daySchedule to the existing scheduleRule,
|
642
|
+
## so the only way to clone the scheduleRule but change the daySchedule is to construct a new scheduleRule with a daySchedule passed in
|
643
|
+
## and copy all other settings from the original scheduleRule
|
644
|
+
rule_period = OpenStudio::Model::ScheduleRule.new(original_rule.scheduleRuleset, original_rule.daySchedule)
|
645
|
+
rule_period.setName(new_rule_name)
|
646
|
+
rule_period.setApplySunday(original_rule.applySunday)
|
647
|
+
rule_period.setApplyMonday(original_rule.applyMonday)
|
648
|
+
rule_period.setApplyTuesday(original_rule.applyTuesday)
|
649
|
+
rule_period.setApplyWednesday(original_rule.applyWednesday)
|
650
|
+
rule_period.setApplyThursday(original_rule.applyThursday)
|
651
|
+
rule_period.setApplyFriday(original_rule.applyFriday)
|
652
|
+
rule_period.setApplySaturday(original_rule.applySaturday)
|
653
|
+
return rule_period
|
654
|
+
end
|
482
655
|
|
483
|
-
|
484
|
-
|
656
|
+
def modify_rule_for_date_period(original_rule, os_start_date, os_end_date, shift_time_start, shift_time_end, adjustment)
|
657
|
+
# The cloned scheduleRule will automatically belongs to the originally scheduleRuleSet
|
658
|
+
# rule_period = original_rule.clone(model).to_ScheduleRule.get
|
659
|
+
# rule_period.daySchedule = original_rule.daySchedule.clone(model)
|
660
|
+
new_rule_name = "#{original_rule.name.to_s} with DF for #{os_start_date.to_s} to #{os_end_date.to_s}"
|
661
|
+
rule_period = clone_rule_with_new_dayschedule(original_rule, new_rule_name)
|
662
|
+
day_rule_period = rule_period.daySchedule
|
663
|
+
day_time_vector = day_rule_period.times
|
664
|
+
day_value_vector = day_rule_period.values
|
665
|
+
if day_time_vector.empty?
|
666
|
+
return false
|
667
|
+
end
|
668
|
+
day_rule_period.clearValues
|
669
|
+
updateDaySchedule(day_rule_period, day_time_vector, day_value_vector, shift_time_start, shift_time_end, adjustment)
|
670
|
+
if rule_period
|
671
|
+
rule_period.setStartDate(os_start_date)
|
672
|
+
rule_period.setEndDate(os_end_date)
|
673
|
+
end
|
674
|
+
return rule_period
|
675
|
+
end
|
485
676
|
|
486
|
-
|
677
|
+
def modify_rule_for_specific_dates(original_rule, os_start_date, os_end_date, shift_time_start, shift_time_end,
|
678
|
+
adjustment, applied_dates)
|
679
|
+
new_rule_name = "#{original_rule.name.to_s} with DF for #{os_start_date.to_s} to #{os_end_date.to_s}"
|
680
|
+
rule_period = clone_rule_with_new_dayschedule(original_rule, new_rule_name)
|
681
|
+
day_rule_period = rule_period.daySchedule
|
682
|
+
day_time_vector = day_rule_period.times
|
683
|
+
day_value_vector = day_rule_period.values
|
684
|
+
if day_time_vector.empty?
|
685
|
+
return false
|
686
|
+
end
|
687
|
+
day_rule_period.clearValues
|
688
|
+
updateDaySchedule(day_rule_period, day_time_vector, day_value_vector, shift_time_start, shift_time_end, adjustment)
|
689
|
+
if rule_period
|
690
|
+
applied_dates.each do |date|
|
691
|
+
rule_period.addSpecificDate(date)
|
692
|
+
end
|
693
|
+
end
|
694
|
+
return rule_period
|
695
|
+
end
|
696
|
+
|
697
|
+
def modify_default_day_for_date_period(schedule_set, default_day, days_covered, os_start_date, os_end_date,
|
698
|
+
shift_time_start, shift_time_end, lpd_factor)
|
699
|
+
# the new rule created for the ScheduleRuleSet by default has the highest priority (ruleIndex=0)
|
700
|
+
new_default_rule = OpenStudio::Model::ScheduleRule.new(schedule_set, default_day)
|
701
|
+
new_default_rule.setName("#{schedule_set.name.to_s} default day with DF for #{os_start_date.to_s} to #{os_end_date.to_s}")
|
702
|
+
new_default_rule.setStartDate(os_start_date)
|
703
|
+
new_default_rule.setEndDate(os_end_date)
|
704
|
+
coverMissingDays(new_default_rule, days_covered)
|
705
|
+
new_default_day = new_default_rule.daySchedule
|
706
|
+
day_time_vector = new_default_day.times
|
707
|
+
day_value_vector = new_default_day.values
|
708
|
+
new_default_day.clearValues
|
709
|
+
new_default_day = updateDaySchedule(new_default_day, day_time_vector, day_value_vector, shift_time_start, shift_time_end, lpd_factor)
|
710
|
+
# schedule_set.setScheduleRuleIndex(new_default_rule, 0)
|
711
|
+
return new_default_rule
|
712
|
+
# TODO: if the scheduleRuleSet has holidaySchedule (which is a ScheduleDay), it cannot be altered
|
487
713
|
end
|
488
714
|
|
489
715
|
def checkDaysCovered(sch_rule, sch_day_covered)
|
@@ -568,7 +794,6 @@ class ReduceLPDByPercentageForPeakHours < OpenStudio::Measure::ModelMeasure
|
|
568
794
|
sch_day.addValue(exist_timestamp, vec_value[i])
|
569
795
|
end
|
570
796
|
end
|
571
|
-
|
572
797
|
return sch_day
|
573
798
|
end
|
574
799
|
|