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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/Rakefile +2 -0
- data/lib/measures/ImproveFanTotalEfficiencybyPercentage/measure.rb +333 -0
- data/lib/measures/ImproveFanTotalEfficiencybyPercentage/measure.xml +150 -0
- data/lib/measures/ReplaceFanTotalEfficiency/measure.rb +330 -0
- data/lib/measures/ReplaceFanTotalEfficiency/measure.xml +150 -0
- data/lib/measures/add_apszhp_to_each_zone/measure.rb +607 -0
- data/lib/measures/add_apszhp_to_each_zone/measure.xml +184 -0
- data/lib/measures/add_energy_recovery_ventilator/measure.rb +354 -0
- data/lib/measures/add_energy_recovery_ventilator/measure.xml +78 -0
- data/lib/measures/improve_simple_glazing_by_percentage/measure.rb +81 -0
- data/lib/measures/improve_simple_glazing_by_percentage/measure.xml +70 -0
- data/lib/measures/reduce_water_use_by_percentage/measure.rb +61 -0
- data/lib/measures/reduce_water_use_by_percentage/measure.xml +62 -0
- data/lib/measures/replace_hvac_with_gshp_and_doas/measure.rb +511 -0
- data/lib/measures/replace_hvac_with_gshp_and_doas/measure.xml +375 -0
- data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_AedgMeasures.rb +454 -0
- data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_Constructions.rb +221 -0
- data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_Geometry.rb +41 -0
- data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_HVAC.rb +1682 -0
- data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_HelperMethods.rb +114 -0
- data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_LightingAndEquipment.rb +99 -0
- data/lib/measures/replace_hvac_with_gshp_and_doas/resources/OsLib_Schedules.rb +142 -0
- data/lib/measures/replace_simple_glazing/measure.rb +86 -0
- data/lib/measures/replace_simple_glazing/measure.xml +78 -0
- data/lib/measures/set_boiler_thermal_efficiency/measure.rb +520 -0
- data/lib/measures/set_boiler_thermal_efficiency/measure.xml +78 -0
- data/lib/measures/set_water_heater_efficiency_heat_lossand_peak_water_flow_rate/measure.rb +207 -0
- data/lib/measures/set_water_heater_efficiency_heat_lossand_peak_water_flow_rate/measure.xml +78 -0
- data/lib/measures/tenant_star_internal_loads/measure.rb +134 -0
- data/lib/measures/tenant_star_internal_loads/measure.xml +67 -0
- data/lib/measures/tenant_star_internal_loads/resources/os_lib_helper_methods.rb +401 -0
- data/lib/measures/vr_fwith_doas/measure.rb +468 -0
- data/lib/measures/vr_fwith_doas/measure.xml +298 -0
- data/lib/measures/vr_fwith_doas/resources/OsLib_AedgMeasures.rb +454 -0
- data/lib/measures/vr_fwith_doas/resources/OsLib_Constructions.rb +221 -0
- data/lib/measures/vr_fwith_doas/resources/OsLib_Geometry.rb +41 -0
- data/lib/measures/vr_fwith_doas/resources/OsLib_HVAC.rb +1516 -0
- data/lib/measures/vr_fwith_doas/resources/OsLib_HelperMethods.rb +114 -0
- data/lib/measures/vr_fwith_doas/resources/OsLib_LightingAndEquipment.rb +99 -0
- data/lib/measures/vr_fwith_doas/resources/OsLib_Schedules.rb +142 -0
- data/lib/openstudio/ee_measures/version.rb +1 -1
- data/openstudio-ee.gemspec +7 -5
- 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
|