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.
@@ -4,6 +4,7 @@
4
4
  # *******************************************************************************
5
5
 
6
6
  # start the measure
7
+ require 'json'
7
8
  class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::ModelMeasure
8
9
  # setup OpenStudio units that we will need
9
10
  TEMP_IP_UNIT = OpenStudio.createUnit('F').get
@@ -28,101 +29,244 @@ class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::Mode
28
29
  def arguments(model)
29
30
  args = OpenStudio::Measure::OSArgumentVector.new
30
31
 
31
- # make an argument for adjustment to cooling setpoint
32
- cooling_adjustment = OpenStudio::Measure::OSArgument.makeDoubleArgument('cooling_adjustment', true)
33
- cooling_adjustment.setDisplayName('Degrees Fahrenheit to Adjust Cooling Setpoint By')
34
- cooling_adjustment.setDefaultValue(2.0)
35
- args << cooling_adjustment
36
-
37
- # make an argument for the start time of cooling adjustment
38
- cooling_daily_starttime = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_daily_starttime', false)
39
- cooling_daily_starttime.setDisplayName('Daily Start Time for Cooling Adjustment')
40
- cooling_daily_starttime.setDescription('Use 24 hour format HH:MM:SS')
41
- cooling_daily_starttime.setDefaultValue('16:01:00')
42
- args << cooling_daily_starttime
43
-
44
- # make an argument for the end time of cooling adjustment
45
- cooling_daily_endtime = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_daily_endtime', false)
46
- cooling_daily_endtime.setDisplayName('Daily End Time for Cooling Adjustment')
47
- cooling_daily_endtime.setDescription('Use 24 hour format HH:MM:SS')
48
- cooling_daily_endtime.setDefaultValue('20:00:00')
49
- args << cooling_daily_endtime
50
-
51
- # make an argument for the start date of cooling adjustment
52
- cooling_startdate = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_startdate', false)
53
- cooling_startdate.setDisplayName('Start Date for Cooling Adjustment')
54
- cooling_startdate.setDescription('In MM-DD format')
55
- cooling_startdate.setDefaultValue('06-01')
56
- args << cooling_startdate
57
-
58
- # make an argument for the end date of cooling adjustment
59
- cooling_enddate = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_enddate', false)
60
- cooling_enddate.setDisplayName('End Date for Cooling Adjustment')
61
- cooling_enddate.setDescription('In MM-DD format')
62
- cooling_enddate.setDefaultValue('09-30')
63
- args << cooling_enddate
64
-
65
32
  # make an argument for adjustment to heating setpoint
66
33
  heating_adjustment = OpenStudio::Measure::OSArgument.makeDoubleArgument('heating_adjustment', true)
67
34
  heating_adjustment.setDisplayName('Degrees Fahrenheit to Adjust heating Setpoint By')
68
35
  heating_adjustment.setDefaultValue(-2.0)
69
36
  args << heating_adjustment
70
37
 
