Class: AddPackagedIceStorage
- Inherits:
-
OpenStudio::Measure::EnergyPlusMeasure
- Object
- OpenStudio::Measure::EnergyPlusMeasure
- AddPackagedIceStorage
- Defined in:
- lib/measures/add_packaged_ice_storage/measure.rb
Overview
start the measure
Instance Method Summary collapse
-
#arguments(workspace) ⇒ 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
human readable name.
-
#run(workspace, runner, user_arguments) ⇒ Object
define what happens when the measure is run.
Instance Method Details
#arguments(workspace) ⇒ Object
define the arguments that the user will input
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 |
# File 'lib/measures/add_packaged_ice_storage/measure.rb', line 61 def arguments(workspace) args = OpenStudio::Measure::OSArgumentVector.new # Add a delimiter for clarify delimiter = OpenStudio::Measure::OSArgument.makeStringArgument('delimiter', false) delimiter.setDisplayName('Select Coils to Replace:') delimiter.setDefaultValue('-----------------------------------------------------------------') args << delimiter # get existing dx coils for user selection coils = workspace.getObjectsByType('Coil:Cooling:DX:SingleSpeed'.to_IddObjectType) coils += workspace.getObjectsByType('Coil:Cooling:DX:TwoSpeed'.to_IddObjectType) coilhash = {} c = coils.sort { |a, b| a.getString(0).get <=> b.getString(0).get } c.each do |coil| c_name = coil.name.to_s coilhash[c_name] = true end # make boolean argument for selecting cooling coils to replace coilhash.each do |k, v| coil_selection = OpenStudio::Measure::OSArgument.makeBoolArgument(k, true) coil_selection.setDisplayName(k) coil_selection.setDefaultValue(v) args << coil_selection end ice_cap = OpenStudio::Measure::OSArgument.makeStringArgument('ice_cap', true) ice_cap.setDisplayName('Input the ice storage capacity [ton-hours]') ice_cap.setDescription('To specify by coil, in alphabetical order, enter values for each separated by comma.') ice_cap.setDefaultValue('AutoSize') args << ice_cap size_mult = OpenStudio::Measure::OSArgument.makeStringArgument('size_mult', false) size_mult.setDisplayName('Enter a sizing multiplier to manually adjust the autosize results for ice tank capacities') size_mult.setDefaultValue('1.0') args << size_mult # make argument for control method ctl = OpenStudio::Measure::OSArgument.makeChoiceArgument('ctl', ['ScheduledModes', 'EMSControlled'], true) ctl.setDisplayName('Select ice storage control method') ctl.setDefaultValue('EMSControlled') args << ctl # obtain default schedule names in TESCurves.idf. This allows users to manually add schedules to the idf and be able to access them in OS or PAT source_idf = OpenStudio::IdfFile.load(OpenStudio::Path.new(File.dirname(__FILE__) + '/resources/TESCurves.idf')).get schedules = source_idf.getObjectsByType('Schedule:Compact'.to_IddObjectType) schedule_names = OpenStudio::StringVector.new schedules.each do |sch| schedule_names << sch.name.to_s end # make argument for TES operating mode schedule sched = OpenStudio::Measure::OSArgument.makeChoiceArgument('sched', schedule_names, true) sched.setDisplayName('Select the operating mode schedule for the new TES coils') sched.setDescription('Use the fields below to set a simple daily ice charge/discharge schedule. Or, select from pre-defined options.') sched.setDefaultValue('Simple User Sched') args << sched # make arguement for weekend TES operation wknd = OpenStudio::Measure::OSArgument.makeBoolArgument('wknd', false) wknd.setDisplayName('Run TES on the weekends') wknd.setDescription('Select if building is occupied on weekends') wknd.setDefaultValue(true) args << wknd # make arguments for operating season season = OpenStudio::Measure::OSArgument.makeStringArgument('season', false) season.setDisplayName('Select season during which the ice cooling may be used') season.setDescription('Use MM/DD-MM/DD format') season.setDefaultValue('01/01-12/31') args << season # make arguments for simple charging period charge_start = OpenStudio::Measure::OSArgument.makeStringArgument('charge_start', false) charge_start.setDisplayName('Input start time for ice charge (hr:min)') charge_start.setDescription('Use 24 hour format') charge_start.setDefaultValue('22:00') args << charge_start charge_end = OpenStudio::Measure::OSArgument.makeStringArgument('charge_end', false) charge_end.setDisplayName('Input end time for ice charge (hr:min)') charge_end.setDescription('Use 24 hour format') charge_end.setDefaultValue('07:00') args << charge_end # make arguments for simple discharging period discharge_start = OpenStudio::Measure::OSArgument.makeStringArgument('discharge_start', false) discharge_start.setDisplayName('Input start time for ice discharge (hr:min)') discharge_start.setDescription("Use 24hour format.\nIf 'AutoSize' is selected for ice capacity, these inputs set an ice capacity sizing factor. Otherwise, these only affect discharging schedule.") discharge_start.setDefaultValue('12:00') args << discharge_start discharge_end = OpenStudio::Measure::OSArgument.makeStringArgument('discharge_end', false) discharge_end.setDisplayName('Input target end time for ice discharge (hr:min)') discharge_end.setDescription('Use 24 hour format') discharge_end.setDefaultValue('18:00') args << discharge_end args # end the arguments method end |
#description ⇒ Object
human readable description
47 48 49 |
# File 'lib/measures/add_packaged_ice_storage/measure.rb', line 47 def description 'This measure removes the cooling coils in the model and replaces them with packaged air conditioning units with integrated ice storage.' end |
#modeler_description ⇒ Object
human readable description of modeling approach
52 53 54 55 56 57 58 |
# File 'lib/measures/add_packaged_ice_storage/measure.rb', line 52 def modeler_description "This measure applies to packaged single zone air conditioning systems or packaged variable air volume systems that were originally modeled with CoilSystem:Cooling:DX or AirLoopHVAC:UnitarySystem container objects. It adds a Coil:Cooling:DX:SingleSpeed:ThermalStorage coil object to each user-selected thermal zone and deletes the existing cooling coil. Users inputs are accepted for cooling coil size, ice storage size, system control method, modes of operation, and operating schedule. The measure requires schedule objects and performance curves from an included resource file TESCurves.idf. Output variables of typical interest are included as well." end |
#name ⇒ Object
human readable name
42 43 44 |
# File 'lib/measures/add_packaged_ice_storage/measure.rb', line 42 def name 'Add Distributed Ice Storage to Air Loop for Load Flexibility' end |
#run(workspace, runner, user_arguments) ⇒ Object
define what happens when the measure is run
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 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 677 678 679 680 681 682 683 684 685 686 |
# File 'lib/measures/add_packaged_ice_storage/measure.rb', line 166 def run(workspace, runner, user_arguments) super(workspace, runner, user_arguments) # use the built-in error checking unless runner.validateUserArguments(arguments(workspace), user_arguments) return false end # load required TESCurves.idf. This contains all the TES performance curves and default schedules source_idf = OpenStudio::IdfFile.load(OpenStudio::Path.new(File.dirname(__FILE__) + '/resources/TESCurves.idf')).get # workspace.addObjects(idf_obj_vector) does not work here. Add each obj individually. source_idf.objects.each do |o| workspace.addObject(o) end runner.registerInfo("#{source_idf.objects.size} performance curves, schedule objects, and output variables were imported from 'TESCurves.idf'.\n\n") # assign user arguments to variables ice_cap = runner.getStringArgumentValue('ice_cap', user_arguments) # ice capacity value (in ton-hours) size_mult = runner.getStringArgumentValue('size_mult', user_arguments) # size multiplier for ice tank capacity - use if autosize is excessively oversizing ctl = runner.getStringArgumentValue('ctl', user_arguments) # control method (schedule or EMS) sched = runner.getStringArgumentValue('sched', user_arguments) # select operating mode schedule (schedule objects located in resources\TESCurves.idf) wknd = runner.getBoolArgumentValue('wknd', user_arguments) # turn tes on/off for weekend operation season = runner.getStringArgumentValue('season', user_arguments) # set operating season for Simple User Sched charge_start = runner.getStringArgumentValue('charge_start', user_arguments) # time ice charging begins charge_end = runner.getStringArgumentValue('charge_end', user_arguments) # time ice charging ends discharge_start = runner.getStringArgumentValue('discharge_start', user_arguments) # time ice discharge begins discharge_end = runner.getStringArgumentValue('discharge_end', user_arguments) # time ice discharge ends # retrieve user selected coils and assign to vector coils = workspace.getObjectsByType('Coil:Cooling:DX:SingleSpeed'.to_IddObjectType) coils += workspace.getObjectsByType('Coil:Cooling:DX:TwoSpeed'.to_IddObjectType) coilhash = {} c = coils.sort { |a, b| a.getString(0).get <=> b.getString(0).get } c.each do |coil| c_name = coil.name.to_s coilhash[c_name] = true end coil_selection = [] coilhash.each do |k, v| temp_var = runner.getBoolArgumentValue(k, user_arguments) coil_selection << k if temp_var end # create other useful variables replacement_count = 0 # tracks number of coils replaced by measure time_size_factor = '' # sets Storage Capacity Sizing Factor {hr} discharge_cop = '63.6' # default COP for Ice Discharge curve_d_shr_ft = 'Discharge-SHR-fT-NREL' # default curve for sensible heat ratio f(T) during ice discharge # convert string time values into floats for math comparisons # ds/de = discharge start/end, cs/ce = charge start/end ds = discharge_start.split(':')[0].to_f + (discharge_start.split(':')[1].to_f / 0.6) de = discharge_end.split(':')[0].to_f + (discharge_end.split(':')[1].to_f / 0.6) cs = charge_start.split(':')[0].to_f + (charge_start.split(':')[1].to_f / 0.6) ce = charge_end.split(':')[0].to_f + (charge_end.split(':')[1].to_f / 0.6) # #Check User Inputs and Define Variables hardcaps = [] if ice_cap != 'AutoSize' if ice_cap == '' runner.registerWarning("No ice capacity was entered for 'User Input' selection, 'AutoSize' was used instead.") ice_cap = 'AutoSize' elsif ice_cap.split(',').size > 1 runner.registerInfo('Ice storage tanks will be hardsized based on user inputs, assigned alphabetically.') ice_cap.split(',').each { |i| hardcaps.push((i.to_f * 0.0126608).to_s) } while hardcaps.size != coil_selection.size runner.registerInfo("No user-defined thermal storage capacity for #{coil_selection[hardcaps.size]}; unit will be AutoSized.") hardcaps.push('AutoSize') end else ice_cap = (ice_cap.to_f * 0.0126608).to_s # convert units from ton-hours to GJ end elsif sched == 'Simple User Sched' time_size_factor = ((de - ds) * size_mult.to_f).to_s elsif sched == 'TES Sched 2: 1-5 Peak' time_size_factor = (4.0 * size_mult.to_f).to_s elsif sched == 'TES Sched 3: 3-8 Peak' time_size_factor = (5.0 * size_mult.to_f).to_s elsif sched == 'TES Sched 4: GSS-T' time_size_factor = (3.0 * size_mult.to_f).to_s else time_size_factor = (4.0 * size_mult.to_f).to_s # sets default time size factor to 4 hours end # Check user schedule inputs and build schedule if sched == 'Simple User Sched' # find empty user input schedule object from TESCurves.idf import user_schedules = workspace.getObjectsByName('Simple User Sched') user_schedule = user_schedules[0] # check ice discharge times to ensure end occurs after start. Exit gracefully if it doesn't. if de < ds runner.registerError('Ice discharge end time occurs before the start time. If ice discharge is desired overnight, create a schedule object in ../resources/TESCurves.idf. Measure was not applied.') return false end # sets charge and discharge mode ** May be modified if Cool_Charge or Cool_Discharge modes become available charge_mode = 4 discharge_mode = 5 # format user input for cooling season values czn = season.split(/[\s-]/) cool_start = czn[0].to_s cool_end = czn[-1].to_s # set cooling season start periods a = 4 # index variable to ensure schedule is built properly under various conditions c = 3 # index variable to help ensure a weekday-only schedule is properly built if cool_start != '01/01' user_schedule.setString(2, "Through: #{cool_start}") user_schedule.setString(3, 'For: AllDays') user_schedule.setString(4, 'Until: 24:00') user_schedule.setString(5, '1') user_schedule.setString(7, 'For: AllDays') a = 8 c = 7 end # build user defined schedule object if cs > ce # assign times to schedule fields user_schedule.setString(a, "Until: #{charge_end}") user_schedule.setString(a + 1, charge_mode.to_s) user_schedule.setString(a + 2, "Until: #{discharge_start}") user_schedule.setString(a + 3, '1') user_schedule.setString(a + 4, "Until: #{discharge_end}") user_schedule.setString(a + 5, discharge_mode.to_s) user_schedule.setString(a + 6, "Until: #{charge_start}") user_schedule.setString(a + 7, '1') user_schedule.setString(a + 8, 'Until: 24:00') user_schedule.setString(a + 9, charge_mode.to_s) b = a + 10 elsif charge_start != '00:00' user_schedule.setString(a, "Until: #{charge_end}") user_schedule.setString(a + 1, charge_mode.to_s) user_schedule.setString(a + 2, "Until: #{discharge_start}") user_schedule.setString(a + 3, '1') user_schedule.setString(a + 4, "Until: #{discharge_end}") user_schedule.setString(a + 5, discharge_mode.to_s) user_schedule.setString(a + 6, 'Until: 24:00') user_schedule.setString(a + 7, '1') b = a + 8 else user_schedule.setString(a, "Until: #{charge_end}") user_schedule.setString(a + 1, charge_mode.to_s) user_schedule.setString(a + 2, "Until: #{discharge_start}") user_schedule.setString(a + 3, '1') user_schedule.setString(a + 4, "Until: #{discharge_end}") user_schedule.setString(a + 5, discharge_mode.to_s) user_schedule.setString(a + 6, 'Until: 24:00') user_schedule.setString(a + 7, '1') b = a + 8 end # make weekend modification if necessary unless wknd user_schedule.setString(c, 'For: WeekDays') user_schedule.setString(b, 'For: Weekends') user_schedule.setString(b + 1, "Until: #{charge_end}") user_schedule.setString(b + 2, charge_mode.to_s) user_schedule.setString(b + 3, 'Until: 24:00') user_schedule.setString(b + 4, '1') b += 5 end # complete cooling season schedule if not through 12/31 if cool_end != '12/31' user_schedule.setString(c - 1, "Through: #{cool_end}") user_schedule.setString(b, 'Through: 12/31') user_schedule.setString(b + 1, 'For: AllDays') user_schedule.setString(b + 2, 'Until: 24:00') user_schedule.setString(b + 3, '1') end end # find objects of interest in the model (used to identify container objects, air loops, and thermal zones) cooling_coil_systems = workspace.getObjectsByType('CoilSystem:Cooling:DX'.to_IddObjectType) air_loops = workspace.getObjectsByType('AirLoopHVAC'.to_IddObjectType) branches = workspace.getObjectsByType('Branch'.to_IddObjectType) hvac_zone_mixers = workspace.getObjectsByType('AirLoopHVAC:ZoneMixer'.to_IddObjectType) zone_connections = workspace.getObjectsByType('ZoneHVAC:EquipmentConnections'.to_IddObjectType) unitary_generic_obj = workspace.getObjectsByType('AirLoopHVAC:UnitarySystem'.to_IddObjectType) node_lists = workspace.getObjectsByType('NodeList'.to_IddObjectType) # create vector of all cooling system container objects cooling_containers = OpenStudio::IdfObjectVector.new cooling_coil_systems.each do |coil_sys| if coil_selection.include?(coil_sys.getString(6).to_s) cooling_containers << coil_sys end end unitary_generic_obj.each do |unitary_sys| if coil_selection.include?(unitary_sys.getString(15).to_s) cooling_containers << unitary_sys end end # exit gracefully if original model does not have coilSystem:Cooling objects if cooling_containers.empty? runner.registerError('This measure only operates on the following EnergyPlus container objects: CoilSystem:Cooling:DX and AirLoopHVAC:UnitarySystem. Measure was not applied.') end # create TES object string template for use in replacing existing coils; incorporates user input variables new_tes_string = "Coil:Cooling:DX:SingleSpeed:ThermalStorage, NAME PLACEHOLDER, !- Name ALWAYS_ON, !- Availability Schedule Name #{ctl}, !- Operating Mode Control Method #{sched}, !- Operation Mode Control Schedule Name Ice, !- Storage Type , !- User Defined Fluid Type , !- Fluid Storage Volume {m3} AutoSize, !- Ice Storage Capacity {GJ} #{time_size_factor}, !- Storage Capacity Sizing Factor {hr} AMBIENT NODE, !- Storage Tank Ambient Temperature Node Name 7.913, !- Storage Tank to Ambient U-value Times Area Heat Transfer Coefficient {W/K} , !- Fluid Storage Tank Rating Temperature {C} AutoSize, !- Rated Evaporator Air Flow Rate {m3/s} EVAP IN NODE, !- Evaporator Air Inlet Node Name EVAP OUT NODE, !- Evaporator Air Outlet Node Name Yes, !- Cooling Only Mode Available AutoSize, !- Cooling Only Mode Rated Total Evaporator Cooling Capacity {W} **IB40 Limits: 10551 W (3 ton) to 70337 W (20 ton)** 0.7, !- Cooling Only Mode Rated Sensible Heat Ratio 3.23372055845678, !- Cooling Only Mode Rated COP {W/W} Cool-Cap-fT, !- Cooling Only Mode Total Evaporator Cooling Capacity Function of Temperature Curve Name ConstantCubic, !- Cooling Only Mode Total Evaporator Cooling Capacity Function of Flow Fraction Curve Name Cool-EIR-fT, !- Cooling Only Mode Energy Input Ratio Function of Temperature Curve Name ConstantCubic, !- Cooling Only Mode Energy Input Ratio Function of Flow Fraction Curve Name Cool-PLF-fPLR, !- Cooling Only Mode Part Load Fraction Correlation Curve Name Cool-SHR-fT, !- Cooling Only Mode Sensible Heat Ratio Function of Temperature Curve Name Cool-SHR-fFF, !- Cooling Only Mode Sensible Heat Ratio Function of Flow Fraction Curve Name No, !- Cooling And Charge Mode Available AutoSize, !- Cooling And Charge Mode Rated Total Evaporator Cooling Capacity 1.0, !- Cooling And Charge Mode Capacity Sizing Factor AutoSize, !- Cooling And Charge Mode Rated Storage Charging Capacity 0.86, !- Cooling And Charge Mode Storage Capacity Sizing Factor 0.7, !- Cooling And Charge Mode Rated Sensible Heat Ratio 3.66668443E+00, !- Cooling And Charge Mode Cooling Rated COP 2.17, !- Cooling And Charge Mode Charging Rated COP CoolCharge-Cool-Cap-fT, !- Cooling And Charge Mode Total Evaporator Cooling Capacity Function of Temperature Curve Name ConstantCubic, !- Cooling And Charge Mode Total Evaporator Cooling Capacity Function of Flow Fraction Curve Name CoolCharge-Cool-EIR-fT, !- Cooling And Charge Mode Evaporator Energy Input Ratio Function of Temperature Curve Name ConstantCubic, !- Cooling And Charge Mode Evaporator Energy Input Ratio Function of Flow Fraction Curve Name Cool-PLF-fPLR, !- Cooling And Charge Mode Evaporator Part Load Fraction Correlation Curve Name CoolCharge-Charge-Cap-fT,!- Cooling And Charge Mode Storage Charge Capacity Function of Temperature Curve Name ConstantCubic, !- Cooling And Charge Mode Storage Charge Capacity Function of Total Evaporator PLR Curve Name CoolCharge-Charge-EIR-fT,!- Cooling And Charge Mode Storage Energy Input Ratio Function of Temperature Curve Name ConstantCubic, !- Cooling And Charge Mode Storage Energy Input Ratio Function of Flow Fraction Curve Name ConstantCubic, !- Cooling And Charge Mode Storage Energy Part Load Fraction Correlation Curve Name Cool-SHR-fT, !- Cooling And Charge Mode Sensible Heat Ratio Function of Temperature Curve Name Cool-SHR-fFF, !- Cooling And Charge Mode Sensible Heat Ratio Function of Flow Fraction Curve Name No, !- Cooling And Discharge Mode Available , !- Cooling And Discharge Mode Rated Total Evaporator Cooling Capacity {W} , !- Cooling And Discharge Mode Evaporator Capacity Sizing Factor , !- Cooling And Discharge Mode Rated Storage Discharging Capacity {W} , !- Cooling And Discharge Mode Storage Discharge Capacity Sizing Factor , !- Cooling And Discharge Mode Rated Sensible Heat Ratio , !- Cooling And Discharge Mode Cooling Rated COP {W/W} , !- Cooling And Discharge Mode Discharging Rated COP {W/W} , !- Cooling And Discharge Mode Total Evaporator Cooling Capacity Function of Temperature Curve Name , !- Cooling And Discharge Mode Total Evaporator Cooling Capacity Function of Flow Fraction Curve Name , !- Cooling And Discharge Mode Evaporator Energy Input Ratio Function of Temperature Curve Name , !- Cooling And Discharge Mode Evaporator Energy Input Ratio Function of Flow Fraction Curve Name , !- Cooling And Discharge Mode Evaporator Part Load Fraction Correlation Curve Name , !- Cooling And Discharge Mode Storage Discharge Capacity Function of Temperature Curve Name , !- Cooling And Discharge Mode Storage Discharge Capacity Function of Flow Fraction Curve Name , !- Cooling And Discharge Mode Storage Discharge Capacity Function of Total Evaporator PLR Curve Name , !- Cooling And Discharge Mode Storage Energy Input Ratio Function of Temperature Curve Name , !- Cooling And Discharge Mode Storage Energy Input Ratio Function of Flow Fraction Curve Name , !- Cooling And Discharge Mode Storage Energy Part Load Fraction Correlation Curve Name , !- Cooling And Discharge Mode Sensible Heat Ratio Function of Temperature Curve Name , !- Cooling And Discharge Mode Sensible Heat Ratio Function of Flow Fraction Curve Name Yes, !- Charge Only Mode Available AutoSize, !- Charge Only Mode Rated Storage Charging Capacity {W} 0.8, !- Charge Only Mode Capacity Sizing Factor 3.09, !- Charge Only Mode Charging Rated COP {W/W} ChargeOnly-Cap-fT, !- Charge Only Mode Storage Charge Capacity Function of Temperature Curve Name ChargeOnly-EIR-fT, !- Charge Only Mode Storage Energy Input Ratio Function of Temperature Curve Name Yes, !- Discharge Only Mode Available AutoSize, !- Discharge Only Mode Rated Storage Discharging Capacity {W} 1.37, !- Discharge Only Mode Capacity Sizing Factor 0.64, !- Discharge Only Mode Rated Sensible Heat Ratio #{discharge_cop}, !- Discharge Only Mode Rated COP {W/W} Discharge-Cap-fT, !- Discharge Only Mode Storage Discharge Capacity Function of Temperature Curve Name Discharge-Cap-fFF, !- Discharge Only Mode Storage Discharge Capacity Function of Flow Fraction Curve Name ConstantBi, !- Discharge Only Mode Energy Input Ratio Function of Temperature Curve Name ConstantCubic, !- Discharge Only Mode Energy Input Ratio Function of Flow Fraction Curve Name ConstantCubic, !- Discharge Only Mode Part Load Fraction Correlation Curve Name #{curve_d_shr_ft}, !- Discharge Only Mode Sensible Heat Ratio Function of Temperature Curve Name Discharge-SHR-fFF, !- Discharge Only Mode Sensible Heat Ratio Function of Flow Fraction Curve Name 0.0, !- Ancillary Electric Power {W} 2.0, !- Cold Weather Operation Minimum Outdoor Air Temperature {C} 0.0, !- Cold Weather Operation Ancillary Power {W} CONDENSER INLET NODE, !- Condenser Air Inlet Node Name CONDENSER OUTLET NODE, !- Condenser Air Outlet Node Name autocalculate, !- Condenser Design Air Flow Rate {m3/s} 1.25, !- Condenser Air Flow Sizing Factor AirCooled, !- Condenser Type , !- Evaporative Condenser Effectiveness {dimensionless} , !- Evaporative Condenser Pump Rated Power Consumption {W} , !- Basin Heater Capacity {W/K} , !- Basin Heater Setpoint Temperature {C} , !- Basin Heater Availability Schedule Name , !- Supply Water Storage Tank Name , !- Condensate Collection Water Storage Tank Name , !- Storage Tank Plant Connection Inlet Node Name , !- Storage Tank Plant Connection Outlet Node Name , !- Storage Tank Plant Connection Design Flow Rate {m3/s} , !- Storage Tank Plant Connection Heat Transfer Effectiveness , !- Storage Tank Minimum Operating Limit Fluid Temperature {C} ; !- Storage Tank Maximum Operating Limit Fluid Temperature {C}" # end of new TES coil string # #Begin Coil Replacement # iterate through all CoilSystem:Cooling objects and replace existing coils with TES coils coil_selection.each do |sel_coil| # get workspace object for selected coil from name sel_coil = workspace.getObjectsByName(sel_coil)[0] ice_cap = hardcaps[replacement_count] unless hardcaps.empty? # get coil type in order to find get appropriate field keys # fields of interest: (0 - Max Cap, 1 - Rated COP, 2 - Inlet Node, 3 - Outlet Node) field_names = [] # may not be required. Check scope in Ruby documentation sel_type = sel_coil.iddObject.type.valueDescription.to_s if sel_type == 'Coil:Cooling:DX:SingleSpeed' field_names = ['Gross Rated Total Cooling Capacity', 'Gross Rated Cooling COP', 'Air Inlet Node Name', 'Air Outlet Node Name'] elsif sel_type == 'Coil:Cooling:DX:TwoSpeed' field_names = ['High Speed Gross Rated Total Cooling Capacity', 'High Speed Gross Rated Cooling COP', 'Air Inlet Node Name', 'Air Outlet Node Name'] end # get field indices associated with desired keys keys = [] field_names.each do |fn| keys << sel_coil.iddObject.getFieldIndex(fn).to_i end # get old coil name and create new coil name old_coil_name = sel_coil.getString(0).to_s utss_name = "UTSS Coil #{replacement_count}" # grab inlet and outlet air nodes form selected coil inlet = sel_coil.getString(keys[2]).to_s outlet = sel_coil.getString(keys[3]).to_s # update inlet and outlet node names - not needed possibly add later if names become confusing # create local ambient node idf_oa_ambient = OpenStudio::IdfObject.new('OutdoorAir:Node'.to_IddObjectType) ws_oa_ambient = workspace.addObject(idf_oa_ambient) oa_ambient = ws_oa_ambient.get oa_ambient.setString(0, "#{utss_name} OA Ambient Node") # create new condenser inlet node idf_condenser_inlet = OpenStudio::IdfObject.new('OutdoorAir:Node'.to_IddObjectType) ws_condenser_inlet = workspace.addObject(idf_condenser_inlet) condenser_inlet = ws_condenser_inlet.get condenser_inlet.setString(0, "#{utss_name} Condenser Inlet Node") # create new condenser inlet node idf_condenser_outlet = OpenStudio::IdfObject.new('OutdoorAir:Node'.to_IddObjectType) ws_condenser_outlet = workspace.addObject(idf_condenser_outlet) condenser_outlet = ws_condenser_outlet.get condenser_outlet.setString(0, "#{utss_name} Condenser Out Node") # create a new UTSS object idf_coil_object = OpenStudio::IdfObject.load(new_tes_string) utss_obj = idf_coil_object.get ws_tes_obj = workspace.addObject(utss_obj) utss = ws_tes_obj.get utss.setString(0, utss_name) # get indices for required utss fields - reused variable names, consider changing if confusing field_names = ['Evaporator Air Inlet Node Name', 'Evaporator Air Outlet Node Name', 'Storage Tank Ambient Temperature Node Name', 'Condenser Air Inlet Node Name', 'Condenser Air Outlet Node Name'] keys = [] field_names.each do |fn| keys << utss.iddObject.getFieldIndex(fn).to_i end # updated required fields in utss object utss.setString(keys[0], inlet) # air inlet node utss.setString(keys[1], outlet) # air outlet node utss.setString(keys[2], oa_ambient.name.to_s) # outdoor ambient node utss.setString(keys[3], condenser_inlet.name.to_s) # condenser inlet node utss.setString(keys[4], condenser_outlet.name.to_s) # condenser outlet node utss.setString(7, ice_cap) # hardsized thermal storage capacity # copy old coil information over to TES object (use low-speed info for 2spd coils) if sel_coil.iddObject.name == 'Coil:Cooling:DX:SingleSpeed' utss.setString(16, sel_coil.getString(2).get) utss.setString(18, sel_coil.getString(4).get) utss.setString(19, sel_coil.getString(9).get) utss.setString(20, sel_coil.getString(10).get) utss.setString(21, sel_coil.getString(11).get) utss.setString(22, sel_coil.getString(12).get) utss.setString(23, sel_coil.getString(13).get) elsif sel_coil.iddObject.name == 'Coil:Cooling:DX:TwoSpeed' utss.setString(16, sel_coil.getString(14).get) utss.setString(18, sel_coil.getString(16).get) utss.setString(19, sel_coil.getString(18).get) utss.setString(20, sel_coil.getString(10).get) utss.setString(21, sel_coil.getString(19).get) utss.setString(22, sel_coil.getString(12).get) utss.setString(23, sel_coil.getString(13).get) end # identify container object in which the coil is used cooling_containers.each do |cont| if cont.iddObject.type.valueDescription.to_s == 'CoilSystem:Cooling:DX' if cont.getString(6).to_s == old_coil_name cont.setString(5, 'Coil:Cooling:DX:SingleSpeed:ThermalStorage') cont.setString(6, utss_name) break end elsif cont.iddObject.type.valueDescription.to_s == 'AirLoopHVAC:UnitarySystem' if cont.getString(15).to_s == old_coil_name cont.setString(14, 'Coil:Cooling:DX:SingleSpeed:ThermalStorage') cont.setString(15, utss_name) break end end end # remove old coil workspace.removeObject(sel_coil.handle) # increment replacement count replacement_count += 1 ## Add EMS Controller Components # create EMS intended schedule sensor once if replacement_count == 1 idf_sched_sensor = OpenStudio::IdfObject.new('EnergyManagementSystem:Sensor'.to_IddObjectType) ws_sched_sensor = workspace.addObject(idf_sched_sensor) new_sched_sensor = ws_sched_sensor.get new_sched_sensor.setString(0, 'TESIntendedSchedule') new_sched_sensor.setString(1, sched) new_sched_sensor.setString(2, 'Schedule Value') end # clean-up variable names for EMS purposes (no spaces allowed) u_name = utss_name.gsub(/\s/, '_') # add EMS sensor for TES control idf_sensor = OpenStudio::IdfObject.new('EnergyManagementSystem:Sensor'.to_IddObjectType) ws_sensor = workspace.addObject(idf_sensor) new_sensor = ws_sensor.get new_sensor.setString(0, "#{u_name}_sTES") new_sensor.setString(1, utss_name) new_sensor.setString(2, 'Cooling Coil Ice Thermal Storage End Fraction') # add EMS actuator for TES control idf_actuator = OpenStudio::IdfObject.new('EnergyManagementSystem:Actuator'.to_IddObjectType) ws_actuator = workspace.addObject(idf_actuator) new_actuator = ws_actuator.get new_actuator.setString(0, "#{u_name}_OpMode") new_actuator.setString(1, utss_name) new_actuator.setString(2, 'Coil:Cooling:DX:SingleSpeed:ThermalStorage') new_actuator.setString(3, 'Operating Mode') # add Global Variable to track min SOC from previous use idf_gvar = OpenStudio::IdfObject.new('EnergyManagementSystem:GlobalVariable'.to_IddObjectType) ws_gvar = workspace.addObject(idf_gvar) new_gvar = ws_gvar.get new_gvar.setString(0, "#{u_name}_MinSOC") # add EMS program for TES control program_string = " EnergyManagementSystem:Program, #{u_name}_Control, !- Name SET #{u_name}_OpMode = TESIntendedSchedule, IF CurrentEnvironment == 1, SET #{u_name}_MinSOC = 1, ENDIF, IF (#{u_name}_OpMode == 5), IF ( #{u_name}_sTES < 0.05 ), SET #{u_name}_OpMode = 1, ENDIF, SET #{u_name}_MinSOC = #{u_name}_sTES, ENDIF, IF (#{u_name}_OpMode == 4), IF ( #{u_name}_sTES > 0.99 ), SET #{u_name}_OpMode = 1, ENDIF, ENDIF;" idf_program = OpenStudio::IdfObject.load(program_string) idf_prgm = idf_program.get workspace.addObject(idf_prgm) # add EMS program calling manager for TES control idf_pcm = OpenStudio::IdfObject.new('EnergyManagementSystem:ProgramCallingManager'.to_IddObjectType) ws_pcm = workspace.addObject(idf_pcm) new_pcm = ws_pcm.get new_pcm.setString(0, "#{u_name}_TES_PrgmCallMgr") new_pcm.setString(1, 'AfterPredictorAfterHVACManagers') new_pcm.setString(2, "#{u_name}_Control") # register info # Coil replaced, Coil Added, EMS Program Added runner.registerInfo("Coil '#{old_coil_name}' was replaced with a unitary thermal storage system named" \ "'#{utss.name}' with a capacity of #{ice_cap} GJ.\n") # end of coil replacement routine end # additional output for schedule verification runner.registerInfo("The user-selected schedule for the ice unit operation is:\n\n#{user_schedule}") # register initial and final conditions runner.registerInitialCondition("The building started with #{cooling_containers.size} cooling coils.") runner.registerFinalCondition("A total of #{replacement_count} cooling coils were replaced with thermal storage coil systems.") true end |