Class: ShiftScheduleByType
- Inherits:
-
OpenStudio::Measure::ModelMeasure
- Object
- OpenStudio::Measure::ModelMeasure
- ShiftScheduleByType
- Defined in:
- lib/measures/ShiftScheduleByType/measure.rb
Overview
Start the measure
Instance Method Summary collapse
-
#arguments(model) ⇒ Object
Define the arguments that the user will input.
-
#description ⇒ Object
human readable description.
-
#modeler_description ⇒ Object
human readable description of modeling approach.
-
#name ⇒ Object
define the name that a user will see, this method may be deprecated as the display name in PAT comes from the name field in measure.xml.
-
#run(model, runner, user_arguments) ⇒ Object
define what happens when the measure is run.
Instance Method Details
#arguments(model) ⇒ Object
Define the arguments that the user will input
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/measures/ShiftScheduleByType/measure.rb', line 35 def arguments(model) args = OpenStudio::Measure::OSArgumentVector.new # Argument to specify the amount of time by which the chosen schedule(s) will be shifted shift_value = OpenStudio::Measure::OSArgument.makeDoubleArgument('shift_value', true) shift_value.setDisplayName('Shift Schedule Profiles Forward (24hr, use decimal for sub hour and negative values for backward shift).') shift_value.setDefaultValue(1) args << shift_value # Argument to choose which schedules will be shifted choices = OpenStudio::StringVector.new choices << 'Cooling' choices << 'Heating' choices << 'CoolHeat' schedchoice = OpenStudio::Measure::OSArgument.makeChoiceArgument('schedchoice', choices) schedchoice.setDisplayName('Choose which schedule class(es) to shift by the specified shift value') schedchoice.setDefaultValue('CoolHeat') args << schedchoice return args end |
#description ⇒ Object
human readable description
25 26 27 |
# File 'lib/measures/ShiftScheduleByType/measure.rb', line 25 def description return 'This measure was developed for the URBANopt Class Project and shifts specific building schedules if they include cooling ("CLG"), heating ("HTG"), or air ("Air") strings. The measure will shift these chosen schedules by an amount specified by the user and will also output a .csv file of the schedules before and after the shift.' end |
#modeler_description ⇒ Object
human readable description of modeling approach
30 31 32 |
# File 'lib/measures/ShiftScheduleByType/measure.rb', line 30 def modeler_description return "Depending on the model's thermostat deadband settings, shifting of exclusively cooling or heating schedules can result in EnergyPlus deadband errors. It is recommended to shift both cooling and heating schedules using the 'coolheat' option for schedchoice. If no schedules for the current model include the cooling, heating, or air strings, none will be shifted. Schedules including the string 'setback' are intentionally excluded from shifts in order to prevent EnergyPlus errors." end |
#name ⇒ Object
define the name that a user will see, this method may be deprecated as the display name in PAT comes from the name field in measure.xml
20 21 22 |
# File 'lib/measures/ShiftScheduleByType/measure.rb', line 20 def name return 'ShiftScheduleByType' end |
#run(model, runner, user_arguments) ⇒ Object
define what happens when the measure is run
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/measures/ShiftScheduleByType/measure.rb', line 58 def run(model, runner, user_arguments) super(model, runner, user_arguments) # use the built-in error checking if !runner.validateUserArguments(arguments(model), user_arguments) return false end # Export CSV file with Schedule setpoints before schedule shifts #model1 = runner.lastOpenStudioModel #model = model1.get interval = 60 header = [] header << 'Time' schedules = [] model.getScheduleDays.each do |schedule| header << schedule.name.get schedules << schedule end dt = OpenStudio::Time.new(0, 0, interval, 0) time = OpenStudio::Time.new(0, 0, 0, 0) stop = OpenStudio::Time.new(1, 0, 0, 0) values = [] while time <= stop row = [] row << time.to_s schedules.each do |schedule| row << schedule.getValue(time) end values << row time += dt end runner.registerInfo("Writing CSV report 'schedulereportbefore.csv'") File.open('schedulereportbefore.csv', 'w') do |file| file.puts header.join(',') values.each do |row| file.puts row.join(',') end end # populate choice argument for schedules that are applied to surfaces in the model schedule_handles = OpenStudio::StringVector.new schedule_display_names = OpenStudio::StringVector.new # putting space types and names into hash schedule_args = model.getScheduleRulesets schedule_args_hash = {} schedule_args.each do |schedule_arg| schedule_args_hash[schedule_arg.name.to_s] = schedule_arg end # looping through sorted hash of schedules schedule_args_hash.sort.map do |key, value| # only include if schedule use count > 0 if value.directUseCount > 0 schedule_handles << value.handle.to_s schedule_display_names << key end end # Assign the user inputs to variables shift_value = runner.getDoubleArgumentValue('shift_value', user_arguments) schedchoice = runner.getStringArgumentValue('schedchoice', user_arguments) apply_to_all_schedules = true # Check shift value for reasonableness if (shift_value / 24) == (shift_value / 24).to_i runner.registerAsNotApplicable('No schedule shift was requested, the model was not changed.') end # Get schedules for measure schedules = [] if apply_to_all_schedules raw_schedules = model.getScheduleRulesets raw_schedules_hash = {} raw_schedules.each do |raw_schedule| raw_schedules_hash[raw_schedule.name.to_s] = raw_schedule end # Looping through sorted hash of schedules raw_schedules_hash.sort.map do |name, value| # Only include if the schedule is used in the model if value.directUseCount > 0 schedule_handles << value.handle.to_s schedule_display_names << name runner.registerInfo("Searching Schedule: #{name}") if !name.downcase.include?("setback") if schedchoice == "Cool" # Cooling and Air Only if (name.to_s.include?("CLG") || name.to_s.include?("Air")) # ADD v to Cooling list schedules << value runner.registerInfo("Schedule #{name} does contain 'CLG' or 'Air'") else runner.registerInfo("Schedule #{name} does not contain 'CLG' or 'Air'") end elsif schedchoice == "Heat" # Heating and Air Only if (name.to_s.include?("HTG") || name.to_s.include?("Air")) # ADD v to Heating list schedules << value runner.registerInfo("Schedule #{name} does contain 'HTG' or 'Air'") else runner.registerInfo("Schedule #{name} does not contain 'HTG' or 'Air'") end elsif schedchoice == "CoolHeat" # Cooling, Heating, and Air if (name.to_s.include?("CLG") || name.to_s.include?("HTG") || name.to_s.include?("Air")) # ADD v to Cooling/Heating list schedules << value runner.registerInfo("Schedule #{name} does contain 'CLG' or 'HTG' or 'Air'") else runner.registerInfo("Schedule #{name} does not contain 'CLG' or 'HTG' or 'Air'") end else runner.registerError('Unexpected value of schedchoice: ' + schedchoice + '.') return false end end end end else runner.registerAsNotApplicable('No schedules included in shift') end # Loop through all chosen schedules schedules.each do |schedule| # Array of all profiles to change profiles = [] # Push default profiles to array default_rule = schedule.defaultDaySchedule profiles << default_rule # Push profiles to array rules = schedule.scheduleRules rules.each do |rule| day_sch = rule.daySchedule profiles << day_sch end # Add design days to array summer_design = schedule.summerDesignDaySchedule winter_design = schedule.winterDesignDaySchedule profiles << summer_design profiles << winter_design # Reporting initial condition of model if apply_to_all_schedules runner.registerInitialCondition("#{schedules.size} schedules are shifted in this model.") else runner.registerInitialCondition("Schedule #{schedule.name} has #{profiles.size} profiles including design days.") end # Rename schedule schedule.setName("#{schedule.name} - (shifted #{shift_value} hours)") shift_hours = shift_value.to_i shift_minutes = ((shift_value - shift_value.to_i) * 60).to_i # Give info messages as I change specific profiles runner.registerInfo("Adjusting #{schedule.name}") # Edit profiles profiles.each do |day_sch| times = day_sch.times values = day_sch.values runner.registerInfo("Old day_sch: #{day_sch}") runner.registerInfo("Old Times: #{times}") runner.registerInfo("Old Values: #{values}") # Time objects to use in measure time_0 = OpenStudio::Time.new(0, 0, 0, 0) time_24 = OpenStudio::Time.new(0, 24, 0, 0) shift_time = OpenStudio::Time.new(0, shift_hours, shift_minutes, 0) # Arrays for values to avoid overlap conflict of times new_times = [] new_values = [] if values.length < 2 # Avoid adjusting schedules with only one cooling/heating/air setpoint new_times = times new_values = values else # Adjust schedules with more than 3 setpoints # Create a pair of times and values for what will be 0 time after adjustment new_times << time_24 if shift_time > time_0 new_values << day_sch.getValue(time_24 - shift_time) else new_values << day_sch.getValue(time_0 - shift_time) end # Clear values day_sch.clearValues # Unfreeze arrays before editing times = times.clone(freeze: false) if times.frozen? values = values.clone(freeze: false) if values.frozen? if values.length == 2 timeschg = values.length - 2 else timeschg = values.length - 3 end # Count number of arrays for i in 0..timeschg new_time = times[i] + shift_time # Adjust wrap around times for times that are t > 24 or t < 0 if new_time < time_0 new_time = new_time + time_24 values.rotate(1) # Move first value to last value in array runner.registerWarning("Times adjusted for wrap around due to new time < 0.") elsif new_time > time_24 new_time = new_time - time_24 values.rotate(-1) # Move last value to first value in array runner.registerWarning("Times adjusted for wrap around due to new time > 24.") else # If 0 < new_time < 24 new_time = new_time end # Make new values new_times = times.insert(i, new_time) new_times.delete_at(i+1) end new_values = values # Set new values equal to original schedule values new_times.freeze end if values.length <= 4 timeschg = values.length - 1 else timeschg = values.length - 2 end for i in 0..(timeschg) day_sch.addValue(new_times[i], new_values[i]) end runner.registerInfo("New day_sch: #{day_sch}") runner.registerInfo("New Times: #{new_times}") runner.registerInfo("New Values: #{new_values}") end end # Report final condition of model if apply_to_all_schedules runner.registerFinalCondition('Shifted time for all profiles for all schedules.') else runner.registerFinalCondition("Shifted time for all profiles used by this schedule.") end # Export CSV file with Schedule setpoints after schedule shifts #model = runner.lastOpenStudioModel #model = model.get interval = 60 header = [] header << 'Time' schedules = [] model.getScheduleDays.each do |schedule| header << schedule.name.get schedules << schedule end dt = OpenStudio::Time.new(0, 0, interval, 0) time = OpenStudio::Time.new(0, 0, 0, 0) stop = OpenStudio::Time.new(1, 0, 0, 0) values = [] while time <= stop row = [] row << time.to_s schedules.each do |schedule| row << schedule.getValue(time) end values << row time += dt end runner.registerInfo("Writing CSV report 'schedulereportafter.csv'") File.open('schedulereportafter.csv', 'w') do |file| file.puts header.join(',') values.each do |row| file.puts row.join(',') end end return true end |