71
- # make an argument for the start time of heating adjustment
72
- heating_daily_starttime = OpenStudio::Measure::OSArgument.makeStringArgument('heating_daily_starttime', false)
73
- heating_daily_starttime.setDisplayName('Start Time for Heating Adjustment')
74
- heating_daily_starttime.setDescription('Use 24 hour format HH:MM:SS')
75
- heating_daily_starttime.setDefaultValue('18:01:00')
76
- args << heating_daily_starttime
77
-
78
- # make an argument for the end time of heating adjustment
79
- heating_daily_endtime = OpenStudio::Measure::OSArgument.makeStringArgument('heating_daily_endtime', false)
80
- heating_daily_endtime.setDisplayName('End Time for Heating Adjustment')
81
- heating_daily_endtime.setDescription('Use 24 hour format HH:MM:SS')
82
- heating_daily_endtime.setDefaultValue('22:00:00')
83
- args << heating_daily_endtime
84
-
85
- # make an argument for the first start date of heating adjustment
86
- heating_startdate_1 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_startdate_1', false)
87
- heating_startdate_1.setDisplayName('Start Date for Heating Adjustment Period 1')
88
- heating_startdate_1.setDescription('In MM-DD format')
89
- heating_startdate_1.setDefaultValue('01-01')
90
- args << heating_startdate_1
91
-
92
- # make an argument for the first end date of heating adjustment
93
- heating_enddate_1 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_enddate_1', false)
94
- heating_enddate_1.setDisplayName('End Date for Heating Adjustment Period 1')
95
- heating_enddate_1.setDescription('In MM-DD format')
96
- heating_enddate_1.setDefaultValue('05-31')
97
- args << heating_enddate_1
98
-
99
- # make an argument for the second start date of heating adjustment
100
- heating_startdate_2 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_startdate_2', false)
101
- heating_startdate_2.setDisplayName('Start Date for Heating Adjustment Period 2')
102
- heating_startdate_2.setDescription('In MM-DD format')
103
- heating_startdate_2.setDefaultValue('10-01')
104
- args << heating_startdate_2
105
-
106
- # make an argument for the second end date of heating adjustment
107
- heating_enddate_2 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_enddate_2', false)
108
- heating_enddate_2.setDisplayName('End Date for Heating Adjustment Period 2')
109
- heating_enddate_2.setDescription('In MM-DD format')
110
- heating_enddate_2.setDefaultValue('12-31')
111
- args << heating_enddate_2
112
-
113
- # make an argument if the thermostat for design days should be altered
114
- alter_design_days = OpenStudio::Measure::OSArgument.makeBoolArgument('alter_design_days', false)
115
- alter_design_days.setDisplayName('Alter Design Day Thermostats')
116
- alter_design_days.setDefaultValue(false)
117
- args << alter_design_days
118
-
119
- auto_date = OpenStudio::Measure::OSArgument.makeBoolArgument('auto_date', false)
120
- auto_date.setDisplayName('Enable Climate-specific Periods Setting?')
121
- auto_date.setDefaultValue(false)
122
- args << auto_date
123
-
124
- alt_periods = OpenStudio::Measure::OSArgument.makeBoolArgument('alt_periods', false)
125
- alt_periods.setDisplayName('Alternate Peak and Take Periods')
38
+ heating_start_date1 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_start_date1', true)
39
+ heating_start_date1.setDisplayName('First start date for heating setpoint adjustment')
40
+ heating_start_date1.setDescription('In MM-DD format')
41
+ heating_start_date1.setDefaultValue('01-01')
42
+ args << heating_start_date1
43
+ heating_end_date1 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_end_date1', true)
44
+ heating_end_date1.setDisplayName('First end date for heating setpoint adjustment')
45
+ heating_end_date1.setDescription('In MM-DD format')
46
+ heating_end_date1.setDefaultValue('03-31')
47
+ args << heating_end_date1
48
+
49
+ heating_start_date2 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_start_date2', false)
50
+ heating_start_date2.setDisplayName('Second start date for heating setpoint adjustment (optional)')
51
+ heating_start_date2.setDescription('Specify a date in MM-DD format if you want a second season of heating setpoint adjustment; leave blank if not needed.')
52
+ heating_start_date2.setDefaultValue('')
53
+ args << heating_start_date2
54
+ heating_end_date2 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_end_date2', false)
55
+ heating_end_date2.setDisplayName('Second end date for heating setpoint adjustment')
56
+ heating_end_date2.setDescription('Specify a date in MM-DD format if you want a second season of heating setpoint adjustment; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
57
+ heating_end_date2.setDefaultValue('')
58
+ args << heating_end_date2
59
+
60
+ heating_start_date3 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_start_date3', false)
61
+ heating_start_date3.setDisplayName('Third start date for heating setpoint adjustment (optional)')
62
+ heating_start_date3.setDescription('Specify a date in MM-DD format if you want a third season of heating setpoint adjustment; leave blank if not needed.')
63
+ heating_start_date3.setDefaultValue('')
64
+ args << heating_start_date3
65
+ heating_end_date3 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_end_date3', false)
66
+ heating_end_date3.setDisplayName('Third end date for heating setpoint adjustment')
67
+ heating_end_date3.setDescription('Specify a date in MM-DD format if you want a third season of heating setpoint adjustment; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
68
+ heating_end_date3.setDefaultValue('')
69
+ args << heating_end_date3
70
+
71
+ heating_start_date4 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_start_date4', false)
72
+ heating_start_date4.setDisplayName('Fourth start date for heating setpoint adjustment (optional)')
73
+ heating_start_date4.setDescription('Specify a date in MM-DD format if you want a fourth season of heating setpoint adjustment; leave blank if not needed.')
74
+ heating_start_date4.setDefaultValue('')
75
+ args << heating_start_date4
76
+ heating_end_date4 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_end_date4', false)
77
+ heating_end_date4.setDisplayName('Fourth end date for heating setpoint adjustment')
78
+ heating_end_date4.setDescription('Specify a date in MM-DD format if you want a fourth season of heating setpoint adjustment; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
79
+ heating_end_date4.setDefaultValue('')
80
+ args << heating_end_date4
81
+
82
+ heating_start_date5 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_start_date5', false)
83
+ heating_start_date5.setDisplayName('Fifth start date for heating setpoint adjustment (optional)')
84
+ heating_start_date5.setDescription('Specify a date in MM-DD format if you want a fifth season of heating setpoint adjustment; leave blank if not needed.')
85
+ heating_start_date5.setDefaultValue('')
86
+ args << heating_start_date5
87
+ heating_end_date5 = OpenStudio::Ruleset::OSArgument.makeStringArgument('heating_end_date5', false)
88
+ heating_end_date5.setDisplayName('Fifth end date for heating setpoint adjustment')
89
+ heating_end_date5.setDescription('Specify a date in MM-DD format if you want a fifth season of heating setpoint adjustment; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
90
+ heating_end_date5.setDefaultValue('')
91
+ args << heating_end_date5
92
+
93
+ heating_start_time1 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_start_time1', true)
94
+ heating_start_time1.setDisplayName('Start time of heating setpoint adjustment for the first season')
95
+ heating_start_time1.setDescription('In HH:MM:SS format')
96
+ heating_start_time1.setDefaultValue('17:00:00')
97
+ args << heating_start_time1
98
+ heating_end_time1 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_end_time1', true)
99
+ heating_end_time1.setDisplayName('End time of heating setpoint adjustment for the first season')
100
+ heating_end_time1.setDescription('In HH:MM:SS format')
101
+ heating_end_time1.setDefaultValue('21:00:00')
102
+ args << heating_end_time1
103
+
104
+ heating_start_time2 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_start_time2', false)
105
+ heating_start_time2.setDisplayName('Start time of heating setpoint adjustment for the second season (optional)')
106
+ heating_start_time2.setDescription('In HH:MM:SS format')
107
+ heating_start_time2.setDefaultValue('')
108
+ args << heating_start_time2
109
+ heating_end_time2 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_end_time2', false)
110
+ heating_end_time2.setDisplayName('End time of heating setpoint adjustment for the second season (optional)')
111
+ heating_end_time2.setDescription('In HH:MM:SS format')
112
+ heating_end_time2.setDefaultValue('')
113
+ args << heating_end_time2
114
+
115
+ heating_start_time3 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_start_time3', false)
116
+ heating_start_time3.setDisplayName('Start time of heating setpoint adjustment for the third season (optional)')
117
+ heating_start_time3.setDescription('In HH:MM:SS format')
118
+ heating_start_time3.setDefaultValue('')
119
+ args << heating_start_time3
120
+ heating_end_time3 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_end_time3', false)
121
+ heating_end_time3.setDisplayName('End time of heating setpoint adjustment for the third season (optional)')
122
+ heating_end_time3.setDescription('In HH:MM:SS format')
123
+ heating_end_time3.setDefaultValue('')
124
+ args << heating_end_time3
125
+
126
+ heating_start_time4 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_start_time4', false)
127
+ heating_start_time4.setDisplayName('Start time of heating setpoint adjustment for the fourth season (optional)')
128
+ heating_start_time4.setDescription('In HH:MM:SS format')
129
+ heating_start_time4.setDefaultValue('')
130
+ args << heating_start_time4
131
+ heating_end_time4 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_end_time4', false)
132
+ heating_end_time4.setDisplayName('End time of heating setpoint adjustment for the fourth season (optional)')
133
+ heating_end_time4.setDescription('In HH:MM:SS format')
134
+ heating_end_time4.setDefaultValue('')
135
+ args << heating_end_time4
136
+
137
+ heating_start_time5 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_start_time5', false)
138
+ heating_start_time5.setDisplayName('Start time of heating setpoint adjustment for the fifth season (optional)')
139
+ heating_start_time5.setDescription('In HH:MM:SS format')
140
+ heating_start_time5.setDefaultValue('')
141
+ args << heating_start_time5
142
+ heating_end_time5 = OpenStudio::Measure::OSArgument.makeStringArgument('heating_end_time5', false)
143
+ heating_end_time5.setDisplayName('End time of heating setpoint adjustment for the fifth season (optional)')
144
+ heating_end_time5.setDescription('In HH:MM:SS format')
145
+ heating_end_time5.setDefaultValue('')
146
+ args << heating_end_time5
147
+
148
+
149
+ # make an argument for adjustment to cooling setpoint
150
+ cooling_adjustment = OpenStudio::Measure::OSArgument.makeDoubleArgument('cooling_adjustment', true)
151
+ cooling_adjustment.setDisplayName('Degrees Fahrenheit to Adjust Cooling Setpoint By')
152
+ cooling_adjustment.setDefaultValue(5.0)
153
+ args << cooling_adjustment
154
+
155
+ cooling_start_date1 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_start_date1', true)
156
+ cooling_start_date1.setDisplayName('First start date for cooling setpoint adjustment')
157
+ cooling_start_date1.setDescription('In MM-DD format')
158
+ cooling_start_date1.setDefaultValue('06-01')
159
+ args << cooling_start_date1
160
+ cooling_end_date1 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_end_date1', true)
161
+ cooling_end_date1.setDisplayName('First end date for cooling setpoint adjustment')
162
+ cooling_end_date1.setDescription('In MM-DD format')
163
+ cooling_end_date1.setDefaultValue('09-30')
164
+ args << cooling_end_date1
165
+
166
+ cooling_start_date2 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_start_date2', false)
167
+ cooling_start_date2.setDisplayName('Second start date for cooling setpoint adjustment (optional)')
168
+ cooling_start_date2.setDescription('Specify a date in MM-DD format if you want a second season of cooling setpoint adjustment; leave blank if not needed.')
169
+ cooling_start_date2.setDefaultValue('')
170
+ args << cooling_start_date2
171
+ cooling_end_date2 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_end_date2', false)
172
+ cooling_end_date2.setDisplayName('Second end date for cooling setpoint adjustment')
173
+ cooling_end_date2.setDescription('Specify a date in MM-DD format if you want a second season of cooling setpoint adjustment; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
174
+ cooling_end_date2.setDefaultValue('')
175
+ args << cooling_end_date2
176
+
177
+ cooling_start_date3 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_start_date3', false)
178
+ cooling_start_date3.setDisplayName('Third start date for cooling setpoint adjustment (optional)')
179
+ cooling_start_date3.setDescription('Specify a date in MM-DD format if you want a third season of cooling setpoint adjustment; leave blank if not needed.')
180
+ cooling_start_date3.setDefaultValue('')
181
+ args << cooling_start_date3
182
+ cooling_end_date3 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_end_date3', false)
183
+ cooling_end_date3.setDisplayName('Third end date for cooling setpoint adjustment')
184
+ cooling_end_date3.setDescription('Specify a date in MM-DD format if you want a third season of cooling setpoint adjustment; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
185
+ cooling_end_date3.setDefaultValue('')
186
+ args << cooling_end_date3
187
+
188
+ cooling_start_date4 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_start_date4', false)
189
+ cooling_start_date4.setDisplayName('Fourth start date for cooling setpoint adjustment (optional)')
190
+ cooling_start_date4.setDescription('Specify a date in MM-DD format if you want a fourth season of cooling setpoint adjustment; leave blank if not needed.')
191
+ cooling_start_date4.setDefaultValue('')
192
+ args << cooling_start_date4
193
+ cooling_end_date4 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_end_date4', false)
194
+ cooling_end_date4.setDisplayName('Fourth end date for cooling setpoint adjustment')
195
+ cooling_end_date4.setDescription('Specify a date in MM-DD format if you want a fourth season of cooling setpoint adjustment; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
196
+ cooling_end_date4.setDefaultValue('')
197
+ args << cooling_end_date4
198
+
199
+ cooling_start_date5 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_start_date5', false)
200
+ cooling_start_date5.setDisplayName('Fifth start date for cooling setpoint adjustment (optional)')
201
+ cooling_start_date5.setDescription('Specify a date in MM-DD format if you want a fifth season of cooling setpoint adjustment; leave blank if not needed.')
202
+ cooling_start_date5.setDefaultValue('')
203
+ args << cooling_start_date5
204
+ cooling_end_date5 = OpenStudio::Ruleset::OSArgument.makeStringArgument('cooling_end_date5', false)
205
+ cooling_end_date5.setDisplayName('Fifth end date for cooling setpoint adjustment')
206
+ cooling_end_date5.setDescription('Specify a date in MM-DD format if you want a fifth season of cooling setpoint adjustment; leave blank if not needed. If either the start or end date is blank, the period is considered not used.')
207
+ cooling_end_date5.setDefaultValue('')
208
+ args << cooling_end_date5
209
+
210
+ cooling_start_time1 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_start_time1', true)
211
+ cooling_start_time1.setDisplayName('Start time of cooling setpoint adjustment for the first season')
212
+ cooling_start_time1.setDescription('In HH:MM:SS format')
213
+ cooling_start_time1.setDefaultValue('17:00:00')
214
+ args << cooling_start_time1
215
+ cooling_end_time1 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_end_time1', true)
216
+ cooling_end_time1.setDisplayName('End time of cooling setpoint adjustment for the first season')
217
+ cooling_end_time1.setDescription('In HH:MM:SS format')
218
+ cooling_end_time1.setDefaultValue('21:00:00')
219
+ args << cooling_end_time1
220
+
221
+ cooling_start_time2 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_start_time2', false)
222
+ cooling_start_time2.setDisplayName('Start time of cooling setpoint adjustment for the second season (optional)')
223
+ cooling_start_time2.setDescription('In HH:MM:SS format')
224
+ cooling_start_time2.setDefaultValue('')
225
+ args << cooling_start_time2
226
+ cooling_end_time2 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_end_time2', false)
227
+ cooling_end_time2.setDisplayName('End time of cooling setpoint adjustment for the second season (optional)')
228
+ cooling_end_time2.setDescription('In HH:MM:SS format')
229
+ cooling_end_time2.setDefaultValue('')
230
+ args << cooling_end_time2
231
+
232
+ cooling_start_time3 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_start_time3', false)
233
+ cooling_start_time3.setDisplayName('Start time of cooling setpoint adjustment for the third season (optional)')
234
+ cooling_start_time3.setDescription('In HH:MM:SS format')
235
+ cooling_start_time3.setDefaultValue('')
236
+ args << cooling_start_time3
237
+ cooling_end_time3 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_end_time3', false)
238
+ cooling_end_time3.setDisplayName('End time of cooling setpoint adjustment for the third season (optional)')
239
+ cooling_end_time3.setDescription('In HH:MM:SS format')
240
+ cooling_end_time3.setDefaultValue('')
241
+ args << cooling_end_time3
242
+
243
+ cooling_start_time4 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_start_time4', false)
244
+ cooling_start_time4.setDisplayName('Start time of cooling setpoint adjustment for the fourth season (optional)')
245
+ cooling_start_time4.setDescription('In HH:MM:SS format')
246
+ cooling_start_time4.setDefaultValue('')
247
+ args << cooling_start_time4
248
+ cooling_end_time4 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_end_time4', false)
249
+ cooling_end_time4.setDisplayName('End time of cooling setpoint adjustment for the fourth season (optional)')
250
+ cooling_end_time4.setDescription('In HH:MM:SS format')
251
+ cooling_end_time4.setDefaultValue('')
252
+ args << cooling_end_time4
253
+
254
+ cooling_start_time5 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_start_time5', false)
255
+ cooling_start_time5.setDisplayName('Start time of cooling setpoint adjustment for the fifth season (optional)')
256
+ cooling_start_time5.setDescription('In HH:MM:SS format')
257
+ cooling_start_time5.setDefaultValue('')
258
+ args << cooling_start_time5
259
+ cooling_end_time5 = OpenStudio::Measure::OSArgument.makeStringArgument('cooling_end_time5', false)
260
+ cooling_end_time5.setDisplayName('End time of cooling setpoint adjustment for the fifth season (optional)')
261
+ cooling_end_time5.setDescription('In HH:MM:SS format')
262
+ cooling_end_time5.setDefaultValue('')
263
+ args << cooling_end_time5
264
+
265
+
266
+ # Use alternative default start and end time for different climate zone
267
+ alt_periods = OpenStudio::Measure::OSArgument.makeBoolArgument('alt_periods', true)
268
+ alt_periods.setDisplayName('Use alternative default start and end time based on the state of the model from the Cambium load profile peak period?')
269
+ alt_periods.setDescription('This will overwrite the start and end time and date provided by the user')
126
270
  alt_periods.setDefaultValue(false)
127
271
  args << alt_periods
128
272
 
@@ -139,191 +283,297 @@ class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::Mode
139
283
  end
140
284
 
141
285
  # assign the user inputs to variables
142
- cooling_adjustment = runner.getDoubleArgumentValue('cooling_adjustment', user_arguments)
143
286
  heating_adjustment = runner.getDoubleArgumentValue('heating_adjustment', user_arguments)
144
- cooling_daily_starttime = runner.getStringArgumentValue('cooling_daily_starttime', user_arguments)
145
- cooling_daily_endtime = runner.getStringArgumentValue('cooling_daily_endtime', user_arguments)
146
- cooling_startdate = runner.getStringArgumentValue('cooling_startdate', user_arguments)
147
- cooling_enddate = runner.getStringArgumentValue('cooling_enddate', user_arguments)
148
- heating_daily_starttime = runner.getStringArgumentValue('heating_daily_starttime', user_arguments)
149
- heating_daily_endtime = runner.getStringArgumentValue('heating_daily_endtime', user_arguments)
150
- heating_startdate_1 = runner.getStringArgumentValue('heating_startdate_1', user_arguments)
151
- heating_enddate_1 = runner.getStringArgumentValue('heating_enddate_1', user_arguments)
152
- heating_startdate_2 = runner.getStringArgumentValue('heating_startdate_2', user_arguments)
153
- heating_enddate_2 = runner.getStringArgumentValue('heating_enddate_2', user_arguments)
154
- alter_design_days = runner.getBoolArgumentValue('alter_design_days', user_arguments) # not used yet
155
- auto_date = runner.getBoolArgumentValue('auto_date', user_arguments)
287
+ cooling_adjustment = runner.getDoubleArgumentValue('cooling_adjustment', user_arguments)
288
+ heating_start_time1 = runner.getStringArgumentValue('heating_start_time1', user_arguments)
289
+ heating_end_time1 = runner.getStringArgumentValue('heating_end_time1', user_arguments)
290
+ heating_start_time2 = runner.getStringArgumentValue('heating_start_time2', user_arguments)
291
+ heating_end_time2 = runner.getStringArgumentValue('heating_end_time2', user_arguments)
292
+ heating_start_time3 = runner.getStringArgumentValue('heating_start_time3', user_arguments)
293
+ heating_end_time3 = runner.getStringArgumentValue('heating_end_time3', user_arguments)
294
+ heating_start_time4 = runner.getStringArgumentValue('heating_start_time4', user_arguments)
295
+ heating_end_time4 = runner.getStringArgumentValue('heating_end_time4', user_arguments)
296
+ heating_start_time5 = runner.getStringArgumentValue('heating_start_time5', user_arguments)
297
+ heating_end_time5 = runner.getStringArgumentValue('heating_end_time5', user_arguments)
298
+ heating_start_date1 = runner.getStringArgumentValue('heating_start_date1', user_arguments)
299
+ heating_end_date1 = runner.getStringArgumentValue('heating_end_date1', user_arguments)
300
+ heating_start_date2 = runner.getStringArgumentValue('heating_start_date2', user_arguments)
301
+ heating_end_date2 = runner.getStringArgumentValue('heating_end_date2', user_arguments)
302
+ heating_start_date3 = runner.getStringArgumentValue('heating_start_date3', user_arguments)
303
+ heating_end_date3 = runner.getStringArgumentValue('heating_end_date3', user_arguments)
304
+ heating_start_date4 = runner.getStringArgumentValue('heating_start_date4', user_arguments)
305
+ heating_end_date4 = runner.getStringArgumentValue('heating_end_date4', user_arguments)
306
+ heating_start_date5 = runner.getStringArgumentValue('heating_start_date5', user_arguments)
307
+ heating_end_date5 = runner.getStringArgumentValue('heating_end_date5', user_arguments)
308
+ cooling_start_time1 = runner.getStringArgumentValue('cooling_start_time1', user_arguments)
309
+ cooling_end_time1 = runner.getStringArgumentValue('cooling_end_time1', user_arguments)
310
+ cooling_start_time2 = runner.getStringArgumentValue('cooling_start_time2', user_arguments)
311
+ cooling_end_time2 = runner.getStringArgumentValue('cooling_end_time2', user_arguments)
312
+ cooling_start_time3 = runner.getStringArgumentValue('cooling_start_time3', user_arguments)
313
+ cooling_end_time3 = runner.getStringArgumentValue('cooling_end_time3', user_arguments)
314
+ cooling_start_time4 = runner.getStringArgumentValue('cooling_start_time4', user_arguments)
315
+ cooling_end_time4 = runner.getStringArgumentValue('cooling_end_time4', user_arguments)
316
+ cooling_start_time5 = runner.getStringArgumentValue('cooling_start_time5', user_arguments)
317
+ cooling_end_time5 = runner.getStringArgumentValue('cooling_end_time5', user_arguments)
318
+ cooling_start_date1 = runner.getStringArgumentValue('cooling_start_date1', user_arguments)
319
+ cooling_end_date1 = runner.getStringArgumentValue('cooling_end_date1', user_arguments)
320
+ cooling_start_date2 = runner.getStringArgumentValue('cooling_start_date2', user_arguments)
321
+ cooling_end_date2 = runner.getStringArgumentValue('cooling_end_date2', user_arguments)
322
+ cooling_start_date3 = runner.getStringArgumentValue('cooling_start_date3', user_arguments)
323
+ cooling_end_date3 = runner.getStringArgumentValue('cooling_end_date3', user_arguments)
324
+ cooling_start_date4 = runner.getStringArgumentValue('cooling_start_date4', user_arguments)
325
+ cooling_end_date4 = runner.getStringArgumentValue('cooling_end_date4', user_arguments)
326
+ cooling_start_date5 = runner.getStringArgumentValue('cooling_start_date5', user_arguments)
327
+ cooling_end_date5 = runner.getStringArgumentValue('cooling_end_date5', user_arguments)
156
328
  alt_periods = runner.getBoolArgumentValue('alt_periods', user_arguments)
157
329
 
158
- cooling_start_month = nil
159
- cooling_start_day = nil
160
- md = /(\d\d)-(\d\d)/.match(cooling_startdate)
161
- if md
162
- cooling_start_month = md[1].to_i
163
- cooling_start_day = md[2].to_i
164
- else
165
- runner.registerError('Start date must be in MM-DD format.')
166
- return false
330
+
331
+ # set the default start and end time based on state
332
+ if alt_periods
333
+ state = model.getWeatherFile.stateProvinceRegion
334
+ if state == ''
335
+ runner.registerError('Unable to find state in model WeatherFile. The measure cannot be applied.')
336
+ return false
337
+ end
338
+ file = File.open(File.join(File.dirname(__FILE__), "../../../files/seasonal_shedding_peak_hours.json"))
339
+ default_peak_periods = JSON.load(file)
340
+ file.close
341
+ unless default_peak_periods.key?state
342
+ runner.registerAsNotApplicable("No default inputs for the state of the WeatherFile #{state}")
343
+ return false
344
+ end
345
+ peak_periods = default_peak_periods[state]
346
+ cooling_start_time1 = heating_start_time1 = peak_periods["winter_peak_start"].split[1]
347
+ cooling_end_time1 = heating_end_time1 = peak_periods["winter_peak_end"].split[1]
348
+ cooling_start_time2 = heating_start_time2 = peak_periods["intermediate_peak_start"].split[1]
349
+ cooling_end_time2 = heating_end_time2 = peak_periods["intermediate_peak_end"].split[1]
350
+ cooling_start_time3 = heating_start_time3 = peak_periods["summer_peak_start"].split[1]
351
+ cooling_end_time3 = heating_end_time3 = peak_periods["summer_peak_end"].split[1]
352
+ cooling_start_time4 = heating_start_time4 = peak_periods["intermediate_peak_start"].split[1]
353
+ cooling_end_time4 = heating_end_time4 = peak_periods["intermediate_peak_end"].split[1]
354
+ cooling_start_time5 = heating_start_time5 = peak_periods["winter_peak_start"].split[1]
355
+ cooling_end_time5 = heating_end_time5 = peak_periods["winter_peak_end"].split[1]
356
+ cooling_start_date1 = heating_start_date1 = '01-01'
357
+ cooling_end_date1 = heating_end_date1 = '03-31'
358
+ cooling_start_date2 = heating_start_date2 = '04-01'
359
+ cooling_end_date2 = heating_end_date2 = '05-31'
360
+ cooling_start_date3 = heating_start_date3 = '06-01'
361
+ cooling_end_date3 = heating_end_date3 = '09-30'
362
+ cooling_start_date4 = heating_start_date4 = '10-01'
363
+ cooling_end_date4 = heating_end_date4 = '11-30'
364
+ cooling_start_date5 = heating_start_date5 = '12-01'
365
+ cooling_end_date5 = heating_end_date5 = '12-31'
167
366
  end
168
- cooling_end_month = nil
169
- cooling_end_day = nil
170
- md = /(\d\d)-(\d\d)/.match(cooling_enddate)
171
- if md
172
- cooling_end_month = md[1].to_i
173
- cooling_end_day = md[2].to_i
174
- else
175
- runner.registerError('End date must be in MM-DD format.')
176
- return false
367
+
368
+ def validate_time_format(star_time, end_time, runner)
369
+ time1 = /(\d\d):(\d\d):(\d\d)/.match(star_time)
370
+ time2 = /(\d\d):(\d\d):(\d\d)/.match(end_time)
371
+ if time1 and time2
372
+ os_starttime = OpenStudio::Time.new(star_time)
373
+ os_endtime = OpenStudio::Time.new(end_time)
374
+ if star_time >= end_time
375
+ runner.registerError('The start time needs to be earlier than the end time.')
376
+ return false
377
+ else
378
+ return os_starttime, os_endtime
379
+ end
380
+ else
381
+ runner.registerError('The provided time is not in HH-MM-SS format.')
382
+ return false
383
+ end
177
384
  end
178
- heating_start_month_1 = nil
179
- heating_start_day_1 = nil
180
- md = /(\d\d)-(\d\d)/.match(heating_startdate_1)
181
- if md
182
- heating_start_month_1 = md[1].to_i
183
- heating_start_day_1 = md[2].to_i
184
- else
185
- runner.registerError('Start date must be in MM-DD format.')
186
- return false
385
+
386
+ def validate_date_format(start_date1, end_date1, runner, model)
387
+ smd = /(\d\d)-(\d\d)/.match(start_date1)
388
+ emd = /(\d\d)-(\d\d)/.match(end_date1)
389
+ if smd.nil? or emd.nil?
390
+ runner.registerError('The provided date is not in MM-DD format.')
391
+ return false
392
+ else
393
+ start_month = smd[1].to_i
394
+ start_day = smd[2].to_i
395
+ end_month = emd[1].to_i
396
+ end_day = emd[2].to_i
397
+ if start_date1 > end_date1
398
+ runner.registerError('The start date cannot be later date the end time.')
399
+ return false
400
+ else
401
+ os_start_date = model.getYearDescription.makeDate(start_month, start_day)
402
+ os_end_date = model.getYearDescription.makeDate(end_month, end_day)
403
+ return os_start_date, os_end_date
404
+ end
405
+ end
187
406
  end
188
- heating_end_month_1 = nil
189
- heating_end_day_1 = nil
190
- md = /(\d\d)-(\d\d)/.match(heating_enddate_1)
191
- if md
192
- heating_end_month_1 = md[1].to_i
193
- heating_end_day_1 = md[2].to_i
407
+
408
+ # First time period for heating
409
+ heating_time_result1 = validate_time_format(heating_start_time1, heating_end_time1, runner)
410
+ if heating_time_result1
411
+ heating_shift_time_start1, heating_shift_time_end1 = heating_time_result1
194
412
  else
195
- runner.registerError('Start date must be in MM-DD format.')
413
+ runner.registerError('The required time period for the adjustment is not in correct format!')
196
414
  return false
197
415
  end
198
- heating_start_month_2 = nil
199
- heating_start_day_2 = nil
200
- md = /(\d\d)-(\d\d)/.match(heating_startdate_2)
201
- if md
202
- heating_start_month_2 = md[1].to_i
203
- heating_start_day_2 = md[2].to_i
416
+ # The other optional time periods
417
+ heating_shift_time_start2,heating_shift_time_end2,heating_shift_time_start3,heating_shift_time_end3,heating_shift_time_start4,heating_shift_time_end4,heating_shift_time_start5,heating_shift_time_end5 = [nil]*8
418
+ if (not heating_start_time2.empty?) and (not heating_end_time2.empty?)
419
+ heating_time_result2 = validate_time_format(heating_start_time2, heating_end_time2, runner)
420
+ if heating_time_result2
421
+ heating_shift_time_start2, heating_shift_time_end2 = heating_time_result2
422
+ end
423
+ end
424
+ if (not heating_start_time3.empty?) and (not heating_end_time3.empty?)
425
+ heating_time_result3 = validate_time_format(heating_start_time3, heating_end_time3, runner)
426
+ if heating_time_result3
427
+ heating_shift_time_start3, heating_shift_time_end3 = heating_time_result3
428
+ end
429
+ end
430
+ if (not heating_start_time4.empty?) and (not heating_end_time4.empty?)
431
+ heating_time_result4 = validate_time_format(heating_start_time4, heating_end_time4, runner)
432
+ if heating_time_result4
433
+ heating_shift_time_start4, heating_shift_time_end4 = heating_time_result4
434
+ end
435
+ end
436
+ if (not heating_start_time5.empty?) and (not heating_end_time5.empty?)
437
+ heating_time_result5 = validate_time_format(heating_start_time5, heating_end_time5, runner)
438
+ if heating_time_result5
439
+ heating_shift_time_start5, heating_shift_time_end5 = heating_time_result5
440
+ end
441
+ end
442
+
443
+ # First time period for cooling
444
+ cooling_time_result1 = validate_time_format(cooling_start_time1, cooling_end_time1, runner)
445
+ if cooling_time_result1
446
+ cooling_shift_time_start1, cooling_shift_time_end1 = cooling_time_result1
204
447
  else
205
- runner.registerError('Start date must be in MM-DD format.')
448
+ runner.registerError('The required time period for the adjustment is not in correct format!')
206
449
  return false
207
450
  end
208
- heating_end_month_2 = nil
209
- heating_end_day_2 = nil
210
- md = /(\d\d)-(\d\d)/.match(heating_enddate_2)
211
- if md
212
- heating_end_month_2 = md[1].to_i
213
- heating_end_day_2 = md[2].to_i
451
+ # The other optional time periods
452
+ cooling_shift_time_start2,cooling_shift_time_end2,cooling_shift_time_start3,cooling_shift_time_end3,cooling_shift_time_start4,cooling_shift_time_end4,cooling_shift_time_start5,cooling_shift_time_end5 = [nil]*8
453
+ if (not cooling_start_time2.empty?) and (not cooling_end_time2.empty?)
454
+ cooling_time_result2 = validate_time_format(cooling_start_time2, cooling_end_time2, runner)
455
+ if cooling_time_result2
456
+ cooling_shift_time_start2, cooling_shift_time_end2 = cooling_time_result2
457
+ end
458
+ end
459
+ if (not cooling_start_time3.empty?) and (not cooling_end_time3.empty?)
460
+ cooling_time_result3 = validate_time_format(cooling_start_time3, cooling_end_time3, runner)
461
+ if cooling_time_result3
462
+ cooling_shift_time_start3, cooling_shift_time_end3 = cooling_time_result3
463
+ end
464
+ end
465
+ if (not cooling_start_time4.empty?) and (not cooling_end_time4.empty?)
466
+ cooling_time_result4 = validate_time_format(cooling_start_time4, cooling_end_time4, runner)
467
+ if cooling_time_result4
468
+ cooling_shift_time_start4, cooling_shift_time_end4 = cooling_time_result4
469
+ end
470
+ end
471
+ if (not cooling_start_time5.empty?) and (not cooling_end_time5.empty?)
472
+ cooling_time_result5 = validate_time_format(cooling_start_time5, cooling_end_time5, runner)
473
+ if cooling_time_result5
474
+ cooling_shift_time_start5, cooling_shift_time_end5 = cooling_time_result5
475
+ end
476
+ end
477
+
478
+ # First date period
479
+ heating_date_result1 = validate_date_format(heating_start_date1, heating_end_date1, runner, model)
480
+ if heating_date_result1
481
+ os_heating_start_date1, os_heating_end_date1 = heating_date_result1
214
482
  else
215
- runner.registerError('Start date must be in MM-DD format.')
483
+ runner.registerError('The required date period for the heating setpoint adjustment is not in correct format!')
216
484
  return false
217
485
  end
486
+ # Other optional date period
487
+ os_heating_start_date2, os_heating_end_date2, os_heating_start_date3, os_heating_end_date3, os_heating_start_date4, os_heating_end_date4, os_heating_start_date5, os_heating_end_date5 = [nil]*8
488
+ if (not heating_start_date2.empty?) and (not heating_end_date2.empty?)
489
+ heating_date_result2 = validate_date_format(heating_start_date2, heating_end_date2, runner, model)
490
+ if heating_date_result2
491
+ os_heating_start_date2, os_heating_end_date2 = heating_date_result2
492
+ end
493
+ end
218
494
 
219
- summerStartDate = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(cooling_start_month), cooling_start_day)
220
- summerEndDate = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(cooling_end_month), cooling_end_day)
221
- winterStartDate1 = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(heating_start_month_1), heating_start_day_1)
222
- winterEndDate1 = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(heating_end_month_1), heating_end_day_1)
223
- winterStartDate2 = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(heating_start_month_2), heating_start_day_2)
224
- winterEndDate2 = OpenStudio::Date.new(OpenStudio::MonthOfYear.new(heating_end_month_2), heating_end_day_2)
225
-
226
- ######### GET CLIMATE ZONES ################
227
- if auto_date
228
- ashraeClimateZone = ''
229
- #climateZoneNUmber = ''
230
- climateZones = model.getClimateZones
231
- climateZones.climateZones.each do |climateZone|
232
- if climateZone.institution == 'ASHRAE'
233
- ashraeClimateZone = climateZone.value
234
- runner.registerInfo("Using ASHRAE Climate zone #{ashraeClimateZone}.")
235
- end
495
+ if (not heating_start_date3.empty?) and (not heating_end_date3.empty?)
496
+ heating_date_result3 = validate_date_format(heating_start_date3, heating_end_date3, runner, model)
497
+ if heating_date_result3
498
+ os_heating_start_date3, os_heating_end_date3 = heating_date_result3
236
499
  end
500
+ end
237
501
 
238
- if ashraeClimateZone == '' # should this be not applicable or error?
239
- runner.registerError("Please assign an ASHRAE Climate Zone to your model using the site tab in the OpenStudio application. The measure can't make AEDG recommendations without this information.")
240
- return false # note - for this to work need to check for false in measure.rb and add return false there as well.
241
- end
242
-
243
- if alt_periods
244
- case ashraeClimateZone
245
- when '3A', '4A'
246
- cooling_daily_starttime = '18:01:00'
247
- cooling_daily_endtime = '21:59:00'
248
- heating_daily_starttime = '17:01:00'
249
- heating_daily_endtime = '20:59:00'
250
- when '5A'
251
- cooling_daily_starttime = '14:01:00'
252
- cooling_daily_endtime = '17:59:00'
253
- heating_daily_starttime = '18:01:00'
254
- heating_daily_endtime = '21:59:00'
255
- when '6A'
256
- cooling_daily_starttime = '13:01:00'
257
- cooling_daily_endtime = '16:59:00'
258
- heating_daily_starttime = '17:01:00'
259
- heating_daily_endtime = '20:59:00'
260
- end
261
- else
262
- case ashraeClimateZone
263
- when '2A', '2B', '4B', '4C', '5B', '5C', '6B'
264
- cooling_daily_starttime = '17:01:00'
265
- cooling_daily_endtime = '20:59:00'
266
- heating_daily_starttime = '17:01:00'
267
- heating_daily_endtime = '20:59:00'
268
- when '3A', '3C'
269
- cooling_daily_starttime = '19:01:00'
270
- cooling_daily_endtime = '22:59:00'
271
- heating_daily_starttime = '17:01:00'
272
- heating_daily_endtime = '20:59:00'
273
- when '3B'
274
- cooling_daily_starttime = '18:01:00'
275
- cooling_daily_endtime = '21:59:00'
276
- heating_daily_starttime = '19:01:00'
277
- heating_daily_endtime = '22:59:00'
278
- when '4A'
279
- cooling_daily_starttime = '12:01:00'
280
- cooling_daily_endtime = '15:59:00'
281
- heating_daily_starttime = '16:01:00'
282
- heating_daily_endtime = '19:59:00'
283
- when '5A'
284
- cooling_daily_starttime = '20:01:00'
285
- cooling_daily_endtime = '23:59:00'
286
- heating_daily_starttime = '17:01:00'
287
- heating_daily_endtime = '20:59:00'
288
- when '6A', '7A'
289
- cooling_daily_starttime = '16:01:00'
290
- cooling_daily_endtime = '19:59:00'
291
- heating_daily_starttime = '18:01:00'
292
- heating_daily_endtime = '21:59:00'
293
- end
502
+ if (not heating_start_date4.empty?) and (not heating_end_date4.empty?)
503
+ heating_date_result4 = validate_date_format(heating_start_date4, heating_end_date4, runner, model)
504
+ if heating_date_result4
505
+ os_heating_start_date4, os_heating_end_date4 = heating_date_result4
294
506
  end
295
507
  end
296
508
 
297
- if cooling_daily_starttime.to_f > cooling_daily_endtime.to_f
298
- runner.registerError('For cooling adjustment, the end time should be larger than the start time.')
299
- return false
509
+ if (not heating_start_date5.empty?) and (not heating_end_date5.empty?)
510
+ heating_date_result5 = validate_date_format(heating_start_date5, heating_end_date5, runner, model)
511
+ if heating_date_result5
512
+ os_heating_start_date5, os_heating_end_date5 = heating_date_result5
513
+ end
300
514
  end
301
- if heating_daily_starttime.to_f > heating_daily_endtime.to_f
302
- runner.registerError('For heating adjustment, the end time should be larger than the start time.')
515
+
516
+ # First date period
517
+ cooling_date_result1 = validate_date_format(cooling_start_date1, cooling_end_date1, runner, model)
518
+ if cooling_date_result1
519
+ os_cooling_start_date1, os_cooling_end_date1 = cooling_date_result1
520
+ else
521
+ runner.registerError('The required date period for the cooling setpoint adjustment is not in correct format!')
303
522
  return false
304
523
  end
524
+ # Other optional date period
525
+ os_cooling_start_date2, os_cooling_end_date2, os_cooling_start_date3, os_cooling_end_date3, os_cooling_start_date4, os_cooling_end_date4, os_cooling_start_date5, os_cooling_end_date5 = [nil]*8
526
+ if (not cooling_start_date2.empty?) and (not cooling_end_date2.empty?)
527
+ cooling_date_result2 = validate_date_format(cooling_start_date2, cooling_end_date2, runner, model)
528
+ if cooling_date_result2
529
+ os_cooling_start_date2, os_cooling_end_date2 = cooling_date_result2
530
+ end
531
+ end
532
+
533
+ if (not cooling_start_date3.empty?) and (not cooling_end_date3.empty?)
534
+ cooling_date_result3 = validate_date_format(cooling_start_date3, cooling_end_date3, runner, model)
535
+ if cooling_date_result3
536
+ os_cooling_start_date3, os_cooling_end_date3 = cooling_date_result3
537
+ end
538
+ end
539
+
540
+ if (not cooling_start_date4.empty?) and (not cooling_end_date4.empty?)
541
+ cooling_date_result4 = validate_date_format(cooling_start_date4, cooling_end_date4, runner, model)
542
+ if cooling_date_result4
543
+ os_cooling_start_date4, os_cooling_end_date4 = cooling_date_result4
544
+ end
545
+ end
546
+
547
+ if (not cooling_start_date5.empty?) and (not cooling_end_date5.empty?)
548
+ cooling_date_result5 = validate_date_format(cooling_start_date5, cooling_end_date5, runner, model)
549
+ if cooling_date_result5
550
+ os_cooling_start_date5, os_cooling_end_date5 = cooling_date_result5
551
+ end
552
+ end
305
553
 
306
554
  # ruby test to see if first charter of string is uppercase letter
307
555
  if cooling_adjustment < 0
308
- runner.registerError('Lowering the cooling setpoint will increase energy use. Please double check your input.')
556
+ runner.registerWarning('Lowering the cooling setpoint will increase energy use. Please double check your input.')
309
557
  elsif cooling_adjustment.abs > 500
310
- runner.registerError("#{cooling_adjustment} is a larger than typical setpoint adjustment. Please double check your input.")
558
+ runner.registerError("#{cooling_adjustment} is larger than typical setpoint adjustment. Please double check your input.")
311
559
  return false
312
560
  elsif cooling_adjustment.abs > 50
313
- runner.registerWarning("#{cooling_adjustment} is a larger than typical setpoint adjustment. Please double check your input.")
561
+ runner.registerWarning("#{cooling_adjustment} is larger than typical setpoint adjustment. Please double check your input.")
314
562
  end
315
563
  if heating_adjustment > 0
316
- runner.registerError('Raising the heating setpoint will increase energy use. Please double check your input.')
564
+ runner.registerWarning('Raising the heating setpoint will increase energy use. Please double check your input.')
317
565
  elsif heating_adjustment.abs > 500
318
- runner.registerError("#{heating_adjustment} is a larger than typical setpoint adjustment. Please double check your input.")
566
+ runner.registerError("#{heating_adjustment} is larger than typical setpoint adjustment. Please double check your input.")
319
567
  return false
320
568
  elsif heating_adjustment.abs > 50
321
- runner.registerWarning("#{heating_adjustment} is a larger than typical setpoint adjustment. Please double check your input.")
569
+ runner.registerWarning("#{heating_adjustment} is larger than typical setpoint adjustment. Please double check your input.")
322
570
  end
323
571
 
324
572
  # define starting units
325
573
  cooling_adjustment_ip = OpenStudio::Quantity.new(cooling_adjustment, TEMP_IP_UNIT)
574
+ cooling_adjustment_si = cooling_adjustment * 5 / 9.0
326
575
  heating_adjustment_ip = OpenStudio::Quantity.new(heating_adjustment, TEMP_IP_UNIT)
576
+ heating_adjustment_si = heating_adjustment * 5 / 9.0
327
577
 
328
578
  # push schedules to hash to avoid making unnecessary duplicates
329
579
  clg_set_schs = {}
@@ -334,56 +584,61 @@ class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::Mode
334
584
  # setup new cooling setpoint schedule
335
585
  clg_set_sch = thermostat.coolingSetpointTemperatureSchedule
336
586
 
337
- if !clg_set_sch.empty?
338
- runner.registerInfo("#{clg_set_sch.get.name.to_s}")
339
-
340
- puts "name: #{clg_set_sch.get.name.to_s}"
341
- puts clg_set_sch.get
342
-
343
- # clone of not already in hash
344
- if clg_set_schs.key?(clg_set_sch.get.name.to_s)
345
- new_clg_set_sch = clg_set_schs[clg_set_sch.get.name.to_s]
587
+ if clg_set_sch.empty?
588
+ runner.registerWarning("Thermostat '#{thermostat.name}' doesn't have a cooling setpoint schedule")
589
+ else
590
+ old_clg_schedule = clg_set_sch.get
591
+ old_schedule_name = old_clg_schedule.name.to_s
592
+ # clone if not already in hash
593
+ if old_clg_schedule.to_ScheduleRuleset.is_initialized
594
+ if clg_set_schs.key?(old_schedule_name)
595
+ new_clg_set_sch = clg_set_schs[old_schedule_name]
596
+ else
597
+ # active_indices = old_clg_schedule.to_ScheduleRuleset.get.getActiveRuleIndices(os_cooling_start_date5, os_cooling_end_date5)
598
+ # active_indices.each do |i|
599
+ # runner.registerInfo("Rule applied to the fifth cooling period: #{old_clg_schedule.to_ScheduleRuleset.get.scheduleRules[i].name.to_s}")
600
+ # end
601
+ new_clg_set_sch = old_clg_schedule.clone(model)
602
+ new_clg_set_sch = new_clg_set_sch.to_Schedule.get
603
+ new_clg_set_sch.setName("#{old_schedule_name} adjusted by #{cooling_adjustment_ip}")
604
+ runner.registerInfo("Cooling schedule #{old_schedule_name} is cloned to #{new_clg_set_sch.name.to_s}")
605
+ # add to the hash
606
+ clg_set_schs[old_schedule_name] = new_clg_set_sch
607
+ end
608
+ # hook up cloned schedule to thermostat
609
+ thermostat.setCoolingSetpointTemperatureSchedule(new_clg_set_sch)
346
610
  else
347
- new_clg_set_sch = clg_set_sch.get.clone(model)
348
- new_clg_set_sch = new_clg_set_sch.to_Schedule.get
349
- new_clg_set_sch.setName("#{clg_set_sch.get.name.to_s} adjusted by #{cooling_adjustment_ip}F")
350
-
351
- puts "cloned new name: #{new_clg_set_sch.name.to_s}"
352
- puts new_clg_set_sch
353
-
354
- # add to the hash
355
- clg_set_schs[clg_set_sch.get.name.to_s] = new_clg_set_sch
611
+ runner.registerWarning("Schedule '#{old_schedule_name}' isn't a ScheduleRuleset object and won't be altered by this measure.")
356
612
  end
357
- # hook up clone to thermostat
358
- thermostat.setCoolingSetpointTemperatureSchedule(new_clg_set_sch)
359
- #runner.registerInfo("#{new_clg_set_sch.get.name.to_s}")
360
- else
361
- runner.registerWarning("Thermostat '#{thermostat.name}' doesn't have a cooling setpoint schedule")
362
613
  end
363
614
 
364
615
  # setup new heating setpoint schedule
365
616
  htg_set_sch = thermostat.heatingSetpointTemperatureSchedule
366
- if !htg_set_sch.empty?
367
- # clone of not already in hash
368
- if htg_set_schs.key?(htg_set_sch.get.name.to_s)
369
- new_htg_set_sch = htg_set_schs[htg_set_sch.get.name.to_s]
617
+ if htg_set_sch.empty?
618
+ runner.registerWarning("Thermostat '#{thermostat.name}' doesn't have a heating setpoint schedule.")
619
+ else
620
+ old_htg_schedule = htg_set_sch.get
621
+ old_schedule_name = old_htg_schedule.name.to_s
622
+ if old_htg_schedule.to_ScheduleRuleset.is_initialized
623
+ if htg_set_schs.key?(old_schedule_name)
624
+ new_htg_set_sch = htg_set_schs[old_schedule_name]
625
+ else
626
+ new_htg_set_sch = old_htg_schedule.clone(model)
627
+ new_htg_set_sch = new_htg_set_sch.to_Schedule.get
628
+ new_htg_set_sch.setName("#{old_schedule_name} adjusted by #{heating_adjustment_ip}")
629
+ runner.registerInfo("Cooling schedule #{old_schedule_name} is cloned to #{new_htg_set_sch.name.to_s}")
630
+ # add to the hash
631
+ htg_set_schs[old_schedule_name] = new_htg_set_sch
632
+ end
633
+ # hook up clone to thermostat
634
+ thermostat.setHeatingSetpointTemperatureSchedule(new_htg_set_sch)
370
635
  else
371
- new_htg_set_sch = htg_set_sch.get.clone(model)
372
- new_htg_set_sch = new_htg_set_sch.to_Schedule.get
373
- new_htg_set_sch.setName("#{htg_set_sch.get.name.to_s} adjusted by #{heating_adjustment_ip}")
374
-
375
- # add to the hash
376
- htg_set_schs[htg_set_sch.get.name.to_s] = new_htg_set_sch
636
+ runner.registerWarning("Schedule '#{old_schedule_name}' isn't a ScheduleRuleset object and won't be altered by this measure.")
377
637
  end
378
- # hook up clone to thermostat
379
- thermostat.setHeatingSetpointTemperatureSchedule(new_htg_set_sch)
380
- else
381
- runner.registerWarning("Thermostat '#{thermostat.name}' doesn't have a heating setpoint schedule.")
638
+
382
639
  end
383
640
  end
384
641
 
385
- puts "clg_set_schs: #{clg_set_schs.inspect}"
386
- puts "htg_set_schs: #{htg_set_schs.inspect}"
387
642
 
388
643
  # setting up variables to use for initial and final condition
389
644
  clg_sch_set_values = [] # may need to flatten this
@@ -399,11 +654,6 @@ class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::Mode
399
654
  runner.registerWarning("Thermal zone '#{zone.name}' has a thermostat but does not appear to be conditioned.")
400
655
  end
401
656
  end
402
- shift_time_cooling_start = OpenStudio::Time.new(cooling_daily_starttime)
403
- shift_time_cooling_end = OpenStudio::Time.new(cooling_daily_endtime)
404
- shift_time3 = OpenStudio::Time.new(0, 24, 0, 0) # not used
405
- shift_time_heating_start = OpenStudio::Time.new(heating_daily_starttime)
406
- shift_time_heating_end = OpenStudio::Time.new(heating_daily_endtime)
407
657
 
408
658
  # daylightsaving adjustment added in visualization, so deprecated here
409
659
  # # 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
@@ -417,177 +667,330 @@ class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::Mode
417
667
  # end
418
668
  # end
419
669
 
670
+ applicable_flag = false
671
+ cooling_adjust_period_inputs = { "period1" => {"date_start"=>os_cooling_start_date1, "date_end"=>os_cooling_end_date1,
672
+ "time_start"=>cooling_shift_time_start1, "time_end"=>cooling_shift_time_end1},
673
+ "period2" => {"date_start"=>os_cooling_start_date2, "date_end"=>os_cooling_end_date2,
674
+ "time_start"=>cooling_shift_time_start2, "time_end"=>cooling_shift_time_end2},
675
+ "period3" => {"date_start"=>os_cooling_start_date3, "date_end"=>os_cooling_end_date3,
676
+ "time_start"=>cooling_shift_time_start3, "time_end"=>cooling_shift_time_end3},
677
+ "period4" => {"date_start"=>os_cooling_start_date4, "date_end"=>os_cooling_end_date4,
678
+ "time_start"=>cooling_shift_time_start4, "time_end"=>cooling_shift_time_end4},
679
+ "period5" => {"date_start"=>os_cooling_start_date5, "date_end"=>os_cooling_end_date5,
680
+ "time_start"=>cooling_shift_time_start5, "time_end"=>cooling_shift_time_end5} }
420
681
  # make cooling schedule adjustments and rename. Put in check to skip and warn if schedule not ruleset
421
- clg_set_schs.each do |sch_name, os_sch| # old name and new object for schedule
422
- if !os_sch.to_ScheduleRuleset.empty?
423
- schedule = os_sch.to_ScheduleRuleset.get
424
- default_rule = schedule.defaultDaySchedule
425
- rules = schedule.scheduleRules
426
- days_covered = Array.new(7, false)
427
-
428
- # TODO: when ruleset has multiple rules for each month or couple of months instead of a full year, should first see if the period overlaps with summer/winter
429
- if rules.length > 0
430
- rules.each do |rule|
431
- winter_rule1 = copy_sch_rule_for_period(model, rule, rule.daySchedule, winterStartDate1, winterEndDate1)
432
- winter_rule2 = copy_sch_rule_for_period(model, rule, rule.daySchedule, winterStartDate2, winterEndDate2)
433
-
434
- summer_rule = rule
435
- checkDaysCovered(summer_rule, days_covered)
436
- summer_rule.setStartDate(summerStartDate)
437
- summer_rule.setEndDate(summerEndDate)
438
-
439
- summer_day = summer_rule.daySchedule
440
- day_time_vector = summer_day.times
441
- day_value_vector = summer_day.values
442
- clg_sch_set_values << summer_day.values # original
443
- summer_day.clearValues
444
-
445
- summer_day = updateDaySchedule(summer_day, day_time_vector, day_value_vector, shift_time_cooling_start, shift_time_cooling_end, cooling_adjustment_ip)
446
- final_clg_sch_set_values << summer_day.values # new
682
+ clg_set_schs.each do |old_sch_name, os_sch| # old name and new object for schedule
683
+ schedule = os_sch.to_ScheduleRuleset.get
684
+ rules = schedule.scheduleRules
685
+ days_covered = Array.new(7, false)
686
+ current_index = 0
687
+ # TODO: when ruleset has multiple rules for each month or couple of months instead of a full year, should first see if the period overlaps with summer/winter
688
+ if rules.length <= 0
689
+ runner.registerWarning("Cooling setpoint schedule '#{old_sch_name}' is a ScheduleRuleSet, but has no ScheduleRules associated. It won't be altered by this measure.")
690
+ else
691
+ runner.registerInfo("schedule rule set #{old_sch_name} has #{rules.length} rules.")
692
+ rules.each do |rule|
693
+ runner.registerInfo("---- Rule No.#{rule.ruleIndex}: #{rule.name.to_s}")
694
+ if rule.dateSpecificationType == "SpecificDates"
695
+ ## if the rule applies to SpecificDates, collect the dates that fall into each adjustment date period,
696
+ ## and create a new rule for each date period with covered specific dates
697
+ runner.registerInfo("======= The rule #{rule.name.to_s} only covers specific dates.")
698
+ ## the specificDates cannot be modified in place because it's a frozen array
699
+ all_specific_dates = []
700
+ rule.specificDates.each { |date| all_specific_dates << date }
701
+ cooling_adjust_period_inputs.each do |period, period_inputs|
702
+ period_inputs["specific_dates"] = []
703
+ os_start_date = period_inputs["date_start"]
704
+ os_end_date = period_inputs["date_end"]
705
+ shift_time_start = period_inputs["time_start"]
706
+ shift_time_end = period_inputs["time_end"]
707
+ if [os_start_date, os_end_date, shift_time_start, shift_time_end].all?
708
+ rule.specificDates.each do |covered_date|
709
+ if covered_date >= os_start_date and covered_date <= os_end_date
710
+ period_inputs["specific_dates"] << covered_date
711
+ all_specific_dates.delete(covered_date)
712
+ end
713
+ end
714
+ 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)}")
715
+ runner.registerInfo("!!! Specific dates haven't been covered: #{all_specific_dates.map(&:to_s)}")
716
+ next if period_inputs["specific_dates"].empty?
717
+ rule_period = modify_rule_for_specific_dates(rule, os_start_date, os_end_date, shift_time_start, shift_time_end,
718
+ cooling_adjustment_si, period_inputs["specific_dates"])
719
+ if rule_period
720
+ applicable_flag = true
721
+ if period == "period1"
722
+ final_clg_sch_set_values << rule_period.daySchedule.values
723
+ end
724
+ if schedule.setScheduleRuleIndex(rule_period, current_index)
725
+ current_index += 1
726
+ runner.registerInfo("-------- The rule #{rule_period.name.to_s} for #{rule_period.dateSpecificationType} is added as priority #{current_index}")
727
+ else
728
+ runner.registerError("Fail to set rule index for #{rule_period.name.to_s}.")
729
+ end
730
+ end
731
+ end
732
+ end
733
+ if all_specific_dates.empty?
734
+ ## if all specific dates have been covered by new rules for each adjustment date period, remove the original rule
735
+ runner.registerInfo("The original rule is removed since no specific date left")
736
+ else
737
+ ## if there's still dates left to be covered, modify the original rule to only cover these dates
738
+ ## (this is just in case that the rule order was not set correctly, and the original rule is still applied to all specific dates;
739
+ ## also to make the logic in OSM more clearer)
740
+ ## the specificDates cannot be modified in place, so create a new rule with the left dates to replace the original rule
741
+ original_rule_update = clone_rule_with_new_dayschedule(rule, rule.name.to_s + " - dates left")
742
+ schedule.setScheduleRuleIndex(original_rule_update, current_index)
743
+ current_index += 1
744
+ all_specific_dates.each do |date|
745
+ original_rule_update.addSpecificDate(date)
746
+ end
747
+ 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)}")
748
+ runner.registerInfo("-------- and is shifted to priority #{current_index}")
749
+ end
750
+ rule.remove
751
+
752
+
753
+ else
754
+ ## If the rule applies to a DateRange, check if the DateRange overlaps with each adjustment date period
755
+ ## if so, create a new rule for that adjustment date period
756
+ runner.registerInfo("******* The rule #{rule.name.to_s} covers date range #{rule.startDate.get} - #{rule.endDate.get}.")
757
+ cooling_adjust_period_inputs.each do |period, period_inputs|
758
+ os_start_date = period_inputs["date_start"]
759
+ os_end_date = period_inputs["date_end"]
760
+ shift_time_start = period_inputs["time_start"]
761
+ shift_time_end = period_inputs["time_end"]
762
+ if [os_start_date, os_end_date, shift_time_start, shift_time_end].all?
763
+ ## check if the original rule applied DateRange overlaps with the adjustment date period
764
+ overlapped, new_start_dates, new_end_dates = check_date_ranges_overlap(rule, os_start_date, os_end_date)
765
+ if overlapped
766
+ new_start_dates.each_with_index do |start_date, i|
767
+ rule_period = modify_rule_for_date_period(rule, start_date, new_end_dates[i], shift_time_start,
768
+ shift_time_end, cooling_adjustment_si)
769
+ if rule_period
770
+ applicable_flag = true
771
+ if period == "period1"
772
+ checkDaysCovered(rule_period, days_covered)
773
+ final_clg_sch_set_values << rule_period.daySchedule.values
774
+ end
775
+ if schedule.setScheduleRuleIndex(rule_period, current_index)
776
+ current_index += 1
777
+ runner.registerInfo("-------- The rule #{rule_period.name.to_s} is added as priority #{current_index}")
778
+ else
779
+ runner.registerError("Fail to set rule index for #{rule_period.name.to_s}.")
780
+ end
781
+ end
782
+ end
783
+ end
784
+
785
+ end
786
+ end
787
+ ## The original rule will be shifted to the currently lowest priority
788
+ ## Setting the rule to an existing index will automatically push all other rules after it down
789
+ if schedule.setScheduleRuleIndex(rule, current_index)
790
+ runner.registerInfo("-------- The original rule #{rule.name.to_s} is shifted to priority #{current_index}")
791
+ current_index += 1
792
+ else
793
+ runner.registerError("Fail to set rule index for #{rule.name.to_s}.")
794
+ end
447
795
  end
