openstudio-ee 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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