openstudio-ee 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/Rakefile +2 -0
  4. data/lib/measures/ImproveFanTotalEfficiencybyPercentage/measure.rb +333 -0
  5. data/lib/measures/ImproveFanTotalEfficiencybyPercentage/measure.xml +150 -0
  6. data/lib/measures/ReplaceFanTotalEfficiency/measure.rb +330 -0
  7. data/lib/measures/ReplaceFanTotalEfficiency/measure.xml +150 -0
  8. data/lib/measures/add_apszhp_to_each_zone/measure.rb +607 -0
  9. data/lib/measures/add_apszhp_to_each_zone/measure.xml +184 -0
  10. data/lib/measures/add_energy_recovery_ventilator/measure.rb +354 -0
  11. data/lib/measures/add_energy_recovery_ventilator/measure.xml +78 -0
  12. data/lib/measures/improve_simple_glazing_by_percentage/measure.rb +81 -0
  13. data/lib/measures/improve_simple_glazing_by_percentage/measure.xml +70 -0
  14. data/lib/measures/reduce_water_use_by_percentage/measure.rb +61 -0
  15. data/lib/measures/reduce_water_use_by_percentage/measure.xml +62 -0
  16. data/lib/measures/replace_hvac_with_gshp_and_doas/measure.rb +511 -0
  17. data/lib/measures/replace_hvac_with_gshp_and_doas/measure.xml +375 -0
  18. data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_AedgMeasures.rb +454 -0
  19. data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_Constructions.rb +221 -0
  20. data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_Geometry.rb +41 -0
  21. data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_HVAC.rb +1682 -0
  22. data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_HelperMethods.rb +114 -0
  23. data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_LightingAndEquipment.rb +99 -0
  24. data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_Schedules.rb +142 -0
  25. data/lib/measures/replace_simple_glazing/measure.rb +86 -0
  26. data/lib/measures/replace_simple_glazing/measure.xml +78 -0
  27. data/lib/measures/set_boiler_thermal_efficiency/measure.rb +520 -0
  28. data/lib/measures/set_boiler_thermal_efficiency/measure.xml +78 -0
  29. data/lib/measures/set_water_heater_efficiency_heat_lossand_peak_water_flow_rate/measure.rb +207 -0
  30. data/lib/measures/set_water_heater_efficiency_heat_lossand_peak_water_flow_rate/measure.xml +78 -0
  31. data/lib/measures/tenant_star_internal_loads/measure.rb +134 -0
  32. data/lib/measures/tenant_star_internal_loads/measure.xml +67 -0
  33. data/lib/measures/tenant_star_internal_loads/resources/os_lib_helper_methods.rb +401 -0
  34. data/lib/measures/vr_fwith_doas/measure.rb +468 -0
  35. data/lib/measures/vr_fwith_doas/measure.xml +298 -0
  36. data/lib/measures/vr_fwith_doas/resources/OsLib_AedgMeasures.rb +454 -0
  37. data/lib/measures/vr_fwith_doas/resources/OsLib_Constructions.rb +221 -0
  38. data/lib/measures/vr_fwith_doas/resources/OsLib_Geometry.rb +41 -0
  39. data/lib/measures/vr_fwith_doas/resources/OsLib_HVAC.rb +1516 -0
  40. data/lib/measures/vr_fwith_doas/resources/OsLib_HelperMethods.rb +114 -0
  41. data/lib/measures/vr_fwith_doas/resources/OsLib_LightingAndEquipment.rb +99 -0
  42. data/lib/measures/vr_fwith_doas/resources/OsLib_Schedules.rb +142 -0
  43. data/lib/openstudio/ee_measures/version.rb +1 -1
  44. data/openstudio-ee.gemspec +7 -5
  45. metadata +48 -9