448
- else
449
- runner.registerWarning("Cooling setpoint schedule '#{sch_name}' is a ScheduleRuleSet, but has no ScheduleRules associated. It won't be altered by this measure.")
450
- end
451
-
452
796
 
453
- if days_covered.include?(false)
454
- winter_rule1 = create_sch_rule_from_default(model, schedule, default_rule, winterStartDate1, winterEndDate1)
455
- winter_rule2 = create_sch_rule_from_default(model, schedule, default_rule, winterStartDate2, winterEndDate2)
456
-
457
- coverMissingDays(winter_rule1, days_covered)
458
- coverMissingDays(winter_rule2, days_covered) # need to cover missing days for both winter rules
459
- checkDaysCovered(winter_rule1, days_covered)
460
-
461
- summer_rule = copy_sch_rule_for_period(model, winter_rule1, default_rule, summerStartDate, summerEndDate)
462
-
463
- summer_day = summer_rule.daySchedule
464
- day_time_vector = summer_day.times
465
- day_value_vector = summer_day.values
466
- summer_day.clearValues
467
-
468
- summer_day = updateDaySchedule(summer_day, day_time_vector, day_value_vector, shift_time_cooling_start, shift_time_cooling_end, cooling_adjustment_ip)
469
- clg_sch_set_values << default_rule.values # original
470
- final_clg_sch_set_values << summer_day.values # new
471
797
  end
