Class: AddChilledWaterStorageTank
- Inherits:
-
OpenStudio::Measure::ModelMeasure
- Object
- OpenStudio::Measure::ModelMeasure
- AddChilledWaterStorageTank
- Defined in:
- lib/measures/add_chilled_water_storage_tank/measure.rb
Overview
start the measure
Instance Method Summary collapse
-
#arguments(model) ⇒ Object
define the arguments that the user will input.
-
#create_sch(model, sch_name, start_time, end_time, start_date, end_date, wknds) ⇒ Object
if_overnight: 1 or 0; wknds (if applicable to weekends): 1 or 0.
-
#description ⇒ Object
human readable description.
-
#hardsize_cooling_tower_two_speed(tower) ⇒ Object
not necessarily can find a cw_loop as the existing primary chiller might be air cooled.
-
#modeler_description ⇒ Object
human readable description of modeling approach.
-
#name ⇒ Object
human readable name.
-
#run(model, runner, user_arguments) ⇒ Object
define what happens when the measure is run.
- #run_osw(osw_path) ⇒ Object
Instance Method Details
#arguments(model) ⇒ Object
define the arguments that the user will input
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 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 |
# File 'lib/measures/add_chilled_water_storage_tank/measure.rb', line 33 def arguments(model) args = OpenStudio::Measure::OSArgumentVector.new # inputs: (1) tank volume # if tank volume is given, use user-specific value, otherwise: # run sizing run first, and get the total cooling capacity of the chillers on the primary loop, based on which tank volume is sized. tank_vol = OpenStudio::Measure::OSArgument.makeDoubleArgument('tank_vol', false) tank_vol.setDisplayName('Thermal storage chilled water tank volume in m3') args << tank_vol # Make choice argument for energy storage objective objective = OpenStudio::Measure::OSArgument.makeChoiceArgument('objective', ['Full Storage', 'Partial Storage'], true) objective.setDisplayName('Select Energy Storage Objective:') objective.setDefaultValue('Partial Storage') args << objective # Make list of chilled water loop(s) from which user can select plant_loops = model.getPlantLoops loop_choices = OpenStudio::StringVector.new plant_loops.each do |loop| if loop.sizingPlant.loopType.to_s == 'Cooling' loop_choices << loop.name.to_s end end loop_choices << "" # Make choice argument for primary loop selection selected_primary_loop_name = OpenStudio::Measure::OSArgument.makeChoiceArgument('selected_primary_loop_name', loop_choices, false) selected_primary_loop_name.setDisplayName('Select Primary Loop:') selected_primary_loop_name.setDescription('This is the primary cooling loop on which the chilled water tank will be added.') pri_loop_name = nil loop_choices.each do |loop_name| pri_loop_name = loop_name if loop_name.downcase.include?('chilled water loop') end if !pri_loop_name.nil? selected_primary_loop_name.setDefaultValue(pri_loop_name) else selected_primary_loop_name.setDescription('Error: No Cooling Loop Found') selected_primary_loop_name.setDefaultValue("") end args << selected_primary_loop_name # TODO: In the future may have the need to add tank to existing secondary loop if any. # But need to check if the found secondary loop matches the selected primary loop # # Make choice argument for secondary loop selection # selected_secondary_loop = OpenStudio::Measure::OSArgument.makeChoiceArgument('selected_secondary_loop', loop_choices, false) # selected_secondary_loop.setDisplayName('Select Secondary Loop:') # selected_secondary_loop.setDescription('This is the secondary cooling loop on which the chilled water tank will be added.') # # Check if any loop includes string "secondary" # sec_loop = nil # loop_choices.each do |loop_name| # sec_loop = loop_name if loop_name.include?('secondary') # end # if !sec_loop.nil? # selected_secondary_loop.setDefaultValue(sec_loop) # else # selected_secondary_loop.setDefaultValue(nil) # end # args << selected_secondary_loop # Make double argument for loop setpoint temperature primary_loop_sp = OpenStudio::Measure::OSArgument.makeDoubleArgument('primary_loop_sp', true) primary_loop_sp.setDisplayName('Primary Loop (charging) Setpoint Temperature degree C:') primary_loop_sp.setDefaultValue(6.7) args << primary_loop_sp # Make double argument for loop setpoint temperature secondary_loop_sp = OpenStudio::Measure::OSArgument.makeDoubleArgument('secondary_loop_sp', true) secondary_loop_sp.setDisplayName('Secondary Loop (discharging) Setpoint Temperature degree C:') # secondary_loop_sp.setDefaultValue(8.5) secondary_loop_sp.setDefaultValue(6.7) args << secondary_loop_sp # Make double argument for loop temperature for chilled water charging tank_charge_sp = OpenStudio::Measure::OSArgument.makeDoubleArgument('tank_charge_sp', true) tank_charge_sp.setDisplayName('Chilled Water Tank Setpoint Temperature degree C:') # tank_charge_sp.setDefaultValue(7.5) tank_charge_sp.setDefaultValue(6.7) args << tank_charge_sp # Make double argument for loop design delta T primary_delta_t = OpenStudio::Measure::OSArgument.makeStringArgument('primary_delta_t', true) primary_delta_t.setDisplayName('Loop Design Temperature Difference degree C:') primary_delta_t.setDescription('Enter numeric value to adjust selected loop settings.') primary_delta_t.setDefaultValue('Use Existing Loop Value') args << primary_delta_t # Make double argument for secondary loop design delta T secondary_delta_t = OpenStudio::Measure::OSArgument.makeDoubleArgument('secondary_delta_t', true) secondary_delta_t.setDisplayName('Secondary Loop Design Temperature Difference degree C') secondary_delta_t.setDefaultValue(4.5) args << secondary_delta_t # make an argument for the start date of thermal storage thermal_storage_startdate = OpenStudio::Measure::OSArgument.makeStringArgument('thermal_storage_startdate', false) thermal_storage_startdate.setDisplayName('Start Date for thermal storage') thermal_storage_startdate.setDescription('In MM-DD format') thermal_storage_startdate.setDefaultValue('01-01') args << thermal_storage_startdate # make an argument for the end date of thermal storage thermal_storage_enddate = OpenStudio::Measure::OSArgument.makeStringArgument('thermal_storage_enddate', false) thermal_storage_enddate.setDisplayName('End Date for thermal storage') thermal_storage_enddate.setDescription('In MM-DD format') thermal_storage_enddate.setDefaultValue('12-31') args << thermal_storage_enddate # Make string arguments for ctes discharge times discharge_start = OpenStudio::Measure::OSArgument.makeStringArgument('discharge_start', true) discharge_start.setDisplayName('Enter Starting Time for Chilled Water Tank Discharge:') discharge_start.setDescription('Use 24 hour format (HR:MM)') discharge_start.setDefaultValue('08:00') args << discharge_start discharge_end = OpenStudio::Measure::OSArgument.makeStringArgument('discharge_end', true) discharge_end.setDisplayName('Enter End Time for Chilled Water Tank Discharge:') discharge_end.setDescription('Use 24 hour format (HR:MM)') discharge_end.setDefaultValue('21:00') args << discharge_end # Make string arguments for ctes charge times charge_start = OpenStudio::Measure::OSArgument.makeStringArgument('charge_start', true) charge_start.setDisplayName('Enter Starting Time for Chilled Water Tank charge:') charge_start.setDescription('Use 24 hour format (HR:MM)') charge_start.setDefaultValue('23:00') args << charge_start charge_end = OpenStudio::Measure::OSArgument.makeStringArgument('charge_end', true) charge_end.setDisplayName('Enter End Time for Chilled Water Tank charge:') charge_end.setDescription('Use 24 hour format (HR:MM)') charge_end.setDefaultValue('07:00') args << charge_end # Make boolean arguments for thermal storage schedule on weekends wknds = OpenStudio::Measure::OSArgument.makeBoolArgument('wknds', true) wknds.setDisplayName('Allow Chilled Water Tank Work on Weekends') wknds.setDefaultValue(false) args << wknds # Output path, for sizing run run_output_path = OpenStudio::Measure::OSArgument.makePathArgument('run_output_path', true, "", false) run_output_path.setDisplayName('Output path for tank sizing run (if tank volume is not provided)') run_output_path.setDefaultValue(".") args << run_output_path # epw file path, for sizing run epw_path = OpenStudio::Measure::OSArgument.makePathArgument('epw_path', true, "", false) epw_path.setDisplayName('epw file path for tank sizing run (if tank volume is not provided)') args << epw_path return args end |
#create_sch(model, sch_name, start_time, end_time, start_date, end_date, wknds) ⇒ Object
if_overnight: 1 or 0; wknds (if applicable to weekends): 1 or 0
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 |
# File 'lib/measures/add_chilled_water_storage_tank/measure.rb', line 260 def create_sch(model, sch_name, start_time, end_time, start_date, end_date, wknds) day_start_time = Time.strptime("00:00", '%H:%M') # create discharging schedule new_sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(model) new_sch_ruleset.setName(sch_name) new_sch_ruleset.defaultDaySchedule.setName(sch_name + ' default') if start_time > end_time if_overnight = 1 else if_overnight = 0 end for min in 1..24*60 if ((end_time - day_start_time)/60).to_i == min time = OpenStudio::Time.new(0, 0, min) new_sch_ruleset.defaultDaySchedule.addValue(time, 1) elsif ((start_time - day_start_time)/60).to_i == min time = OpenStudio::Time.new(0, 0, min) new_sch_ruleset.defaultDaySchedule.addValue(time, 0) elsif min == 24*60 time = OpenStudio::Time.new(0, 0, min) new_sch_ruleset.defaultDaySchedule.addValue(time, if_overnight) end end start_month = start_date.monthOfYear.value start_day = start_date.dayOfMonth end_month = end_date.monthOfYear.value end_day = end_date.dayOfMonth ts_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_sch_ruleset.defaultDaySchedule) ts_rule.setName("#{new_sch_ruleset.name} #{start_month}/#{start_day}-#{end_month}/#{end_day} Rule") ts_rule.setStartDate(start_date) ts_rule.setEndDate(end_date) ts_rule.setApplyWeekdays(true) if wknds ts_rule.setApplyWeekends(true) else ts_rule.setApplyWeekends(false) end unless start_month == 1 && start_day == 1 new_rule_day = OpenStudio::Model::ScheduleDay.new(model) new_rule_day.addValue(OpenStudio::Time.new(0,24), 0) new_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_rule_day) new_rule.setName("#{new_sch_ruleset.name} 01/01-#{start_month}/#{start_day} Rule") new_rule.setStartDate(model.getYearDescription.makeDate(1, 1)) new_rule.setEndDate(model.getYearDescription.makeDate(start_month, start_day)) new_rule.setApplyAllDays(true) end unless end_month == 12 && end_day == 31 new_rule_day = OpenStudio::Model::ScheduleDay.new(model) new_rule_day.addValue(OpenStudio::Time.new(0,24), 0) new_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_rule_day) new_rule.setName("#{new_sch_ruleset.name} #{end_month}/#{end_day}-12/31 Rule") new_rule.setStartDate(model.getYearDescription.makeDate(end_month, end_day)) new_rule.setEndDate(model.getYearDescription.makeDate(12, 31)) new_rule.setApplyAllDays(true) end return new_sch_ruleset end |
#description ⇒ Object
human readable description
23 24 25 |
# File 'lib/measures/add_chilled_water_storage_tank/measure.rb', line 23 def description return 'This measure adds a chilled water storage tank to a chilled water loop for the purpose of thermal energy storage.' end |
#hardsize_cooling_tower_two_speed(tower) ⇒ Object
not necessarily can find a cw_loop as the existing primary chiller might be air cooled.
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'lib/measures/add_chilled_water_storage_tank/measure.rb', line 392 def hardsize_cooling_tower_two_speed(tower) # implement the applySizingValues function for CoolingTowerTwoSpeed here since it's not yet implemented in OS standards rated_water_flow_rate = tower.autosizedDesignWaterFlowRate if rated_water_flow_rate.is_initialized tower.setDesignWaterFlowRate(rated_water_flow_rate.get) end high_fan_speed_fan_power = tower.autosizedHighFanSpeedFanPower if high_fan_speed_fan_power.is_initialized tower.setHighFanSpeedFanPower(high_fan_speed_fan_power.get) end high_fan_speed_air_flow_rate = tower.autosizedHighFanSpeedAirFlowRate if high_fan_speed_air_flow_rate.is_initialized tower.setHighFanSpeedAirFlowRate(high_fan_speed_air_flow_rate.get) end high_fan_speed_u_factor_times_area_value = tower.autosizedHighFanSpeedUFactorTimesAreaValue if high_fan_speed_u_factor_times_area_value.is_initialized tower.setHighFanSpeedUFactorTimesAreaValue(high_fan_speed_u_factor_times_area_value.get) end low_fan_speed_air_flow_rate = tower.autosizedLowFanSpeedAirFlowRate if low_fan_speed_air_flow_rate.is_initialized tower.setLowFanSpeedAirFlowRate(low_fan_speed_air_flow_rate.get) end low_fan_speed_fan_power = tower.autosizedLowFanSpeedFanPower if low_fan_speed_fan_power.is_initialized tower.setLowFanSpeedFanPower(low_fan_speed_fan_power.get) end low_fan_speed_u_factor_times_area_value = tower.autosizedLowFanSpeedUFactorTimesAreaValue if low_fan_speed_u_factor_times_area_value.is_initialized tower.setLowFanSpeedUFactorTimesAreaValue(low_fan_speed_u_factor_times_area_value.get) end free_convection_regime_air_flow_rate = tower.autosizedFreeConvectionRegimeAirFlowRate if free_convection_regime_air_flow_rate.is_initialized tower.setFreeConvectionRegimeAirFlowRate(free_convection_regime_air_flow_rate.get) end free_convection_regime_u_factor_times_area_value = tower.autosizedFreeConvectionRegimeUFactorTimesAreaValue if free_convection_regime_u_factor_times_area_value.is_initialized tower.setFreeConvectionRegimeUFactorTimesAreaValue(free_convection_regime_u_factor_times_area_value.get) end end |
#modeler_description ⇒ Object
human readable description of modeling approach
28 29 30 |
# File 'lib/measures/add_chilled_water_storage_tank/measure.rb', line 28 def modeler_description return 'This measure adds a chilled water storage tank and links it to an existing chilled water loop.' end |
#name ⇒ Object
human readable name
17 18 19 20 |
# File 'lib/measures/add_chilled_water_storage_tank/measure.rb', line 17 def name # Measure name should be the title case of the class name. return 'Add Chilled Water Storage Tank' end |
#run(model, runner, user_arguments) ⇒ Object
define what happens when the measure is run
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 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 |
# File 'lib/measures/add_chilled_water_storage_tank/measure.rb', line 186 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 def run_osw(osw_path) cli_path = OpenStudio.getOpenStudioCLI cmd = "\"#{cli_path}\" run -w \"#{osw_path}\"" puts cmd system(cmd) end # assign the user inputs to variables objective = runner.getStringArgumentValue('objective', user_arguments) selected_primary_loop_name = runner.getStringArgumentValue('selected_primary_loop_name', user_arguments) # if user_arguments['selected_primary_loop_name'].hasValue # selected_primary_loop_name = runner.getStringArgumentValue('selected_primary_loop_name', user_arguments) if !selected_primary_loop_name.empty? # get the primary cooling loop selected_primary_loop = model.getModelObjectByName(selected_primary_loop_name) if selected_primary_loop.is_initialized selected_primary_loop = selected_primary_loop.get.to_PlantLoop.get else # The provided value is not a plant loop in the model runner.registerError("The provided primary loop name doesn't exist in the model.") return false end else loop_choices = [] model.getPlantLoops.each do |loop| if loop.sizingPlant.loopType.to_s == 'Cooling' loop_choices << loop.name.to_s end end if loop_choices.empty? # No cooling loop; the measure is not applicable runner.registerAsNotApplicable("No cooling loop in the model. The measure is not applicable.") return true else # There is cooling loop in the model but user didn't specify one, # and the cooling loop name does not include 'chilled water loop' runner.registerError("Please select a primary loop name to run the measure. The available cooling loop(s) in the model is #{loop_choices.join(', ')}") return false end end primary_loop_sp = runner.getDoubleArgumentValue('primary_loop_sp', user_arguments) secondary_loop_sp = runner.getDoubleArgumentValue('secondary_loop_sp', user_arguments) tank_charge_sp = runner.getDoubleArgumentValue('tank_charge_sp', user_arguments) primary_delta_t = runner.getStringArgumentValue('primary_delta_t', user_arguments) secondary_delta_t = runner.getDoubleArgumentValue('secondary_delta_t', user_arguments) thermal_storage_startdate = runner.getStringArgumentValue('thermal_storage_startdate', user_arguments) thermal_storage_enddate = runner.getStringArgumentValue('thermal_storage_enddate', user_arguments) discharge_start = runner.getStringArgumentValue('discharge_start', user_arguments) discharge_end = runner.getStringArgumentValue('discharge_end', user_arguments) charge_start = runner.getStringArgumentValue('charge_start', user_arguments) charge_end = runner.getStringArgumentValue('charge_end', user_arguments) wknds = runner.getBoolArgumentValue('wknds', user_arguments) # check time format begin discharge_start = Time.strptime(discharge_start, '%H:%M') discharge_end = Time.strptime(discharge_end, '%H:%M') charge_start = Time.strptime(charge_start, '%H:%M') charge_end = Time.strptime(charge_end, '%H:%M') rescue ArgumentError runner.registerError('Both discharge start and end time, charge start and end time are required, and should be in format of %H:%M, e.g., 16:00.') return false end # if_overnight: 1 or 0; wknds (if applicable to weekends): 1 or 0 def create_sch(model, sch_name, start_time, end_time, start_date, end_date, wknds) day_start_time = Time.strptime("00:00", '%H:%M') # create discharging schedule new_sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(model) new_sch_ruleset.setName(sch_name) new_sch_ruleset.defaultDaySchedule.setName(sch_name + ' default') if start_time > end_time if_overnight = 1 else if_overnight = 0 end for min in 1..24*60 if ((end_time - day_start_time)/60).to_i == min time = OpenStudio::Time.new(0, 0, min) new_sch_ruleset.defaultDaySchedule.addValue(time, 1) elsif ((start_time - day_start_time)/60).to_i == min time = OpenStudio::Time.new(0, 0, min) new_sch_ruleset.defaultDaySchedule.addValue(time, 0) elsif min == 24*60 time = OpenStudio::Time.new(0, 0, min) new_sch_ruleset.defaultDaySchedule.addValue(time, if_overnight) end end start_month = start_date.monthOfYear.value start_day = start_date.dayOfMonth end_month = end_date.monthOfYear.value end_day = end_date.dayOfMonth ts_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_sch_ruleset.defaultDaySchedule) ts_rule.setName("#{new_sch_ruleset.name} #{start_month}/#{start_day}-#{end_month}/#{end_day} Rule") ts_rule.setStartDate(start_date) ts_rule.setEndDate(end_date) ts_rule.setApplyWeekdays(true) if wknds ts_rule.setApplyWeekends(true) else ts_rule.setApplyWeekends(false) end unless start_month == 1 && start_day == 1 new_rule_day = OpenStudio::Model::ScheduleDay.new(model) new_rule_day.addValue(OpenStudio::Time.new(0,24), 0) new_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_rule_day) new_rule.setName("#{new_sch_ruleset.name} 01/01-#{start_month}/#{start_day} Rule") new_rule.setStartDate(model.getYearDescription.makeDate(1, 1)) new_rule.setEndDate(model.getYearDescription.makeDate(start_month, start_day)) new_rule.setApplyAllDays(true) end unless end_month == 12 && end_day == 31 new_rule_day = OpenStudio::Model::ScheduleDay.new(model) new_rule_day.addValue(OpenStudio::Time.new(0,24), 0) new_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_rule_day) new_rule.setName("#{new_sch_ruleset.name} #{end_month}/#{end_day}-12/31 Rule") new_rule.setStartDate(model.getYearDescription.makeDate(end_month, end_day)) new_rule.setEndDate(model.getYearDescription.makeDate(12, 31)) new_rule.setApplyAllDays(true) end return new_sch_ruleset end if discharge_start > discharge_end runner.registerInfo('Dischage start time is later than discharge ' \ 'end time (discharge overnight). Verify schedule inputs.') end if charge_start.between?(discharge_start, discharge_end) || charge_end.between?(discharge_start, discharge_end) runner.registerWarning('The tank charge and discharge periods overlap. ' \ 'Examine results for unexpected operation; ' \ 'verify schedule inputs.') end if objective == 'Full Storage' lasting_hrs = (discharge_end - discharge_start)/3600 # delta Time in second, convert to hours elsif objective == 'Partial Storage' lasting_hrs = (discharge_end - discharge_start)/3600/2.0 else runner.registerError("Wrong energy storage objective input, can be either 'Full Storage' or 'Partial Storage'. ") return false end # check date inputs md = /(\d\d)-(\d\d)/.match(thermal_storage_startdate) if md thermal_storage_start_month = md[1].to_i thermal_storage_start_day = md[2].to_i else runner.registerError('Start date must be in MM-DD format.') return false end md = /(\d\d)-(\d\d)/.match(thermal_storage_enddate) if md thermal_storage_end_month = md[1].to_i thermal_storage_end_day = md[2].to_i else runner.registerError('End date must be in MM-DD format.') return false end thermal_storage_startdate_os = model.getYearDescription.makeDate(thermal_storage_start_month, thermal_storage_start_day) thermal_storage_enddate_os = model.getYearDescription.makeDate(thermal_storage_end_month, thermal_storage_end_day) # report initial condition of model runner.registerInitialCondition("Original primary chilled water loop: #{selected_primary_loop.name}.") # Convert Delta T if needed from F to C (Overwrites string variables as floats) if primary_delta_t != 'Use Existing Loop Value' && primary_delta_t.to_f != 0.0 primary_delta_t = primary_delta_t.to_f else # Could add additional checks here for invalid (non-numerical) entries primary_delta_t = selected_primary_loop.sizingPlant.loopDesignTemperatureDifference end # get the condenser water loop cw_loop = nil model.getPlantLoops.each do |loop| if loop.sizingPlant.loopType.to_s.downcase == 'condenser' cw_loop = loop if cw_loop.nil? # confirm if this condenser loop contains demand component of chiller that is in the selected_primary_loop common_comps = cw_loop.demandComponents & selected_primary_loop.supplyComponents chiller_in_both_loops = false common_comps.each do |comp| chiller_in_both_loops = true if comp.to_ChillerElectricEIR.is_initialized || comp.to_ChillerAbsorption.is_initialized || comp.to_ChillerAbsorptionIndirect.is_initialized end cw_loop = nil unless chiller_in_both_loops end end # not necessarily can find a cw_loop as the existing primary chiller might be air cooled. def hardsize_cooling_tower_two_speed(tower) # implement the applySizingValues function for CoolingTowerTwoSpeed here since it's not yet implemented in OS standards rated_water_flow_rate = tower.autosizedDesignWaterFlowRate if rated_water_flow_rate.is_initialized tower.setDesignWaterFlowRate(rated_water_flow_rate.get) end high_fan_speed_fan_power = tower.autosizedHighFanSpeedFanPower if high_fan_speed_fan_power.is_initialized tower.setHighFanSpeedFanPower(high_fan_speed_fan_power.get) end high_fan_speed_air_flow_rate = tower.autosizedHighFanSpeedAirFlowRate if high_fan_speed_air_flow_rate.is_initialized tower.setHighFanSpeedAirFlowRate(high_fan_speed_air_flow_rate.get) end high_fan_speed_u_factor_times_area_value = tower.autosizedHighFanSpeedUFactorTimesAreaValue if high_fan_speed_u_factor_times_area_value.is_initialized tower.setHighFanSpeedUFactorTimesAreaValue(high_fan_speed_u_factor_times_area_value.get) end low_fan_speed_air_flow_rate = tower.autosizedLowFanSpeedAirFlowRate if low_fan_speed_air_flow_rate.is_initialized tower.setLowFanSpeedAirFlowRate(low_fan_speed_air_flow_rate.get) end low_fan_speed_fan_power = tower.autosizedLowFanSpeedFanPower if low_fan_speed_fan_power.is_initialized tower.setLowFanSpeedFanPower(low_fan_speed_fan_power.get) end low_fan_speed_u_factor_times_area_value = tower.autosizedLowFanSpeedUFactorTimesAreaValue if low_fan_speed_u_factor_times_area_value.is_initialized tower.setLowFanSpeedUFactorTimesAreaValue(low_fan_speed_u_factor_times_area_value.get) end free_convection_regime_air_flow_rate = tower.autosizedFreeConvectionRegimeAirFlowRate if free_convection_regime_air_flow_rate.is_initialized tower.setFreeConvectionRegimeAirFlowRate(free_convection_regime_air_flow_rate.get) end free_convection_regime_u_factor_times_area_value = tower.autosizedFreeConvectionRegimeUFactorTimesAreaValue if free_convection_regime_u_factor_times_area_value.is_initialized tower.setFreeConvectionRegimeUFactorTimesAreaValue(free_convection_regime_u_factor_times_area_value.get) end end # if user provides this input, if not, do autosizing if user_arguments['tank_vol'].hasValue tank_vol = runner.getDoubleArgumentValue('tank_vol', user_arguments) if cw_loop # autosize cooling tower in the condenser loop to avoid invalid hard-sized parameters cw_loop.supplyComponents.each do |comp| if comp.to_CoolingTowerSingleSpeed.is_initialized cooling_tower = comp.to_CoolingTowerSingleSpeed.get cooling_tower.autosizeDesignWaterFlowRate cooling_tower.autosizeFanPoweratDesignAirFlowRate cooling_tower.autosizeDesignAirFlowRate cooling_tower.autosizeUFactorTimesAreaValueatDesignAirFlowRate cooling_tower.autosizeAirFlowRateinFreeConvectionRegime cooling_tower.autosizeUFactorTimesAreaValueatFreeConvectionAirFlowRate runner.registerInfo("CoolingTowerSingleSpeed #{cooling_tower.name} has been set to autosize.") elsif comp.to_CoolingTowerTwoSpeed.is_initialized cooling_tower = comp.to_CoolingTowerTwoSpeed.get cooling_tower.autosizeDesignWaterFlowRate cooling_tower.autosizeHighFanSpeedFanPower cooling_tower.autosizeHighFanSpeedAirFlowRate cooling_tower.autosizeHighFanSpeedUFactorTimesAreaValue cooling_tower.autosizeLowFanSpeedAirFlowRate cooling_tower.autosizeLowFanSpeedFanPower cooling_tower.autosizeLowFanSpeedUFactorTimesAreaValue cooling_tower.autosizeFreeConvectionRegimeAirFlowRate cooling_tower.autosizeFreeConvectionRegimeUFactorTimesAreaValue runner.registerInfo("CoolingTowerTwoSpeed #{cooling_tower.name} has been set to autosize.") elsif comp.to_CoolingTowerVariableSpeed.is_initialized cooling_tower = comp.to_CoolingTowerVariableSpeed.get cooling_tower.autosize runner.registerInfo("CoolingTowerVariableSpeed #{cooling_tower.name} has been set to autosize.") end end end else # unless user_arguments['run_output_path'].hasValue # runner.registerError("Need to provide run output path for sizing run of tank volume. ") # return false # end run_output_path = runner.getPathArgumentValue('run_output_path', user_arguments) Dir.mkdir(run_output_path.to_s) unless File.exist?(run_output_path.to_s) sizing_output_path = File.(File.join(run_output_path.to_s, 'sizing_run')) Dir.mkdir(sizing_output_path.to_s) unless File.exist?(sizing_output_path.to_s) # Change the simulation to only run the sizing days sim_control = model.getSimulationControl sim_control.setRunSimulationforSizingPeriods(true) sim_control.setRunSimulationforWeatherFileRunPeriods(false) sizing_osw_path = File.join(sizing_output_path.to_s, 'sizing.osm') model.save(sizing_osw_path, true) # true is overwrite if File.exist?(model.weatherFile.get.path.get.to_s) epw_path = model.weatherFile.get.path.get else unless user_arguments['epw_path'].hasValue runner.registerError("Need to provide epw file path for sizing run of tank volume, the current epw file specified in osm cannot be found. ") return false end epw_path = runner.getPathArgumentValue('epw_path', user_arguments) end # create_osw for sizing osw = {} osw["weather_file"] = epw_path osw["seed_file"] = sizing_osw_path osw_path = File.join(sizing_output_path.to_s, "sizing.osw") File.open(osw_path, 'w') do |f| f << JSON.pretty_generate(osw) end model.resetSqlFile run_osw(osw_path) sleep(1) sql_path = OpenStudio::Path.new(File.join(sizing_output_path.to_s, "run", "eplusout.sql")) if OpenStudio.exists(sql_path) sql = OpenStudio::SqlFile.new(sql_path) unless sql.connectionOpen runner.registerError("The sizing run failed without valid a sql file. Look at the eplusout.err file in #{File.dirname(sql_path.to_s)} to see the cause.") return false end # Attach the sql file from the run to the model model.setSqlFile(sql) end total_cooling_cap = 0 # initial selected_primary_loop.supplyComponents.each do |comp| if comp.to_ChillerElectricEIR.is_initialized total_cooling_cap += comp.to_ChillerElectricEIR.get.autosizedReferenceCapacity.get elsif comp.to_ChillerAbsorption.is_initialized total_cooling_cap += comp.to_ChillerAbsorption.get.autosizedNominalCapacity.get elsif comp.to_ChillerAbsorptionIndirect.is_initialized total_cooling_cap += comp.to_ChillerAbsorptionIndirect.get.autosizedNominalCapacity.get end end if cw_loop # hard size cooling tower in the condenser loop cw_loop.supplyComponents.each do |comp| if comp.to_CoolingTowerSingleSpeed.is_initialized cooling_tower = comp.to_CoolingTowerSingleSpeed.get cooling_tower.applySizingValues runner.registerInfo("Autosized parameters from the sizing run have been set to CoolingTowerSingleSpeed #{cooling_tower.name}") elsif comp.to_CoolingTowerTwoSpeed.is_initialized cooling_tower = comp.to_CoolingTowerTwoSpeed.get hardsize_cooling_tower_two_speed(cooling_tower) runner.registerInfo("Autosized parameters from the sizing run have been set to CoolingTowerTwoSpeed #{cooling_tower.name}") elsif comp.to_CoolingTowerVariableSpeed.is_initialized cooling_tower = comp.to_CoolingTowerVariableSpeed.get cooling_tower.applySizingValues runner.registerInfo("Autosized parameters from the sizing run have been set to CoolingTowerVariableSpeed #{cooling_tower.name}") end end end # assuming average load ratio of chiller is 1/3 throughout the day tank_vol = total_cooling_cap/3.0 * 3600 * lasting_hrs / (4182 * 1000 * secondary_delta_t) # heat capacity of water 4182J/kg.K, water density 1000g/m3, lasting 8 hours end # change back to normal run sim_control = model.getSimulationControl sim_control.setRunSimulationforSizingPeriods(false) sim_control.setRunSimulationforWeatherFileRunPeriods(true) sec_loop = OpenStudio::Model::PlantLoop.new(model) sec_loop.setName("Chilled Water Secondary Loop") selected_primary_loop.setName("Chilled Water Primary Loop") sizing_sec_plant = sec_loop.sizingPlant sizing_sec_plant.setLoopType('Cooling') sizing_sec_plant.setDesignLoopExitTemperature(tank_charge_sp) sizing_sec_plant.setLoopDesignTemperatureDifference(secondary_delta_t) sizing_pri_plant = selected_primary_loop.sizingPlant sizing_pri_plant.setLoopType('Cooling') sizing_pri_plant.setDesignLoopExitTemperature(primary_loop_sp) sizing_pri_plant.setLoopDesignTemperatureDifference(primary_delta_t) # add chilled water tank to the primary loop as demand and secondary loop as supply chw_storage_tank = OpenStudio::Model::ThermalStorageChilledWaterStratified.new(model) tank_temp_sch = OpenStudio::Model::ScheduleRuleset.new(model) tank_temp_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), tank_charge_sp) chw_storage_tank.setTankVolume(tank_vol) chw_storage_tank.setSetpointTemperatureSchedule(tank_temp_sch) sec_loop.addSupplyBranchForComponent(chw_storage_tank) selected_primary_loop.addDemandBranchForComponent(chw_storage_tank) # create discharging and charging schedule and apply to chilled water tank and primary loop discharge_sch = create_sch(model, 'Chilled water tank discharge schedule', discharge_start, discharge_end, thermal_storage_startdate_os, thermal_storage_enddate_os, wknds) charge_sch = create_sch(model, 'Chilled water tank charge schedule', charge_start, charge_end, thermal_storage_startdate_os, thermal_storage_enddate_os, wknds) chw_storage_tank.setUseSideAvailabilitySchedule(discharge_sch) chw_storage_tank.setSourceSideAvailabilitySchedule(charge_sch) avm_sch = OpenStudio::Model::AvailabilityManagerScheduled.new(model) avm_sch.setSchedule(charge_sch) selected_primary_loop.addAvailabilityManager(avm_sch) if objective == "Partial Storage" sec_chiller = OpenStudio::Model::ChillerElectricEIR.new(model) # use default curves sec_chiller.setName("CoolSysSecondary Chiller") sec_loop.addSupplyBranchForComponent(sec_chiller) if cw_loop.nil? sec_chiller.setCondenserType("AirCooled") else sec_chiller.setCondenserType("WaterCooled") cw_loop.addDemandBranchForComponent(sec_chiller) end # add plant equipment operation schema if partial storage clg_op_scheme_tank = OpenStudio::Model::PlantEquipmentOperationCoolingLoad.new(model) clg_op_scheme_tank.addEquipment(chw_storage_tank) clg_op_scheme_sec_chiller = OpenStudio::Model::PlantEquipmentOperationCoolingLoad.new(model) clg_op_scheme_sec_chiller.addEquipment(sec_chiller) undischarge_sch = create_sch(model, 'Chilled water tank not discharge schedule', discharge_end, discharge_start, thermal_storage_startdate_os, thermal_storage_enddate_os, wknds) # in this way, sequence in E+ will be tank first then chiller. In fact the sequence here doesn't matter as each schema is coupled with schedule sec_loop.setPlantEquipmentOperationCoolingLoad(clg_op_scheme_tank) sec_loop.setPlantEquipmentOperationCoolingLoadSchedule(discharge_sch) sec_loop.setPrimaryPlantEquipmentOperationScheme(clg_op_scheme_sec_chiller) sec_loop.setPrimaryPlantEquipmentOperationSchemeSchedule(undischarge_sch) # clg_op_scheme = OpenStudio::Model::PlantEquipmentOperationCoolingLoad.new(model) # tank_supply_watt = 2.0 * (4182 * tank_vol * 1000 * secondary_delta_t / (3600 * lasting_hrs)) # double the cooling cap in case thermal storage is not used at larger cooling load. # # the sequence of addEquipment and addLoadRange can't be switched # clg_op_scheme.addEquipment(sec_chiller) # clg_op_scheme.addLoadRange(tank_supply_watt, [chw_storage_tank]) # sec_loop.setPlantEquipmentOperationCoolingLoad(clg_op_scheme) end # add secondary loop bypass pipe sec_supply_bypass = OpenStudio::Model::PipeAdiabatic.new(model) sec_supply_bypass.setName("Chilled Water Secondary Loop Supply Bypass Pipe") sec_loop.addSupplyBranchForComponent(sec_supply_bypass) # move all primary loop demand components (except inlet and outlet pipes) to the secondary loop selected_primary_loop.demandComponents(selected_primary_loop.demandSplitter, selected_primary_loop.demandMixer).each do |demand_comp| if demand_comp.to_StraightComponent.is_initialized next if demand_comp.to_Node.is_initialized || demand_comp.to_PipeAdiabatic.is_initialized demand_comp.to_StraightComponent.get.removeFromLoop sec_loop.addDemandBranchForComponent(demand_comp.to_StraightComponent.get) elsif demand_comp.to_WaterToAirComponent.is_initialized demand_comp.to_WaterToAirComponent.get.removeFromPlantLoop sec_loop.addDemandBranchForComponent(demand_comp.to_WaterToAirComponent.get) elsif demand_comp.to_WaterToWaterComponent.is_initialized next if demand_comp.to_ThermalStorageChilledWaterStratified.is_initialized # skip the newly added chilled water tank demand_comp.to_WaterToWaterComponent.get.removeFromPlantLoop sec_loop.addDemandBranchForComponent(demand_comp.to_WaterToWaterComponent.get) end end # move pump from demand component if any to secondary loop selected_primary_loop.demandComponents.each do |comp| if comp.to_PumpConstantSpeed.is_initialized comp.to_PumpConstantSpeed.get.removeFromLoop comp.to_PumpConstantSpeed.get.addToNode(sec_loop.demandInletNode) elsif comp.to_PumpVariableSpeed.is_initialized comp.to_PumpVariableSpeed.get.removeFromLoop comp.to_PumpVariableSpeed.get.addToNode(sec_loop.demandInletNode) end end # set common pipe simulation to "None", meaning primary-only, not mixing primary and secondary in one loop. selected_primary_loop.setCommonPipeSimulation("None") sec_loop.setCommonPipeSimulation("None") # add node setpoint manager pri_supply_temp_sch = OpenStudio::Model::ScheduleRuleset.new(model) pri_supply_temp_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), primary_loop_sp) sec_supply_temp_sch = OpenStudio::Model::ScheduleRuleset.new(model) sec_supply_temp_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), secondary_loop_sp) chw_pri_supply_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, pri_supply_temp_sch) chw_pri_supply_stpt_manager.addToNode(selected_primary_loop.supplyOutletNode) chw_sec_supply_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, sec_supply_temp_sch) chw_sec_supply_stpt_manager.addToNode(sec_loop.supplyOutletNode) # echo the new space's name back to the user runner.registerInfo("Chilled water tank #{chw_storage_tank.name} was added.") # report final condition of model runner.registerFinalCondition("The building finished with new chilled water tank #{chw_storage_tank.name} added to the chilled water loop #{selected_primary_loop.name}.") return true end |
#run_osw(osw_path) ⇒ Object
194 195 196 197 198 199 |
# File 'lib/measures/add_chilled_water_storage_tank/measure.rb', line 194 def run_osw(osw_path) cli_path = OpenStudio.getOpenStudioCLI cmd = "\"#{cli_path}\" run -w \"#{osw_path}\"" puts cmd system(cmd) end |