openstudio-extension 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -0
  3. data/.rubocop.yml +9 -0
  4. data/Gemfile +3 -1
  5. data/Jenkinsfile +10 -0
  6. data/README.md +230 -12
  7. data/Rakefile +88 -3
  8. data/bin/console +3 -3
  9. data/doc_templates/LICENSE.md +27 -0
  10. data/doc_templates/README.md.erb +42 -0
  11. data/doc_templates/copyright_erb.txt +36 -0
  12. data/doc_templates/copyright_js.txt +4 -0
  13. data/doc_templates/copyright_ruby.txt +34 -0
  14. data/init_templates/README.md +37 -0
  15. data/init_templates/gemspec.txt +32 -0
  16. data/init_templates/openstudio_module.rb +50 -0
  17. data/init_templates/spec.rb +47 -0
  18. data/init_templates/spec_helper.rb +49 -0
  19. data/init_templates/template_gemfile.txt +17 -0
  20. data/init_templates/template_rakefile.txt +15 -0
  21. data/init_templates/version.rb +40 -0
  22. data/lib/files/openstudio-extension-gem-test.ddy +536 -0
  23. data/lib/files/openstudio-extension-gem-test.epw +8768 -0
  24. data/lib/files/openstudio-extension-gem-test.stat +554 -0
  25. data/lib/measures/openstudio_extension_test_measure/LICENSE.md +27 -0
  26. data/lib/measures/openstudio_extension_test_measure/README.md +26 -0
  27. data/lib/measures/openstudio_extension_test_measure/README.md.erb +42 -0
  28. data/lib/measures/openstudio_extension_test_measure/measure.rb +72 -0
  29. data/lib/measures/openstudio_extension_test_measure/measure.xml +83 -0
  30. data/lib/measures/openstudio_extension_test_measure/resources/os_lib_helper_methods.rb +399 -0
  31. data/lib/measures/openstudio_extension_test_measure/tests/OpenStudioExtensionTestMeasure_Test.rb +75 -0
  32. data/lib/openstudio/extension.rb +220 -0
  33. data/lib/openstudio/extension/core/CreateResults.rb +879 -0
  34. data/lib/openstudio/extension/core/check_air_sys_temps.rb +190 -0
  35. data/lib/openstudio/extension/core/check_calibration.rb +155 -0
  36. data/lib/openstudio/extension/core/check_cond_zns.rb +84 -0
  37. data/lib/openstudio/extension/core/check_domestic_hot_water.rb +334 -0
  38. data/lib/openstudio/extension/core/check_envelope_conductance.rb +453 -0
  39. data/lib/openstudio/extension/core/check_eui_by_end_use.rb +162 -0
  40. data/lib/openstudio/extension/core/check_eui_reasonableness.rb +135 -0
  41. data/lib/openstudio/extension/core/check_fan_pwr.rb +98 -0
  42. data/lib/openstudio/extension/core/check_internal_loads.rb +393 -0
  43. data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +226 -0
  44. data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +326 -0
  45. data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +464 -0
  46. data/lib/openstudio/extension/core/check_mech_sys_type.rb +139 -0
  47. data/lib/openstudio/extension/core/check_part_loads.rb +451 -0
  48. data/lib/openstudio/extension/core/check_placeholder.rb +75 -0
  49. data/lib/openstudio/extension/core/check_plant_cap.rb +123 -0
  50. data/lib/openstudio/extension/core/check_plant_temps.rb +159 -0
  51. data/lib/openstudio/extension/core/check_plenum_loads.rb +87 -0
  52. data/lib/openstudio/extension/core/check_pump_pwr.rb +108 -0
  53. data/lib/openstudio/extension/core/check_sch_coord.rb +241 -0
  54. data/lib/openstudio/extension/core/check_schedules.rb +311 -0
  55. data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +158 -0
  56. data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +148 -0
  57. data/lib/openstudio/extension/core/check_weather_files.rb +132 -0
  58. data/lib/openstudio/extension/core/deer_vintages.rb +311 -0
  59. data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +491 -0
  60. data/lib/openstudio/extension/core/os_lib_cofee.rb +259 -0
  61. data/lib/openstudio/extension/core/os_lib_constructions.rb +378 -0
  62. data/lib/openstudio/extension/core/os_lib_geometry.rb +1022 -0
  63. data/lib/openstudio/extension/core/os_lib_helper_methods.rb +399 -0
  64. data/lib/openstudio/extension/core/os_lib_hvac.rb +2171 -0
  65. data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +214 -0
  66. data/lib/openstudio/extension/core/os_lib_model_generation.rb +817 -0
  67. data/lib/openstudio/extension/core/os_lib_model_simplification.rb +1049 -0
  68. data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +165 -0
  69. data/lib/openstudio/extension/core/os_lib_reporting.rb +4652 -0
  70. data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +200 -0
  71. data/lib/openstudio/extension/core/os_lib_schedules.rb +963 -0
  72. data/lib/openstudio/extension/rake_task.rb +149 -0
  73. data/lib/openstudio/extension/runner.rb +644 -0
  74. data/lib/openstudio/extension/version.rb +40 -0
  75. data/openstudio-extension.gemspec +20 -15
  76. metadata +150 -14
  77. data/.travis.yml +0 -7
  78. data/lib/OpenStudio/Extension/rake_task.rb +0 -84
  79. data/lib/OpenStudio/Extension/version.rb +0 -33
  80. data/lib/OpenStudio/extension.rb +0 -65