472
-
473
- ######################################################################
474
- else
475
- runner.registerWarning("Schedule '#{sch_name}' isn't a ScheduleRuleset object and won't be altered by this measure.")
476
- os_sch.remove # remove un-used clone
477
798
  end
478
- end
479
-
480
- # make heating schedule adjustments and rename. Put in check to skip and warn if schedule not ruleset
481
- htg_set_schs.each do |sch_name, os_sch| # old name and new object for schedule
482
- if !os_sch.to_ScheduleRuleset.empty?
483
- schedule = os_sch.to_ScheduleRuleset.get
484
- default_rule = schedule.defaultDaySchedule
485
- rules = schedule.scheduleRules
486
- days_covered = Array.new(7, false)
487
-
488
- if rules.length > 0
489
- rules.each do |rule|
490
- summer_rule = copy_sch_rule_for_period(model, rule, rule.daySchedule, summerStartDate, summerEndDate)
491
-
492
- checkDaysCovered(summer_rule, days_covered)
493
-
494
- winter_rule1 = rule
495
- winter_rule1.setStartDate(winterStartDate1)
496
- winter_rule1.setEndDate(winterEndDate1)
497
- htg_sch_set_values << rule.daySchedule.values # original
498
799
 
499
- winter_day1 = winter_rule1.daySchedule
500
- day_time_vector = winter_day1.times
501
- day_value_vector = winter_day1.values
502
- winter_day1.clearValues
503
-
504
- winter_day1 = updateDaySchedule(winter_day1, day_time_vector, day_value_vector, shift_time_heating_start, shift_time_heating_end, heating_adjustment_ip)
505
- final_htg_sch_set_values << winter_day1.values # new
506
-
507
- winter_rule2 = copy_sch_rule_for_period(model, winter_rule1, winter_rule1.daySchedule, winterStartDate2, winterEndDate2)
800
+ default_day = schedule.defaultDaySchedule
801
+ if days_covered.include?(false)
802
+ runner.registerInfo("Some days use default day. Adding new scheduleRule from defaultDaySchedule for applicable date period.")
803
+ cooling_adjust_period_inputs.each do |period, period_inputs|
804
+ os_start_date = period_inputs["date_start"]
805
+ os_end_date = period_inputs["date_end"]
806
+ shift_time_start = period_inputs["time_start"]
807
+ shift_time_end = period_inputs["time_end"]
808
+ if [os_start_date, os_end_date, shift_time_start, shift_time_end].all?
809
+ new_default_rule_period = modify_default_day_for_date_period(schedule, default_day, days_covered, os_start_date, os_end_date,
810
+ shift_time_start, shift_time_end, cooling_adjustment_si)
811
+ schedule.setScheduleRuleIndex(new_default_rule_period, current_index)
812
+ applicable_flag = true
813
+ if period == 'period1'
814
+ final_clg_sch_set_values << new_default_rule_period.daySchedule.values
815
+ end
508
816
  end