@@ -0,0 +1,607 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Author: Julien Marrec
4
+ # email: julien.marrec@gmail.com
5
+
6
+ # start the measure
7
+ class AddAPSZHPToEachZone < OpenStudio::Ruleset::ModelUserScript
8
+ # define the name that a user will see, this method may be deprecated as
9
+ # the display name in PAT comes from the name field in measure.xml
10
+ def name
11
+ return 'Add a PSZ-HP to each zone'
12
+ end
13
+
14
+ def description
15
+ return 'This will add a Rooftop Packaged Single Zone Heat Pump (RTU with DX cooling and DX heating coils) to each zone of the model.'
16
+ end
17
+
18
+ def modeler_description
19
+ return "Add a System 4 - PSZ-HP - unit for each zone. This is a single zone system.
20
+ Parameters:
21
+ - Double: COP cooling and COP heating (Double)
22
+ - Boolean: supplementary electric heating coil (Boolean)
23
+ - Pressure rise (Optional Double)
24
+ - Deletion of existing HVAC equipment (Boolean)
25
+ - DCV enabled or not (Boolean)
26
+ - Fan type: Variable Volume Fan (VFD) or not (Constant Volume) (Choice)
27
+ - Filter for the zone name (String): only zones that contains the string you input in filter will receive this system."
28
+ end
29
+
30
+ # define the arguments that the user will input
31
+ def arguments(model)
32
+ args = OpenStudio::Ruleset::OSArgumentVector.new
33
+
34
+ delete_existing = OpenStudio::Ruleset::OSArgument.makeBoolArgument('delete_existing', true)
35
+ delete_existing.setDisplayName('Delete any existing HVAC equipment?')
36
+ args << delete_existing
37
+
38
+ cop_cooling = OpenStudio::Ruleset::OSArgument.makeDoubleArgument('cop_cooling', true)
39
+ cop_cooling.setDisplayName('COP Cooling (SI)')
40
+ cop_cooling.setDefaultValue(3.1)
41
+ args << cop_cooling
42
+
43
+ cop_heating = OpenStudio::Ruleset::OSArgument.makeDoubleArgument('cop_heating', true)
44
+ cop_heating.setDisplayName('COP Heating (SI)')
45
+ cop_heating.setDefaultValue(3.1)
46
+ args << cop_heating
47
+
48
+ has_electric_coil = OpenStudio::Ruleset::OSArgument.makeBoolArgument('has_electric_coil', false)
49
+ has_electric_coil.setDisplayName('Include supplementary electric heating coils?')
50
+ has_electric_coil.setDefaultValue(true)
51
+ args << has_electric_coil
52
+
53
+ has_dcv = OpenStudio::Ruleset::OSArgument.makeBoolArgument('has_dcv', false)
54
+ has_dcv.setDisplayName('Enable Demand Controlled Ventilation?')
55
+ has_dcv.setDefaultValue(false)
56
+ args << has_dcv
57
+
58
+ chs = OpenStudio::StringVector.new
59
+ chs << 'Constant Volume (default)'
60
+ chs << 'Variable Volume (VFD)'
61
+ fan_type = OpenStudio::Ruleset::OSArgument.makeChoiceArgument('fan_type', chs, true)
62
+ fan_type.setDisplayName('Select fan type:')
63
+ args << fan_type
64
+
65
+ fan_pressure_rise = OpenStudio::Ruleset::OSArgument.makeDoubleArgument('fan_pressure_rise', false)
66
+ fan_pressure_rise.setDisplayName('Fan Pressure Rise (Pa)')
67
+ fan_pressure_rise.setDescription('Leave blank for default value')
68
+ # fan_pressure_rise.setDefaultValue(0)
69
+ args << fan_pressure_rise
70
+
71
+ chs = OpenStudio::StringVector.new
72
+ chs << 'By Space Type'
73
+ chs << "By Space Type's 'Standards Space Type'"
74
+ chs << 'By Zone Filter'
75
+ filter_type = OpenStudio::Ruleset::OSArgument.makeChoiceArgument('filter_type', chs, true)
76
+ filter_type.setDisplayName('How do you want to choose the affected zones?')
77
+ args << filter_type
78
+
79
+ # create an argument for a space type to be used in the model. Only return those that are used
80
+ spaceTypes = model.getSpaceTypes
81
+ usedSpaceTypes_handle = OpenStudio::StringVector.new
82
+ usedSpaceTypes_displayName = OpenStudio::StringVector.new
83
+
84
+ # Should normally be an OpenStudio::StringVector.new but it doesn't have a uniq! method and it works with a regular hash..
85
+ standardsSpaceType = []
86
+
87
+ spaceTypes.each do |spaceType|
88
+ if !spaceType.spaces.empty? # only show space types used in the building
89
+ usedSpaceTypes_handle << spaceType.handle.to_s
90
+ usedSpaceTypes_displayName << spaceType.name.to_s
91
+
92
+ if !spaceType.standardsSpaceType.empty?
93
+ standardsSpaceType << spaceType.standardsSpaceType.get
94
+ end
95
+ end
96
+ end
97
+
98
+ # make an argument for space type
99
+ space_type = OpenStudio::Ruleset::OSArgument.makeChoiceArgument('space_type', usedSpaceTypes_handle, usedSpaceTypes_displayName, false)
100
+ space_type.setDisplayName('a. Which Space Type?')
101
+ args << space_type
102
+
103
+ # Argument for Standards Space Type
104
+
105
+ # First, make it unique
106
+ standardsSpaceType.uniq!
107
+ standards_space_type = OpenStudio::Ruleset::OSArgument.makeChoiceArgument('standards_space_type', standardsSpaceType, false)
108
+ standards_space_type.setDisplayName('b. Which Standards Space Type')
109
+ args << standards_space_type
110
+
111
+ zone_filter = OpenStudio::Ruleset::OSArgument.makeStringArgument('zone_filter', false)
112
+ zone_filter.setDisplayName('c. Only Apply to Zones that contain the following string')
113
+ zone_filter.setDescription("Case insensitive. For example, type 'retail' to apply to zones that have the word 'retail' or 'REtaiL' in their name. Leave blank to apply to all zones")
114
+ args << zone_filter
115
+
116
+ return args
117
+ end # end the arguments method
118
+
119
+ # define what happens when the measure is run
120
+ def run(model, runner, user_arguments)
121
+ super(model, runner, user_arguments)
122
+
123
+ # use the built-in error checking
124
+ if !runner.validateUserArguments(arguments(model), user_arguments)
125
+ return false
126
+ end
127
+
128
+ # Retrieve arguments' values
129
+ delete_existing = runner.getBoolArgumentValue('delete_existing', user_arguments)
130
+ cop_cooling = runner.getDoubleArgumentValue('cop_cooling', user_arguments)
131
+ cop_heating = runner.getDoubleArgumentValue('cop_heating', user_arguments)
132
+ has_electric_coil = runner.getBoolArgumentValue('has_electric_coil', user_arguments)
133
+ has_dcv = runner.getBoolArgumentValue('has_dcv', user_arguments)
134
+
135
+ # Get fan_pressure_rise: this is an OptionalDouble - we'll use '.get' later
136
+ fan_pressure_rise = runner.getOptionalDoubleArgumentValue('fan_pressure_rise', user_arguments)
137
+
138
+ # FanType
139
+ fan_type = runner.getStringArgumentValue('fan_type', user_arguments)
140
+ runner.registerInfo("Fan type: #{fan_type}")
141
+
142
+ if fan_type == 'Variable Volume (VFD)'
143
+ has_vfd = true
144
+ else
145
+ has_vfd = false
146
+ end
147
+
148
+ filter_type = runner.getStringArgumentValue('filter_type', user_arguments)
149
+
150
+ if filter_type == 'By Space Type'
151
+ space_type = runner.getOptionalWorkspaceObjectChoiceValue('space_type', user_arguments, model)
152
+ if !space_type.empty?
153
+ space_type = space_type.get
154
+ if !space_type.to_SpaceType.empty?
155
+ space_type = space_type.to_SpaceType.get
156
+ zones = []
157
+ space_type.spaces.each do |space|
158
+ if !space.thermalZone.empty?
159
+ z = space.thermalZone.get
160
+ zones << z
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ elsif filter_type == "By Space Type's 'Standards Space Type'"
167
+
168
+ standards_space_type = runner.getOptionalStringArgumentValue('standards_space_type', user_arguments)
169
+ puts standards_space_type.class
170
+
171
+ if !standards_space_type.empty?
172
+ standards_space_type = standards_space_type.get
173
+ puts standards_space_type
174
+ space_types = model.getSpaceTypes
175
+
176
+ zones = []
177
+
178
+ space_types.each do |space_type|
179
+ if space_type.standardsSpaceType.to_s.casecmp(standards_space_type).zero?
180
+ space_type.spaces.each do |space|
181
+ if !space.thermalZone.empty?
182
+ z = space.thermalZone.get
183
+ # We MUST check if zone isn't in there yet (or at the end do zones.uniq!) because several spaces can refer to the same thermal zone!
184
+ if !zones.include?(z)
185
+ zones << z
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ else
194
+ # Zone filter
195
+ zone_filter = runner.getOptionalStringArgumentValue('zone_filter', user_arguments)
196
+
197
+ # Get all thermal zones
198
+ all_zones = model.getThermalZones
199
+
200
+ # Array to store the zones that match the filter
201
+ zones = []
202
+ all_zones.each do |z|
203
+ # Skip zone if name doesn't include zone_filter
204
+ # Putting everything in Upper Case to make it case insensitive
205
+ if !zone_filter.empty?
206
+ if z.name.to_s.upcase.include? zone_filter.to_s.upcase
207
+ zones << z
208
+ end
209
+ end
210
+ end
211
+
212
+ if zones.empty?
213
+ runner.registerError("Your zone filter #{zone_filter} did not match anything")
214
+ return false
215
+ end
216
+
217
+ end # End of if filter_type
218
+
219
+ # Output zone names to console
220
+ puts "\n\n================ ZONES THAT MATCHED THE FILTER ================\n"
221
+ zones.each do |z|
222
+ puts z.name
223
+ end
224
+
225
+ # info for initial condition
226
+ initial_num_air_loops_demand_control = 0
227
+ final_num_air_loops_demand_control = 0
228
+ initial_num_fan_VFD = 0
229
+ final_num_fan_VFD = 0
230
+ delete_existing_air_loops = 0
231
+ delete_existing_chiller_loops = 0
232
+ delete_existing_condenser_loops = 0
233
+ affected_loops = 0
234
+
235
+ # If we need to delete existing HVAC loops, we'll store the PRE-EXISTING Loops in the following variables,
236
+ # They will be used for clean up at the end
237
+ if delete_existing
238
+ air_loops = model.getAirLoopHVACs
239
+ runner.registerInfo("Number of initial AirLoopHVACs: #{air_loops.size}")
240
+ plant_loops = model.getPlantLoops
241
+ runner.registerInfo("Number of initial PlantLoops: #{plant_loops.size}")
242
+ end
243
+
244
+ # For each thermal zones (zones is initialized above, depending on which filter you chose)
245
+ zones.each do |z|
246
+ # Create a system 4 (PSZ-HP)
247
+ air_handler = OpenStudio::Model.addSystemType4(model).to_AirLoopHVAC.get
248
+
249
+ # Set name of Air Loop to be thermal_zone + 'Airloop'
250
+ # Local variable name convention for a non-constant (dynamic) value is 'snake_case'
251
+ base_name = z.name.to_s
252
+ air_handler.setName(base_name + ' AirLoop')
253
+
254
+ # Get existing fan, created with System 4, constant volume by default
255
+ old_fan = air_handler.supplyComponents(OpenStudio::Model::FanConstantVolume.iddObjectType).first
256
+ old_fan = old_fan.to_FanConstantVolume.get
257
+
258
+ # If you want a VFD, we replace it with a Variable Volume one
259
+ if has_vfd
260
+
261
+ # Get the outlet node after the existing fan on the loop
262
+ next_node = old_fan.outletModelObject.get.to_Node.get
263
+
264
+ # Create the new Variable speed fan
265
+ fan = OpenStudio::Model::FanVariableVolume.new(model)
266
+
267
+ # Add the new fan to the oulet node of the existing fan
268
+ # before deleting the existing one
269
+ fan.addToNode(next_node)
270
+
271
+ # Remove the existing fan. When this happens, either the pump's
272
+ # inlet or outlet node will be deleted and the other will remain
273
+ old_fan.remove
274
+
275
+ # Rename the fan clearly
276
+ fan.setName(base_name + ' Variable Volume Fan')
277
+
278
+ # If fan_pressure_rise has a non zero null value, assign it.
279
+ if !fan_pressure_rise.empty?
280
+ # We need the .get because this is an OptionalDouble. the .get will return a Double (float)
281
+ fan.setPressureRise(fan_pressure_rise.get)
282
+ runner.registerInfo("Fan '#{fan.name}' was assigned pressure rise of '#{fan_pressure_rise.get}' Pa")
283
+ end
284
+
285
+ final_num_fan_VFD += 1
286
+
287
+ else
288
+ # If VFD isn't wanted, we just rename the constant volume fan
289
+ old_fan.setName(base_name + ' Constant Volume Fan')
290
+
291
+ # If fan_pressure_rise has a non zero null value, assign it.
292
+ if !fan_pressure_rise.empty?
293
+ # We need the .get because this is an OptionalDouble. the .get will return a Double (float)
294
+ old_fan.setPressureRise(fan_pressure_rise.get)
295
+ puts "Fan '#{old_fan.name}' was assigned pressure rise of '#{fan_pressure_rise.get}' Pa"
296
+ end
297
+
298
+ end
299
+
300
+ # The Cooling coil expects an OptionalDouble
301
+ coil = air_handler.supplyComponents(OpenStudio::Model::CoilCoolingDXSingleSpeed.iddObjectType).first
302
+ coil = coil.to_CoilCoolingDXSingleSpeed.get
303
+ # Set CoolingCoil COP
304
+ coil.setRatedCOP(OpenStudio::OptionalDouble.new(cop_cooling))
305
+ # Set CoolingCoil Name
306
+ coil.setName(base_name + ' Coil Cooling DX Single Speed')
307
+
308
+ # The Heating coil expects a Double
309
+ coilheating = air_handler.supplyComponents(OpenStudio::Model::CoilHeatingDXSingleSpeed.iddObjectType).first
310
+ coilheating = coilheating.to_CoilHeatingDXSingleSpeed.get
311
+ # Set HeatingCoil COP
312
+ coilheating.setRatedCOP(cop_heating)
313
+ # Set HeatingCoil Name
314
+ coilheating.setName(base_name + ' Coil Heating DX Single Speed')
315
+
316
+ # Delete the electric heating coil if unwanted
317
+ if !has_electric_coil
318
+ coilheatingelec = air_handler.supplyComponents(OpenStudio::Model::CoilHeatingElectric.iddObjectType).first
319
+ coilheatingelec.remove
320
+ end
321
+
322
+ # Enable DCV (dunno if working)
323
+ if has_dcv
324
+
325
+ # get air_handler supply components
326
+ supply_components = air_handler.supplyComponents
327
+
328
+ # find AirLoopHVACOutdoorAirSystem on loop
329
+ supply_components.each do |supply_component|
330
+ hVACComponent = supply_component.to_AirLoopHVACOutdoorAirSystem
331
+ if !hVACComponent.empty?
332
+ hVACComponent = hVACComponent.get
333
+
334
+ # get ControllerOutdoorAir
335
+ controller_oa = hVACComponent.getControllerOutdoorAir
336
+ controller_oa.setName(base_name + ' Controller Outdoor Air')
337
+
338
+ # get ControllerMechanicalVentilation
339
+ controller_mv = controller_oa.controllerMechanicalVentilation
340
+
341
+ # check if demand control is enabled, if not, then enable it
342
+ if controller_mv.demandControlledVentilation == true
343
+ initial_num_air_loops_demand_control += 1
344
+ else
345
+ controller_mv.setDemandControlledVentilation(true)
346
+ puts "Enabling demand control ventilation for #{air_handler.name}"
347
+ end # End of if
348
+ final_num_air_loops_demand_control += 1
349
+
350
+ end # End of HVACComponent.empty?
351
+ end # end of supply component do loop
352
+
353
+ end # End of has_dcv loop
354
+
355
+ # Add a branch for the zone in question
356
+ air_handler.addBranchForZone(z)
357
+
358
+ # Counter
359
+ affected_loops += 1
360
+ end # end of do loop on each thermal zone
361
+
362
+ # CLEAN-UP SECTION
363
+ # Idea: loop on PRE-EXISTING AirLoops, delete all that don't have any zones anymore
364
+ # Then loop on chiller loop, delete all that don't have a coil connected to an air loop
365
+ # then loop on condenser water, delette all that don't have a chiller anymore
366
+
367
+ # If we need to delete existing HVAC loops, we'll loop on the PRE-EXISTING Loops we stored earlier
368
+ if delete_existing
369
+
370
+ # Arrays to store the affected loops
371
+ chiller_plant_loops = []
372
+ boiler_plant_loops = []
373
+ condenser_plant_loops = []
374
+
375
+ # Display separator for clarity
376
+ runner.registerInfo('')
377
+ runner.registerInfo('========================== CLEAN-UP: AIR LOOPS ==========================')
378
+
379
+ # Loop on the pre-existing air loops (not the ones that were created above)
380
+ air_loops.each do |air_loop|
381
+ # Check if it's got a thermal zone attached left or not..
382
+ # We assume we'll delete it unless...
383
+ delete_flag = true
384
+
385
+ air_loop.demandComponents.each do |comp|
386
+ # If there is at least a single zone left, we can't delete it
387
+ if comp.to_ThermalZone.is_initialized
388
+ delete_flag = false
389
+ end # end of if
390
+ end # end of do loop on comp
391
+
392
+ # If deletion is warranted
393
+ if delete_flag
394
+ # before deletion, let's get the potential associated plant loop.
395
+ if air_loop.supplyComponents(OpenStudio::Model::CoilCoolingWater.iddObjectType).empty?
396
+ puts "Air loop '#{air_loop.name}' DOES NOT HAVE a CoilHeatingWater"
397
+ else
398
+ cooling_coil = air_loop.supplyComponents(OpenStudio::Model::CoilCoolingWater.iddObjectType).first.to_CoilCoolingWater.get
399
+ chiller_plant_loop = cooling_coil.plantLoop.get
400
+ # Store handle in array
401
+ chiller_plant_loops << chiller_plant_loop
402
+ runner.registerInfo("Air loop '#{air_loop.name}' has a CoilCoolingWater, connected to CHILLER plant loop '#{chiller_plant_loop.name}'")
403
+ end
404
+ if air_loop.supplyComponents(OpenStudio::Model::CoilHeatingWater.iddObjectType).empty?
405
+ puts "Air loop '#{air_loop.name}' DOES NOT HAVE a CoilHeatingWater"
406
+ else
407
+ heating_coil = air_loop.supplyComponents(OpenStudio::Model::CoilCoolingWater.iddObjectType).first.to_CoilCoolingWater.get
408
+ boiler_plant_loop = heating_coil.plantLoop.get
409
+ # Store handle in array
410
+ boiler_plant_loops << boiler_plant_loop
411
+ runner.registerInfo("Air loop '#{air_loop.name}' has a CoilHeatinggWater, connected to BOILER plant loop '#{boiler_plant_loop.name}'")
412
+ end
413
+
414
+ # Now we can delete and report.
415
+ air_loop.remove
416
+ runner.registerInfo("DELETED: Air loop '#{air_loop.name}' doesn't have Thermal zones attached and was removed")
417
+ delete_existing_air_loops += 1
418
+ else
419
+ runner.registerInfo("Air Loop '#{air_loop.name}' has thermal zones and was not deleted")
420
+ end # end if delete_flag
421
+ end # end air_loops.each do
422
+
423
+ # Display separator for clarity
424
+ runner.registerInfo('')
425
+ runner.registerInfo('====================== CLEAN-UP: CHILLER PLANT LOOPS ======================')
426
+
427
+ # First pass on plant loops: chilled water loops.
428
+ chiller_plant_loops.each do |chiller_plant_loop|
429
+ puts "Chiller plant loop name: #{chiller_plant_loop.name}"
430
+
431
+ # Check if the chiller plant loop has remaining demand components
432
+
433
+ # Delete flag: first assumption is that yes... unless!
434
+ delete_flag = true
435
+
436
+ if chiller_plant_loop.demandComponents(OpenStudio::Model::CoilCoolingWater.iddObjectType).empty?
437
+ puts "Chiller Plant loop '#{chiller_plant_loop.name}' DOES NOT HAVE a CoilCoolingWater"
438
+ else
439
+ puts "Chiller Plant loop '#{chiller_plant_loop.name}' has a CoilCoolingWater"
440
+ cooling_coil = chiller_plant_loop.demandComponents(OpenStudio::Model::CoilCoolingWater.iddObjectType).first.to_CoilCoolingWater.get
441
+ if cooling_coil.airLoopHVAC.empty?
442
+ puts "But Cooling coil '#{cooling_coil.name}' is not connected to any airloopHVAC"
443
+ else
444
+ runner.registerInfo("And Cooling coil '#{cooling_coil.name}' is connected to airloopHVAC '#{cooling_coil.airLoopHVAC.get.name}' and therefore can't be deleted")
445
+ # In this case, we can't delete the chiller plant loop
446
+ delete_flag = false
447
+ end # end cooling_coil.airLoopHVAC.empty?
448
+
449
+ end # end of chiller_plant_loop.demandComponents CoilCoolingWater
450
+
451
+ # We know it's a chiller plant so this is likely unnecessary, but better safe than sorry
452
+ if chiller_plant_loop.demandComponents(OpenStudio::Model::WaterUseConnections.iddObjectType).empty?
453
+ puts "Chiller Plant loop '#{chiller_plant_loop.name}' DOES NOT HAVE WaterUseConnections"
454
+ else
455
+ runner.registerInfo("Chiller Plant loop '#{chiller_plant_loop.name}' has WaterUseConnections and therefore can't be deleted")
456
+ delete_flag = false
457
+ end
458
+
459
+ # If deletion is warranted
460
+ if delete_flag
461
+
462
+ # This section below is actually optional (but it's nice to only delete affected ones
463
+ # before deletion, let's get the potential associated condenser water plant loop.
464
+ if chiller_plant_loop.supplyComponents(OpenStudio::Model::ChillerElectricEIR.iddObjectType).empty?
465
+ puts "Chiller Plant loop '#{chiller_plant_loop.name}' DOES NOT HAVE an electric chiller"
466
+ else
467
+ chiller = chiller_plant_loop.supplyComponents(OpenStudio::Model::ChillerElectricEIR.iddObjectType).first.to_ChillerElectricEIR.get
468
+ puts "Chiller Plant loop '#{chiller_plant_loop.name}' has an electric chiller '#{chiller.name}' with condenser type '#{chiller.condenserType}'"
469
+ # Check directly if chiller has a secondaryPlantLoop (no need to check if chiller.condenserType == 'WaterCooled' first)
470
+ if chiller.secondaryPlantLoop.is_initialized
471
+ # Chiller is WaterCooled therefore should be connected to a condenser water loop
472
+ condenser_plant_loop = chiller.secondaryPlantLoop.get
473
+ condenser_plant_loops << condenser_plant_loop
474
+ runner.registerInfo("Chiller PlantLoop '#{chiller_plant_loop.name}' has a Water Cooled chiller connected to Condenser Plant Loop '#{condenser_plant_loop.name}'")
475
+ end
476
+ end
477
+
478
+ # Now we can delete and report.
479
+ chiller_plant_loop.remove
480
+ delete_existing_chiller_loops += 1
481
+ # Should I delete the chiller as well? It remains...
482
+
483
+ runner.registerInfo("DELETED: Chiller PlantLoop '#{chiller_plant_loop.name}' wasn't connected to any AirLoopHVAC nor WaterUseConnections and therefore was removed")
484
+
485
+ end # end of delete_flag
486
+ end # end of chiller_plant_loops.each do
487
+
488
+ # Display separator for clarity
489
+ runner.registerInfo('')
490
+ runner.registerInfo('===================== CLEAN-UP: CONDENSER PLANT LOOPS ====================')
491
+ # Second pass on plant loops: condenser water loops.
492
+ condenser_plant_loops.each do |condenser_plant_loop|
493
+ delete_flag = true
494
+
495
+ # If it has got a chiller as a demand component, it could still be empty
496
+ if !condenser_plant_loop.demandComponents(OpenStudio::Model::ChillerElectricEIR.iddObjectType).empty?
497
+
498
+ chiller = condenser_plant_loop.demandComponents(OpenStudio::Model::ChillerElectricEIR.iddObjectType).first.to_ChillerElectricEIR.get
499
+
500
+ # If chiller is actually connected to a chilled water node, then we shall not delete it
501
+ if !chiller.chilledWaterInletNodeName.empty?
502
+ runner.registerInfo("On Condenser PlantLoop '#{condenser_plant_loop.name}, there is a demand component of type Chiller '#{chiller.name}'" \
503
+ ' that is connected to a chilled water loop and therefore cannot be deleted')
504
+ delete_flag = false
505
+ else
506
+ puts "Plant loop '#{condenser_plant_loop.name}, Chiller '#{chiller.name}' isn't connected to a chilled water loop"
507
+ end # end of if chiller.chilledWaterInletNodeName
508
+ end # end of plant_loop.demandComponents
509
+
510
+ # if deletion is warranted
511
+ if delete_flag
512
+ condenser_plant_loop.remove
513
+ delete_existing_condenser_loops += 1
514
+ runner.registerInfo("DELETED: Plant loop '#{condenser_plant_loop.name}' isn't connected to any chilled water loop")
515
+ end
516
+ end
517
+
518
+ runner.registerInfo('')
519
+ runner.registerInfo("For more information, go to 'Advanced Output'")
520
+
521
+ # This is the generic way of looping on all loops, checking if it's a condenser plant loop, and to delete it unless it's got a chiller that is connected to chilled water plant loop
522
+ # plant_loops.each do |plant_loop|
523
+ # # Skip the chiller_plant_loops
524
+ # #next if chiller_plant_loops.include? plant_loop
525
+ # if chiller_plant_loops.include? plant_loop
526
+ # runner.registerInfo("Skipping Plant loop '#{plant_loop.name}' because it is a chiller plant")
527
+ # next
528
+ # end
529
+ # runner.registerInfo("Plant loop '#{plant_loop.name}'")
530
+ #
531
+ # # Until we know that it is a condenser loop for sure, we assume we can't delete it
532
+ # delete_flag = false
533
+ #
534
+ # # If it has got a chiller as a demand component, it's a condenser water loop
535
+ # if not plant_loop.demandComponents(OpenStudio::Model::ChillerElectricEIR::iddObjectType).empty?
536
+ # # Now, we assume we'll delete the loop unless it's actually connected and therefore usefull
537
+ # delete_flag = true
538
+ # chiller = plant_loop.demandComponents(OpenStudio::Model::ChillerElectricEIR::iddObjectType).first.to_ChillerElectricEIR.get
539
+ # # If chiller is actually connected to a chilled water node, then we shall not delete it
540
+ # if not chiller.chilledWaterInletNodeName.empty?
541
+ # runner.registerInfo("On Condenser PlantLoop '#{plant_loop.name}, there is a demand component of type Chiller '#{chiller.name}'" +
542
+ # " that is connected to a chilled water loop and therefore cannot be deleted")
543
+ # delete_flag = false
544
+ # else
545
+ # runner.registerInfo("Plant loop '#{plant_loop.name}, Chiller '#{chiller.name}' isn't connected to a chilled water loop")
546
+ # end #end of if chiller.chilledWaterInletNodeName
547
+ # end #end of plant_loop.demandComponents
548
+ #
549
+ # # if deletion is warranted
550
+ # if delete_flag
551
+ # plant_loop.remove
552
+ # delete_existing_condenser_loops += 1
553
+ # runner.registerInfo("DELETED: Plant loop '#{plant_loop.name}'")
554
+ # end
555
+ #
556
+ # end #end of plant_loops.each do
557
+
558
+ # Third pass on plant loops: boiler water loops.
559
+ # TO WRITE
560
+
561
+ end # end of if delete_existing
562
+
563
+ # Report Initial Condition
564
+ if delete_existing
565
+ air_loop_str = "\n #{delete_existing_air_loops} existing AirLoopHVACs have been deleted"
566
+ chiller_plant_loop_str = "\n #{delete_existing_chiller_loops} existing Chiller PlantLoops have been deleted"
567
+ condenser_plant_loop_str = "\n #{delete_existing_condenser_loops} existing Condenser PlantLoops have been deleted"
568
+ else
569
+ air_loop_str = ''
570
+ chiller_plant_loop_str = ''
571
+ condenser_plant_loop_str = ''
572
+ end # end of delete_existing
573
+
574
+ runner.registerInitialCondition("Initially #{initial_num_air_loops_demand_control} air loops had demand controlled ventilation enabled" +
575
+ air_loop_str + chiller_plant_loop_str + condenser_plant_loop_str + "\n")
576
+
577
+ # Report final condition
578
+ base_str = "There are #{OpenStudio.toNeatString(affected_loops, 0, true)} zones for which a PSZ-HP system was " \
579
+ "created with a Cooling COP (SI) of #{OpenStudio.toNeatString(cop_cooling, 2, true)} " \
580
+ "and a Heating COP (SI) of #{OpenStudio.toNeatString(cop_heating, 2, true)}."
581
+
582
+ if has_electric_coil
583
+ elec_str = 'Supplementary electric heating coils were added.'
584
+ else
585
+ elec_str = 'Supplementary electrical heating coils were NOT included.'
586
+ end # end of has_electric_coil
587
+
588
+ if has_vfd
589
+ fan_str = "Fan type was changed to be Variable Volume (VFD) for #{final_num_fan_VFD} fans."
590
+ else
591
+ fan_str = 'Fan type was chosen to be Constant Volume.'
592
+ end # end of has_vfd
593
+
594
+ if final_num_air_loops_demand_control == 0
595
+ dcv_str = "Demand Controlled Ventilation wasn't enabled for the new air loops"
596
+ else
597
+ dcv_str = "#{final_num_air_loops_demand_control} air loops now have demand controlled ventilation enabled"
598
+ end
599
+
600
+ runner.registerFinalCondition(base_str + "\n" + elec_str + "\n" + fan_str + "\n" + dcv_str + "\n \n")
601
+
602
+ return true
603
+ end # end the run method
604
+ end # end the measure
605
+
606
+ # this allows the measure to be used by the application
607
+ AddAPSZHPToEachZone.new.registerWithApplication