@@ -0,0 +1,399 @@
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) 2008-2019, Alliance for Sustainable Energy, LLC.
3
+ # All rights reserved.
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # (1) Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # (3) Neither the name of the copyright holder nor the names of any contributors
15
+ # may be used to endorse or promote products derived from this software without
16
+ # specific prior written permission from the respective party.
17
+ #
18
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
19
+ # of modifications or other derivative works may not use the "OpenStudio"
20
+ # trademark, "OS", "os", or any other confusingly similar designation without
21
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
24
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
27
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
28
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ # *******************************************************************************
35
+
36
+ module OsLib_HelperMethods
37
+ # populate choice argument from model objects
38
+ def self.populateChoiceArgFromModelObjects(model, modelObject_args_hash, includeBuilding = nil)
39
+ # populate choice argument for constructions that are applied to surfaces in the model
40
+ modelObject_handles = OpenStudio::StringVector.new
41
+ modelObject_display_names = OpenStudio::StringVector.new
42
+
43
+ # looping through sorted hash of constructions
44
+ modelObject_args_hash.sort.map do |key, value|
45
+ modelObject_handles << value.handle.to_s
46
+ modelObject_display_names << key
47
+ end
48
+
49
+ unless includeBuilding.nil?
50
+ # add building to string vector with space type
51
+ building = model.getBuilding
52
+ modelObject_handles << building.handle.to_s
53
+ modelObject_display_names << includeBuilding
54
+ end
55
+
56
+ result = { 'modelObject_handles' => modelObject_handles, 'modelObject_display_names' => modelObject_display_names }
57
+ return result
58
+ end
59
+
60
+ # create variables in run from user arguments
61
+ def self.createRunVariables(runner, model, user_arguments, arguments)
62
+ result = {}
63
+
64
+ error = false
65
+ # use the built-in error checking
66
+ unless runner.validateUserArguments(arguments, user_arguments)
67
+ error = true
68
+ runner.registerError('Invalid argument values.')
69
+ end
70
+
71
+ user_arguments.each do |argument|
72
+ # get argument info
73
+ arg = user_arguments[argument]
74
+ arg_type = arg.print.lines($/)[1]
75
+
76
+ # create argument variable
77
+ if arg_type.include? 'Double, Required'
78
+ eval("result[\"#{arg.name}\"] = runner.getDoubleArgumentValue(\"#{arg.name}\", user_arguments)")
79
+ elsif arg_type.include? 'Integer, Required'
80
+ eval("result[\"#{arg.name}\"] = runner.getIntegerArgumentValue(\"#{arg.name}\", user_arguments)")
81
+ elsif arg_type.include? 'String, Required'
82
+ eval("result[\"#{arg.name}\"] = runner.getStringArgumentValue(\"#{arg.name}\", user_arguments)")
83
+ elsif arg_type.include? 'Boolean, Required'
84
+ eval("result[\"#{arg.name}\"] = runner.getBoolArgumentValue(\"#{arg.name}\", user_arguments)")
85
+ elsif arg_type.include? 'Choice, Required'
86
+ eval("result[\"#{arg.name}\"] = runner.getStringArgumentValue(\"#{arg.name}\", user_arguments)")
87
+ else
88
+ puts 'not setup to handle all argument types yet, or any optional arguments'
89
+ end
90
+ end
91
+
92
+ if error
93
+ return false
94
+ else
95
+ return result
96
+ end
97
+ end
98
+
99
+ # check choice argument made from model objects
100
+ def self.checkChoiceArgFromModelObjects(object, variableName, to_ObjectType, runner, user_arguments)
101
+ apply_to_building = false
102
+ modelObject = nil
103
+ if object.empty?
104
+ handle = runner.getStringArgumentValue(variableName, user_arguments)
105
+ if handle.empty?
106
+ runner.registerError("No #{variableName} was chosen.") # this logic makes this not work on an optional model object argument
107
+ else
108
+ runner.registerError("The selected #{variableName} with handle '#{handle}' was not found in the model. It may have been removed by another measure.")
109
+ end
110
+ return false
111
+ else
112
+ if !eval("object.get.#{to_ObjectType}").empty?
113
+ modelObject = eval("object.get.#{to_ObjectType}").get
114
+ elsif !object.get.to_Building.empty?
115
+ apply_to_building = true
116
+ else
117
+ runner.registerError("Script Error - argument not showing up as #{variableName}.")
118
+ return false
119
+ end
120
+ end
121
+
122
+ result = { 'modelObject' => modelObject, 'apply_to_building' => apply_to_building }
123
+ end
124
+
125
+ # check choice argument made from model objects
126
+ def self.checkOptionalChoiceArgFromModelObjects(object, variableName, to_ObjectType, runner, user_arguments)
127
+ apply_to_building = false
128
+ modelObject = nil
129
+ if object.empty?
130
+ handle = runner.getOptionalStringArgumentValue(variableName, user_arguments)
131
+ if handle.empty?
132
+ # do nothing, this is a valid option
133
+ puts 'hello'
134
+ modelObject = nil
135
+ apply_to_building = false
136
+ else
137
+ runner.registerError("The selected #{variableName} with handle '#{handle}' was not found in the model. It may have been removed by another measure.")
138
+ return false
139
+ end
140
+ else
141
+ if !eval("object.get.#{to_ObjectType}").empty?
142
+ modelObject = eval("object.get.#{to_ObjectType}").get
143
+ elsif !object.get.to_Building.empty?
144
+ apply_to_building = true
145
+ else
146
+ runner.registerError("Script Error - argument not showing up as #{variableName}.")
147
+ return false
148
+ end
149
+ end
150
+
151
+ result = { 'modelObject' => modelObject, 'apply_to_building' => apply_to_building }
152
+ end
153
+
154
+ # check value of double arguments
155
+ def self.checkDoubleAndIntegerArguments(runner, user_arguments, arg_check_hash)
156
+ error = false
157
+
158
+ # get hash values
159
+ min = arg_check_hash['min']
160
+ max = arg_check_hash['max']
161
+ min_eq_bool = arg_check_hash['min_eq_bool']
162
+ max_eq_bool = arg_check_hash['max_eq_bool']
163
+
164
+ arg_check_hash['arg_array'].each do |argument|
165
+ argument = user_arguments[argument]
166
+
167
+ # get arg values
168
+ arg_value = nil
169
+ if argument.hasValue
170
+ arg_value = argument.valueDisplayName.to_f # instead of valueAsDouble so it allows integer arguments as well
171
+ elsif argument.hasDefaultValue
172
+ arg_value = argument.defaultValueDisplayName.to_f
173
+ end
174
+ arg_display = argument.displayName
175
+
176
+ unless min.nil?
177
+ if min_eq_bool
178
+ if arg_value < min
179
+ runner.registerError("Please enter value greater than or equal to #{min} for #{arg_display}.") # add in argument display name
180
+ error = true
181
+ end
182
+ else
183
+ if arg_value <= min
184
+ runner.registerError("Please enter value greater than #{min} for #{arg_display}.") # add in argument display name
185
+ error = true
186
+ end
187
+ end
188
+ end
189
+ unless max.nil?
190
+ if max_eq_bool
191
+ if arg_value > max
192
+ runner.registerError("Please enter value less than or equal to #{max} for #{arg_display}.") # add in argument display name
193
+ error = true
194
+ end
195
+ else
196
+ if arg_value >= max
197
+ runner.registerError("Please enter value less than #{max} for #{arg_display}.") # add in argument display name
198
+ error = true
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ # check for any errors
205
+ if error
206
+ return false
207
+ else
208
+ return true
209
+ end
210
+ end
211
+
212
+ # open channel to log info/warning/error messages
213
+ def self.setup_log_msgs(runner, debug = false)
214
+ # Open a channel to log info/warning/error messages
215
+ @msg_log = OpenStudio::StringStreamLogSink.new
216
+ if debug
217
+ @msg_log.setLogLevel(OpenStudio::Debug)
218
+ else
219
+ @msg_log.setLogLevel(OpenStudio::Info)
220
+ end
221
+ @start_time = Time.new
222
+ @runner = runner
223
+ end
224
+
225
+ # Get all the log messages and put into output
226
+ # for users to see.
227
+ def self.log_msgs
228
+ @msg_log.logMessages.each do |msg|
229
+ # DLM: you can filter on log channel here for now
230
+ if /openstudio.*/.match(msg.logChannel) # /openstudio\.model\..*/
231
+ # Skip certain messages that are irrelevant/misleading
232
+ next if msg.logMessage.include?('Skipping layer') || # Annoying/bogus "Skipping layer" warnings
233
+ msg.logChannel.include?('runmanager') || # RunManager messages
234
+ msg.logChannel.include?('setFileExtension') || # .ddy extension unexpected
235
+ msg.logChannel.include?('Translator') || # Forward translator and geometry translator
236
+ msg.logMessage.include?('UseWeatherFile') # 'UseWeatherFile' is not yet a supported option for YearDescription
237
+
238
+ # Report the message in the correct way
239
+ if msg.logLevel == OpenStudio::Info
240
+ @runner.registerInfo(msg.logMessage)
241
+ elsif msg.logLevel == OpenStudio::Warn
242
+ @runner.registerWarning("[#{msg.logChannel}] #{msg.logMessage}")
243
+ elsif msg.logLevel == OpenStudio::Error
244
+ @runner.registerError("[#{msg.logChannel}] #{msg.logMessage}")
245
+ elsif msg.logLevel == OpenStudio::Debug && @debug
246
+ @runner.registerInfo("DEBUG - #{msg.logMessage}")
247
+ end
248
+ end
249
+ end
250
+ @runner.registerInfo("Total Time = #{(Time.new - @start_time).round}sec.")
251
+ end
252
+
253
+ def self.check_upstream_measure_for_arg(runner, arg_name)
254
+ # 2.x methods (currently setup for measure display name but snake_case arg names)
255
+ arg_name_value = {}
256
+ runner.workflow.workflowSteps.each do |step|
257
+ if step.to_MeasureStep.is_initialized
258
+ measure_step = step.to_MeasureStep.get
259
+
260
+ measure_name = measure_step.measureDirName
261
+ if measure_step.name.is_initialized
262
+ measure_name = measure_step.name.get # this is instance name in PAT
263
+ end
264
+ if measure_step.result.is_initialized
265
+ result = measure_step.result.get
266
+ result.stepValues.each do |arg|
267
+ name = arg.name
268
+ value = arg.valueAsVariant.to_s
269
+ if name == arg_name
270
+ arg_name_value[:value] = value
271
+ arg_name_value[:measure_name] = measure_name
272
+ return arg_name_value # stop after find first one
273
+ end
274
+ end
275
+ else
276
+ # puts "No result for #{measure_name}"
277
+ end
278
+ else
279
+ # puts "This step is not a measure"
280
+ end
281
+ end
282
+
283
+ return arg_name_value
284
+ end
285
+
286
+ # populate choice argument from model objects. areaType should be string like "floorArea" or "exteriorArea"
287
+ # note: it seems like spaceType.floorArea does account for multiplier, so I don't have to call this method unless I have a custom collection of spaces.
288
+ def self.getAreaOfSpacesInArray(model, spaceArray, areaType = 'floorArea')
289
+ # find selected floor spaces, make array and get floor area.
290
+ totalArea = 0
291
+ spaceAreaHash = {}
292
+ spaceArray.each do |space|
293
+ spaceArea = eval("space.#{areaType}*space.multiplier")
294
+ spaceAreaHash[space] = spaceArea
295
+ totalArea += spaceArea
296
+ end
297
+
298
+ result = { 'totalArea' => totalArea, 'spaceAreaHash' => spaceAreaHash }
299
+ return result
300
+ end
301
+
302
+ # runs conversion and neat string, and returns value with units in string, optionally before or after the value
303
+ def self.neatConvertWithUnitDisplay(double, fromString, toString, digits, unitBefore = false, unitAfter = true, space = true, parentheses = true)
304
+ # convert units
305
+ doubleConverted = OpenStudio.convert(double, fromString, toString)
306
+ if !doubleConverted.nil?
307
+ doubleConverted = doubleConverted.get
308
+ else
309
+ puts "Couldn't convert values, check string choices passed in. From: #{fromString}, To: #{toString}"
310
+ end
311
+
312
+ # get neat version of converted
313
+ neatConverted = OpenStudio.toNeatString(doubleConverted, digits, true)
314
+
315
+ # add prefix
316
+ if unitBefore
317
+ if space == true && parentheses == true
318
+ prefix = "(#{toString}) "
319
+ elsif space == true && parentheses == false
320
+ prefix = "(#{toString})"
321
+ elsif space == false && parentheses == true
322
+ prefix = "#{toString} "
323
+ else
324
+ prefix = toString.to_s
325
+ end
326
+ else
327
+ prefix = ''
328
+ end
329
+
330
+ # add suffix
331
+ if unitAfter
332
+ if space == true && parentheses == true
333
+ suffix = " (#{toString})"
334
+ elsif space == true && parentheses == false
335
+ suffix = "(#{toString})"
336
+ elsif space == false && parentheses == true
337
+ suffix = " #{toString}"
338
+ else
339
+ suffix = toString.to_s
340
+ end
341
+ else
342
+ suffix = ''
343
+ end
344
+
345
+ finalString = "#{prefix}#{neatConverted}#{suffix}"
346
+
347
+ return finalString
348
+ end
349
+
350
+ # helper that loops through lifecycle costs getting total costs under "Construction" and add to counter if occurs during year 0
351
+ def self.getTotalCostForObjects(objectArray, category = 'Construction', onlyYearFromStartZero = true)
352
+ counter = 0
353
+ objectArray.each do |object|
354
+ object_LCCs = object.lifeCycleCosts
355
+ object_LCCs.each do |object_LCC|
356
+ if object_LCC.category == category
357
+ if onlyYearFromStartZero == false || object_LCC.yearsFromStart == 0
358
+ counter += object_LCC.totalCost
359
+ end
360
+ end
361
+ end
362
+ end
363
+
364
+ return counter
365
+ end
366
+
367
+ # helper that loops through lifecycle costs getting total costs under "Construction" and add to counter if occurs during year 0
368
+ def self.getSpaceTypeStandardsInformation(spaceTypeArray)
369
+ # hash of space types
370
+ spaceTypeStandardsInfoHash = {}
371
+
372
+ spaceTypeArray.each do |spaceType|
373
+ # get standards building
374
+ if !spaceType.standardsBuildingType.empty?
375
+ standardsBuilding = spaceType.standardsBuildingType.get
376
+ else
377
+ standardsBuilding = nil
378
+ end
379
+
380
+ # get standards space type
381
+ if !spaceType.standardsSpaceType.empty?
382
+ standardsSpaceType = spaceType.standardsSpaceType.get
383
+ else
384
+ standardsSpaceType = nil
385
+ end
386
+
387
+ # populate hash
388
+ spaceTypeStandardsInfoHash[spaceType] = [standardsBuilding, standardsSpaceType]
389
+ end
390
+
391
+ return spaceTypeStandardsInfoHash
392
+ end
393
+
394
+ # OpenStudio has built in toNeatString method
395
+ # OpenStudio::toNeatString(double,2,true)# double,decimals, show commas
396
+
397
+ # OpenStudio has built in helper for unit conversion. That can be done using OpenStudio::convert() as shown below.
398
+ # OpenStudio::convert(double,"from unit string","to unit string").get
399
+ end
@@ -0,0 +1,2171 @@
1
+ # *******************************************************************************
2
+ # OpenStudio(R), Copyright (c) 2008-2019, Alliance for Sustainable Energy, LLC.
3
+ # All rights reserved.
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are met:
6
+ #
7
+ # (1) Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # (3) Neither the name of the copyright holder nor the names of any contributors
15
+ # may be used to endorse or promote products derived from this software without
16
+ # specific prior written permission from the respective party.
17
+ #
18
+ # (4) Other than as required in clauses (1) and (2), distributions in any form
19
+ # of modifications or other derivative works may not use the "OpenStudio"
20
+ # trademark, "OS", "os", or any other confusingly similar designation without
21
+ # specific prior written permission from Alliance for Sustainable Energy, LLC.
22
+ #
23
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
24
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
27
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
28
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
30
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+ # *******************************************************************************
35
+
36
+ module OsLib_HVAC
37
+ # do something
38
+ def self.doSomething(input)
39
+ # do something
40
+ output = input
41
+
42
+ result = output
43
+ return result
44
+ end
45
+
46
+ # validate and make plenum zones
47
+ def self.validateAndAddPlenumZonesToSystem(model, runner, options = {})
48
+ # set defaults to use if user inputs not passed in
49
+ defaults = {
50
+ 'zonesPlenum' => nil,
51
+ 'zonesPrimary' => nil,
52
+ 'type' => 'ceilingReturn'
53
+ }
54
+
55
+ # merge user inputs with defaults
56
+ options = defaults.merge(options)
57
+
58
+ # array of valid ceiling plenums
59
+ zoneSurfaceHash = {}
60
+ zonePlenumHash = {}
61
+
62
+ if options['zonesPlenum'].nil?
63
+ runner.registerWarning('No plenum zones were passed in, validateAndAddPlenumZonesToSystem will not alter the model.')
64
+ else
65
+ options['zonesPlenum'].each do |zone|
66
+ # get spaces in zone
67
+ spaces = zone.spaces
68
+ # get adjacent spaces
69
+ spaces.each do |space|
70
+ # get surfaces
71
+ surfaces = space.surfaces
72
+ # loop through surfaces looking for floors with surface boundary condition, grab zone that surface's parent space is in.
73
+ surfaces.each do |surface|
74
+ if (surface.outsideBoundaryCondition == 'Surface') && (surface.surfaceType == 'Floor')
75
+ next if surface.adjacentSurface.empty?
76
+ adjacentSurface = surface.adjacentSurface.get
77
+ next if adjacentSurface.space.empty?
78
+ adjacentSurfaceSpace = adjacentSurface.space.get
79
+ next if adjacentSurfaceSpace.thermalZone.empty?
80
+ adjacentSurfaceSpaceZone = adjacentSurfaceSpace.thermalZone.get
81
+ if options['zonesPrimary'].include? adjacentSurfaceSpaceZone
82
+ if zoneSurfaceHash[adjacentSurfaceSpaceZone].nil? || (surface.grossArea > zoneSurfaceHash[adjacentSurfaceSpaceZone])
83
+ adjacentSurfaceSpaceZone.setReturnPlenum(zone)
84
+ zoneSurfaceHash[adjacentSurfaceSpaceZone] = surface.grossArea
85
+ zonePlenumHash[adjacentSurfaceSpaceZone] = zone
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ # report out results of zone-plenum hash
95
+ zonePlenumHash.each do |zone, plenum|
96
+ runner.registerInfo("#{plenum.name} has been set as a return air plenum for #{zone.name}.")
97
+ end
98
+
99
+ # pass back zone-plenum hash
100
+ result = zonePlenumHash
101
+ return result
102
+ end
103
+
104
+ def self.sortZones(model, runner, options = {})
105
+ # set defaults to use if user inputs not passed in
106
+ defaults = { 'standardBuildingTypeTest' => nil, # not used for now
107
+ 'secondarySpaceTypeTest' => nil,
108
+ 'ceilingReturnPlenumSpaceType' => nil }
109
+
110
+ # merge user inputs with defaults
111
+ options = defaults.merge(options)
112
+
113
+ # set up zone type arrays
114
+ zonesPrimary = []
115
+ zonesSecondary = []
116
+ zonesPlenum = []
117
+ zonesUnconditioned = []
118
+
119
+ # get thermal zones
120
+ zones = model.getThermalZones
121
+ zones.each do |zone|
122
+ # assign appropriate zones to zonesPlenum or zonesUnconditioned (those that don't have thermostats or zone HVAC equipment)
123
+ # if not conditioned then add to zonesPlenum or zonesUnconditioned
124
+ if zone.thermostatSetpointDualSetpoint.is_initialized || !zone.equipment.empty?
125
+ # zone is conditioned. check if its space type is secondary or primary
126
+ spaces = zone.spaces
127
+ spaces.each do |space|
128
+ # if a zone has already been assigned as secondary, skip
129
+ next if zonesSecondary.include? zone
130
+ # get space type if it exists
131
+ next if space.spaceType.empty?
132
+ spaceType = space.spaceType.get
133
+ # get standards information
134
+ # for now skip standardsBuildingType and just rely on the standardsSpaceType. Seems like enough.
135
+ next if spaceType.standardsSpaceType.empty?
136
+ standardSpaceType = spaceType.standardsSpaceType.get
137
+ # test space type against secondary space type array
138
+ # if any space type in zone is secondary, assign zone as secondary
139
+ if options['secondarySpaceTypeTest'].include? standardSpaceType
140
+ zonesSecondary << zone
141
+ end
142
+ end
143
+ # if zone not assigned as secondary, assign as primary
144
+ unless zonesSecondary.include? zone
145
+ zonesPrimary << zone
146
+ end
147
+ else
148
+ # determine if zone is a plenum zone or general unconditioned zone
149
+ # assume it is a plenum if it has at least one planum space
150
+ zone.spaces.each do |space|
151
+ # if a zone has already been assigned as a plenum, skip
152
+ next if zonesPlenum.include? zone
153
+ # if zone not assigned as a plenum, get space type if it exists
154
+ # compare to plenum space type if it has been assigned
155
+ if space.spaceType.is_initialized && (options['ceilingReturnPlenumSpaceType'].nil? == false)
156
+ spaceType = space.spaceType.get
157
+ if spaceType == options['ceilingReturnPlenumSpaceType']
158
+ zonesPlenum << zone # zone has a plenum space; assign it as a plenum
159
+ end
160
+ end
161
+ end
162
+ # if zone not assigned as a plenum, assign it as unconditioned
163
+ unless zonesPlenum.include? zone
164
+ zonesUnconditioned << zone
165
+ end
166
+ end
167
+ end
168
+
169
+ zonesSorted = { 'zonesPrimary' => zonesPrimary,
170
+ 'zonesSecondary' => zonesSecondary,
171
+ 'zonesPlenum' => zonesPlenum,
172
+ 'zonesUnconditioned' => zonesUnconditioned }
173
+ # pass back zonesSorted hash
174
+ result = zonesSorted
175
+ return result
176
+ end
177
+
178
+ def self.reportConditions(model, runner, condition,extra_string = '')
179
+
180
+ airloops = model.getAirLoopHVACs
181
+ plantLoops = model.getPlantLoops
182
+ zones = model.getThermalZones
183
+
184
+ # count up zone equipment (not counting zone exhaust fans)
185
+ zoneHasEquip = false
186
+ zonesWithEquipCounter = 0
187
+
188
+ zones.each do |zone|
189
+ if zone.equipment.size > 0
190
+ zone.equipment.each do |equip|
191
+ unless equip.to_FanZoneExhaust.is_initialized
192
+ zonesWithEquipCounter += 1
193
+ break
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ if condition == "initial"
200
+ runner.registerInitialCondition("The building started with #{airloops.size} air loops and #{plantLoops.size} plant loops. #{zonesWithEquipCounter} zones were conditioned with zone equipment.")
201
+ elsif condition == "final"
202
+ runner.registerFinalCondition("The building finished with #{airloops.size} air loops and #{plantLoops.size} plant loops. #{zonesWithEquipCounter} zones are conditioned with zone equipment. #{extra_string}")
203
+ end
204
+
205
+ end
206
+
207
+ def self.removeEquipment(model, runner)
208
+ airloops = model.getAirLoopHVACs
209
+ plantLoops = model.getPlantLoops
210
+ zones = model.getThermalZones
211
+
212
+ # remove all airloops
213
+ airloops.each(&:remove)
214
+
215
+ # remove all zone equipment except zone exhaust fans
216
+ zones.each do |zone|
217
+ zone.equipment.each do |equip|
218
+ if equip.to_FanZoneExhaust.is_initialized
219
+ else
220
+ equip.remove
221
+ end
222
+ end
223
+ end
224
+
225
+ # remove plant loops
226
+ plantLoops.each do |plantloop|
227
+ # get the demand components and see if water use connection, then save it
228
+ # notify user with info statement if supply side of plant loop had heat exchanger for refrigeration
229
+ usedForSHWorRefrigeration = false
230
+ plantloop.demandComponents.each do |comp| # AP code to check your comments above
231
+ if comp.to_WaterUseConnections.is_initialized || comp.to_CoilWaterHeatingDesuperheater.is_initialized
232
+ usedForSHWorRefrigeration = true
233
+ end
234
+ end
235
+ if usedForSHWorRefrigeration == false
236
+ plantloop.remove
237
+ else
238
+ runner.registerWarning("#{plantloop.name} is used for SHW or refrigeration heat reclaim. Loop will not be deleted")
239
+ end
240
+ end
241
+ end
242
+
243
+ def self.assignHVACSchedules(model, runner, options = {})
244
+ require "#{File.dirname(__FILE__)}/os_lib_schedules"
245
+
246
+ schedulesHVAC = {}
247
+ airloops = model.getAirLoopHVACs
248
+
249
+ # find airloop with most primary spaces
250
+ max_primary_spaces = 0
251
+ representative_airloop = false
252
+ building_HVAC_schedule = false
253
+ building_ventilation_schedule = false
254
+ unless options['remake_schedules']
255
+ # if remake schedules not selected, get relevant schedules from model if they exist
256
+ airloops.each do |air_loop|
257
+ primary_spaces = 0
258
+ air_loop.thermalZones.each do |thermal_zone|
259
+ thermal_zone.spaces.each do |space|
260
+ if space.spaceType.is_initialized
261
+ if space.spaceType.get.name.get.include? options['primarySpaceType']
262
+ primary_spaces += 1
263
+ end
264
+ end
265
+ end
266
+ end
267
+ if primary_spaces > max_primary_spaces
268
+ max_primary_spaces = primary_spaces
269
+ representative_airloop = air_loop
270
+ end
271
+ end
272
+ end
273
+ if representative_airloop
274
+ building_HVAC_schedule = representative_airloop.availabilitySchedule
275
+ building_ventilation_schedule_optional = representative_airloop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.maximumFractionofOutdoorAirSchedule
276
+ if building_ventilation_schedule_optional.is_initialized
277
+ building_ventilation_schedule = building_ventilation_schedule.get
278
+ end
279
+ end
280
+ # build new airloop schedules if existing model doesn't have them
281
+ if options['primarySpaceType'] == 'Classroom'
282
+ # ventilation schedule
283
+ unless building_ventilation_schedule
284
+ ruleset_name = 'AEDG K-12 Ventilation Schedule'
285
+ winter_design_day = [[24, 1]]
286
+ summer_design_day = [[24, 1]]
287
+ default_day = ['Weekday', [6, 0], [18, 1], [24, 0]]
288
+ rules = []
289
+ rules << ['Weekend', '1/1-12/31', 'Sat/Sun', [24, 0]]
290
+ rules << ['Summer Weekday', '7/1-8/31', 'Mon/Tue/Wed/Thu/Fri', [8, 0], [13, 1], [24, 0]]
291
+ options_ventilation = { 'name' => ruleset_name,
292
+ 'winter_design_day' => winter_design_day,
293
+ 'summer_design_day' => summer_design_day,
294
+ 'default_day' => default_day,
295
+ 'rules' => rules }
296
+ building_ventilation_schedule = OsLib_Schedules.createComplexSchedule(model, options_ventilation)
297
+ end
298
+ # HVAC availability schedule
299
+ unless building_HVAC_schedule
300
+ ruleset_name = 'AEDG K-12 HVAC Availability Schedule'
301
+ winter_design_day = [[24, 1]]
302
+ summer_design_day = [[24, 1]]
303
+ default_day = ['Weekday', [6, 0], [18, 1], [24, 0]]
304
+ rules = []
305
+ rules << ['Weekend', '1/1-12/31', 'Sat/Sun', [24, 0]]
306
+ rules << ['Summer Weekday', '7/1-8/31', 'Mon/Tue/Wed/Thu/Fri', [8, 0], [13, 1], [24, 0]]
307
+ options_hvac = { 'name' => ruleset_name,
308
+ 'winter_design_day' => winter_design_day,
309
+ 'summer_design_day' => summer_design_day,
310
+ 'default_day' => default_day,
311
+ 'rules' => rules }
312
+ building_HVAC_schedule = OsLib_Schedules.createComplexSchedule(model, options_hvac)
313
+ end
314
+ elsif options['primarySpaceType'] == 'Office'
315
+ # ventilation schedule
316
+ unless building_ventilation_schedule
317
+ ruleset_name = 'AEDG Office Ventilation Schedule'
318
+ winter_design_day = [[24, 1]] # ML These are not always on in PNNL model
319
+ summer_design_day = [[24, 1]] # ML These are not always on in PNNL model
320
+ default_day = ['Weekday', [7, 0], [22, 1], [24, 0]] # ML PNNL has a one hour ventilation offset
321
+ rules = []
322
+ rules << ['Saturday', '1/1-12/31', 'Sat', [7, 0], [18, 1], [24, 0]] # ML PNNL has a one hour ventilation offset
323
+ rules << ['Sunday', '1/1-12/31', 'Sun', [24, 0]]
324
+ options_ventilation = { 'name' => ruleset_name,
325
+ 'winter_design_day' => winter_design_day,
326
+ 'summer_design_day' => summer_design_day,
327
+ 'default_day' => default_day,
328
+ 'rules' => rules }
329
+ building_ventilation_schedule = OsLib_Schedules.createComplexSchedule(model, options_ventilation)
330
+ end
331
+ # HVAC availability schedule
332
+ unless building_HVAC_schedule
333
+ ruleset_name = 'AEDG Office HVAC Availability Schedule'
334
+ winter_design_day = [[24, 1]] # ML These are not always on in PNNL model
335
+ summer_design_day = [[24, 1]] # ML These are not always on in PNNL model
336
+ default_day = ['Weekday', [6, 0], [22, 1], [24, 0]] # ML PNNL has a one hour ventilation offset
337
+ rules = []
338
+ rules << ['Saturday', '1/1-12/31', 'Sat', [6, 0], [18, 1], [24, 0]] # ML PNNL has a one hour ventilation offset
339
+ rules << ['Sunday', '1/1-12/31', 'Sun', [24, 0]]
340
+ options_hvac = { 'name' => ruleset_name,
341
+ 'winter_design_day' => winter_design_day,
342
+ 'summer_design_day' => summer_design_day,
343
+ 'default_day' => default_day,
344
+ 'rules' => rules }
345
+ building_HVAC_schedule = OsLib_Schedules.createComplexSchedule(model, options_hvac)
346
+ end
347
+ # special loops for radiant system (different temperature setpoints)
348
+ if options['allHVAC']['zone'] == 'Radiant'
349
+ # create hot water schedule for radiant heating loop
350
+ schedulesHVAC['radiant_hot_water'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HW-Radiant-Loop-Temp-Schedule',
351
+ 'default_day' => ['All Days', [24, 45.0]])
352
+ # create hot water schedule for radiant cooling loop
353
+ schedulesHVAC['radiant_chilled_water'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG CW-Radiant-Loop-Temp-Schedule',
354
+ 'default_day' => ['All Days', [24, 15.0]])
355
+ # create mean radiant heating and cooling setpoint schedules
356
+ # ML ideally, should grab schedules tied to zone thermostat and make modified versions that follow the setback pattern
357
+ # for now, create new ones that match the recommended HVAC schedule
358
+ # mean radiant heating setpoint schedule (PNNL values)
359
+ ruleset_name = 'AEDG Office Mean Radiant Heating Setpoint Schedule'
360
+ winter_design_day = [[24, 18.8]]
361
+ summer_design_day = [[6, 18.3], [22, 18.8], [24, 18.3]]
362
+ default_day = ['Weekday', [6, 18.3], [22, 18.8], [24, 18.3]]
363
+ rules = []
364
+ rules << ['Saturday', '1/1-12/31', 'Sat', [6, 18.3], [18, 18.8], [24, 18.3]]
365
+ rules << ['Sunday', '1/1-12/31', 'Sun', [24, 18.3]]
366
+ options_radiant_heating = { 'name' => ruleset_name,
367
+ 'winter_design_day' => winter_design_day,
368
+ 'summer_design_day' => summer_design_day,
369
+ 'default_day' => default_day,
370
+ 'rules' => rules }
371
+ mean_radiant_heating_schedule = OsLib_Schedules.createComplexSchedule(model, options_radiant_heating)
372
+ schedulesHVAC['mean_radiant_heating'] = mean_radiant_heating_schedule
373
+ # mean radiant cooling setpoint schedule (PNNL values)
374
+ ruleset_name = 'AEDG Office Mean Radiant Cooling Setpoint Schedule'
375
+ winter_design_day = [[6, 26.7], [22, 24.0], [24, 26.7]]
376
+ summer_design_day = [[24, 24.0]]
377
+ default_day = ['Weekday', [6, 26.7], [22, 24.0], [24, 26.7]]
378
+ rules = []
379
+ rules << ['Saturday', '1/1-12/31', 'Sat', [6, 26.7], [18, 24.0], [24, 26.7]]
380
+ rules << ['Sunday', '1/1-12/31', 'Sun', [24, 26.7]]
381
+ options_radiant_cooling = { 'name' => ruleset_name,
382
+ 'winter_design_day' => winter_design_day,
383
+ 'summer_design_day' => summer_design_day,
384
+ 'default_day' => default_day,
385
+ 'rules' => rules }
386
+ mean_radiant_cooling_schedule = OsLib_Schedules.createComplexSchedule(model, options_radiant_cooling)
387
+ schedulesHVAC['mean_radiant_cooling'] = mean_radiant_cooling_schedule
388
+ end
389
+ end
390
+ # SAT schedule
391
+ if options['allHVAC']['primary']['doas']
392
+ # primary airloop is DOAS
393
+ schedulesHVAC['primary_sat'] = sch_ruleset_DOAS_setpoint = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG DOAS Temperature Setpoint Schedule',
394
+ 'default_day' => ['All Days', [24, 20.0]])
395
+ else
396
+ # primary airloop is multizone VAV that cools
397
+ schedulesHVAC['primary_sat'] = sch_ruleset_DOAS_setpoint = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG Cold Deck Temperature Setpoint Schedule',
398
+ 'default_day' => ['All Days', [24, 12.8]])
399
+ end
400
+ schedulesHVAC['ventilation'] = building_ventilation_schedule
401
+ schedulesHVAC['hvac'] = building_HVAC_schedule
402
+ # build new plant schedules as needed
403
+ zoneHVACHotWaterPlant = ['FanCoil', 'DualDuct', 'Baseboard'] # dual duct has fan coil and baseboard
404
+ zoneHVACChilledWaterPlant = ['FanCoil', 'DualDuct'] # dual duct has fan coil
405
+ # hot water
406
+ if (options['allHVAC']['primary']['heat'] == 'Water') || (options['allHVAC']['secondary']['heat'] == 'Water') || zoneHVACHotWaterPlant.include?(options['allHVAC']['zone'])
407
+ schedulesHVAC['hot_water'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HW-Loop-Temp-Schedule',
408
+ 'default_day' => ['All Days', [24, 67.0]])
409
+ end
410
+ # chilled water
411
+ if (options['allHVAC']['primary']['cool'] == 'Water') || (options['allHVAC']['secondary']['cool'] == 'Water') || zoneHVACChilledWaterPlant.include?(options['allHVAC']['zone'])
412
+ schedulesHVAC['chilled_water'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG CW-Loop-Temp-Schedule',
413
+ 'default_day' => ['All Days', [24, 6.7]])
414
+ end
415
+ # heat pump condenser loop schedules
416
+ if options['allHVAC']['zone'] == 'GSHP'
417
+ # there will be a heat pump condenser loop
418
+ # loop setpoint schedule
419
+ schedulesHVAC['hp_loop'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Temp-Schedule',
420
+ 'default_day' => ['All Days', [24, 21]])
421
+ # cooling component schedule (#ML won't need this if a ground loop is actually modeled)
422
+ schedulesHVAC['hp_loop_cooling'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Clg-Temp-Schedule',
423
+ 'default_day' => ['All Days', [24, 21]])
424
+ # heating component schedule
425
+ schedulesHVAC['hp_loop_heating'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Htg-Temp-Schedule',
426
+ 'default_day' => ['All Days', [24, 5]])
427
+ end
428
+ if options['allHVAC']['zone'] == 'WSHP'
429
+ # there will be a heat pump condenser loop
430
+ # loop setpoint schedule
431
+ schedulesHVAC['hp_loop'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Temp-Schedule',
432
+ 'default_day' => ['All Days', [24, 30]]) # PNNL
433
+ # cooling component schedule (#ML won't need this if a ground loop is actually modeled)
434
+ schedulesHVAC['hp_loop_cooling'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Clg-Temp-Schedule',
435
+ 'default_day' => ['All Days', [24, 30]]) # PNNL
436
+ # heating component schedule
437
+ schedulesHVAC['hp_loop_heating'] = OsLib_Schedules.createComplexSchedule(model, 'name' => 'AEDG HP-Loop-Htg-Temp-Schedule',
438
+ 'default_day' => ['All Days', [24, 20]]) # PNNL
439
+ end
440
+
441
+ # pass back schedulesHVAC hash
442
+ result = schedulesHVAC
443
+ return result
444
+ end
445
+
446
+ def self.createHotWaterPlant(model, runner, hot_water_setpoint_schedule, loop_type)
447
+ hot_water_plant = OpenStudio::Model::PlantLoop.new(model)
448
+ hot_water_plant.setName("AEDG #{loop_type} Loop")
449
+ hot_water_plant.setMaximumLoopTemperature(100)
450
+ hot_water_plant.setMinimumLoopTemperature(10)
451
+ loop_sizing = hot_water_plant.sizingPlant
452
+ loop_sizing.setLoopType('Heating')
453
+ if loop_type == 'Hot Water'
454
+ loop_sizing.setDesignLoopExitTemperature(82)
455
+ elsif loop_type == 'Radiant Hot Water'
456
+ loop_sizing.setDesignLoopExitTemperature(60) # ML follows convention of sizing temp being larger than supplu temp
457
+ end
458
+ loop_sizing.setLoopDesignTemperatureDifference(11)
459
+ # create a pump
460
+ pump = OpenStudio::Model::PumpVariableSpeed.new(model)
461
+ pump.setRatedPumpHead(119563) # Pa
462
+ pump.setMotorEfficiency(0.9)
463
+ pump.setCoefficient1ofthePartLoadPerformanceCurve(0)
464
+ pump.setCoefficient2ofthePartLoadPerformanceCurve(0.0216)
465
+ pump.setCoefficient3ofthePartLoadPerformanceCurve(-0.0325)
466
+ pump.setCoefficient4ofthePartLoadPerformanceCurve(1.0095)
467
+ # create a boiler
468
+ boiler = OpenStudio::Model::BoilerHotWater.new(model)
469
+ boiler.setNominalThermalEfficiency(0.9)
470
+ # create a scheduled setpoint manager
471
+ setpoint_manager_scheduled = OpenStudio::Model::SetpointManagerScheduled.new(model, hot_water_setpoint_schedule)
472
+ # create a supply bypass pipe
473
+ pipe_supply_bypass = OpenStudio::Model::PipeAdiabatic.new(model)
474
+ # create a supply outlet pipe
475
+ pipe_supply_outlet = OpenStudio::Model::PipeAdiabatic.new(model)
476
+ # create a demand bypass pipe
477
+ pipe_demand_bypass = OpenStudio::Model::PipeAdiabatic.new(model)
478
+ # create a demand inlet pipe
479
+ pipe_demand_inlet = OpenStudio::Model::PipeAdiabatic.new(model)
480
+ # create a demand outlet pipe
481
+ pipe_demand_outlet = OpenStudio::Model::PipeAdiabatic.new(model)
482
+ # connect components to plant loop
483
+ # supply side components
484
+ hot_water_plant.addSupplyBranchForComponent(boiler)
485
+ hot_water_plant.addSupplyBranchForComponent(pipe_supply_bypass)
486
+ pump.addToNode(hot_water_plant.supplyInletNode)
487
+ pipe_supply_outlet.addToNode(hot_water_plant.supplyOutletNode)
488
+ setpoint_manager_scheduled.addToNode(hot_water_plant.supplyOutletNode)
489
+ # demand side components (water coils are added as they are added to airloops and zoneHVAC)
490
+ hot_water_plant.addDemandBranchForComponent(pipe_demand_bypass)
491
+ pipe_demand_inlet.addToNode(hot_water_plant.demandInletNode)
492
+ pipe_demand_outlet.addToNode(hot_water_plant.demandOutletNode)
493
+
494
+ # pass back hot water plant
495
+ result = hot_water_plant
496
+ return result
497
+ end
498
+
499
+ def self.createChilledWaterPlant(model, runner, chilled_water_setpoint_schedule, loop_type, chillerType)
500
+ # chilled water plant
501
+ chilled_water_plant = OpenStudio::Model::PlantLoop.new(model)
502
+ chilled_water_plant.setName("AEDG #{loop_type} Loop")
503
+ chilled_water_plant.setMaximumLoopTemperature(98)
504
+ chilled_water_plant.setMinimumLoopTemperature(1)
505
+ loop_sizing = chilled_water_plant.sizingPlant
506
+ loop_sizing.setLoopType('Cooling')
507
+ if loop_type == 'Chilled Water'
508
+ loop_sizing.setDesignLoopExitTemperature(6.7)
509
+ elsif loop_type == 'Radiant Chilled Water'
510
+ loop_sizing.setDesignLoopExitTemperature(15)
511
+ end
512
+ loop_sizing.setLoopDesignTemperatureDifference(6.7)
513
+ # create a pump
514
+ pump = OpenStudio::Model::PumpVariableSpeed.new(model)
515
+ pump.setRatedPumpHead(149453) # Pa
516
+ pump.setMotorEfficiency(0.9)
517
+ pump.setCoefficient1ofthePartLoadPerformanceCurve(0)
518
+ pump.setCoefficient2ofthePartLoadPerformanceCurve(0.0216)
519
+ pump.setCoefficient3ofthePartLoadPerformanceCurve(-0.0325)
520
+ pump.setCoefficient4ofthePartLoadPerformanceCurve(1.0095)
521
+ # create a chiller
522
+ if chillerType == 'WaterCooled'
523
+ # create clgCapFuncTempCurve
524
+ clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
525
+ clgCapFuncTempCurve.setCoefficient1Constant(1.07E+00)
526
+ clgCapFuncTempCurve.setCoefficient2x(4.29E-02)
527
+ clgCapFuncTempCurve.setCoefficient3xPOW2(4.17E-04)
528
+ clgCapFuncTempCurve.setCoefficient4y(-8.10E-03)
529
+ clgCapFuncTempCurve.setCoefficient5yPOW2(-4.02E-05)
530
+ clgCapFuncTempCurve.setCoefficient6xTIMESY(-3.86E-04)
531
+ clgCapFuncTempCurve.setMinimumValueofx(0)
532
+ clgCapFuncTempCurve.setMaximumValueofx(20)
533
+ clgCapFuncTempCurve.setMinimumValueofy(0)
534
+ clgCapFuncTempCurve.setMaximumValueofy(50)
535
+ # create eirFuncTempCurve
536
+ eirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
537
+ eirFuncTempCurve.setCoefficient1Constant(4.68E-01)
538
+ eirFuncTempCurve.setCoefficient2x(-1.38E-02)
539
+ eirFuncTempCurve.setCoefficient3xPOW2(6.98E-04)
540
+ eirFuncTempCurve.setCoefficient4y(1.09E-02)
541
+ eirFuncTempCurve.setCoefficient5yPOW2(4.62E-04)
542
+ eirFuncTempCurve.setCoefficient6xTIMESY(-6.82E-04)
543
+ eirFuncTempCurve.setMinimumValueofx(0)
544
+ eirFuncTempCurve.setMaximumValueofx(20)
545
+ eirFuncTempCurve.setMinimumValueofy(0)
546
+ eirFuncTempCurve.setMaximumValueofy(50)
547
+ # create eirFuncPlrCurve
548
+ eirFuncPlrCurve = OpenStudio::Model::CurveQuadratic.new(model)
549
+ eirFuncPlrCurve.setCoefficient1Constant(1.41E-01)
550
+ eirFuncPlrCurve.setCoefficient2x(6.55E-01)
551
+ eirFuncPlrCurve.setCoefficient3xPOW2(2.03E-01)
552
+ eirFuncPlrCurve.setMinimumValueofx(0)
553
+ eirFuncPlrCurve.setMaximumValueofx(1.2)
554
+ # construct chiller
555
+ chiller = OpenStudio::Model::ChillerElectricEIR.new(model, clgCapFuncTempCurve, eirFuncTempCurve, eirFuncPlrCurve)
556
+ chiller.setReferenceCOP(6.1)
557
+ chiller.setCondenserType('WaterCooled')
558
+ chiller.setChillerFlowMode('ConstantFlow')
559
+ elsif chillerType == 'AirCooled'
560
+ # create clgCapFuncTempCurve
561
+ clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
562
+ clgCapFuncTempCurve.setCoefficient1Constant(1.05E+00)
563
+ clgCapFuncTempCurve.setCoefficient2x(3.36E-02)
564
+ clgCapFuncTempCurve.setCoefficient3xPOW2(2.15E-04)
565
+ clgCapFuncTempCurve.setCoefficient4y(-5.18E-03)
566
+ clgCapFuncTempCurve.setCoefficient5yPOW2(-4.42E-05)
567
+ clgCapFuncTempCurve.setCoefficient6xTIMESY(-2.15E-04)
568
+ clgCapFuncTempCurve.setMinimumValueofx(0)
569
+ clgCapFuncTempCurve.setMaximumValueofx(20)
570
+ clgCapFuncTempCurve.setMinimumValueofy(0)
571
+ clgCapFuncTempCurve.setMaximumValueofy(50)
572
+ # create eirFuncTempCurve
573
+ eirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
574
+ eirFuncTempCurve.setCoefficient1Constant(5.83E-01)
575
+ eirFuncTempCurve.setCoefficient2x(-4.04E-03)
576
+ eirFuncTempCurve.setCoefficient3xPOW2(4.68E-04)
577
+ eirFuncTempCurve.setCoefficient4y(-2.24E-04)
578
+ eirFuncTempCurve.setCoefficient5yPOW2(4.81E-04)
579
+ eirFuncTempCurve.setCoefficient6xTIMESY(-6.82E-04)
580
+ eirFuncTempCurve.setMinimumValueofx(0)
581
+ eirFuncTempCurve.setMaximumValueofx(20)
582
+ eirFuncTempCurve.setMinimumValueofy(0)
583
+ eirFuncTempCurve.setMaximumValueofy(50)
584
+ # create eirFuncPlrCurve
585
+ eirFuncPlrCurve = OpenStudio::Model::CurveQuadratic.new(model)
586
+ eirFuncPlrCurve.setCoefficient1Constant(4.19E-02)
587
+ eirFuncPlrCurve.setCoefficient2x(6.25E-01)
588
+ eirFuncPlrCurve.setCoefficient3xPOW2(3.23E-01)
589
+ eirFuncPlrCurve.setMinimumValueofx(0)
590
+ eirFuncPlrCurve.setMaximumValueofx(1.2)
591
+ # construct chiller
592
+ chiller = OpenStudio::Model::ChillerElectricEIR.new(model, clgCapFuncTempCurve, eirFuncTempCurve, eirFuncPlrCurve)
593
+ chiller.setReferenceCOP(2.93)
594
+ chiller.setCondenserType('AirCooled')
595
+ chiller.setChillerFlowMode('ConstantFlow')
596
+ end
597
+ # create a scheduled setpoint manager
598
+ setpoint_manager_scheduled = OpenStudio::Model::SetpointManagerScheduled.new(model, chilled_water_setpoint_schedule)
599
+ # create a supply bypass pipe
600
+ pipe_supply_bypass = OpenStudio::Model::PipeAdiabatic.new(model)
601
+ # create a supply outlet pipe
602
+ pipe_supply_outlet = OpenStudio::Model::PipeAdiabatic.new(model)
603
+ # create a demand bypass pipe
604
+ pipe_demand_bypass = OpenStudio::Model::PipeAdiabatic.new(model)
605
+ # create a demand inlet pipe
606
+ pipe_demand_inlet = OpenStudio::Model::PipeAdiabatic.new(model)
607
+ # create a demand outlet pipe
608
+ pipe_demand_outlet = OpenStudio::Model::PipeAdiabatic.new(model)
609
+ # connect components to plant loop
610
+ # supply side components
611
+ chilled_water_plant.addSupplyBranchForComponent(chiller)
612
+ chilled_water_plant.addSupplyBranchForComponent(pipe_supply_bypass)
613
+ pump.addToNode(chilled_water_plant.supplyInletNode)
614
+ pipe_supply_outlet.addToNode(chilled_water_plant.supplyOutletNode)
615
+ setpoint_manager_scheduled.addToNode(chilled_water_plant.supplyOutletNode)
616
+ # demand side components (water coils are added as they are added to airloops and ZoneHVAC)
617
+ chilled_water_plant.addDemandBranchForComponent(pipe_demand_bypass)
618
+ pipe_demand_inlet.addToNode(chilled_water_plant.demandInletNode)
619
+ pipe_demand_outlet.addToNode(chilled_water_plant.demandOutletNode)
620
+
621
+ # pass back chilled water plant
622
+ result = chilled_water_plant
623
+ return result
624
+ end
625
+
626
+ def self.createCondenserLoop(model, runner, options)
627
+ condenserLoops = {}
628
+
629
+ # check for water-cooled chillers
630
+ waterCooledChiller = false
631
+ model.getChillerElectricEIRs.each do |chiller|
632
+ next if waterCooledChiller == true
633
+ if chiller.condenserType == 'WaterCooled'
634
+ waterCooledChiller = true
635
+ end
636
+ end
637
+ # create condenser loop for water-cooled chillers
638
+ if waterCooledChiller
639
+ # create condenser loop for water-cooled chiller(s)
640
+ condenser_loop = OpenStudio::Model::PlantLoop.new(model)
641
+ condenser_loop.setName('AEDG Condenser Loop')
642
+ condenser_loop.setMaximumLoopTemperature(80)
643
+ condenser_loop.setMinimumLoopTemperature(5)
644
+ loop_sizing = condenser_loop.sizingPlant
645
+ loop_sizing.setLoopType('Condenser')
646
+ loop_sizing.setDesignLoopExitTemperature(29.4)
647
+ loop_sizing.setLoopDesignTemperatureDifference(5.6)
648
+ # create a pump
649
+ pump = OpenStudio::Model::PumpVariableSpeed.new(model)
650
+ pump.setRatedPumpHead(134508) # Pa
651
+ pump.setMotorEfficiency(0.9)
652
+ pump.setCoefficient1ofthePartLoadPerformanceCurve(0)
653
+ pump.setCoefficient2ofthePartLoadPerformanceCurve(0.0216)
654
+ pump.setCoefficient3ofthePartLoadPerformanceCurve(-0.0325)
655
+ pump.setCoefficient4ofthePartLoadPerformanceCurve(1.0095)
656
+ # create a cooling tower
657
+ tower = OpenStudio::Model::CoolingTowerVariableSpeed.new(model)
658
+ # create a supply bypass pipe
659
+ pipe_supply_bypass = OpenStudio::Model::PipeAdiabatic.new(model)
660
+ # create a supply outlet pipe
661
+ pipe_supply_outlet = OpenStudio::Model::PipeAdiabatic.new(model)
662
+ # create a demand bypass pipe
663
+ pipe_demand_bypass = OpenStudio::Model::PipeAdiabatic.new(model)
664
+ # create a demand inlet pipe
665
+ pipe_demand_inlet = OpenStudio::Model::PipeAdiabatic.new(model)
666
+ # create a demand outlet pipe
667
+ pipe_demand_outlet = OpenStudio::Model::PipeAdiabatic.new(model)
668
+ # create a setpoint manager
669
+ setpoint_manager_follow_oa = OpenStudio::Model::SetpointManagerFollowOutdoorAirTemperature.new(model)
670
+ setpoint_manager_follow_oa.setOffsetTemperatureDifference(0)
671
+ setpoint_manager_follow_oa.setMaximumSetpointTemperature(80)
672
+ setpoint_manager_follow_oa.setMinimumSetpointTemperature(5)
673
+ # connect components to plant loop
674
+ # supply side components
675
+ condenser_loop.addSupplyBranchForComponent(tower)
676
+ condenser_loop.addSupplyBranchForComponent(pipe_supply_bypass)
677
+ pump.addToNode(condenser_loop.supplyInletNode)
678
+ pipe_supply_outlet.addToNode(condenser_loop.supplyOutletNode)
679
+ setpoint_manager_follow_oa.addToNode(condenser_loop.supplyOutletNode)
680
+ # demand side components
681
+ model.getChillerElectricEIRs.each do |chiller|
682
+ if chiller.condenserType == 'WaterCooled' # works only if chillers not already connected to condenser loop(s)
683
+ condenser_loop.addDemandBranchForComponent(chiller)
684
+ end
685
+ end
686
+ condenser_loop.addDemandBranchForComponent(pipe_demand_bypass)
687
+ pipe_demand_inlet.addToNode(condenser_loop.demandInletNode)
688
+ pipe_demand_outlet.addToNode(condenser_loop.demandOutletNode)
689
+ condenserLoops['condenser_loop'] = condenser_loop
690
+ end
691
+ if options['zoneHVAC'].include? 'GSHP'
692
+ # create condenser loop for heat pumps
693
+ condenser_loop = OpenStudio::Model::PlantLoop.new(model)
694
+ condenser_loop.setName('AEDG Heat Pump Loop')
695
+ condenser_loop.setMaximumLoopTemperature(80)
696
+ condenser_loop.setMinimumLoopTemperature(1)
697
+ loop_sizing = condenser_loop.sizingPlant
698
+ loop_sizing.setLoopType('Condenser')
699
+ if options['zoneHVAC'] == 'GSHP'
700
+ loop_sizing.setDesignLoopExitTemperature(21)
701
+ loop_sizing.setLoopDesignTemperatureDifference(5)
702
+ elsif options['zoneHVAC'] == 'WSHP'
703
+ loop_sizing.setDesignLoopExitTemperature(30) # PNNL
704
+ loop_sizing.setLoopDesignTemperatureDifference(20) # PNNL
705
+ end
706
+ # create a pump
707
+ pump = OpenStudio::Model::PumpVariableSpeed.new(model)
708
+ pump.setRatedPumpHead(134508) # Pa
709
+ pump.setMotorEfficiency(0.9)
710
+ pump.setCoefficient1ofthePartLoadPerformanceCurve(0)
711
+ pump.setCoefficient2ofthePartLoadPerformanceCurve(0.0216)
712
+ pump.setCoefficient3ofthePartLoadPerformanceCurve(-0.0325)
713
+ pump.setCoefficient4ofthePartLoadPerformanceCurve(1.0095)
714
+ # create a supply bypass pipe
715
+ pipe_supply_bypass = OpenStudio::Model::PipeAdiabatic.new(model)
716
+ # create a supply outlet pipe
717
+ pipe_supply_outlet = OpenStudio::Model::PipeAdiabatic.new(model)
718
+ # create a demand bypass pipe
719
+ pipe_demand_bypass = OpenStudio::Model::PipeAdiabatic.new(model)
720
+ # create a demand inlet pipe
721
+ pipe_demand_inlet = OpenStudio::Model::PipeAdiabatic.new(model)
722
+ # create a demand outlet pipe
723
+ pipe_demand_outlet = OpenStudio::Model::PipeAdiabatic.new(model)
724
+ # create setpoint managers
725
+ setpoint_manager_scheduled_loop = OpenStudio::Model::SetpointManagerScheduled.new(model, options['loop_setpoint_schedule'])
726
+ setpoint_manager_scheduled_cooling = OpenStudio::Model::SetpointManagerScheduled.new(model, options['cooling_setpoint_schedule'])
727
+ setpoint_manager_scheduled_heating = OpenStudio::Model::SetpointManagerScheduled.new(model, options['heating_setpoint_schedule'])
728
+ # connect components to plant loop
729
+ # supply side components
730
+ condenser_loop.addSupplyBranchForComponent(pipe_supply_bypass)
731
+ pump.addToNode(condenser_loop.supplyInletNode)
732
+ pipe_supply_outlet.addToNode(condenser_loop.supplyOutletNode)
733
+ setpoint_manager_scheduled_loop.addToNode(condenser_loop.supplyOutletNode)
734
+ # demand side components
735
+ condenser_loop.addDemandBranchForComponent(pipe_demand_bypass)
736
+ pipe_demand_inlet.addToNode(condenser_loop.demandInletNode)
737
+ pipe_demand_outlet.addToNode(condenser_loop.demandOutletNode)
738
+ # add additional components according to specific system type
739
+ if options['zoneHVAC'] == 'GSHP'
740
+ # add district cooling and heating to supply side
741
+ district_cooling = OpenStudio::Model::DistrictCooling.new(model)
742
+ district_cooling.setNominalCapacity(1000000000000) # large number; no autosizing
743
+ condenser_loop.addSupplyBranchForComponent(district_cooling)
744
+ setpoint_manager_scheduled_cooling.addToNode(district_cooling.outletModelObject.get.to_Node.get)
745
+ district_heating = OpenStudio::Model::DistrictHeating.new(model)
746
+ district_heating.setNominalCapacity(1000000000000) # large number; no autosizing
747
+ district_heating.addToNode(district_cooling.outletModelObject.get.to_Node.get)
748
+ setpoint_manager_scheduled_heating.addToNode(district_heating.outletModelObject.get.to_Node.get)
749
+ # add heat pumps to demand side after they get created
750
+ elsif options['zoneHVAC'] == 'WSHP'
751
+ # add a boiler and cooling tower to supply side
752
+ # create a boiler
753
+ boiler = OpenStudio::Model::BoilerHotWater.new(model)
754
+ boiler.setNominalThermalEfficiency(0.9)
755
+ condenser_loop.addSupplyBranchForComponent(boiler)
756
+ setpoint_manager_scheduled_heating.addToNode(boiler.outletModelObject.get.to_Node.get)
757
+ # create a cooling tower
758
+ tower = OpenStudio::Model::CoolingTowerSingleSpeed.new(model)
759
+ tower.addToNode(boiler.outletModelObject.get.to_Node.get)
760
+ setpoint_manager_scheduled_cooling.addToNode(tower.outletModelObject.get.to_Node.get)
761
+ end
762
+ condenserLoops['heat_pump_loop'] = condenser_loop
763
+ end
764
+
765
+ # pass back condenser loop(s)
766
+ result = condenserLoops
767
+ return result
768
+ end
769
+
770
+ def self.createPrimaryAirLoops(model, runner, options)
771
+ primary_airloops = []
772
+ # create primary airloop for each story
773
+ assignedThermalZones = []
774
+ model.getBuildingStorys.each do |building_story|
775
+ # ML stories need to be reordered from the ground up
776
+ thermalZonesToAdd = []
777
+ building_story.spaces.each do |space|
778
+ # make sure spaces are assigned to thermal zones
779
+ # otherwise might want to send a warning
780
+ if space.thermalZone
781
+ thermal_zone = space.thermalZone.get
782
+ # grab primary zones
783
+ if options['zonesPrimary'].include? thermal_zone
784
+ # make sure zone was not already assigned to another air loop
785
+ unless assignedThermalZones.include? thermal_zone
786
+ # make sure thermal zones are not duplicated (spaces can share thermal zones)
787
+ unless thermalZonesToAdd.include? thermal_zone
788
+ thermalZonesToAdd << thermal_zone
789
+ end
790
+ end
791
+ end
792
+ end
793
+ end
794
+ # make sure thermal zones don't get added to more than one air loop
795
+ assignedThermalZones << thermalZonesToAdd
796
+
797
+ # create new air loop if story contains primary zones
798
+ unless thermalZonesToAdd.empty?
799
+ airloop_primary = OpenStudio::Model::AirLoopHVAC.new(model)
800
+ airloop_primary.setName("AEDG Air Loop HVAC #{building_story.name}")
801
+ # modify system sizing properties
802
+ sizing_system = airloop_primary.sizingSystem
803
+ # set central heating and cooling temperatures for sizing
804
+ sizing_system.setCentralCoolingDesignSupplyAirTemperature(12.8)
805
+ sizing_system.setCentralHeatingDesignSupplyAirTemperature(40) # ML OS default is 16.7
806
+ # load specification
807
+ sizing_system.setSystemOutdoorAirMethod('VentilationRateProcedure') # ML OS default is ZoneSum
808
+ if options['primaryHVAC']['doas']
809
+ sizing_system.setTypeofLoadtoSizeOn('VentilationRequirement') # DOAS
810
+ sizing_system.setAllOutdoorAirinCooling(true) # DOAS
811
+ sizing_system.setAllOutdoorAirinHeating(true) # DOAS
812
+ else
813
+ sizing_system.setTypeofLoadtoSizeOn('Sensible') # VAV
814
+ sizing_system.setAllOutdoorAirinCooling(false) # VAV
815
+ sizing_system.setAllOutdoorAirinHeating(false) # VAV
816
+ end
817
+
818
+ air_loop_comps = []
819
+ # set availability schedule
820
+ airloop_primary.setAvailabilitySchedule(options['hvac_schedule'])
821
+ # create air loop fan
822
+ if options['primaryHVAC']['fan'] == 'Variable'
823
+ # create variable speed fan and set system sizing accordingly
824
+ sizing_system.setMinimumSystemAirFlowRatio(0.3) # DCV
825
+ # variable speed fan
826
+ fan = OpenStudio::Model::FanVariableVolume.new(model, model.alwaysOnDiscreteSchedule)
827
+ fan.setFanEfficiency(0.69)
828
+ fan.setPressureRise(1125) # Pa
829
+ fan.autosizeMaximumFlowRate
830
+ fan.setFanPowerMinimumFlowFraction(0.6)
831
+ fan.setMotorEfficiency(0.9)
832
+ fan.setMotorInAirstreamFraction(1.0)
833
+ air_loop_comps << fan
834
+ else
835
+ sizing_system.setMinimumSystemAirFlowRatio(1.0) # No DCV
836
+ # constant speed fan
837
+ fan = OpenStudio::Model::FanConstantVolume.new(model, model.alwaysOnDiscreteSchedule)
838
+ fan.setFanEfficiency(0.6)
839
+ fan.setPressureRise(500) # Pa
840
+ fan.autosizeMaximumFlowRate
841
+ fan.setMotorEfficiency(0.9)
842
+ fan.setMotorInAirstreamFraction(1.0)
843
+ air_loop_comps << fan
844
+ end
845
+ # create heating coil
846
+ if options['primaryHVAC']['heat'] == 'Water'
847
+ # water coil
848
+ heating_coil = OpenStudio::Model::CoilHeatingWater.new(model, model.alwaysOnDiscreteSchedule)
849
+ air_loop_comps << heating_coil
850
+ else
851
+ # gas coil
852
+ heating_coil = OpenStudio::Model::CoilHeatingGas.new(model, model.alwaysOnDiscreteSchedule)
853
+ air_loop_comps << heating_coil
854
+ end
855
+ # create cooling coil
856
+ if options['primaryHVAC']['cool'] == 'Water'
857
+ # water coil
858
+ cooling_coil = OpenStudio::Model::CoilCoolingWater.new(model, model.alwaysOnDiscreteSchedule)
859
+ air_loop_comps << cooling_coil
860
+ elsif options['primaryHVAC']['cool'] == 'SingleDX'
861
+ # single speed DX coil
862
+ # create cooling coil
863
+ # create clgCapFuncTempCurve
864
+ clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
865
+ clgCapFuncTempCurve.setCoefficient1Constant(0.42415)
866
+ clgCapFuncTempCurve.setCoefficient2x(0.04426)
867
+ clgCapFuncTempCurve.setCoefficient3xPOW2(-0.00042)
868
+ clgCapFuncTempCurve.setCoefficient4y(0.00333)
869
+ clgCapFuncTempCurve.setCoefficient5yPOW2(-0.00008)
870
+ clgCapFuncTempCurve.setCoefficient6xTIMESY(-0.00021)
871
+ clgCapFuncTempCurve.setMinimumValueofx(17)
872
+ clgCapFuncTempCurve.setMaximumValueofx(22)
873
+ clgCapFuncTempCurve.setMinimumValueofy(13)
874
+ clgCapFuncTempCurve.setMaximumValueofy(46)
875
+ # create clgCapFuncFlowFracCurve
876
+ clgCapFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model)
877
+ clgCapFuncFlowFracCurve.setCoefficient1Constant(0.77136)
878
+ clgCapFuncFlowFracCurve.setCoefficient2x(0.34053)
879
+ clgCapFuncFlowFracCurve.setCoefficient3xPOW2(-0.11088)
880
+ clgCapFuncFlowFracCurve.setMinimumValueofx(0.75918)
881
+ clgCapFuncFlowFracCurve.setMaximumValueofx(1.13877)
882
+ # create clgEirFuncTempCurve
883
+ clgEirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
884
+ clgEirFuncTempCurve.setCoefficient1Constant(1.23649)
885
+ clgEirFuncTempCurve.setCoefficient2x(-0.02431)
886
+ clgEirFuncTempCurve.setCoefficient3xPOW2(0.00057)
887
+ clgEirFuncTempCurve.setCoefficient4y(-0.01434)
888
+ clgEirFuncTempCurve.setCoefficient5yPOW2(0.00063)
889
+ clgEirFuncTempCurve.setCoefficient6xTIMESY(-0.00038)
890
+ clgEirFuncTempCurve.setMinimumValueofx(17)
891
+ clgEirFuncTempCurve.setMaximumValueofx(22)
892
+ clgEirFuncTempCurve.setMinimumValueofy(13)
893
+ clgEirFuncTempCurve.setMaximumValueofy(46)
894
+ # create clgEirFuncFlowFracCurve
895
+ clgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model)
896
+ clgEirFuncFlowFracCurve.setCoefficient1Constant(1.20550)
897
+ clgEirFuncFlowFracCurve.setCoefficient2x(-0.32953)
898
+ clgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.12308)
899
+ clgEirFuncFlowFracCurve.setMinimumValueofx(0.75918)
900
+ clgEirFuncFlowFracCurve.setMaximumValueofx(1.13877)
901
+ # create clgPlrCurve
902
+ clgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model)
903
+ clgPlrCurve.setCoefficient1Constant(0.77100)
904
+ clgPlrCurve.setCoefficient2x(0.22900)
905
+ clgPlrCurve.setCoefficient3xPOW2(0.0)
906
+ clgPlrCurve.setMinimumValueofx(0.0)
907
+ clgPlrCurve.setMaximumValueofx(1.0)
908
+ # cooling coil
909
+ cooling_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model,
910
+ model.alwaysOnDiscreteSchedule,
911
+ clgCapFuncTempCurve,
912
+ clgCapFuncFlowFracCurve,
913
+ clgEirFuncTempCurve,
914
+ clgEirFuncFlowFracCurve,
915
+ clgPlrCurve)
916
+ cooling_coil.setRatedCOP(OpenStudio::OptionalDouble.new(4))
917
+ air_loop_comps << cooling_coil
918
+ else
919
+ # two speed DX coil (PNNL curves)
920
+ # create cooling coil
921
+ # create clgCapFuncTempCurve
922
+ clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
923
+ clgCapFuncTempCurve.setCoefficient1Constant(1.39072)
924
+ clgCapFuncTempCurve.setCoefficient2x(-0.0529058)
925
+ clgCapFuncTempCurve.setCoefficient3xPOW2(0.0018423)
926
+ clgCapFuncTempCurve.setCoefficient4y(0.00058267)
927
+ clgCapFuncTempCurve.setCoefficient5yPOW2(-0.000186814)
928
+ clgCapFuncTempCurve.setCoefficient6xTIMESY(0.000265159)
929
+ clgCapFuncTempCurve.setMinimumValueofx(16.5556)
930
+ clgCapFuncTempCurve.setMaximumValueofx(22.1111)
931
+ clgCapFuncTempCurve.setMinimumValueofy(23.7778)
932
+ clgCapFuncTempCurve.setMaximumValueofy(47.66)
933
+ # create clgCapFuncFlowFracCurve
934
+ clgCapFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model)
935
+ clgCapFuncFlowFracCurve.setCoefficient1Constant(0.718954)
936
+ clgCapFuncFlowFracCurve.setCoefficient2x(0.435436)
937
+ clgCapFuncFlowFracCurve.setCoefficient3xPOW2(-0.154193)
938
+ clgCapFuncFlowFracCurve.setMinimumValueofx(0.75)
939
+ clgCapFuncFlowFracCurve.setMaximumValueofx(1.25)
940
+ # create clgEirFuncTempCurve
941
+ clgEirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
942
+ clgEirFuncTempCurve.setCoefficient1Constant(-0.536161)
943
+ clgEirFuncTempCurve.setCoefficient2x(0.105138)
944
+ clgEirFuncTempCurve.setCoefficient3xPOW2(-0.00172659)
945
+ clgEirFuncTempCurve.setCoefficient4y(0.0149848)
946
+ clgEirFuncTempCurve.setCoefficient5yPOW2(0.000659948)
947
+ clgEirFuncTempCurve.setCoefficient6xTIMESY(-0.0017385)
948
+ clgEirFuncTempCurve.setMinimumValueofx(16.5556)
949
+ clgEirFuncTempCurve.setMaximumValueofx(22.1111)
950
+ clgEirFuncTempCurve.setMinimumValueofy(23.7778)
951
+ clgEirFuncTempCurve.setMaximumValueofy(47.66)
952
+ # create clgEirFuncFlowFracCurve
953
+ clgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model)
954
+ clgEirFuncFlowFracCurve.setCoefficient1Constant(1.19525)
955
+ clgEirFuncFlowFracCurve.setCoefficient2x(-0.306138)
956
+ clgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.110973)
957
+ clgEirFuncFlowFracCurve.setMinimumValueofx(0.75)
958
+ clgEirFuncFlowFracCurve.setMaximumValueofx(1.25)
959
+ # create clgPlrCurve
960
+ clgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model)
961
+ clgPlrCurve.setCoefficient1Constant(0.77100)
962
+ clgPlrCurve.setCoefficient2x(0.22900)
963
+ clgPlrCurve.setCoefficient3xPOW2(0.0)
964
+ clgPlrCurve.setMinimumValueofx(0.0)
965
+ clgPlrCurve.setMaximumValueofx(1.0)
966
+ # cooling coil
967
+ cooling_coil = OpenStudio::Model::CoilCoolingDXTwoSpeed.new(model,
968
+ model.alwaysOnDiscreteSchedule,
969
+ clgCapFuncTempCurve,
970
+ clgCapFuncFlowFracCurve,
971
+ clgEirFuncTempCurve,
972
+ clgEirFuncFlowFracCurve,
973
+ clgPlrCurve,
974
+ clgCapFuncTempCurve,
975
+ clgEirFuncTempCurve)
976
+ cooling_coil.setRatedHighSpeedCOP(4)
977
+ cooling_coil.setRatedLowSpeedCOP(4)
978
+ air_loop_comps << cooling_coil
979
+ end
980
+ unless options['zoneHVAC'] == 'DualDuct'
981
+ # create controller outdoor air
982
+ controller_OA = OpenStudio::Model::ControllerOutdoorAir.new(model)
983
+ controller_OA.autosizeMinimumOutdoorAirFlowRate
984
+ controller_OA.autosizeMaximumOutdoorAirFlowRate
985
+ # create ventilation schedules and assign to OA controller
986
+ if options['primaryHVAC']['doas']
987
+ controller_OA.setMinimumFractionofOutdoorAirSchedule(model.alwaysOnDiscreteSchedule)
988
+ controller_OA.setMaximumFractionofOutdoorAirSchedule(model.alwaysOnDiscreteSchedule)
989
+ else
990
+ # multizone VAV that ventilates
991
+ controller_OA.setMaximumFractionofOutdoorAirSchedule(options['ventilation_schedule'])
992
+ controller_OA.setEconomizerControlType('DifferentialEnthalpy')
993
+ # add night cycling (ML would people actually do this for a VAV system?))
994
+ airloop_primary.setNightCycleControlType('CycleOnAny') # ML Does this work with variable speed fans?
995
+ end
996
+ controller_OA.setHeatRecoveryBypassControlType('BypassWhenOAFlowGreaterThanMinimum')
997
+ # create outdoor air system
998
+ system_OA = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, controller_OA)
999
+ air_loop_comps << system_OA
1000
+ # create ERV
1001
+ heat_exchanger = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(model)
1002
+ heat_exchanger.setAvailabilitySchedule(model.alwaysOnDiscreteSchedule)
1003
+ sensible_eff = 0.75
1004
+ latent_eff = 0.69
1005
+ heat_exchanger.setSensibleEffectivenessat100CoolingAirFlow(sensible_eff)
1006
+ heat_exchanger.setSensibleEffectivenessat100HeatingAirFlow(sensible_eff)
1007
+ heat_exchanger.setSensibleEffectivenessat75CoolingAirFlow(sensible_eff)
1008
+ heat_exchanger.setSensibleEffectivenessat75HeatingAirFlow(sensible_eff)
1009
+ heat_exchanger.setLatentEffectivenessat100CoolingAirFlow(latent_eff)
1010
+ heat_exchanger.setLatentEffectivenessat100HeatingAirFlow(latent_eff)
1011
+ heat_exchanger.setLatentEffectivenessat75CoolingAirFlow(latent_eff)
1012
+ heat_exchanger.setLatentEffectivenessat75HeatingAirFlow(latent_eff)
1013
+ heat_exchanger.setFrostControlType('ExhaustOnly')
1014
+ heat_exchanger.setThresholdTemperature(-12.2)
1015
+ heat_exchanger.setInitialDefrostTimeFraction(0.1670)
1016
+ heat_exchanger.setRateofDefrostTimeFractionIncrease(0.0240)
1017
+ heat_exchanger.setEconomizerLockout(false)
1018
+ end
1019
+ # create scheduled setpoint manager for airloop
1020
+ if options['primaryHVAC']['doas'] || (options['zoneHVAC'] == 'DualDuct')
1021
+ # DOAS or VAV for cooling and not ventilation
1022
+ setpoint_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, options['primary_sat_schedule'])
1023
+ else
1024
+ # VAV for cooling and ventilation
1025
+ setpoint_manager = OpenStudio::Model::SetpointManagerOutdoorAirReset.new(model)
1026
+ setpoint_manager.setSetpointatOutdoorLowTemperature(15.6)
1027
+ setpoint_manager.setOutdoorLowTemperature(14.4)
1028
+ setpoint_manager.setSetpointatOutdoorHighTemperature(12.8)
1029
+ setpoint_manager.setOutdoorHighTemperature(21.1)
1030
+ end
1031
+ # connect components to airloop
1032
+ # find the supply inlet node of the airloop
1033
+ airloop_supply_inlet = airloop_primary.supplyInletNode
1034
+ # add the components to the airloop
1035
+ air_loop_comps.each do |comp|
1036
+ comp.addToNode(airloop_supply_inlet)
1037
+ if comp.to_CoilHeatingWater.is_initialized
1038
+ options['hot_water_plant'].addDemandBranchForComponent(comp)
1039
+ elsif comp.to_CoilCoolingWater.is_initialized
1040
+ options['chilled_water_plant'].addDemandBranchForComponent(comp)
1041
+ end
1042
+ end
1043
+ # add erv to outdoor air system
1044
+ unless options['zoneHVAC'] == 'DualDuct'
1045
+ heat_exchanger.addToNode(system_OA.outboardOANode.get)
1046
+ end
1047
+ # add setpoint manager to supply equipment outlet node
1048
+ setpoint_manager.addToNode(airloop_primary.supplyOutletNode)
1049
+ # add thermal zones to airloop
1050
+ thermalZonesToAdd.each do |zone|
1051
+ # make an air terminal for the zone
1052
+ if options['primaryHVAC']['fan'] == 'Variable'
1053
+ air_terminal = OpenStudio::Model::AirTerminalSingleDuctVAVNoReheat.new(model, model.alwaysOnDiscreteSchedule)
1054
+ else
1055
+ air_terminal = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, model.alwaysOnDiscreteSchedule)
1056
+ end
1057
+ # attach new terminal to the zone and to the airloop
1058
+ airloop_primary.addBranchForZone(zone, air_terminal.to_StraightComponent)
1059
+ end
1060
+ primary_airloops << airloop_primary
1061
+ end
1062
+ end
1063
+
1064
+ # pass back primary airloops
1065
+ result = primary_airloops
1066
+ return result
1067
+ end
1068
+
1069
+ def self.createSecondaryAirLoops(model, runner, options)
1070
+ secondary_airloops = []
1071
+ # create secondary airloop for each secondary zone
1072
+ model.getThermalZones.each do |zone|
1073
+ if options['zonesSecondary'].include? zone
1074
+ # create secondary airloop
1075
+ airloop_secondary = OpenStudio::Model::AirLoopHVAC.new(model)
1076
+ airloop_secondary.setName("AEDG Air Loop HVAC #{zone.name}")
1077
+ # modify system sizing properties
1078
+ sizing_system = airloop_secondary.sizingSystem
1079
+ # set central heating and cooling temperatures for sizing
1080
+ sizing_system.setCentralCoolingDesignSupplyAirTemperature(12.8)
1081
+ sizing_system.setCentralHeatingDesignSupplyAirTemperature(40) # ML OS default is 16.7
1082
+ # load specification
1083
+ sizing_system.setSystemOutdoorAirMethod('VentilationRateProcedure') # ML OS default is ZoneSum
1084
+ sizing_system.setTypeofLoadtoSizeOn('Sensible') # PSZ
1085
+ sizing_system.setAllOutdoorAirinCooling(false) # PSZ
1086
+ sizing_system.setAllOutdoorAirinHeating(false) # PSZ
1087
+ sizing_system.setMinimumSystemAirFlowRatio(1.0) # Constant volume fan
1088
+ air_loop_comps = []
1089
+ # set availability schedule (HVAC operation schedule)
1090
+ airloop_secondary.setAvailabilitySchedule(options['hvac_schedule'])
1091
+ if options['secondaryHVAC']['fan'] == 'Variable'
1092
+ # create variable speed fan and set system sizing accordingly
1093
+ sizing_system.setMinimumSystemAirFlowRatio(0.3) # DCV
1094
+ # variable speed fan
1095
+ fan = OpenStudio::Model::FanVariableVolume.new(model, model.alwaysOnDiscreteSchedule)
1096
+ fan.setFanEfficiency(0.69)
1097
+ fan.setPressureRise(1125) # Pa
1098
+ fan.autosizeMaximumFlowRate
1099
+ fan.setFanPowerMinimumFlowFraction(0.6)
1100
+ fan.setMotorEfficiency(0.9)
1101
+ fan.setMotorInAirstreamFraction(1.0)
1102
+ air_loop_comps << fan
1103
+ else
1104
+ sizing_system.setMinimumSystemAirFlowRatio(1.0) # No DCV
1105
+ # constant speed fan
1106
+ fan = OpenStudio::Model::FanConstantVolume.new(model, model.alwaysOnDiscreteSchedule)
1107
+ fan.setFanEfficiency(0.6)
1108
+ fan.setPressureRise(500) # Pa
1109
+ fan.autosizeMaximumFlowRate
1110
+ fan.setMotorEfficiency(0.9)
1111
+ fan.setMotorInAirstreamFraction(1.0)
1112
+ air_loop_comps << fan
1113
+ end
1114
+ # create cooling coil
1115
+ if options['secondaryHVAC']['cool'] == 'Water'
1116
+ # water coil
1117
+ cooling_coil = OpenStudio::Model::CoilCoolingWater.new(model, model.alwaysOnDiscreteSchedule)
1118
+ air_loop_comps << cooling_coil
1119
+ elsif options['secondaryHVAC']['cool'] == 'SingleDX'
1120
+ # single speed DX coil
1121
+ # create cooling coil
1122
+ # create clgCapFuncTempCurve
1123
+ clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
1124
+ clgCapFuncTempCurve.setCoefficient1Constant(0.42415)
1125
+ clgCapFuncTempCurve.setCoefficient2x(0.04426)
1126
+ clgCapFuncTempCurve.setCoefficient3xPOW2(-0.00042)
1127
+ clgCapFuncTempCurve.setCoefficient4y(0.00333)
1128
+ clgCapFuncTempCurve.setCoefficient5yPOW2(-0.00008)
1129
+ clgCapFuncTempCurve.setCoefficient6xTIMESY(-0.00021)
1130
+ clgCapFuncTempCurve.setMinimumValueofx(17)
1131
+ clgCapFuncTempCurve.setMaximumValueofx(22)
1132
+ clgCapFuncTempCurve.setMinimumValueofy(13)
1133
+ clgCapFuncTempCurve.setMaximumValueofy(46)
1134
+ # create clgCapFuncFlowFracCurve
1135
+ clgCapFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model)
1136
+ clgCapFuncFlowFracCurve.setCoefficient1Constant(0.77136)
1137
+ clgCapFuncFlowFracCurve.setCoefficient2x(0.34053)
1138
+ clgCapFuncFlowFracCurve.setCoefficient3xPOW2(-0.11088)
1139
+ clgCapFuncFlowFracCurve.setMinimumValueofx(0.75918)
1140
+ clgCapFuncFlowFracCurve.setMaximumValueofx(1.13877)
1141
+ # create clgEirFuncTempCurve
1142
+ clgEirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
1143
+ clgEirFuncTempCurve.setCoefficient1Constant(1.23649)
1144
+ clgEirFuncTempCurve.setCoefficient2x(-0.02431)
1145
+ clgEirFuncTempCurve.setCoefficient3xPOW2(0.00057)
1146
+ clgEirFuncTempCurve.setCoefficient4y(-0.01434)
1147
+ clgEirFuncTempCurve.setCoefficient5yPOW2(0.00063)
1148
+ clgEirFuncTempCurve.setCoefficient6xTIMESY(-0.00038)
1149
+ clgEirFuncTempCurve.setMinimumValueofx(17)
1150
+ clgEirFuncTempCurve.setMaximumValueofx(22)
1151
+ clgEirFuncTempCurve.setMinimumValueofy(13)
1152
+ clgEirFuncTempCurve.setMaximumValueofy(46)
1153
+ # create clgEirFuncFlowFracCurve
1154
+ clgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model)
1155
+ clgEirFuncFlowFracCurve.setCoefficient1Constant(1.20550)
1156
+ clgEirFuncFlowFracCurve.setCoefficient2x(-0.32953)
1157
+ clgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.12308)
1158
+ clgEirFuncFlowFracCurve.setMinimumValueofx(0.75918)
1159
+ clgEirFuncFlowFracCurve.setMaximumValueofx(1.13877)
1160
+ # create clgPlrCurve
1161
+ clgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model)
1162
+ clgPlrCurve.setCoefficient1Constant(0.77100)
1163
+ clgPlrCurve.setCoefficient2x(0.22900)
1164
+ clgPlrCurve.setCoefficient3xPOW2(0.0)
1165
+ clgPlrCurve.setMinimumValueofx(0.0)
1166
+ clgPlrCurve.setMaximumValueofx(1.0)
1167
+ # cooling coil
1168
+ cooling_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model,
1169
+ model.alwaysOnDiscreteSchedule,
1170
+ clgCapFuncTempCurve,
1171
+ clgCapFuncFlowFracCurve,
1172
+ clgEirFuncTempCurve,
1173
+ clgEirFuncFlowFracCurve,
1174
+ clgPlrCurve)
1175
+ cooling_coil.setRatedCOP(OpenStudio::OptionalDouble.new(4))
1176
+ air_loop_comps << cooling_coil
1177
+ else
1178
+ # two speed DX coil (PNNL curves)
1179
+ # create cooling coil
1180
+ # create clgCapFuncTempCurve
1181
+ clgCapFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
1182
+ clgCapFuncTempCurve.setCoefficient1Constant(1.39072)
1183
+ clgCapFuncTempCurve.setCoefficient2x(-0.0529058)
1184
+ clgCapFuncTempCurve.setCoefficient3xPOW2(0.0018423)
1185
+ clgCapFuncTempCurve.setCoefficient4y(0.00058267)
1186
+ clgCapFuncTempCurve.setCoefficient5yPOW2(-0.000186814)
1187
+ clgCapFuncTempCurve.setCoefficient6xTIMESY(0.000265159)
1188
+ clgCapFuncTempCurve.setMinimumValueofx(16.5556)
1189
+ clgCapFuncTempCurve.setMaximumValueofx(22.1111)
1190
+ clgCapFuncTempCurve.setMinimumValueofy(23.7778)
1191
+ clgCapFuncTempCurve.setMaximumValueofy(47.66)
1192
+ # create clgCapFuncFlowFracCurve
1193
+ clgCapFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model)
1194
+ clgCapFuncFlowFracCurve.setCoefficient1Constant(0.718954)
1195
+ clgCapFuncFlowFracCurve.setCoefficient2x(0.435436)
1196
+ clgCapFuncFlowFracCurve.setCoefficient3xPOW2(-0.154193)
1197
+ clgCapFuncFlowFracCurve.setMinimumValueofx(0.75)
1198
+ clgCapFuncFlowFracCurve.setMaximumValueofx(1.25)
1199
+ # create clgEirFuncTempCurve
1200
+ clgEirFuncTempCurve = OpenStudio::Model::CurveBiquadratic.new(model)
1201
+ clgEirFuncTempCurve.setCoefficient1Constant(-0.536161)
1202
+ clgEirFuncTempCurve.setCoefficient2x(0.105138)
1203
+ clgEirFuncTempCurve.setCoefficient3xPOW2(-0.00172659)
1204
+ clgEirFuncTempCurve.setCoefficient4y(0.0149848)
1205
+ clgEirFuncTempCurve.setCoefficient5yPOW2(0.000659948)
1206
+ clgEirFuncTempCurve.setCoefficient6xTIMESY(-0.0017385)
1207
+ clgEirFuncTempCurve.setMinimumValueofx(16.5556)
1208
+ clgEirFuncTempCurve.setMaximumValueofx(22.1111)
1209
+ clgEirFuncTempCurve.setMinimumValueofy(23.7778)
1210
+ clgEirFuncTempCurve.setMaximumValueofy(47.66)
1211
+ # create clgEirFuncFlowFracCurve
1212
+ clgEirFuncFlowFracCurve = OpenStudio::Model::CurveQuadratic.new(model)
1213
+ clgEirFuncFlowFracCurve.setCoefficient1Constant(1.19525)
1214
+ clgEirFuncFlowFracCurve.setCoefficient2x(-0.306138)
1215
+ clgEirFuncFlowFracCurve.setCoefficient3xPOW2(0.110973)
1216
+ clgEirFuncFlowFracCurve.setMinimumValueofx(0.75)
1217
+ clgEirFuncFlowFracCurve.setMaximumValueofx(1.25)
1218
+ # create clgPlrCurve
1219
+ clgPlrCurve = OpenStudio::Model::CurveQuadratic.new(model)
1220
+ clgPlrCurve.setCoefficient1Constant(0.77100)
1221
+ clgPlrCurve.setCoefficient2x(0.22900)
1222
+ clgPlrCurve.setCoefficient3xPOW2(0.0)
1223
+ clgPlrCurve.setMinimumValueofx(0.0)
1224
+ clgPlrCurve.setMaximumValueofx(1.0)
1225
+ # cooling coil
1226
+ cooling_coil = OpenStudio::Model::CoilCoolingDXTwoSpeed.new(model,
1227
+ model.alwaysOnDiscreteSchedule,
1228
+ clgCapFuncTempCurve,
1229
+ clgCapFuncFlowFracCurve,
1230
+ clgEirFuncTempCurve,
1231
+ clgEirFuncFlowFracCurve,
1232
+ clgPlrCurve,
1233
+ clgCapFuncTempCurve,
1234
+ clgEirFuncTempCurve)
1235
+ cooling_coil.setRatedHighSpeedCOP(4)
1236
+ cooling_coil.setRatedLowSpeedCOP(4)
1237
+ air_loop_comps << cooling_coil
1238
+ end
1239
+ if options['secondaryHVAC']['heat'] == 'Water'
1240
+ # water coil
1241
+ heating_coil = OpenStudio::Model::CoilHeatingWater.new(model, model.alwaysOnDiscreteSchedule)
1242
+ air_loop_comps << heating_coil
1243
+ else
1244
+ # gas coil
1245
+ heating_coil = OpenStudio::Model::CoilHeatingGas.new(model, model.alwaysOnDiscreteSchedule)
1246
+ air_loop_comps << heating_coil
1247
+ end
1248
+ # create controller outdoor air
1249
+ controller_OA = OpenStudio::Model::ControllerOutdoorAir.new(model)
1250
+ controller_OA.autosizeMinimumOutdoorAirFlowRate
1251
+ controller_OA.autosizeMaximumOutdoorAirFlowRate
1252
+ controller_OA.setEconomizerControlType('DifferentialEnthalpy')
1253
+ controller_OA.setMaximumFractionofOutdoorAirSchedule(options['ventilation_schedule'])
1254
+ controller_OA.setHeatRecoveryBypassControlType('BypassWhenOAFlowGreaterThanMinimum')
1255
+ # create outdoor air system
1256
+ system_OA = OpenStudio::Model::AirLoopHVACOutdoorAirSystem.new(model, controller_OA)
1257
+ air_loop_comps << system_OA
1258
+ # create ERV
1259
+ heat_exchanger = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(model)
1260
+ heat_exchanger.setAvailabilitySchedule(model.alwaysOnDiscreteSchedule)
1261
+ sensible_eff = 0.75
1262
+ latent_eff = 0.69
1263
+ heat_exchanger.setSensibleEffectivenessat100CoolingAirFlow(sensible_eff)
1264
+ heat_exchanger.setSensibleEffectivenessat100HeatingAirFlow(sensible_eff)
1265
+ heat_exchanger.setSensibleEffectivenessat75CoolingAirFlow(sensible_eff)
1266
+ heat_exchanger.setSensibleEffectivenessat75HeatingAirFlow(sensible_eff)
1267
+ heat_exchanger.setLatentEffectivenessat100CoolingAirFlow(latent_eff)
1268
+ heat_exchanger.setLatentEffectivenessat100HeatingAirFlow(latent_eff)
1269
+ heat_exchanger.setLatentEffectivenessat75CoolingAirFlow(latent_eff)
1270
+ heat_exchanger.setLatentEffectivenessat75HeatingAirFlow(latent_eff)
1271
+ heat_exchanger.setFrostControlType('ExhaustOnly')
1272
+ heat_exchanger.setThresholdTemperature(-12.2)
1273
+ heat_exchanger.setInitialDefrostTimeFraction(0.1670)
1274
+ heat_exchanger.setRateofDefrostTimeFractionIncrease(0.0240)
1275
+ heat_exchanger.setEconomizerLockout(false)
1276
+ # create setpoint manager for airloop
1277
+ setpoint_manager = OpenStudio::Model::SetpointManagerSingleZoneReheat.new(model)
1278
+ setpoint_manager.setMinimumSupplyAirTemperature(10)
1279
+ setpoint_manager.setMaximumSupplyAirTemperature(50)
1280
+ setpoint_manager.setControlZone(zone)
1281
+ # connect components to airloop
1282
+ # find the supply inlet node of the airloop
1283
+ airloop_supply_inlet = airloop_secondary.supplyInletNode
1284
+ # add the components to the airloop
1285
+ air_loop_comps.each do |comp|
1286
+ comp.addToNode(airloop_supply_inlet)
1287
+ if comp.to_CoilHeatingWater.is_initialized
1288
+ options['hot_water_plant'].addDemandBranchForComponent(comp)
1289
+ elsif comp.to_CoilCoolingWater.is_initialized
1290
+ options['chilled_water_plant'].addDemandBranchForComponent(comp)
1291
+ end
1292
+ end
1293
+ # add erv to outdoor air system
1294
+ heat_exchanger.addToNode(system_OA.outboardOANode.get)
1295
+ # add setpoint manager to supply equipment outlet node
1296
+ setpoint_manager.addToNode(airloop_secondary.supplyOutletNode)
1297
+ # add thermal zone to airloop
1298
+ if options['secondaryHVAC']['fan'] == 'Variable'
1299
+ air_terminal = OpenStudio::Model::AirTerminalSingleDuctVAVNoReheat.new(model, model.alwaysOnDiscreteSchedule)
1300
+ else
1301
+ air_terminal = OpenStudio::Model::AirTerminalSingleDuctUncontrolled.new(model, model.alwaysOnDiscreteSchedule)
1302
+ end
1303
+ # attach new terminal to the zone and to the airloop
1304
+ airloop_secondary.addBranchForZone(zone, air_terminal.to_StraightComponent)
1305
+ # add night cycling
1306
+ airloop_secondary.setNightCycleControlType('CycleOnAny') # ML Does this work with variable speed fans?
1307
+ secondary_airloops << airloop_secondary
1308
+ end
1309
+ end
1310
+
1311
+ # pass back secondary airloops
1312
+ result = secondary_airloops
1313
+ return result
1314
+ end
1315
+
1316
+ def self.createPrimaryZoneEquipment(model, runner, options)
1317
+ model.getThermalZones.each do |zone|
1318
+ if options['zonesPrimary'].include? zone
1319
+ if options['zoneHVAC'] == 'FanCoil'
1320
+ # create fan coil
1321
+ # create fan
1322
+ fan = OpenStudio::Model::FanOnOff.new(model, model.alwaysOnDiscreteSchedule)
1323
+ fan.setFanEfficiency(0.5)
1324
+ fan.setPressureRise(75) # Pa
1325
+ fan.autosizeMaximumFlowRate
1326
+ fan.setMotorEfficiency(0.9)
1327
+ fan.setMotorInAirstreamFraction(1.0)
1328
+ # create cooling coil and connect to chilled water plant
1329
+ cooling_coil = OpenStudio::Model::CoilCoolingWater.new(model, model.alwaysOnDiscreteSchedule)
1330
+ options['chilled_water_plant'].addDemandBranchForComponent(cooling_coil)
1331
+ # create heating coil and connect to hot water plant
1332
+ heating_coil = OpenStudio::Model::CoilHeatingWater.new(model, model.alwaysOnDiscreteSchedule)
1333
+ options['hot_water_plant'].addDemandBranchForComponent(heating_coil)
1334
+ # construct fan coil
1335
+ fan_coil = OpenStudio::Model::ZoneHVACFourPipeFanCoil.new(model,
1336
+ model.alwaysOnDiscreteSchedule,
1337
+ fan,
1338
+ cooling_coil,
1339
+ heating_coil)
1340
+ fan_coil.setMaximumOutdoorAirFlowRate(0)
1341
+ # add fan coil to thermal zone
1342
+ fan_coil.addToThermalZone(zone)
1343
+ elsif options['zoneHVAC'].include? 'GSHP'
1344
+ # create water source heat pump and attach to heat pump loop
1345
+ # create fan
1346
+ fan = OpenStudio::Model::FanOnOff.new(model, model.alwaysOnDiscreteSchedule)
1347
+ fan.setFanEfficiency(0.5)
1348
+ fan.setPressureRise(75) # Pa
1349
+ fan.autosizeMaximumFlowRate
1350
+ fan.setMotorEfficiency(0.9)
1351
+ fan.setMotorInAirstreamFraction(1.0)
1352
+ # create cooling coil and connect to heat pump loop
1353
+ cooling_coil = OpenStudio::Model::CoilCoolingWaterToAirHeatPumpEquationFit.new(model)
1354
+ cooling_coil.setRatedCoolingCoefficientofPerformance(6.45)
1355
+ cooling_coil.setTotalCoolingCapacityCoefficient1(-9.149069561)
1356
+ cooling_coil.setTotalCoolingCapacityCoefficient2(10.87814026)
1357
+ cooling_coil.setTotalCoolingCapacityCoefficient3(-1.718780157)
1358
+ cooling_coil.setTotalCoolingCapacityCoefficient4(0.746414818)
1359
+ cooling_coil.setTotalCoolingCapacityCoefficient5(0.0)
1360
+ cooling_coil.setSensibleCoolingCapacityCoefficient1(-5.462690012)
1361
+ cooling_coil.setSensibleCoolingCapacityCoefficient2(17.95968138)
1362
+ cooling_coil.setSensibleCoolingCapacityCoefficient3(-11.87818402)
1363
+ cooling_coil.setSensibleCoolingCapacityCoefficient4(-0.980163419)
1364
+ cooling_coil.setSensibleCoolingCapacityCoefficient5(0.767285761)
1365
+ cooling_coil.setSensibleCoolingCapacityCoefficient6(0.0)
1366
+ cooling_coil.setCoolingPowerConsumptionCoefficient1(-3.205409884)
1367
+ cooling_coil.setCoolingPowerConsumptionCoefficient2(-0.976409399)
1368
+ cooling_coil.setCoolingPowerConsumptionCoefficient3(3.97892546)
1369
+ cooling_coil.setCoolingPowerConsumptionCoefficient4(0.938181818)
1370
+ cooling_coil.setCoolingPowerConsumptionCoefficient5(0.0)
1371
+ options['heat_pump_loop'].addDemandBranchForComponent(cooling_coil)
1372
+ # create heating coil and connect to heat pump loop
1373
+ heating_coil = OpenStudio::Model::CoilHeatingWaterToAirHeatPumpEquationFit.new(model)
1374
+ heating_coil.setRatedHeatingCoefficientofPerformance(4.0)
1375
+ heating_coil.setHeatingCapacityCoefficient1(-1.361311959)
1376
+ heating_coil.setHeatingCapacityCoefficient2(-2.471798046)
1377
+ heating_coil.setHeatingCapacityCoefficient3(4.173164514)
1378
+ heating_coil.setHeatingCapacityCoefficient4(0.640757401)
1379
+ heating_coil.setHeatingCapacityCoefficient5(0.0)
1380
+ heating_coil.setHeatingPowerConsumptionCoefficient1(-2.176941116)
1381
+ heating_coil.setHeatingPowerConsumptionCoefficient2(0.832114286)
1382
+ heating_coil.setHeatingPowerConsumptionCoefficient3(1.570743399)
1383
+ heating_coil.setHeatingPowerConsumptionCoefficient4(0.690793651)
1384
+ heating_coil.setHeatingPowerConsumptionCoefficient5(0.0)
1385
+ options['heat_pump_loop'].addDemandBranchForComponent(heating_coil)
1386
+ # create supplemental heating coil
1387
+ supplemental_heating_coil = OpenStudio::Model::CoilHeatingElectric.new(model, model.alwaysOnDiscreteSchedule)
1388
+ # construct heat pump
1389
+ heat_pump = OpenStudio::Model::ZoneHVACWaterToAirHeatPump.new(model,
1390
+ model.alwaysOnDiscreteSchedule,
1391
+ fan,
1392
+ heating_coil,
1393
+ cooling_coil,
1394
+ supplemental_heating_coil)
1395
+ heat_pump.setSupplyAirFlowRateWhenNoCoolingorHeatingisNeeded(OpenStudio::OptionalDouble.new(0))
1396
+ heat_pump.setOutdoorAirFlowRateDuringCoolingOperation(OpenStudio::OptionalDouble.new(0))
1397
+ heat_pump.setOutdoorAirFlowRateDuringHeatingOperation(OpenStudio::OptionalDouble.new(0))
1398
+ heat_pump.setOutdoorAirFlowRateWhenNoCoolingorHeatingisNeeded(OpenStudio::OptionalDouble.new(0))
1399
+ # add heat pump to thermal zone
1400
+ heat_pump.addToThermalZone(zone)
1401
+ elsif options['zoneHVAC'] == 'Baseboard'
1402
+ # create baseboard heater add add to thermal zone and hot water loop
1403
+ baseboard_coil = OpenStudio::Model::CoilHeatingWaterBaseboard.new(model)
1404
+ baseboard_heater = OpenStudio::Model::ZoneHVACBaseboardConvectiveWater.new(model, model.alwaysOnDiscreteSchedule, baseboard_coil)
1405
+ baseboard_heater.addToThermalZone(zone)
1406
+ options['hot_water_plant'].addDemandBranchForComponent(baseboard_coil)
1407
+ elsif options['zoneHVAC'] == 'Radiant'
1408
+ # create low temperature radiant object and add to thermal zone and radiant plant loops
1409
+ # create hot water coil and attach to radiant hot water loop
1410
+ heating_coil = OpenStudio::Model::CoilHeatingLowTempRadiantVarFlow.new(model, options['mean_radiant_heating_setpoint_schedule'])
1411
+ options['radiant_hot_water_plant'].addDemandBranchForComponent(heating_coil)
1412
+ # create chilled water coil and attach to radiant chilled water loop
1413
+ cooling_coil = OpenStudio::Model::CoilCoolingLowTempRadiantVarFlow.new(model, options['mean_radiant_cooling_setpoint_schedule'])
1414
+ options['radiant_chilled_water_plant'].addDemandBranchForComponent(cooling_coil)
1415
+ low_temp_radiant = OpenStudio::Model::ZoneHVACLowTempRadiantVarFlow.new(model,
1416
+ model.alwaysOnDiscreteSchedule,
1417
+ heating_coil,
1418
+ cooling_coil)
1419
+ low_temp_radiant.setRadiantSurfaceType('Floors')
1420
+ low_temp_radiant.setHydronicTubingInsideDiameter(0.012)
1421
+ low_temp_radiant.setTemperatureControlType('MeanRadiantTemperature')
1422
+ low_temp_radiant.addToThermalZone(zone)
1423
+ # create radiant floor construction and substitute for existing floor (interior or exterior) constructions
1424
+ # create materials for radiant floor construction
1425
+ layers = []
1426
+ # ignore layer below insulation, which will depend on boundary condition
1427
+ layers << rigid_insulation_1in = OpenStudio::Model::StandardOpaqueMaterial.new(model, 'Rough', 0.0254, 0.02, 56.06, 1210)
1428
+ layers << concrete_2in = OpenStudio::Model::StandardOpaqueMaterial.new(model, 'MediumRough', 0.0508, 2.31, 2322, 832)
1429
+ layers << concrete_2in
1430
+ # create radiant floor construction from materials
1431
+ radiant_floor = OpenStudio::Model::ConstructionWithInternalSource.new(layers)
1432
+ radiant_floor.setSourcePresentAfterLayerNumber(2)
1433
+ radiant_floor.setSourcePresentAfterLayerNumber(2)
1434
+ # assign radiant construction to zone floor
1435
+ zone.spaces.each do |space|
1436
+ space.surfaces.each do |surface|
1437
+ if surface.surfaceType == 'Floor'
1438
+ surface.setConstruction(radiant_floor)
1439
+ end
1440
+ end
1441
+ end
1442
+ elsif options['zoneHVAC'] == 'DualDuct'
1443
+ # create baseboard heater add add to thermal zone and hot water loop
1444
+ baseboard_coil = OpenStudio::Model::CoilHeatingWaterBaseboard.new(model)
1445
+ baseboard_heater = OpenStudio::Model::ZoneHVACBaseboardConvectiveWater.new(model, model.alwaysOnDiscreteSchedule, baseboard_coil)
1446
+ baseboard_heater.addToThermalZone(zone)
1447
+ options['hot_water_plant'].addDemandBranchForComponent(baseboard_coil)
1448
+ # create fan coil (to mimic functionality of DOAS)
1449
+ # variable speed fan
1450
+ fan = OpenStudio::Model::FanVariableVolume.new(model, model.alwaysOnDiscreteSchedule)
1451
+ fan.setFanEfficiency(0.69)
1452
+ fan.setPressureRise(75) # Pa #ML This number is a guess; zone equipment pretending to be a DOAS
1453
+ fan.autosizeMaximumFlowRate
1454
+ fan.setFanPowerMinimumFlowFraction(0.6)
1455
+ fan.setMotorEfficiency(0.9)
1456
+ fan.setMotorInAirstreamFraction(1.0)
1457
+ # create chilled water coil and attach to chilled water loop
1458
+ cooling_coil = OpenStudio::Model::CoilCoolingWater.new(model, model.alwaysOnDiscreteSchedule)
1459
+ options['chilled_water_plant'].addDemandBranchForComponent(cooling_coil)
1460
+ # create hot water coil and attach to hot water loop
1461
+ heating_coil = OpenStudio::Model::CoilHeatingWater.new(model, model.alwaysOnDiscreteSchedule)
1462
+ options['hot_water_plant'].addDemandBranchForComponent(heating_coil)
1463
+ # construct fan coil (DOAS) and attach to thermal zone
1464
+ fan_coil_doas = OpenStudio::Model::ZoneHVACFourPipeFanCoil.new(model,
1465
+ options['ventilation_schedule'],
1466
+ fan,
1467
+ cooling_coil,
1468
+ heating_coil)
1469
+ fan_coil_doas.setCapacityControlMethod('VariableFanVariableFlow')
1470
+ fan_coil_doas.addToThermalZone(zone)
1471
+ end
1472
+ end
1473
+ end
1474
+ end
1475
+
1476
+ def self.addDCV(model, runner, options)
1477
+ unless options['primary_airloops'].nil?
1478
+ options['primary_airloops'].each do |airloop|
1479
+ if options['allHVAC']['primary']['fan'] == 'Variable'
1480
+ controller_mv = airloop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.controllerMechanicalVentilation
1481
+ controller_mv.setDemandControlledVentilation(true)
1482
+ runner.registerInfo("Enabling demand control ventilation for #{airloop.name}")
1483
+ end
1484
+ end
1485
+ end
1486
+
1487
+ unless options['secondary_airloops'].nil?
1488
+ options['secondary_airloops'].each do |airloop|
1489
+ if options['allHVAC']['secondary']['fan'] == 'Variable'
1490
+ controller_mv = airloop.airLoopHVACOutdoorAirSystem.get.getControllerOutdoorAir.controllerMechanicalVentilation
1491
+ controller_mv.setDemandControlledVentilation(true)
1492
+ runner.registerInfo("Enabling demand control ventilation for #{airloop.name}")
1493
+ end
1494
+ end
1495
+ end
1496
+ end
1497
+
1498
+ def self.getSpacesAndSpaceTypesFromThermalZone(zone, runner)
1499
+ # set flag
1500
+ space_type_hash = {}
1501
+
1502
+ # check if zone has spaces
1503
+ if zone.spaces.empty?
1504
+ runner.registerWarning("#{zone.name} doesn't have any spaces.")
1505
+ else
1506
+ # check if all spaces have the same space type
1507
+ zone.spaces.each do |space|
1508
+ if !space.spaceType.is_initialized
1509
+ runner.registerWarning("One or more spaces in #{zone.name} doesn't have a space type assigned.")
1510
+ space_type_hash[space] = false
1511
+ return space_type
1512
+ else
1513
+ space_type_hash[space] = space.spaceType.get
1514
+ end
1515
+ end
1516
+ end
1517
+
1518
+ return space_type_hash
1519
+ end
1520
+
1521
+ def self.get_or_add_hot_water_loop(model)
1522
+ # How water loop
1523
+ hw_loop = nil
1524
+ model.getLoops.each do |loop|
1525
+ if loop.name.to_s == 'Hot Water Loop' # sizingPlant has loopType method to do this better
1526
+ hw_loop = loop.to_PlantLoop.get
1527
+ end
1528
+ end
1529
+
1530
+ if hw_loop.nil?
1531
+ hw_loop = OpenStudio::Model::PlantLoop.new(model)
1532
+ hw_loop.setName('Hot Water Loop')
1533
+ hw_sizing_plant = hw_loop.sizingPlant
1534
+ hw_sizing_plant.setLoopType('Heating')
1535
+ hw_sizing_plant.setDesignLoopExitTemperature(82.0) # TODO: units
1536
+ hw_sizing_plant.setLoopDesignTemperatureDifference(11.0)
1537
+
1538
+ hw_pump = OpenStudio::Model::PumpVariableSpeed.new(model)
1539
+
1540
+ boiler = OpenStudio::Model::BoilerHotWater.new(model)
1541
+
1542
+ boiler_eff_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
1543
+ boiler_eff_f_of_temp.setName('Boiler Efficiency')
1544
+ boiler_eff_f_of_temp.setCoefficient1Constant(1.0)
1545
+ boiler_eff_f_of_temp.setInputUnitTypeforX('Dimensionless')
1546
+ boiler_eff_f_of_temp.setInputUnitTypeforY('Dimensionless')
1547
+ boiler_eff_f_of_temp.setOutputUnitType('Dimensionless')
1548
+
1549
+ boiler.setNormalizedBoilerEfficiencyCurve(boiler_eff_f_of_temp)
1550
+ boiler.setEfficiencyCurveTemperatureEvaluationVariable('LeavingBoiler')
1551
+
1552
+ boiler_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
1553
+
1554
+ hw_supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
1555
+
1556
+ # Add the components to the hot water loop
1557
+ hw_supply_inlet_node = hw_loop.supplyInletNode
1558
+ hw_supply_outlet_node = hw_loop.supplyOutletNode
1559
+ hw_pump.addToNode(hw_supply_inlet_node)
1560
+ hw_loop.addSupplyBranchForComponent(boiler)
1561
+ hw_loop.addSupplyBranchForComponent(boiler_bypass_pipe)
1562
+ hw_supply_outlet_pipe.addToNode(hw_supply_outlet_node)
1563
+
1564
+ # Add a setpoint manager to control the
1565
+ # hot water to a constant temperature
1566
+ hw_t_c = OpenStudio.convert(153, 'F', 'C').get
1567
+ hw_t_sch = OpenStudio::Model::ScheduleRuleset.new(model)
1568
+ hw_t_sch.setName('HW Temp')
1569
+ hw_t_sch.defaultDaySchedule.setName('HW Temp Default')
1570
+ hw_t_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), hw_t_c)
1571
+ hw_t_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, hw_t_sch)
1572
+ hw_t_stpt_manager.addToNode(hw_supply_outlet_node)
1573
+
1574
+ end
1575
+
1576
+ return hw_loop
1577
+ end
1578
+
1579
+ def self.get_or_add_water_cooled_chiller_loops(model)
1580
+ # Chilled Water Plant
1581
+ # todo - add in logic here that if existing chw_loop is air cooled, replace it with this one.
1582
+ chw_loop = nil
1583
+ model.getLoops.each do |loop|
1584
+ if loop.name.to_s == 'Chilled Water Loop'
1585
+ chw_loop = loop.to_PlantLoop.get
1586
+ end
1587
+ end
1588
+
1589
+ if chw_loop.nil?
1590
+ chw_loop = OpenStudio::Model::PlantLoop.new(model)
1591
+ chw_loop.setName('Chilled Water Loop')
1592
+ chw_sizing_plant = chw_loop.sizingPlant
1593
+ chw_sizing_plant.setLoopType('Cooling')
1594
+ chw_sizing_plant.setDesignLoopExitTemperature(7.22) # TODO: units
1595
+ chw_sizing_plant.setLoopDesignTemperatureDifference(6.67)
1596
+
1597
+ chw_pump = OpenStudio::Model::PumpVariableSpeed.new(model)
1598
+
1599
+ clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
1600
+ clg_cap_f_of_temp.setCoefficient1Constant(1.0215158)
1601
+ clg_cap_f_of_temp.setCoefficient2x(0.037035864)
1602
+ clg_cap_f_of_temp.setCoefficient3xPOW2(0.0002332476)
1603
+ clg_cap_f_of_temp.setCoefficient4y(-0.003894048)
1604
+ clg_cap_f_of_temp.setCoefficient5yPOW2(-6.52536e-005)
1605
+ clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.0002680452)
1606
+ clg_cap_f_of_temp.setMinimumValueofx(5.0)
1607
+ clg_cap_f_of_temp.setMaximumValueofx(10.0)
1608
+ clg_cap_f_of_temp.setMinimumValueofy(24.0)
1609
+ clg_cap_f_of_temp.setMaximumValueofy(35.0)
1610
+
1611
+ eir_f_of_avail_to_nom_cap = OpenStudio::Model::CurveBiquadratic.new(model)
1612
+ eir_f_of_avail_to_nom_cap.setCoefficient1Constant(0.70176857)
1613
+ eir_f_of_avail_to_nom_cap.setCoefficient2x(-0.00452016)
1614
+ eir_f_of_avail_to_nom_cap.setCoefficient3xPOW2(0.0005331096)
1615
+ eir_f_of_avail_to_nom_cap.setCoefficient4y(-0.005498208)
1616
+ eir_f_of_avail_to_nom_cap.setCoefficient5yPOW2(0.0005445792)
1617
+ eir_f_of_avail_to_nom_cap.setCoefficient6xTIMESY(-0.0007290324)
1618
+ eir_f_of_avail_to_nom_cap.setMinimumValueofx(5.0)
1619
+ eir_f_of_avail_to_nom_cap.setMaximumValueofx(10.0)
1620
+ eir_f_of_avail_to_nom_cap.setMinimumValueofy(24.0)
1621
+ eir_f_of_avail_to_nom_cap.setMaximumValueofy(35.0)
1622
+
1623
+ eir_f_of_plr = OpenStudio::Model::CurveQuadratic.new(model)
1624
+ eir_f_of_plr.setCoefficient1Constant(0.06369119)
1625
+ eir_f_of_plr.setCoefficient2x(0.58488832)
1626
+ eir_f_of_plr.setCoefficient3xPOW2(0.35280274)
1627
+ eir_f_of_plr.setMinimumValueofx(0.0)
1628
+ eir_f_of_plr.setMaximumValueofx(1.0)
1629
+
1630
+ chiller = OpenStudio::Model::ChillerElectricEIR.new(model,
1631
+ clg_cap_f_of_temp,
1632
+ eir_f_of_avail_to_nom_cap,
1633
+ eir_f_of_plr)
1634
+
1635
+ chiller_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
1636
+
1637
+ chw_supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
1638
+
1639
+ # Add the components to the chilled water loop
1640
+ chw_supply_inlet_node = chw_loop.supplyInletNode
1641
+ chw_supply_outlet_node = chw_loop.supplyOutletNode
1642
+ chw_pump.addToNode(chw_supply_inlet_node)
1643
+ chw_loop.addSupplyBranchForComponent(chiller)
1644
+ chw_loop.addSupplyBranchForComponent(chiller_bypass_pipe)
1645
+ chw_supply_outlet_pipe.addToNode(chw_supply_outlet_node)
1646
+
1647
+ # Add a setpoint manager to control the
1648
+ # chilled water to a constant temperature
1649
+ chw_t_c = OpenStudio.convert(44, 'F', 'C').get
1650
+ chw_t_sch = OpenStudio::Model::ScheduleRuleset.new(model)
1651
+ chw_t_sch.setName('CHW Temp')
1652
+ chw_t_sch.defaultDaySchedule.setName('HW Temp Default')
1653
+ chw_t_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), chw_t_c)
1654
+ chw_t_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, chw_t_sch)
1655
+ chw_t_stpt_manager.addToNode(chw_supply_outlet_node)
1656
+
1657
+ end
1658
+
1659
+ # Condenser System
1660
+ cw_loop = nil
1661
+ model.getLoops.each do |loop|
1662
+ if loop.name.to_s == 'Condenser Water Loop'
1663
+ cw_loop = loop.to_PlantLoop.get
1664
+ end
1665
+ end
1666
+
1667
+ if cw_loop.nil?
1668
+ cw_loop = OpenStudio::Model::PlantLoop.new(model)
1669
+ cw_loop.setName('Condenser Water Loop')
1670
+ cw_sizing_plant = cw_loop.sizingPlant
1671
+ cw_sizing_plant.setLoopType('Condenser')
1672
+ cw_sizing_plant.setDesignLoopExitTemperature(29.4) # TODO: units
1673
+ cw_sizing_plant.setLoopDesignTemperatureDifference(5.6)
1674
+
1675
+ cw_pump = OpenStudio::Model::PumpVariableSpeed.new(model)
1676
+
1677
+ clg_tower = OpenStudio::Model::CoolingTowerSingleSpeed.new(model)
1678
+
1679
+ clg_tower_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
1680
+
1681
+ cw_supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
1682
+
1683
+ # Add the components to the condenser water loop
1684
+ cw_supply_inlet_node = cw_loop.supplyInletNode
1685
+ cw_supply_outlet_node = cw_loop.supplyOutletNode
1686
+ cw_pump.addToNode(cw_supply_inlet_node)
1687
+ cw_loop.addSupplyBranchForComponent(clg_tower)
1688
+ cw_loop.addSupplyBranchForComponent(clg_tower_bypass_pipe)
1689
+ cw_supply_outlet_pipe.addToNode(cw_supply_outlet_node)
1690
+ cw_loop.addDemandBranchForComponent(chiller)
1691
+
1692
+ # Add a setpoint manager to control the
1693
+ # condenser water to follow the OA temp
1694
+ cw_t_stpt_manager = OpenStudio::Model::SetpointManagerFollowOutdoorAirTemperature.new(model)
1695
+ cw_t_stpt_manager.addToNode(cw_supply_outlet_node)
1696
+
1697
+ end
1698
+
1699
+ return chw_loop
1700
+ end
1701
+
1702
+ def self.get_or_add_air_cooled_chiller_loop(model)
1703
+ # Chilled Water Plant
1704
+ chw_loop = nil
1705
+ model.getLoops.each do |loop|
1706
+ if loop.name.to_s == 'Chilled Water Loop'
1707
+ chw_loop = loop.to_PlantLoop.get
1708
+ end
1709
+ end
1710
+
1711
+ if chw_loop.nil?
1712
+ chw_loop = OpenStudio::Model::PlantLoop.new(model)
1713
+ chw_loop.setName('Chilled Water Loop')
1714
+ chw_sizing_plant = chw_loop.sizingPlant
1715
+ chw_sizing_plant.setLoopType('Cooling')
1716
+ chw_sizing_plant.setDesignLoopExitTemperature(7.22) # TODO: units
1717
+ chw_sizing_plant.setLoopDesignTemperatureDifference(6.67)
1718
+
1719
+ chw_pump = OpenStudio::Model::PumpVariableSpeed.new(model)
1720
+
1721
+ clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
1722
+ clg_cap_f_of_temp.setCoefficient1Constant(1.0215158)
1723
+ clg_cap_f_of_temp.setCoefficient2x(0.037035864)
1724
+ clg_cap_f_of_temp.setCoefficient3xPOW2(0.0002332476)
1725
+ clg_cap_f_of_temp.setCoefficient4y(-0.003894048)
1726
+ clg_cap_f_of_temp.setCoefficient5yPOW2(-6.52536e-005)
1727
+ clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.0002680452)
1728
+ clg_cap_f_of_temp.setMinimumValueofx(5.0)
1729
+ clg_cap_f_of_temp.setMaximumValueofx(10.0)
1730
+ clg_cap_f_of_temp.setMinimumValueofy(24.0)
1731
+ clg_cap_f_of_temp.setMaximumValueofy(35.0)
1732
+
1733
+ eir_f_of_avail_to_nom_cap = OpenStudio::Model::CurveBiquadratic.new(model)
1734
+ eir_f_of_avail_to_nom_cap.setCoefficient1Constant(0.70176857)
1735
+ eir_f_of_avail_to_nom_cap.setCoefficient2x(-0.00452016)
1736
+ eir_f_of_avail_to_nom_cap.setCoefficient3xPOW2(0.0005331096)
1737
+ eir_f_of_avail_to_nom_cap.setCoefficient4y(-0.005498208)
1738
+ eir_f_of_avail_to_nom_cap.setCoefficient5yPOW2(0.0005445792)
1739
+ eir_f_of_avail_to_nom_cap.setCoefficient6xTIMESY(-0.0007290324)
1740
+ eir_f_of_avail_to_nom_cap.setMinimumValueofx(5.0)
1741
+ eir_f_of_avail_to_nom_cap.setMaximumValueofx(10.0)
1742
+ eir_f_of_avail_to_nom_cap.setMinimumValueofy(24.0)
1743
+ eir_f_of_avail_to_nom_cap.setMaximumValueofy(35.0)
1744
+
1745
+ eir_f_of_plr = OpenStudio::Model::CurveQuadratic.new(model)
1746
+ eir_f_of_plr.setCoefficient1Constant(0.06369119)
1747
+ eir_f_of_plr.setCoefficient2x(0.58488832)
1748
+ eir_f_of_plr.setCoefficient3xPOW2(0.35280274)
1749
+ eir_f_of_plr.setMinimumValueofx(0.0)
1750
+ eir_f_of_plr.setMaximumValueofx(1.0)
1751
+
1752
+ chiller = OpenStudio::Model::ChillerElectricEIR.new(model,
1753
+ clg_cap_f_of_temp,
1754
+ eir_f_of_avail_to_nom_cap,
1755
+ eir_f_of_plr)
1756
+
1757
+ chiller_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
1758
+
1759
+ chw_supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
1760
+
1761
+ # Add the components to the chilled water loop
1762
+ chw_supply_inlet_node = chw_loop.supplyInletNode
1763
+ chw_supply_outlet_node = chw_loop.supplyOutletNode
1764
+ chw_pump.addToNode(chw_supply_inlet_node)
1765
+ chw_loop.addSupplyBranchForComponent(chiller)
1766
+ chw_loop.addSupplyBranchForComponent(chiller_bypass_pipe)
1767
+ chw_supply_outlet_pipe.addToNode(chw_supply_outlet_node)
1768
+
1769
+ # Add a setpoint manager to control the
1770
+ # chilled water to a constant temperature
1771
+ chw_t_c = OpenStudio.convert(44, 'F', 'C').get
1772
+ chw_t_sch = OpenStudio::Model::ScheduleRuleset.new(model)
1773
+ chw_t_sch.setName('CHW Temp')
1774
+ chw_t_sch.defaultDaySchedule.setName('HW Temp Default')
1775
+ chw_t_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), chw_t_c)
1776
+ chw_t_stpt_manager = OpenStudio::Model::SetpointManagerScheduled.new(model, chw_t_sch)
1777
+ chw_t_stpt_manager.addToNode(chw_supply_outlet_node)
1778
+
1779
+ end
1780
+
1781
+ return chw_loop
1782
+ end
1783
+
1784
+ def self._add_coil_cooling_dx_two_speed(model)
1785
+ clg_coil = nil
1786
+
1787
+ always_on = model.alwaysOnDiscreteSchedule
1788
+
1789
+ clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
1790
+ clg_cap_f_of_temp.setCoefficient1Constant(0.42415)
1791
+ clg_cap_f_of_temp.setCoefficient2x(0.04426)
1792
+ clg_cap_f_of_temp.setCoefficient3xPOW2(-0.00042)
1793
+ clg_cap_f_of_temp.setCoefficient4y(0.00333)
1794
+ clg_cap_f_of_temp.setCoefficient5yPOW2(-0.00008)
1795
+ clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.00021)
1796
+ clg_cap_f_of_temp.setMinimumValueofx(17.0)
1797
+ clg_cap_f_of_temp.setMaximumValueofx(22.0)
1798
+ clg_cap_f_of_temp.setMinimumValueofy(13.0)
1799
+ clg_cap_f_of_temp.setMaximumValueofy(46.0)
1800
+
1801
+ clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
1802
+ clg_cap_f_of_flow.setCoefficient1Constant(0.77136)
1803
+ clg_cap_f_of_flow.setCoefficient2x(0.34053)
1804
+ clg_cap_f_of_flow.setCoefficient3xPOW2(-0.11088)
1805
+ clg_cap_f_of_flow.setMinimumValueofx(0.75918)
1806
+ clg_cap_f_of_flow.setMaximumValueofx(1.13877)
1807
+
1808
+ clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
1809
+ clg_energy_input_ratio_f_of_temp.setCoefficient1Constant(1.23649)
1810
+ clg_energy_input_ratio_f_of_temp.setCoefficient2x(-0.02431)
1811
+ clg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(0.00057)
1812
+ clg_energy_input_ratio_f_of_temp.setCoefficient4y(-0.01434)
1813
+ clg_energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.00063)
1814
+ clg_energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.00038)
1815
+ clg_energy_input_ratio_f_of_temp.setMinimumValueofx(17.0)
1816
+ clg_energy_input_ratio_f_of_temp.setMaximumValueofx(22.0)
1817
+ clg_energy_input_ratio_f_of_temp.setMinimumValueofy(13.0)
1818
+ clg_energy_input_ratio_f_of_temp.setMaximumValueofy(46.0)
1819
+
1820
+ clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
1821
+ clg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.20550)
1822
+ clg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.32953)
1823
+ clg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.12308)
1824
+ clg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.75918)
1825
+ clg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.13877)
1826
+
1827
+ clg_part_load_ratio = OpenStudio::Model::CurveQuadratic.new(model)
1828
+ clg_part_load_ratio.setCoefficient1Constant(0.77100)
1829
+ clg_part_load_ratio.setCoefficient2x(0.22900)
1830
+ clg_part_load_ratio.setCoefficient3xPOW2(0.0)
1831
+ clg_part_load_ratio.setMinimumValueofx(0.0)
1832
+ clg_part_load_ratio.setMaximumValueofx(1.0)
1833
+
1834
+ clg_cap_f_of_temp_low_spd = OpenStudio::Model::CurveBiquadratic.new(model)
1835
+ clg_cap_f_of_temp_low_spd.setCoefficient1Constant(0.42415)
1836
+ clg_cap_f_of_temp_low_spd.setCoefficient2x(0.04426)
1837
+ clg_cap_f_of_temp_low_spd.setCoefficient3xPOW2(-0.00042)
1838
+ clg_cap_f_of_temp_low_spd.setCoefficient4y(0.00333)
1839
+ clg_cap_f_of_temp_low_spd.setCoefficient5yPOW2(-0.00008)
1840
+ clg_cap_f_of_temp_low_spd.setCoefficient6xTIMESY(-0.00021)
1841
+ clg_cap_f_of_temp_low_spd.setMinimumValueofx(17.0)
1842
+ clg_cap_f_of_temp_low_spd.setMaximumValueofx(22.0)
1843
+ clg_cap_f_of_temp_low_spd.setMinimumValueofy(13.0)
1844
+ clg_cap_f_of_temp_low_spd.setMaximumValueofy(46.0)
1845
+
1846
+ clg_energy_input_ratio_f_of_temp_low_spd = OpenStudio::Model::CurveBiquadratic.new(model)
1847
+ clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient1Constant(1.23649)
1848
+ clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient2x(-0.02431)
1849
+ clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient3xPOW2(0.00057)
1850
+ clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient4y(-0.01434)
1851
+ clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient5yPOW2(0.00063)
1852
+ clg_energy_input_ratio_f_of_temp_low_spd.setCoefficient6xTIMESY(-0.00038)
1853
+ clg_energy_input_ratio_f_of_temp_low_spd.setMinimumValueofx(17.0)
1854
+ clg_energy_input_ratio_f_of_temp_low_spd.setMaximumValueofx(22.0)
1855
+ clg_energy_input_ratio_f_of_temp_low_spd.setMinimumValueofy(13.0)
1856
+ clg_energy_input_ratio_f_of_temp_low_spd.setMaximumValueofy(46.0)
1857
+
1858
+ clg_coil = OpenStudio::Model::CoilCoolingDXTwoSpeed.new(model,
1859
+ always_on,
1860
+ clg_cap_f_of_temp,
1861
+ clg_cap_f_of_flow,
1862
+ clg_energy_input_ratio_f_of_temp,
1863
+ clg_energy_input_ratio_f_of_flow,
1864
+ clg_part_load_ratio,
1865
+ clg_cap_f_of_temp_low_spd,
1866
+ clg_energy_input_ratio_f_of_temp_low_spd)
1867
+
1868
+ clg_coil.setRatedLowSpeedSensibleHeatRatio(OpenStudio::OptionalDouble.new(0.69))
1869
+ clg_coil.setBasinHeaterCapacity(10)
1870
+ clg_coil.setBasinHeaterSetpointTemperature(2.0)
1871
+
1872
+ return clg_coil
1873
+ end
1874
+
1875
+ def self._add_coil_cooling_dx_single_speed_sys_type_1(model)
1876
+ clg_coil = nil
1877
+
1878
+ always_on = model.alwaysOnDiscreteSchedule
1879
+
1880
+ clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
1881
+ clg_cap_f_of_temp.setCoefficient1Constant(0.942587793)
1882
+ clg_cap_f_of_temp.setCoefficient2x(0.009543347)
1883
+ clg_cap_f_of_temp.setCoefficient3xPOW2(0.000683770)
1884
+ clg_cap_f_of_temp.setCoefficient4y(-0.011042676)
1885
+ clg_cap_f_of_temp.setCoefficient5yPOW2(0.000005249)
1886
+ clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.000009720)
1887
+ clg_cap_f_of_temp.setMinimumValueofx(17.0)
1888
+ clg_cap_f_of_temp.setMaximumValueofx(22.0)
1889
+ clg_cap_f_of_temp.setMinimumValueofy(13.0)
1890
+ clg_cap_f_of_temp.setMaximumValueofy(46.0)
1891
+
1892
+ clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
1893
+ clg_cap_f_of_flow.setCoefficient1Constant(0.8)
1894
+ clg_cap_f_of_flow.setCoefficient2x(0.2)
1895
+ clg_cap_f_of_flow.setCoefficient3xPOW2(0.0)
1896
+ clg_cap_f_of_flow.setMinimumValueofx(0.5)
1897
+ clg_cap_f_of_flow.setMaximumValueofx(1.5)
1898
+
1899
+ energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
1900
+ energy_input_ratio_f_of_temp.setCoefficient1Constant(0.342414409)
1901
+ energy_input_ratio_f_of_temp.setCoefficient2x(0.034885008)
1902
+ energy_input_ratio_f_of_temp.setCoefficient3xPOW2(-0.000623700)
1903
+ energy_input_ratio_f_of_temp.setCoefficient4y(0.004977216)
1904
+ energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.000437951)
1905
+ energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.000728028)
1906
+ energy_input_ratio_f_of_temp.setMinimumValueofx(17.0)
1907
+ energy_input_ratio_f_of_temp.setMaximumValueofx(22.0)
1908
+ energy_input_ratio_f_of_temp.setMinimumValueofy(13.0)
1909
+ energy_input_ratio_f_of_temp.setMaximumValueofy(46.0)
1910
+
1911
+ energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
1912
+ energy_input_ratio_f_of_flow.setCoefficient1Constant(1.1552)
1913
+ energy_input_ratio_f_of_flow.setCoefficient2x(-0.1808)
1914
+ energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0256)
1915
+ energy_input_ratio_f_of_flow.setMinimumValueofx(0.5)
1916
+ energy_input_ratio_f_of_flow.setMaximumValueofx(1.5)
1917
+
1918
+ part_load_fraction = OpenStudio::Model::CurveQuadratic.new(model)
1919
+ part_load_fraction.setCoefficient1Constant(0.85)
1920
+ part_load_fraction.setCoefficient2x(0.15)
1921
+ part_load_fraction.setCoefficient3xPOW2(0.0)
1922
+ part_load_fraction.setMinimumValueofx(0.0)
1923
+ part_load_fraction.setMaximumValueofx(1.0)
1924
+
1925
+ clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model,
1926
+ always_on,
1927
+ clg_cap_f_of_temp,
1928
+ clg_cap_f_of_flow,
1929
+ energy_input_ratio_f_of_temp,
1930
+ energy_input_ratio_f_of_flow,
1931
+ part_load_fraction)
1932
+
1933
+ return clg_coil
1934
+ end
1935
+
1936
+ def self._add_coil_cooling_dx_single_speed_sys_type_2(model)
1937
+ clg_coil = nil
1938
+
1939
+ always_on = model.alwaysOnDiscreteSchedule
1940
+
1941
+ clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
1942
+ clg_cap_f_of_temp.setCoefficient1Constant(0.942587793)
1943
+ clg_cap_f_of_temp.setCoefficient2x(0.009543347)
1944
+ clg_cap_f_of_temp.setCoefficient3xPOW2(0.0018423)
1945
+ clg_cap_f_of_temp.setCoefficient4y(-0.011042676)
1946
+ clg_cap_f_of_temp.setCoefficient5yPOW2(0.000005249)
1947
+ clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.000009720)
1948
+ clg_cap_f_of_temp.setMinimumValueofx(17.0)
1949
+ clg_cap_f_of_temp.setMaximumValueofx(22.0)
1950
+ clg_cap_f_of_temp.setMinimumValueofy(13.0)
1951
+ clg_cap_f_of_temp.setMaximumValueofy(46.0)
1952
+
1953
+ clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
1954
+ clg_cap_f_of_flow.setCoefficient1Constant(0.718954)
1955
+ clg_cap_f_of_flow.setCoefficient2x(0.435436)
1956
+ clg_cap_f_of_flow.setCoefficient3xPOW2(-0.154193)
1957
+ clg_cap_f_of_flow.setMinimumValueofx(0.75)
1958
+ clg_cap_f_of_flow.setMaximumValueofx(1.25)
1959
+
1960
+ clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
1961
+ clg_energy_input_ratio_f_of_temp.setCoefficient1Constant(0.342414409)
1962
+ clg_energy_input_ratio_f_of_temp.setCoefficient2x(0.034885008)
1963
+ clg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(-0.000623700)
1964
+ clg_energy_input_ratio_f_of_temp.setCoefficient4y(0.004977216)
1965
+ clg_energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.000437951)
1966
+ clg_energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.000728028)
1967
+ clg_energy_input_ratio_f_of_temp.setMinimumValueofx(17.0)
1968
+ clg_energy_input_ratio_f_of_temp.setMaximumValueofx(22.0)
1969
+ clg_energy_input_ratio_f_of_temp.setMinimumValueofy(13.0)
1970
+ clg_energy_input_ratio_f_of_temp.setMaximumValueofy(46.0)
1971
+
1972
+ clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
1973
+ clg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.1552)
1974
+ clg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.1808)
1975
+ clg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0256)
1976
+ clg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.5)
1977
+ clg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.5)
1978
+
1979
+ clg_part_load_fraction = OpenStudio::Model::CurveQuadratic.new(model)
1980
+ clg_part_load_fraction.setCoefficient1Constant(0.75)
1981
+ clg_part_load_fraction.setCoefficient2x(0.25)
1982
+ clg_part_load_fraction.setCoefficient3xPOW2(0.0)
1983
+ clg_part_load_fraction.setMinimumValueofx(0.0)
1984
+ clg_part_load_fraction.setMaximumValueofx(1.0)
1985
+
1986
+ clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model,
1987
+ always_on,
1988
+ clg_cap_f_of_temp,
1989
+ clg_cap_f_of_flow,
1990
+ clg_energy_input_ratio_f_of_temp,
1991
+ clg_energy_input_ratio_f_of_flow,
1992
+ clg_part_load_fraction)
1993
+
1994
+ return clg_coil
1995
+ end
1996
+
1997
+ def self._add_coil_cooling_dx_single_speed_sys_type_3(model)
1998
+ clg_coil = nil
1999
+
2000
+ always_on = model.alwaysOnDiscreteSchedule
2001
+
2002
+ clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
2003
+ clg_cap_f_of_temp.setCoefficient1Constant(0.42415)
2004
+ clg_cap_f_of_temp.setCoefficient2x(0.04426)
2005
+ clg_cap_f_of_temp.setCoefficient3xPOW2(-0.00042)
2006
+ clg_cap_f_of_temp.setCoefficient4y(0.00333)
2007
+ clg_cap_f_of_temp.setCoefficient5yPOW2(-0.00008)
2008
+ clg_cap_f_of_temp.setCoefficient6xTIMESY(-0.00021)
2009
+ clg_cap_f_of_temp.setMinimumValueofx(17.0)
2010
+ clg_cap_f_of_temp.setMaximumValueofx(22.0)
2011
+ clg_cap_f_of_temp.setMinimumValueofy(13.0)
2012
+ clg_cap_f_of_temp.setMaximumValueofy(46.0)
2013
+
2014
+ clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
2015
+ clg_cap_f_of_flow.setCoefficient1Constant(0.77136)
2016
+ clg_cap_f_of_flow.setCoefficient2x(0.34053)
2017
+ clg_cap_f_of_flow.setCoefficient3xPOW2(-0.11088)
2018
+ clg_cap_f_of_flow.setMinimumValueofx(0.75918)
2019
+ clg_cap_f_of_flow.setMaximumValueofx(1.13877)
2020
+
2021
+ clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
2022
+ clg_energy_input_ratio_f_of_temp.setCoefficient1Constant(1.23649)
2023
+ clg_energy_input_ratio_f_of_temp.setCoefficient2x(-0.02431)
2024
+ clg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(0.00057)
2025
+ clg_energy_input_ratio_f_of_temp.setCoefficient4y(-0.01434)
2026
+ clg_energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.00063)
2027
+ clg_energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.00038)
2028
+ clg_energy_input_ratio_f_of_temp.setMinimumValueofx(17.0)
2029
+ clg_energy_input_ratio_f_of_temp.setMaximumValueofx(22.0)
2030
+ clg_energy_input_ratio_f_of_temp.setMinimumValueofy(13.0)
2031
+ clg_energy_input_ratio_f_of_temp.setMaximumValueofy(46.0)
2032
+
2033
+ clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
2034
+ clg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.20550)
2035
+ clg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.32953)
2036
+ clg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.12308)
2037
+ clg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.75918)
2038
+ clg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.13877)
2039
+
2040
+ clg_part_load_ratio = OpenStudio::Model::CurveQuadratic.new(model)
2041
+ clg_part_load_ratio.setCoefficient1Constant(0.77100)
2042
+ clg_part_load_ratio.setCoefficient2x(0.22900)
2043
+ clg_part_load_ratio.setCoefficient3xPOW2(0.0)
2044
+ clg_part_load_ratio.setMinimumValueofx(0.0)
2045
+ clg_part_load_ratio.setMaximumValueofx(1.0)
2046
+
2047
+ clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model,
2048
+ always_on,
2049
+ clg_cap_f_of_temp,
2050
+ clg_cap_f_of_flow,
2051
+ clg_energy_input_ratio_f_of_temp,
2052
+ clg_energy_input_ratio_f_of_flow,
2053
+ clg_part_load_ratio)
2054
+
2055
+ return clg_coil
2056
+ end
2057
+
2058
+ def self._add_coil_cooling_dx_single_speed_sys_type_4(model)
2059
+ clg_coil = nil
2060
+
2061
+ always_on = model.alwaysOnDiscreteSchedule
2062
+
2063
+ clg_cap_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
2064
+ clg_cap_f_of_temp.setCoefficient1Constant(0.766956)
2065
+ clg_cap_f_of_temp.setCoefficient2x(0.0107756)
2066
+ clg_cap_f_of_temp.setCoefficient3xPOW2(-0.0000414703)
2067
+ clg_cap_f_of_temp.setCoefficient4y(0.00134961)
2068
+ clg_cap_f_of_temp.setCoefficient5yPOW2(-0.000261144)
2069
+ clg_cap_f_of_temp.setCoefficient6xTIMESY(0.000457488)
2070
+ clg_cap_f_of_temp.setMinimumValueofx(17.0)
2071
+ clg_cap_f_of_temp.setMaximumValueofx(22.0)
2072
+ clg_cap_f_of_temp.setMinimumValueofy(13.0)
2073
+ clg_cap_f_of_temp.setMaximumValueofy(46.0)
2074
+
2075
+ clg_cap_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
2076
+ clg_cap_f_of_flow.setCoefficient1Constant(0.8)
2077
+ clg_cap_f_of_flow.setCoefficient2x(0.2)
2078
+ clg_cap_f_of_flow.setCoefficient3xPOW2(0.0)
2079
+ clg_cap_f_of_flow.setMinimumValueofx(0.5)
2080
+ clg_cap_f_of_flow.setMaximumValueofx(1.5)
2081
+
2082
+ clg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveBiquadratic.new(model)
2083
+ clg_energy_input_ratio_f_of_temp.setCoefficient1Constant(0.297145)
2084
+ clg_energy_input_ratio_f_of_temp.setCoefficient2x(0.0430933)
2085
+ clg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(-0.000748766)
2086
+ clg_energy_input_ratio_f_of_temp.setCoefficient4y(0.00597727)
2087
+ clg_energy_input_ratio_f_of_temp.setCoefficient5yPOW2(0.000482112)
2088
+ clg_energy_input_ratio_f_of_temp.setCoefficient6xTIMESY(-0.000956448)
2089
+ clg_energy_input_ratio_f_of_temp.setMinimumValueofx(17.0)
2090
+ clg_energy_input_ratio_f_of_temp.setMaximumValueofx(22.0)
2091
+ clg_energy_input_ratio_f_of_temp.setMinimumValueofy(13.0)
2092
+ clg_energy_input_ratio_f_of_temp.setMaximumValueofy(46.0)
2093
+
2094
+ clg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
2095
+ clg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.156)
2096
+ clg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.1816)
2097
+ clg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0256)
2098
+ clg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.5)
2099
+ clg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.5)
2100
+
2101
+ clg_part_load_ratio = OpenStudio::Model::CurveQuadratic.new(model)
2102
+ clg_part_load_ratio.setCoefficient1Constant(0.75)
2103
+ clg_part_load_ratio.setCoefficient2x(0.25)
2104
+ clg_part_load_ratio.setCoefficient3xPOW2(0.0)
2105
+ clg_part_load_ratio.setMinimumValueofx(0.0)
2106
+ clg_part_load_ratio.setMaximumValueofx(1.0)
2107
+
2108
+ clg_coil = OpenStudio::Model::CoilCoolingDXSingleSpeed.new(model,
2109
+ always_on,
2110
+ clg_cap_f_of_temp,
2111
+ clg_cap_f_of_flow,
2112
+ clg_energy_input_ratio_f_of_temp,
2113
+ clg_energy_input_ratio_f_of_flow,
2114
+ clg_part_load_ratio)
2115
+ return clg_coil
2116
+ end
2117
+
2118
+ def self._add_coil_heating_dx_single_speed(model)
2119
+ htg_coil = nil
2120
+
2121
+ always_on = model.alwaysOnDiscreteSchedule
2122
+
2123
+ htg_cap_f_of_temp = OpenStudio::Model::CurveCubic.new(model)
2124
+ htg_cap_f_of_temp.setCoefficient1Constant(0.758746)
2125
+ htg_cap_f_of_temp.setCoefficient2x(0.027626)
2126
+ htg_cap_f_of_temp.setCoefficient3xPOW2(0.000148716)
2127
+ htg_cap_f_of_temp.setCoefficient4xPOW3(0.0000034992)
2128
+ htg_cap_f_of_temp.setMinimumValueofx(-20.0)
2129
+ htg_cap_f_of_temp.setMaximumValueofx(20.0)
2130
+
2131
+ htg_cap_f_of_flow = OpenStudio::Model::CurveCubic.new(model)
2132
+ htg_cap_f_of_flow.setCoefficient1Constant(0.84)
2133
+ htg_cap_f_of_flow.setCoefficient2x(0.16)
2134
+ htg_cap_f_of_flow.setCoefficient3xPOW2(0.0)
2135
+ htg_cap_f_of_flow.setCoefficient4xPOW3(0.0)
2136
+ htg_cap_f_of_flow.setMinimumValueofx(0.5)
2137
+ htg_cap_f_of_flow.setMaximumValueofx(1.5)
2138
+
2139
+ htg_energy_input_ratio_f_of_temp = OpenStudio::Model::CurveCubic.new(model)
2140
+ htg_energy_input_ratio_f_of_temp.setCoefficient1Constant(1.19248)
2141
+ htg_energy_input_ratio_f_of_temp.setCoefficient2x(-0.0300438)
2142
+ htg_energy_input_ratio_f_of_temp.setCoefficient3xPOW2(0.00103745)
2143
+ htg_energy_input_ratio_f_of_temp.setCoefficient4xPOW3(-0.000023328)
2144
+ htg_energy_input_ratio_f_of_temp.setMinimumValueofx(-20.0)
2145
+ htg_energy_input_ratio_f_of_temp.setMaximumValueofx(20.0)
2146
+
2147
+ htg_energy_input_ratio_f_of_flow = OpenStudio::Model::CurveQuadratic.new(model)
2148
+ htg_energy_input_ratio_f_of_flow.setCoefficient1Constant(1.3824)
2149
+ htg_energy_input_ratio_f_of_flow.setCoefficient2x(-0.4336)
2150
+ htg_energy_input_ratio_f_of_flow.setCoefficient3xPOW2(0.0512)
2151
+ htg_energy_input_ratio_f_of_flow.setMinimumValueofx(0.0)
2152
+ htg_energy_input_ratio_f_of_flow.setMaximumValueofx(1.0)
2153
+
2154
+ htg_part_load_fraction = OpenStudio::Model::CurveQuadratic.new(model)
2155
+ htg_part_load_fraction.setCoefficient1Constant(0.75)
2156
+ htg_part_load_fraction.setCoefficient2x(0.25)
2157
+ htg_part_load_fraction.setCoefficient3xPOW2(0.0)
2158
+ htg_part_load_fraction.setMinimumValueofx(0.0)
2159
+ htg_part_load_fraction.setMaximumValueofx(1.0)
2160
+
2161
+ htg_coil = OpenStudio::Model::CoilHeatingDXSingleSpeed.new(model,
2162
+ always_on,
2163
+ htg_cap_f_of_temp,
2164
+ htg_cap_f_of_flow,
2165
+ htg_energy_input_ratio_f_of_temp,
2166
+ htg_energy_input_ratio_f_of_flow,
2167
+ htg_part_load_fraction)
2168
+
2169
+ return htg_coil
2170
+ end
2171
+ end