509
- else
510
- runner.registerWarning("Cooling setpoint schedule '#{sch_name}' is a ScheduleRuleSet, but has no ScheduleRules associated. It won't be altered by this measure.")
511
817
  end
512
818
 
819
+ end
513
820
 
514
- if days_covered.include?(false)
515
- summer_rule = create_sch_rule_from_default(model, schedule, default_rule, summerStartDate, summerEndDate)
516
-
517
- coverMissingDays(summer_rule, days_covered)
518
- checkDaysCovered(summer_rule, days_covered)
519
-
520
- winter_rule1 = copy_sch_rule_for_period(model, summer_rule, default_rule, winterStartDate1, winterEndDate1)
521
- winter_day1 = winter_rule1.daySchedule
522
- day_time_vector = winter_day1.times
523
- day_value_vector = winter_day1.values
524
- winter_day1.clearValues
821
+ end
525
822
 
526
- winter_day1 = updateDaySchedule(winter_day1, day_time_vector, day_value_vector, shift_time_heating_start, shift_time_heating_end, heating_adjustment_ip)
527
823
 
528
- htg_sch_set_values << default_rule.values # original
529
- final_htg_sch_set_values << winter_day1.values # new
824
+ ######################################################################
825
+ heating_adjust_period_inputs = { "period1" => {"date_start"=>os_heating_start_date1, "date_end"=>os_heating_end_date1,
826
+ "time_start"=>heating_shift_time_start1, "time_end"=>heating_shift_time_end1},
827
+ "period2" => {"date_start"=>os_heating_start_date2, "date_end"=>os_heating_end_date2,
828
+ "time_start"=>heating_shift_time_start2, "time_end"=>heating_shift_time_end2},
829
+ "period3" => {"date_start"=>os_heating_start_date3, "date_end"=>os_heating_end_date3,
830
+ "time_start"=>heating_shift_time_start3, "time_end"=>heating_shift_time_end3},
831
+ "period4" => {"date_start"=>os_heating_start_date4, "date_end"=>os_heating_end_date4,
832
+ "time_start"=>heating_shift_time_start4, "time_end"=>heating_shift_time_end4},
833
+ "period5" => {"date_start"=>os_heating_start_date5, "date_end"=>os_heating_end_date5,
834
+ "time_start"=>heating_shift_time_start5, "time_end"=>heating_shift_time_end5} }
835
+ # make heating schedule adjustments and rename. Put in check to skip and warn if schedule not ruleset
836
+ htg_set_schs.each do |old_sch_name, os_sch| # old name and new object for schedule
837
+ schedule = os_sch.to_ScheduleRuleset.get
838
+ rules = schedule.scheduleRules
839
+ days_covered = Array.new(7, false)
840
+ current_index = 0
841
+ if rules.length <= 0
842
+ runner.registerWarning("Heating setpoint schedule '#{old_sch_name}' is a ScheduleRuleSet, but has no ScheduleRules associated. It won't be altered by this measure.")
843
+ else
844
+ runner.registerInfo("schedule rule set #{old_sch_name} has #{rules.length} rules.")
845
+ rules.each do |rule|
846
+ runner.registerInfo("---- Rule No.#{rule.ruleIndex}: #{rule.name.to_s}")
847
+ if rule.dateSpecificationType == "SpecificDates"
848
+ ## if the rule applies to SpecificDates, collect the dates that fall into each adjustment date period,
849
+ ## and create a new rule for each date period with covered specific dates
850
+ runner.registerInfo("======= The rule #{rule.name.to_s} only covers specific dates.")
851
+ ## the specificDates cannot be modified in place because it's a frozen array
852
+ all_specific_dates = []
853
+ rule.specificDates.each { |date| all_specific_dates << date }
854
+ heating_adjust_period_inputs.each do |period, period_inputs|
855
+ period_inputs["specific_dates"] = []
856
+ os_start_date = period_inputs["date_start"]
857
+ os_end_date = period_inputs["date_end"]
858
+ shift_time_start = period_inputs["time_start"]
859
+ shift_time_end = period_inputs["time_end"]
860
+ if [os_start_date, os_end_date, shift_time_start, shift_time_end].all?
861
+ rule.specificDates.each do |covered_date|
862
+ if covered_date >= os_start_date and covered_date <= os_end_date
863
+ period_inputs["specific_dates"] << covered_date
864
+ all_specific_dates.delete(covered_date)
865
+ end
866
+ end
867
+ 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)}")
868
+ rule_period = modify_rule_for_specific_dates(rule, os_start_date, os_end_date, shift_time_start, shift_time_end,
869
+ heating_adjustment_si, period_inputs["specific_dates"])
870
+ if rule_period
871
+ applicable_flag = true
872
+ if period == "period1"
873
+ final_htg_sch_set_values << rule_period.daySchedule.values
874
+ end
875
+ if schedule.setScheduleRuleIndex(rule_period, current_index)
876
+ current_index += 1
877
+ runner.registerInfo("-------- The rule #{rule_period.name.to_s} for #{rule_period.dateSpecificationType} is added as priority #{current_index}")
878
+ else
879
+ runner.registerError("Fail to set rule index for #{rule_period.name.to_s}.")
880
+ end
881
+ end
882
+ end
883
+ end
884
+ if all_specific_dates.empty?
885
+ ## if all specific dates have been covered by new rules for each adjustment date period, remove the original rule
886
+ runner.registerInfo("The original rule is removed since no specific date left")
887
+ else
888
+ ## if there's still dates left to be covered, modify the original rule to only cover these dates
889
+ ## (this is just in case that the rule order was not set correctly, and the original rule is still applied to all specific dates;
890
+ ## also to make the logic in OSM more clearer)
891
+ ## the specificDates cannot be modified in place, so create a new rule with the left dates to replace the original rule
892
+ original_rule_update = clone_rule_with_new_dayschedule(rule, rule.name.to_s + " - dates left")
893
+ schedule.setScheduleRuleIndex(original_rule_update, current_index)
894
+ current_index += 1
895
+ all_specific_dates.each do |date|
896
+ original_rule_update.addSpecificDate(date)
897
+ end
898
+ 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)}")
899
+ runner.registerInfo("-------- and is shifted to priority #{current_index}")
900
+ end
901
+ rule.remove
902
+
903
+ else
904
+ heating_adjust_period_inputs.each do |period, period_inputs|
905
+ os_start_date = period_inputs["date_start"]
906
+ os_end_date = period_inputs["date_end"]
907
+ shift_time_start = period_inputs["time_start"]
908
+ shift_time_end = period_inputs["time_end"]
909
+ if [os_start_date, os_end_date, shift_time_start, shift_time_end].all?
910
+ overlapped, new_start_dates, new_end_dates = check_date_ranges_overlap(rule, os_start_date, os_end_date)
911
+ if overlapped
912
+ new_start_dates.each_with_index do |start_date, i|
913
+ rule_period = modify_rule_for_date_period(rule, start_date, new_end_dates[i], shift_time_start,
914
+ shift_time_end, heating_adjustment_si)
915
+ if rule_period
916
+ applicable_flag = true
917
+ if period == "period1"
918
+ checkDaysCovered(rule_period, days_covered)
919
+ final_htg_sch_set_values << rule_period.daySchedule.values
920
+ end
921
+ if schedule.setScheduleRuleIndex(rule_period, current_index)
922
+ current_index += 1
923
+ runner.registerInfo("-------- The rule #{rule_period.name.to_s} is added as priority #{current_index}")
924
+ else
925
+ runner.registerError("Fail to set rule index for #{rule_period.name.to_s}.")
926
+ end
927
+ end
928
+ end
929
+ end
930
+ end
931
+ end
932
+ # The original rule will be shifted to the currently lowest priority
933
+ # Setting the rule to an existing index will automatically push all other rules after it down
934
+ if schedule.setScheduleRuleIndex(rule, current_index)
935
+ runner.registerInfo("-------- The original rule #{rule.name.to_s} is shifted to priority #{current_index}")
936
+ current_index += 1
937
+ else
938
+ runner.registerError("Fail to set rule index for #{rule.name.to_s}.")
939
+ end
940
+ end
530
941
 
531
- winter_rule2 = copy_sch_rule_for_period(model, winter_rule1, winter_rule1.daySchedule, winterStartDate2, winterEndDate2)
532
942
  end
943
+ end
533
944
 
534
- else
535
- runner.registerWarning("Schedule '#{sch_name}' isn't a ScheduleRuleset object and won't be altered by this measure.")
536
- os_sch.remove # remove un-used clone
945
+ default_day = schedule.defaultDaySchedule
946
+ if days_covered.include?(false)
947
+ runner.registerInfo("Some days use default day. Adding new scheduleRule from defaultDaySchedule for applicable date period.")
948
+ heating_adjust_period_inputs.each do |period, period_inputs|
949
+ os_start_date = period_inputs["date_start"]
950
+ os_end_date = period_inputs["date_end"]
951
+ shift_time_start = period_inputs["time_start"]
952
+ shift_time_end = period_inputs["time_end"]
953
+ if [os_start_date, os_end_date, shift_time_start, shift_time_end].all?
954
+ new_default_rule_period = modify_default_day_for_date_period(schedule, default_day, days_covered, os_start_date, os_end_date,
955
+ shift_time_start, shift_time_end, heating_adjustment_si)
956
+ schedule.setScheduleRuleIndex(new_default_rule_period, current_index)
957
+ applicable_flag = true
958
+ if period == 'period1'
959
+ final_htg_sch_set_values << new_default_rule_period.daySchedule.values
960
+ end
961
+ end
962
+ end
537
963
  end
538
964
  end
539
965
 
540
- # get min and max heating and cooling and convert to IP
541
- clg_sch_set_values = clg_sch_set_values.flatten
542
- htg_sch_set_values = htg_sch_set_values.flatten
543
-
544
- puts "clg_sch_set_values: #{clg_sch_set_values.inspect}"
545
- puts "htg_sch_set_values: #{htg_sch_set_values.inspect}"
546
-
547
- # set NA flag if can't get values for schedules (e.g. if all compact)
548
- applicable_flag = false
549
966
 
550
- # get min and max if values exist
551
- if !clg_sch_set_values.empty?
552
- min_clg_si = OpenStudio::Quantity.new(clg_sch_set_values.min, TEMP_SI_UNIT)
553
- max_clg_si = OpenStudio::Quantity.new(clg_sch_set_values.max, TEMP_SI_UNIT)
554
- min_clg_ip = OpenStudio.convert(min_clg_si, TEMP_IP_UNIT).get
555
- max_clg_ip = OpenStudio.convert(max_clg_si, TEMP_IP_UNIT).get
556
- applicable_flag = true
557
- else
558
- min_clg_ip = 'NA'
559
- max_clg_ip = 'NA'
967
+ model.getEnergyManagementSystemActuators.each do |ems_actuator|
968
+ if ems_actuator.actuatedComponent.is_initialized
969
+ old_sch_name = ems_actuator.actuatedComponent.get.name.to_s
970
+ if clg_set_schs.key?old_sch_name
971
+ replaced_sch = clg_set_schs[old_sch_name]
972
+ ems_actuator.setActuatedComponent(replaced_sch)
973
+ runner.registerInfo("The actuator component for EMS actuator #{ems_actuator.name.to_s} has been changed from #{old_sch_name} to #{replaced_sch.name.to_s}")
974
+ elsif htg_set_schs.key?old_sch_name
975
+ replaced_sch = htg_set_schs[old_sch_name]
976
+ ems_actuator.setActuatedComponent(replaced_sch)
977
+ runner.registerInfo("The actuator component for EMS actuator #{ems_actuator.name.to_s} has been changed from #{old_sch_name} to #{replaced_sch.name.to_s}")
978
+ end
979
+ end
560
980
  end
561
981
 
562
- # get min and max if values exist
563
- if !htg_sch_set_values.empty?
564
- min_htg_si = OpenStudio::Quantity.new(htg_sch_set_values.min, TEMP_SI_UNIT)
565
- max_htg_si = OpenStudio::Quantity.new(htg_sch_set_values.max, TEMP_SI_UNIT)
566
- min_htg_ip = OpenStudio.convert(min_htg_si, TEMP_IP_UNIT).get
567
- max_htg_ip = OpenStudio.convert(max_htg_si, TEMP_IP_UNIT).get
568
- applicable_flag = true
569
- else
570
- min_htg_ip = 'NA'
571
- max_htg_ip = 'NA'
572
- end
573
982
 
574
983
  # not applicable if no schedules can be altered
575
984
  if applicable_flag == false
576
985
  runner.registerAsNotApplicable('No thermostat schedules in the models could be altered.')
577
986
  end
578
987
 
579
- # reporting initial condition of model
580
- starting_spaces = model.getSpaces
581
- runner.registerInitialCondition("Initial cooling setpoints used in the model range from #{min_clg_ip} to #{max_clg_ip}. Initial heating setpoints used in the model range from #{min_htg_ip} to #{max_htg_ip}.")
582
-
583
988
  # get min and max heating and cooling and convert to IP for final
584
989
  final_clg_sch_set_values = final_clg_sch_set_values.flatten
585
990
  final_htg_sch_set_values = final_htg_sch_set_values.flatten
586
991
 
587
- puts "final_clg_sch_set_values: #{final_clg_sch_set_values.inspect}"
588
- puts "final_htg_sch_set_values: #{final_htg_sch_set_values.inspect}"
589
992
 
590
- if !clg_sch_set_values.empty?
993
+ if !final_clg_sch_set_values.empty?
591
994
  final_min_clg_si = OpenStudio::Quantity.new(final_clg_sch_set_values.min, TEMP_SI_UNIT)
592
995
  final_max_clg_si = OpenStudio::Quantity.new(final_clg_sch_set_values.max, TEMP_SI_UNIT)
593
996
  final_min_clg_ip = OpenStudio.convert(final_min_clg_si, TEMP_IP_UNIT).get
@@ -598,7 +1001,7 @@ class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::Mode
598
1001
  end
599
1002
 
600
1003
  # get min and max if values exist
601
- if !htg_sch_set_values.empty?
1004
+ if !final_htg_sch_set_values.empty?
602
1005
  final_min_htg_si = OpenStudio::Quantity.new(final_htg_sch_set_values.min, TEMP_SI_UNIT)
603
1006
  final_max_htg_si = OpenStudio::Quantity.new(final_htg_sch_set_values.max, TEMP_SI_UNIT)
604
1007
  final_min_htg_ip = OpenStudio.convert(final_min_htg_si, TEMP_IP_UNIT).get
@@ -610,12 +1013,117 @@ class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::Mode
610
1013
 
611
1014
  # reporting final condition of model
612
1015
  finishing_spaces = model.getSpaces
613
- runner.registerFinalCondition("Final cooling setpoints used in the model range from #{final_min_clg_ip} to #{final_max_clg_ip}. Final heating setpoints used in the model range from #{final_min_htg_ip} to #{final_max_htg_ip}.\n The cooling setpoints are increased by #{cooling_adjustment}F,from #{cooling_daily_starttime} to #{cooling_daily_endtime}. \n The heating setpoints are decreased by #{0-heating_adjustment}F,from #{heating_daily_starttime} to #{heating_daily_endtime}.")
1016
+ runner.registerFinalCondition("Final cooling setpoints used in the model range from #{final_min_clg_ip} to #{final_max_clg_ip}. Final heating setpoints used in the model range from #{final_min_htg_ip} to #{final_max_htg_ip}.")
614
1017
 
615
1018
  return true
616
1019
  end
617
1020
 
618
1021
 
1022
+ def check_date_ranges_overlap(rule, adjust_start_date, adjust_end_date)
1023
+ ## check if the original rule applied DateRange overlaps with the adjustment date period
1024
+ overlapped = false
1025
+ new_start_dates = []
1026
+ new_end_dates = []
1027
+ if rule.endDate.get >= rule.startDate.get and rule.startDate.get <= adjust_end_date and rule.endDate.get >= adjust_start_date
1028
+ overlapped = true
1029
+ new_start_dates << [adjust_start_date, rule.startDate.get].max
1030
+ new_end_dates << [adjust_end_date, rule.endDate.get].min
1031
+ elsif rule.endDate.get < rule.startDate.get
1032
+ ## If the DateRange has a endDate < startDate, the range wraps around the year.
1033
+ if rule.endDate.get >= adjust_start_date
1034
+ overlapped = true
1035
+ new_start_dates << adjust_start_date
1036
+ new_end_dates << rule.endDate.get
1037
+ end
1038
+ if rule.startDate.get <= adjust_end_date
1039
+ overlapped = true
1040
+ new_start_dates << rule.startDate.get
1041
+ new_end_dates << adjust_end_date
1042
+ end
1043
+ end
1044
+ return overlapped, new_start_dates, new_end_dates
1045
+ end
1046
+
1047
+ def clone_rule_with_new_dayschedule(original_rule, new_rule_name)
1048
+ ## Cloning a scheduleRule will automatically clone the daySchedule associated with it, but it's a shallow copy,
1049
+ ## because the daySchedule is a resource that can be used by many scheduleRule
1050
+ ## Therefore, once the daySchedule is modified for the cloned scheduleRule, the original daySchedule is also changed
1051
+ ## Also, there's no function to assign a new daySchedule to the existing scheduleRule,
1052
+ ## so the only way to clone the scheduleRule but change the daySchedule is to construct a new scheduleRule with a daySchedule passed in
1053
+ ## and copy all other settings from the original scheduleRule
1054
+ rule_period = OpenStudio::Model::ScheduleRule.new(original_rule.scheduleRuleset, original_rule.daySchedule)
1055
+ rule_period.setName(new_rule_name)
1056
+ rule_period.setApplySunday(original_rule.applySunday)
1057
+ rule_period.setApplyMonday(original_rule.applyMonday)
1058
+ rule_period.setApplyTuesday(original_rule.applyTuesday)
1059
+ rule_period.setApplyWednesday(original_rule.applyWednesday)
1060
+ rule_period.setApplyThursday(original_rule.applyThursday)
1061
+ rule_period.setApplyFriday(original_rule.applyFriday)
1062
+ rule_period.setApplySaturday(original_rule.applySaturday)
1063
+ return rule_period
1064
+ end
1065
+
1066
+ def modify_rule_for_date_period(original_rule, os_start_date, os_end_date, shift_time_start, shift_time_end, adjustment)
1067
+ # The cloned scheduleRule will automatically belongs to the originally scheduleRuleSet
1068
+ # rule_period = original_rule.clone(model).to_ScheduleRule.get
1069
+ # rule_period.daySchedule = original_rule.daySchedule.clone(model)
1070
+ new_rule_name = "#{original_rule.name.to_s} with DF for #{os_start_date.to_s} to #{os_end_date.to_s}"
1071
+ rule_period = clone_rule_with_new_dayschedule(original_rule, new_rule_name)
1072
+ day_rule_period = rule_period.daySchedule
1073
+ day_time_vector = day_rule_period.times
1074
+ day_value_vector = day_rule_period.values
1075
+ if day_time_vector.empty?
1076
+ return false
1077
+ end
1078
+ day_rule_period.clearValues
1079
+ updateDaySchedule(day_rule_period, day_time_vector, day_value_vector, shift_time_start, shift_time_end, adjustment)
1080
+ if rule_period
1081
+ rule_period.setStartDate(os_start_date)
1082
+ rule_period.setEndDate(os_end_date)
1083
+ end
1084
+ return rule_period
1085
+ end
1086
+
1087
+ def modify_rule_for_specific_dates(original_rule, os_start_date, os_end_date, shift_time_start, shift_time_end,
1088
+ adjustment, applied_dates)
1089
+ new_rule_name = "#{original_rule.name.to_s} with DF for #{os_start_date.to_s} to #{os_end_date.to_s}"
1090
+ rule_period = clone_rule_with_new_dayschedule(original_rule, new_rule_name)
1091
+ day_rule_period = rule_period.daySchedule
1092
+ day_time_vector = day_rule_period.times
1093
+ day_value_vector = day_rule_period.values
1094
+ if day_time_vector.empty?
1095
+ return false
1096
+ end
1097
+ day_rule_period.clearValues
1098
+ updateDaySchedule(day_rule_period, day_time_vector, day_value_vector, shift_time_start, shift_time_end, adjustment)
1099
+ if rule_period
1100
+ applied_dates.each do |date|
1101
+ rule_period.addSpecificDate(date)
1102
+ end
1103
+ end
1104
+ return rule_period
1105
+ end
1106
+
1107
+ def modify_default_day_for_date_period(schedule_set, default_day, days_covered, os_start_date, os_end_date,
1108
+ shift_time_start, shift_time_end, adjustment)
1109
+ # the new rule created for the ScheduleRuleSet by default has the highest priority (ruleIndex=0)
1110
+ new_default_rule = OpenStudio::Model::ScheduleRule.new(schedule_set, default_day)
1111
+ 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}")
1112
+ new_default_rule.setStartDate(os_start_date)
1113
+ new_default_rule.setEndDate(os_end_date)
1114
+ coverMissingDays(new_default_rule, days_covered)
1115
+ new_default_day = new_default_rule.daySchedule
1116
+ day_time_vector = new_default_day.times
1117
+ day_value_vector = new_default_day.values
1118
+ new_default_day.clearValues
1119
+ new_default_day = updateDaySchedule(new_default_day, day_time_vector, day_value_vector, shift_time_start, shift_time_end, adjustment)
1120
+ # schedule_set.setScheduleRuleIndex(new_default_rule, 0)
1121
+ return new_default_rule
1122
+ # TODO: if the scheduleRuleSet has holidaySchedule (which is a ScheduleDay), it cannot be altered
1123
+ end
1124
+
1125
+
1126
+
619
1127
  def checkDaysCovered(sch_rule, sch_day_covered)
620
1128
  if sch_rule.applySunday
621
1129
  sch_day_covered[0] = true
@@ -665,9 +1173,44 @@ class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::Mode
665
1173
 
666
1174
  end
667
1175
 
1176
+ def updateDaySchedule(sch_day, vec_time, vec_value, time_begin, time_end, adjustment)
1177
+ count = 0
1178
+ vec_time.each_with_index do |exist_timestamp, i|
1179
+ adjusted_value = vec_value[i] + adjustment
1180
+ if exist_timestamp > time_begin && exist_timestamp < time_end && count == 0
1181
+ sch_day.addValue(time_begin, vec_value[i])
1182
+ sch_day.addValue(exist_timestamp, adjusted_value)
1183
+ count = 1
1184
+ elsif exist_timestamp == time_end && count == 0
1185
+ sch_day.addValue(time_begin, vec_value[i])
1186
+ sch_day.addValue(exist_timestamp, adjusted_value)
1187
+ count = 2
1188
+ elsif exist_timestamp == time_begin && count == 0
1189
+ sch_day.addValue(exist_timestamp, vec_value[i])
1190
+ count = 1
1191
+ elsif exist_timestamp > time_end && count == 0
1192
+ sch_day.addValue(time_begin, vec_value[i])
1193
+ sch_day.addValue(time_end, adjusted_value)
1194
+ sch_day.addValue(exist_timestamp, vec_value[i])
1195
+ count = 2
1196
+ elsif exist_timestamp > time_begin && exist_timestamp < time_end && count==1
1197
+ sch_day.addValue(exist_timestamp, adjusted_value)
1198
+ elsif exist_timestamp == time_end && count==1
1199
+ sch_day.addValue(exist_timestamp, adjusted_value)
1200
+ count = 2
1201
+ elsif exist_timestamp > time_end && count == 1
1202
+ sch_day.addValue(time_end, adjusted_value)
1203
+ sch_day.addValue(exist_timestamp, vec_value[i])
1204
+ count = 2
1205
+ else
1206
+ sch_day.addValue(exist_timestamp, vec_value[i])
1207
+ end
1208
+ end
1209
+ return sch_day
1210
+ end
668
1211
 
669
1212
  # TODO check if this function works
670
- def updateDaySchedule(sch_day, vec_time, vec_value, time_begin, time_end, adjustment)
1213
+ def updateDaySchedule_old(sch_day, vec_time, vec_value, time_begin, time_end, adjustment)
671
1214
  # indicator: 0:schedule unchanged, 1:schedule changed at least once, 2:schedule change completed
672
1215
  count = 0
673
1216
  for i in 0..(vec_time.size - 1)
@@ -700,28 +1243,6 @@ class AdjustThermostatSetpointsByDegreesForPeakHours < OpenStudio::Measure::Mode
700
1243
  return sch_day
701
1244
  end
702
1245
 
703
- # copy ScheduleRule sch_rule, copy ScheduleDay sch_day and assign to new schedule rule
704
- def copy_sch_rule_for_period(model, sch_rule, sch_day, start_date, end_date)
705
- new_rule = sch_rule.clone(model).to_ScheduleRule.get
706
- new_rule.setStartDate(start_date)
707
- new_rule.setEndDate(end_date)
708
-
709
- new_day_sch = sch_day.clone(model)
710
- new_day_sch.setParent(new_rule)
711
-
712
- return new_rule
713
- end
714
-
715
- def create_sch_rule_from_default(model, sch_ruleset, default_sch_fule, start_date, end_date)
716
- new_rule = OpenStudio::Model::ScheduleRule.new(sch_ruleset)
717
- new_rule.setStartDate(start_date)
718
- new_rule.setEndDate(end_date)
719
-
720
- new_day_sch = default_sch_fule.clone(model)
721
- new_day_sch.setParent(new_rule)
722
-
723
- return new_rule
724
- end
725
1246
  end
726
1247
 
727
1248