openstudio-extension 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +29 -26
  3. data/.rspec +3 -3
  4. data/.rubocop.yml +10 -9
  5. data/CHANGELOG.md +16 -0
  6. data/Gemfile +6 -6
  7. data/Jenkinsfile +10 -10
  8. data/README.md +251 -250
  9. data/Rakefile +119 -119
  10. data/bin/console +14 -14
  11. data/bin/setup +8 -8
  12. data/doc_templates/LICENSE.md +26 -26
  13. data/doc_templates/README.md.erb +41 -41
  14. data/doc_templates/copyright_erb.txt +35 -35
  15. data/doc_templates/copyright_js.txt +3 -3
  16. data/doc_templates/copyright_ruby.txt +33 -33
  17. data/init_templates/README.md +37 -37
  18. data/init_templates/gemspec.txt +32 -32
  19. data/init_templates/openstudio_module.rb +50 -50
  20. data/init_templates/spec.rb +47 -47
  21. data/init_templates/spec_helper.rb +49 -49
  22. data/init_templates/template_gemfile.txt +17 -17
  23. data/init_templates/template_rakefile.txt +15 -15
  24. data/init_templates/version.rb +40 -40
  25. data/lib/files/openstudio-extension-gem-test.ddy +536 -536
  26. data/lib/files/openstudio-extension-gem-test.epw +8768 -8768
  27. data/lib/files/openstudio-extension-gem-test.stat +554 -554
  28. data/lib/measures/openstudio_extension_test_measure/LICENSE.md +26 -26
  29. data/lib/measures/openstudio_extension_test_measure/README.md +26 -26
  30. data/lib/measures/openstudio_extension_test_measure/README.md.erb +41 -41
  31. data/lib/measures/openstudio_extension_test_measure/measure.rb +72 -72
  32. data/lib/measures/openstudio_extension_test_measure/measure.xml +83 -83
  33. data/lib/measures/openstudio_extension_test_measure/resources/os_lib_helper_methods.rb +399 -399
  34. data/lib/measures/openstudio_extension_test_measure/tests/{OpenStudioExtensionTestMeasure_Test.rb → openstudio_extension_test_measure_test.rb} +74 -75
  35. data/lib/openstudio-extension.rb +1 -1
  36. data/lib/openstudio/extension.rb +234 -229
  37. data/lib/openstudio/extension/core/CreateResults.rb +886 -886
  38. data/lib/openstudio/extension/core/check_air_sys_temps.rb +190 -190
  39. data/lib/openstudio/extension/core/check_calibration.rb +155 -155
  40. data/lib/openstudio/extension/core/check_cond_zns.rb +84 -84
  41. data/lib/openstudio/extension/core/check_domestic_hot_water.rb +334 -334
  42. data/lib/openstudio/extension/core/check_envelope_conductance.rb +453 -453
  43. data/lib/openstudio/extension/core/check_eui_by_end_use.rb +162 -162
  44. data/lib/openstudio/extension/core/check_eui_reasonableness.rb +135 -135
  45. data/lib/openstudio/extension/core/check_fan_pwr.rb +98 -98
  46. data/lib/openstudio/extension/core/check_internal_loads.rb +393 -393
  47. data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +226 -226
  48. data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +326 -326
  49. data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +464 -464
  50. data/lib/openstudio/extension/core/check_mech_sys_type.rb +139 -139
  51. data/lib/openstudio/extension/core/check_part_loads.rb +451 -451
  52. data/lib/openstudio/extension/core/check_placeholder.rb +75 -75
  53. data/lib/openstudio/extension/core/check_plant_cap.rb +123 -123
  54. data/lib/openstudio/extension/core/check_plant_temps.rb +159 -159
  55. data/lib/openstudio/extension/core/check_plenum_loads.rb +87 -87
  56. data/lib/openstudio/extension/core/check_pump_pwr.rb +108 -108
  57. data/lib/openstudio/extension/core/check_sch_coord.rb +241 -241
  58. data/lib/openstudio/extension/core/check_schedules.rb +311 -311
  59. data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +158 -158
  60. data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +148 -148
  61. data/lib/openstudio/extension/core/check_weather_files.rb +132 -132
  62. data/lib/openstudio/extension/core/deer_vintages.rb +311 -311
  63. data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +491 -491
  64. data/lib/openstudio/extension/core/os_lib_cofee.rb +258 -258
  65. data/lib/openstudio/extension/core/os_lib_constructions.rb +378 -378
  66. data/lib/openstudio/extension/core/os_lib_geometry.rb +1022 -1022
  67. data/lib/openstudio/extension/core/os_lib_helper_methods.rb +399 -399
  68. data/lib/openstudio/extension/core/os_lib_hvac.rb +2171 -2171
  69. data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +214 -214
  70. data/lib/openstudio/extension/core/os_lib_model_generation.rb +817 -817
  71. data/lib/openstudio/extension/core/os_lib_model_simplification.rb +1049 -1049
  72. data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +165 -165
  73. data/lib/openstudio/extension/core/os_lib_reporting.rb +4651 -4651
  74. data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +200 -200
  75. data/lib/openstudio/extension/core/os_lib_schedules.rb +963 -963
  76. data/lib/openstudio/extension/rake_task.rb +159 -149
  77. data/lib/openstudio/extension/runner.rb +667 -644
  78. data/lib/openstudio/extension/runner_config.rb +114 -0
  79. data/lib/openstudio/extension/version.rb +40 -40
  80. data/openstudio-extension.gemspec +34 -33
  81. metadata +39 -37
@@ -1,464 +1,464 @@
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_QAQC
37
- # include any general notes about QAQC method here
38
- # 'standard performance curves' based on what is in OpenStudio standards for prototype building
39
- # initially the tollerance will be hard coded vs. passed in as a method argument
40
-
41
- # checks the number of unmet hours in the model
42
- def check_mech_sys_part_load_eff(category, target_standard, min_pass, max_pass, name_only = false)
43
- if target_standard.include?('90.1')
44
- display_standard = "ASHRAE #{target_standard}"
45
- else
46
- display_standard = target_standard
47
- end
48
-
49
- component_type_array = ['ChillerElectricEIR', 'CoilCoolingDXSingleSpeed', 'CoilCoolingDXTwoSpeed', 'CoilHeatingDXSingleSpeed']
50
-
51
- # summary of the check
52
- check_elems = OpenStudio::AttributeVector.new
53
- check_elems << OpenStudio::Attribute.new('name', 'Mechanical System Part Load Efficiency')
54
- check_elems << OpenStudio::Attribute.new('category', category)
55
- check_elems << OpenStudio::Attribute.new('description', "Check 40% and 80% part load efficency against #{display_standard} for the following compenent types: #{component_type_array.join(', ')}. Checking EIR Function of Part Load Ratio curve for chiller and EIR Function of Flow Fraction for DX coils.")
56
- # TODO: - add in check for VAV fan
57
-
58
- # stop here if only name is requested this is used to populate display name for arguments
59
- if name_only == true
60
- results = []
61
- check_elems.each do |elem|
62
- results << elem.valueAsString
63
- end
64
- return results
65
- end
66
-
67
- # Versions of OpenStudio greater than 2.4.0 use a modified version of
68
- # openstudio-standards with different method calls. These methods
69
- # require a "Standard" object instead of the standard being passed into method calls.
70
- # This Standard object is used throughout the QAQC check.
71
- if OpenStudio::VersionString.new(OpenStudio.openStudioVersion) < OpenStudio::VersionString.new('2.4.3')
72
- use_old_gem_code = true
73
- else
74
- use_old_gem_code = false
75
- std = Standard.build(target_standard)
76
- end
77
-
78
- begin
79
- # TODO: - in future would be nice to dynamically genrate list of possible options from standards json
80
- chiller_air_cooled_condenser_types = ['WithCondenser', 'WithoutCondenser']
81
- chiller_water_cooled_compressor_types = ['Reciprocating', 'Scroll', 'Rotary Screw', 'Centrifugal']
82
- absorption_types = ['Single Effect', 'Double Effect Indirect Fired', 'Double Effect Direct Fired']
83
-
84
- # check getChillerElectricEIRs objects (will also have curve check in different script)
85
- @model.getChillerElectricEIRs.each do |component|
86
- # get curve and evaluate
87
- electric_input_to_cooling_output_ratio_function_of_PLR = component.electricInputToCoolingOutputRatioFunctionOfPLR
88
- curve_40_pct = electric_input_to_cooling_output_ratio_function_of_PLR.evaluate(0.4)
89
- curve_80_pct = electric_input_to_cooling_output_ratio_function_of_PLR.evaluate(0.8)
90
-
91
- # find ac properties
92
- if use_old_gem_code
93
- search_criteria = component.find_search_criteria(target_standard)
94
- else
95
- search_criteria = std.chiller_electric_eir_find_search_criteria(component)
96
- end
97
-
98
- # extend search_criteria for absorption_type
99
- absorption_types.each do |absorption_type|
100
- if component.name.to_s.include?(absorption_type)
101
- search_criteria['absorption_type'] = absorption_type
102
- next
103
- end
104
- end
105
- # extend search_criteria for condenser type or compressor type
106
- if search_criteria['cooling_type'] == 'AirCooled'
107
- chiller_air_cooled_condenser_types.each do |condenser_type|
108
- if component.name.to_s.include?(condenser_type)
109
- search_criteria['condenser_type'] = condenser_type
110
- next
111
- end
112
- end
113
- # if no match and also no absorption_type then issue warning
114
- if !search_criteria.key?('condenser_type') || search_criteria['condenser_type'].nil?
115
- if !search_criteria.key?('absorption_type') || search_criteria['absorption_type'].nil?
116
- check_elems << OpenStudio::Attribute.new('flag', "Can't find unique search criteria for #{component.name}. #{search_criteria}")
117
- next # don't go past here
118
- end
119
- end
120
- elsif search_criteria['cooling_type'] == 'WaterCooled'
121
- chiller_air_cooled_condenser_types.each do |compressor_type|
122
- if component.name.to_s.include?(compressor_type)
123
- search_criteria['compressor_type'] = compressor_type
124
- next
125
- end
126
- end
127
- # if no match and also no absorption_type then issue warning
128
- if !search_criteria.key?('compressor_type') || search_criteria['compressor_type'].nil?
129
- if !search_criteria.key?('absorption_type') || search_criteria['absorption_type'].nil?
130
- check_elems << OpenStudio::Attribute.new('flag', "Can't find unique search criteria for #{component.name}. #{search_criteria}")
131
- next # don't go past here
132
- end
133
- end
134
- end
135
-
136
- # lookup chiller
137
- if use_old_gem_code
138
- capacity_w = component.find_capacity
139
- else
140
- capacity_w = std.chiller_electric_eir_find_capacity(component)
141
- end
142
- capacity_tons = OpenStudio.convert(capacity_w, 'W', 'ton').get
143
-
144
- if use_old_gem_code
145
- chlr_props = component.model.find_object($os_standards['chillers'], search_criteria, capacity_tons, Date.today)
146
- chlr_props
147
- chlr_props = std.model_find_object(std.standards_data['chillers'], search_criteria, capacity_tons, Date.today)
148
- end
149
- if chlr_props.nil?
150
- check_elems << OpenStudio::Attribute.new('flag', "Didn't find chiller for #{component.name}. #{search_criteria}")
151
- next # don't go past here in loop if can't find curve
152
- end
153
-
154
- # temp model to hold temp curve
155
- model_temp = OpenStudio::Model::Model.new
156
-
157
- # create temp curve
158
- target_curve_name = chlr_props['eirfplr']
159
- if target_curve_name.nil?
160
- check_elems << OpenStudio::Attribute.new('flag', "Can't find target eirfplr curve for #{component.name}")
161
- next # don't go past here in loop if can't find curve
162
- end
163
- if use_old_gem_code
164
- temp_curve = model_temp.add_curve(target_curve_name)
165
- else
166
- temp_curve = std.model_add_curve(model_temp, target_curve_name)
167
- end
168
-
169
- target_curve_40_pct = temp_curve.evaluate(0.4)
170
- target_curve_80_pct = temp_curve.evaluate(0.8)
171
-
172
- # check curve at two points
173
- if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
174
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
175
- elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
176
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
177
- end
178
- if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
179
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
180
- elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
181
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
182
- end
183
- end
184
-
185
- # check getCoilCoolingDXSingleSpeeds objects (will also have curve check in different script)
186
- @model.getCoilCoolingDXSingleSpeeds.each do |component|
187
- # get curve and evaluate
188
- eir_function_of_flow_fraction_curve = component.energyInputRatioFunctionOfFlowFractionCurve
189
- curve_40_pct = eir_function_of_flow_fraction_curve.evaluate(0.4)
190
- curve_80_pct = eir_function_of_flow_fraction_curve.evaluate(0.8)
191
-
192
- # find ac properties
193
- if use_old_gem_code
194
- search_criteria = component.find_search_criteria(target_standard)
195
- else
196
- search_criteria = std.coil_dx_find_search_criteria(component)
197
- end
198
-
199
- if use_old_gem_code
200
- capacity_w = component.find_capacity
201
- else
202
- capacity_w = std.coil_cooling_dx_single_speed_find_capacity(component)
203
- end
204
- capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
205
-
206
- if use_old_gem_code
207
- if component.heat_pump?
208
- ac_props = component.model.find_object($os_standards['heat_pumps'], search_criteria, capacity_btu_per_hr, Date.today)
209
- else
210
- ac_props = component.model.find_object($os_standards['unitary_acs'], search_criteria, capacity_btu_per_hr, Date.today)
211
- end
212
- else
213
- if std.coil_dx_heat_pump?(component)
214
- ac_props = std.model_find_object(std.standards_data['heat_pumps'], search_criteria, capacity_btu_per_hr, Date.today)
215
- else
216
- ac_props = std.model_find_object(std.standards_data['unitary_acs'], search_criteria, capacity_btu_per_hr, Date.today)
217
- end
218
- end
219
-
220
- # temp model to hold temp curve
221
- model_temp = OpenStudio::Model::Model.new
222
-
223
- # create temp curve
224
- target_curve_name = ac_props['cool_eir_fflow']
225
- if target_curve_name.nil?
226
- check_elems << OpenStudio::Attribute.new('flag', "Can't find target cool_eir_fflow curve for #{component.name}")
227
- next # don't go past here in loop if can't find curve
228
- end
229
- if use_old_gem_code
230
- temp_curve = model_temp.add_curve(target_curve_name)
231
- else
232
- temp_curve = std.model_add_curve(model_temp, target_curve_name)
233
- end
234
- target_curve_40_pct = temp_curve.evaluate(0.4)
235
- target_curve_80_pct = temp_curve.evaluate(0.8)
236
-
237
- # check curve at two points
238
- if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
239
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
240
- elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
241
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
242
- end
243
- if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
244
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
245
- elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
246
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
247
- end
248
- end
249
-
250
- # check CoilCoolingDXTwoSpeed objects (will also have curve check in different script)
251
- @model.getCoilCoolingDXTwoSpeeds.each do |component|
252
- # get curve and evaluate
253
- eir_function_of_flow_fraction_curve = component.energyInputRatioFunctionOfFlowFractionCurve
254
- curve_40_pct = eir_function_of_flow_fraction_curve.evaluate(0.4)
255
- curve_80_pct = eir_function_of_flow_fraction_curve.evaluate(0.8)
256
-
257
- # find ac properties
258
- if use_old_gem_code
259
- search_criteria = component.find_search_criteria(target_standard)
260
- else
261
- search_criteria = std.coil_dx_find_search_criteria(component)
262
- end
263
-
264
- if use_old_gem_code
265
- capacity_w = component.find_capacity
266
- else
267
- capacity_w = std.coil_cooling_dx_two_speed_find_capacity(component)
268
- end
269
- capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
270
-
271
- if use_old_gem_code
272
- ac_props = component.model.find_object($os_standards['unitary_acs'], search_criteria, capacity_btu_per_hr, Date.today)
273
- else
274
- ac_props = std.model_find_object(std.standards_data['unitary_acs'], search_criteria, capacity_btu_per_hr, Date.today)
275
- end
276
-
277
- # temp model to hold temp curve
278
- model_temp = OpenStudio::Model::Model.new
279
-
280
- # create temp curve
281
- target_curve_name = ac_props['cool_eir_fflow']
282
- if target_curve_name.nil?
283
- check_elems << OpenStudio::Attribute.new('flag', "Can't find target cool_eir_flow curve for #{component.name}")
284
- next # don't go past here in loop if can't find curve
285
- end
286
- if use_old_gem_code
287
- temp_curve = model_temp.add_curve(target_curve_name)
288
- else
289
- temp_curve = std.model_add_curve(model_temp, target_curve_name)
290
- end
291
- target_curve_40_pct = temp_curve.evaluate(0.4)
292
- target_curve_80_pct = temp_curve.evaluate(0.8)
293
-
294
- # check curve at two points
295
- if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
296
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
297
- elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
298
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
299
- end
300
- if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
301
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
302
- elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
303
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
304
- end
305
- end
306
-
307
- # check CoilCoolingDXTwoSpeed objects (will also have curve check in different script)
308
- @model.getCoilHeatingDXSingleSpeeds.each do |component|
309
- # get curve and evaluate
310
- eir_function_of_flow_fraction_curve = component.energyInputRatioFunctionofFlowFractionCurve # why lowercase of here but not in CoilCoolingDX objects
311
- curve_40_pct = eir_function_of_flow_fraction_curve.evaluate(0.4)
312
- curve_80_pct = eir_function_of_flow_fraction_curve.evaluate(0.8)
313
-
314
- # find ac properties
315
- if use_old_gem_code
316
- search_criteria = component.find_search_criteria(target_standard)
317
- else
318
- search_criteria = std.coil_dx_find_search_criteria(component)
319
- end
320
-
321
- if use_old_gem_code
322
- capacity_w = component.find_capacity
323
- else
324
- capacity_w = std.coil_heating_dx_single_speed_find_capacity(component)
325
- end
326
- capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
327
-
328
- if use_old_gem_code
329
- ac_props = component.model.find_object($os_standards['heat_pumps_heating'], search_criteria, capacity_btu_per_hr, Date.today)
330
- else
331
- ac_props = std.model_find_object(std.standards_data['heat_pumps_heating'], search_criteria, capacity_btu_per_hr, Date.today)
332
- end
333
- if ac_props.nil?
334
- target_curve_name = nil
335
- else
336
- target_curve_name = ac_props['heat_eir_fflow']
337
- end
338
-
339
- # temp model to hold temp curve
340
- model_temp = OpenStudio::Model::Model.new
341
-
342
- # create temp curve
343
- if target_curve_name.nil?
344
- check_elems << OpenStudio::Attribute.new('flag', "Can't find target curve for #{component.name}")
345
- next # don't go past here in loop if can't find curve
346
- end
347
- if use_old_gem_code
348
- temp_curve = model_temp.add_curve(target_curve_name)
349
- else
350
- temp_curve = std.model_add_curve(model_temp, target_curve_name)
351
- end
352
-
353
- # Ensure that the curve was found in standards before attempting to evaluate
354
- if temp_curve.nil?
355
- check_elems << OpenStudio::Attribute.new('flag', "Can't find coefficients of curve called #{target_curve_name} for #{component.name}, cannot check part-load performance.")
356
- next
357
- end
358
-
359
- target_curve_40_pct = temp_curve.evaluate(0.4)
360
- target_curve_80_pct = temp_curve.evaluate(0.8)
361
-
362
- # check curve at two points
363
- if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
364
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
365
- elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
366
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
367
- end
368
- if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
369
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
370
- elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
371
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
372
- end
373
- end
374
-
375
- # check
376
- @model.getFanVariableVolumes.each do |component|
377
- # skip if not on multi-zone system.
378
- if component.airLoopHVAC.is_initialized
379
- airloop = component.airLoopHVAC.get
380
-
381
- next unless airloop.thermalZones.size > 1.0
382
- end
383
-
384
- # skip of brake horsepower is 0
385
- if use_old_gem_code
386
- next if component.brake_horsepower == 0.0
387
- else
388
- next if std.fan_brake_horsepower(component) == 0.0
389
- end
390
-
391
- # temp model for use by temp model and target curve
392
- model_temp = OpenStudio::Model::Model.new
393
-
394
- # get coeficents for fan
395
- model_fan_coefs = []
396
- model_fan_coefs << component.fanPowerCoefficient1.get
397
- model_fan_coefs << component.fanPowerCoefficient2.get
398
- model_fan_coefs << component.fanPowerCoefficient3.get
399
- model_fan_coefs << component.fanPowerCoefficient4.get
400
- model_fan_coefs << component.fanPowerCoefficient5.get
401
-
402
- # make model curve
403
- model_curve = OpenStudio::Model::CurveQuartic.new(model_temp)
404
- model_curve.setCoefficient1Constant(model_fan_coefs[0])
405
- model_curve.setCoefficient2x(model_fan_coefs[1])
406
- model_curve.setCoefficient3xPOW2(model_fan_coefs[2])
407
- model_curve.setCoefficient4xPOW3(model_fan_coefs[3])
408
- model_curve.setCoefficient5xPOW4(model_fan_coefs[4])
409
- curve_40_pct = model_curve.evaluate(0.4)
410
- curve_80_pct = model_curve.evaluate(0.8)
411
-
412
- # get target coefs
413
- target_fan = OpenStudio::Model::FanVariableVolume.new(model_temp)
414
- if use_old_gem_code
415
- target_fan.set_control_type('Multi Zone VAV with Static Pressure Reset')
416
- else
417
- std.fan_variable_volume_set_control_type(target_fan, 'Multi Zone VAV with VSD and Static Pressure Reset')
418
- end
419
-
420
- # get coeficents for fan
421
- target_fan_coefs = []
422
- target_fan_coefs << target_fan.fanPowerCoefficient1.get
423
- target_fan_coefs << target_fan.fanPowerCoefficient2.get
424
- target_fan_coefs << target_fan.fanPowerCoefficient3.get
425
- target_fan_coefs << target_fan.fanPowerCoefficient4.get
426
- target_fan_coefs << target_fan.fanPowerCoefficient5.get
427
-
428
- # make model curve
429
- target_curve = OpenStudio::Model::CurveQuartic.new(model_temp)
430
- target_curve.setCoefficient1Constant(target_fan_coefs[0])
431
- target_curve.setCoefficient2x(target_fan_coefs[1])
432
- target_curve.setCoefficient3xPOW2(target_fan_coefs[2])
433
- target_curve.setCoefficient4xPOW3(target_fan_coefs[3])
434
- target_curve.setCoefficient5xPOW4(target_fan_coefs[4])
435
- target_curve_40_pct = target_curve.evaluate(0.4)
436
- target_curve_80_pct = target_curve.evaluate(0.8)
437
-
438
- # check curve at two points
439
- if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
440
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
441
- elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
442
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
443
- end
444
- if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
445
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
446
- elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
447
- check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
448
- end
449
- end
450
- rescue StandardError => e
451
- # brief description of ruby error
452
- check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
453
-
454
- # backtrace of ruby error for diagnostic use
455
- if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
456
- end
457
-
458
- # add check_elms to new attribute
459
- check_elem = OpenStudio::Attribute.new('check', check_elems)
460
-
461
- return check_elem
462
- # note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
463
- end
464
- end
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_QAQC
37
+ # include any general notes about QAQC method here
38
+ # 'standard performance curves' based on what is in OpenStudio standards for prototype building
39
+ # initially the tollerance will be hard coded vs. passed in as a method argument
40
+
41
+ # checks the number of unmet hours in the model
42
+ def check_mech_sys_part_load_eff(category, target_standard, min_pass, max_pass, name_only = false)
43
+ if target_standard.include?('90.1')
44
+ display_standard = "ASHRAE #{target_standard}"
45
+ else
46
+ display_standard = target_standard
47
+ end
48
+
49
+ component_type_array = ['ChillerElectricEIR', 'CoilCoolingDXSingleSpeed', 'CoilCoolingDXTwoSpeed', 'CoilHeatingDXSingleSpeed']
50
+
51
+ # summary of the check
52
+ check_elems = OpenStudio::AttributeVector.new
53
+ check_elems << OpenStudio::Attribute.new('name', 'Mechanical System Part Load Efficiency')
54
+ check_elems << OpenStudio::Attribute.new('category', category)
55
+ check_elems << OpenStudio::Attribute.new('description', "Check 40% and 80% part load efficency against #{display_standard} for the following compenent types: #{component_type_array.join(', ')}. Checking EIR Function of Part Load Ratio curve for chiller and EIR Function of Flow Fraction for DX coils.")
56
+ # TODO: - add in check for VAV fan
57
+
58
+ # stop here if only name is requested this is used to populate display name for arguments
59
+ if name_only == true
60
+ results = []
61
+ check_elems.each do |elem|
62
+ results << elem.valueAsString
63
+ end
64
+ return results
65
+ end
66
+
67
+ # Versions of OpenStudio greater than 2.4.0 use a modified version of
68
+ # openstudio-standards with different method calls. These methods
69
+ # require a "Standard" object instead of the standard being passed into method calls.
70
+ # This Standard object is used throughout the QAQC check.
71
+ if OpenStudio::VersionString.new(OpenStudio.openStudioVersion) < OpenStudio::VersionString.new('2.4.3')
72
+ use_old_gem_code = true
73
+ else
74
+ use_old_gem_code = false
75
+ std = Standard.build(target_standard)
76
+ end
77
+
78
+ begin
79
+ # TODO: - in future would be nice to dynamically genrate list of possible options from standards json
80
+ chiller_air_cooled_condenser_types = ['WithCondenser', 'WithoutCondenser']
81
+ chiller_water_cooled_compressor_types = ['Reciprocating', 'Scroll', 'Rotary Screw', 'Centrifugal']
82
+ absorption_types = ['Single Effect', 'Double Effect Indirect Fired', 'Double Effect Direct Fired']
83
+
84
+ # check getChillerElectricEIRs objects (will also have curve check in different script)
85
+ @model.getChillerElectricEIRs.each do |component|
86
+ # get curve and evaluate
87
+ electric_input_to_cooling_output_ratio_function_of_PLR = component.electricInputToCoolingOutputRatioFunctionOfPLR
88
+ curve_40_pct = electric_input_to_cooling_output_ratio_function_of_PLR.evaluate(0.4)
89
+ curve_80_pct = electric_input_to_cooling_output_ratio_function_of_PLR.evaluate(0.8)
90
+
91
+ # find ac properties
92
+ if use_old_gem_code
93
+ search_criteria = component.find_search_criteria(target_standard)
94
+ else
95
+ search_criteria = std.chiller_electric_eir_find_search_criteria(component)
96
+ end
97
+
98
+ # extend search_criteria for absorption_type
99
+ absorption_types.each do |absorption_type|
100
+ if component.name.to_s.include?(absorption_type)
101
+ search_criteria['absorption_type'] = absorption_type
102
+ next
103
+ end
104
+ end
105
+ # extend search_criteria for condenser type or compressor type
106
+ if search_criteria['cooling_type'] == 'AirCooled'
107
+ chiller_air_cooled_condenser_types.each do |condenser_type|
108
+ if component.name.to_s.include?(condenser_type)
109
+ search_criteria['condenser_type'] = condenser_type
110
+ next
111
+ end
112
+ end
113
+ # if no match and also no absorption_type then issue warning
114
+ if !search_criteria.key?('condenser_type') || search_criteria['condenser_type'].nil?
115
+ if !search_criteria.key?('absorption_type') || search_criteria['absorption_type'].nil?
116
+ check_elems << OpenStudio::Attribute.new('flag', "Can't find unique search criteria for #{component.name}. #{search_criteria}")
117
+ next # don't go past here
118
+ end
119
+ end
120
+ elsif search_criteria['cooling_type'] == 'WaterCooled'
121
+ chiller_air_cooled_condenser_types.each do |compressor_type|
122
+ if component.name.to_s.include?(compressor_type)
123
+ search_criteria['compressor_type'] = compressor_type
124
+ next
125
+ end
126
+ end
127
+ # if no match and also no absorption_type then issue warning
128
+ if !search_criteria.key?('compressor_type') || search_criteria['compressor_type'].nil?
129
+ if !search_criteria.key?('absorption_type') || search_criteria['absorption_type'].nil?
130
+ check_elems << OpenStudio::Attribute.new('flag', "Can't find unique search criteria for #{component.name}. #{search_criteria}")
131
+ next # don't go past here
132
+ end
133
+ end
134
+ end
135
+
136
+ # lookup chiller
137
+ if use_old_gem_code
138
+ capacity_w = component.find_capacity
139
+ else
140
+ capacity_w = std.chiller_electric_eir_find_capacity(component)
141
+ end
142
+ capacity_tons = OpenStudio.convert(capacity_w, 'W', 'ton').get
143
+
144
+ if use_old_gem_code
145
+ chlr_props = component.model.find_object($os_standards['chillers'], search_criteria, capacity_tons, Date.today)
146
+ chlr_props
147
+ chlr_props = std.model_find_object(std.standards_data['chillers'], search_criteria, capacity_tons, Date.today)
148
+ end
149
+ if chlr_props.nil?
150
+ check_elems << OpenStudio::Attribute.new('flag', "Didn't find chiller for #{component.name}. #{search_criteria}")
151
+ next # don't go past here in loop if can't find curve
152
+ end
153
+
154
+ # temp model to hold temp curve
155
+ model_temp = OpenStudio::Model::Model.new
156
+
157
+ # create temp curve
158
+ target_curve_name = chlr_props['eirfplr']
159
+ if target_curve_name.nil?
160
+ check_elems << OpenStudio::Attribute.new('flag', "Can't find target eirfplr curve for #{component.name}")
161
+ next # don't go past here in loop if can't find curve
162
+ end
163
+ if use_old_gem_code
164
+ temp_curve = model_temp.add_curve(target_curve_name)
165
+ else
166
+ temp_curve = std.model_add_curve(model_temp, target_curve_name)
167
+ end
168
+
169
+ target_curve_40_pct = temp_curve.evaluate(0.4)
170
+ target_curve_80_pct = temp_curve.evaluate(0.8)
171
+
172
+ # check curve at two points
173
+ if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
174
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
175
+ elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
176
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
177
+ end
178
+ if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
179
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
180
+ elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
181
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
182
+ end
183
+ end
184
+
185
+ # check getCoilCoolingDXSingleSpeeds objects (will also have curve check in different script)
186
+ @model.getCoilCoolingDXSingleSpeeds.each do |component|
187
+ # get curve and evaluate
188
+ eir_function_of_flow_fraction_curve = component.energyInputRatioFunctionOfFlowFractionCurve
189
+ curve_40_pct = eir_function_of_flow_fraction_curve.evaluate(0.4)
190
+ curve_80_pct = eir_function_of_flow_fraction_curve.evaluate(0.8)
191
+
192
+ # find ac properties
193
+ if use_old_gem_code
194
+ search_criteria = component.find_search_criteria(target_standard)
195
+ else
196
+ search_criteria = std.coil_dx_find_search_criteria(component)
197
+ end
198
+
199
+ if use_old_gem_code
200
+ capacity_w = component.find_capacity
201
+ else
202
+ capacity_w = std.coil_cooling_dx_single_speed_find_capacity(component)
203
+ end
204
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
205
+
206
+ if use_old_gem_code
207
+ if component.heat_pump?
208
+ ac_props = component.model.find_object($os_standards['heat_pumps'], search_criteria, capacity_btu_per_hr, Date.today)
209
+ else
210
+ ac_props = component.model.find_object($os_standards['unitary_acs'], search_criteria, capacity_btu_per_hr, Date.today)
211
+ end
212
+ else
213
+ if std.coil_dx_heat_pump?(component)
214
+ ac_props = std.model_find_object(std.standards_data['heat_pumps'], search_criteria, capacity_btu_per_hr, Date.today)
215
+ else
216
+ ac_props = std.model_find_object(std.standards_data['unitary_acs'], search_criteria, capacity_btu_per_hr, Date.today)
217
+ end
218
+ end
219
+
220
+ # temp model to hold temp curve
221
+ model_temp = OpenStudio::Model::Model.new
222
+
223
+ # create temp curve
224
+ target_curve_name = ac_props['cool_eir_fflow']
225
+ if target_curve_name.nil?
226
+ check_elems << OpenStudio::Attribute.new('flag', "Can't find target cool_eir_fflow curve for #{component.name}")
227
+ next # don't go past here in loop if can't find curve
228
+ end
229
+ if use_old_gem_code
230
+ temp_curve = model_temp.add_curve(target_curve_name)
231
+ else
232
+ temp_curve = std.model_add_curve(model_temp, target_curve_name)
233
+ end
234
+ target_curve_40_pct = temp_curve.evaluate(0.4)
235
+ target_curve_80_pct = temp_curve.evaluate(0.8)
236
+
237
+ # check curve at two points
238
+ if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
239
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
240
+ elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
241
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
242
+ end
243
+ if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
244
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
245
+ elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
246
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
247
+ end
248
+ end
249
+
250
+ # check CoilCoolingDXTwoSpeed objects (will also have curve check in different script)
251
+ @model.getCoilCoolingDXTwoSpeeds.each do |component|
252
+ # get curve and evaluate
253
+ eir_function_of_flow_fraction_curve = component.energyInputRatioFunctionOfFlowFractionCurve
254
+ curve_40_pct = eir_function_of_flow_fraction_curve.evaluate(0.4)
255
+ curve_80_pct = eir_function_of_flow_fraction_curve.evaluate(0.8)
256
+
257
+ # find ac properties
258
+ if use_old_gem_code
259
+ search_criteria = component.find_search_criteria(target_standard)
260
+ else
261
+ search_criteria = std.coil_dx_find_search_criteria(component)
262
+ end
263
+
264
+ if use_old_gem_code
265
+ capacity_w = component.find_capacity
266
+ else
267
+ capacity_w = std.coil_cooling_dx_two_speed_find_capacity(component)
268
+ end
269
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
270
+
271
+ if use_old_gem_code
272
+ ac_props = component.model.find_object($os_standards['unitary_acs'], search_criteria, capacity_btu_per_hr, Date.today)
273
+ else
274
+ ac_props = std.model_find_object(std.standards_data['unitary_acs'], search_criteria, capacity_btu_per_hr, Date.today)
275
+ end
276
+
277
+ # temp model to hold temp curve
278
+ model_temp = OpenStudio::Model::Model.new
279
+
280
+ # create temp curve
281
+ target_curve_name = ac_props['cool_eir_fflow']
282
+ if target_curve_name.nil?
283
+ check_elems << OpenStudio::Attribute.new('flag', "Can't find target cool_eir_flow curve for #{component.name}")
284
+ next # don't go past here in loop if can't find curve
285
+ end
286
+ if use_old_gem_code
287
+ temp_curve = model_temp.add_curve(target_curve_name)
288
+ else
289
+ temp_curve = std.model_add_curve(model_temp, target_curve_name)
290
+ end
291
+ target_curve_40_pct = temp_curve.evaluate(0.4)
292
+ target_curve_80_pct = temp_curve.evaluate(0.8)
293
+
294
+ # check curve at two points
295
+ if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
296
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
297
+ elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
298
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
299
+ end
300
+ if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
301
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
302
+ elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
303
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
304
+ end
305
+ end
306
+
307
+ # check CoilCoolingDXTwoSpeed objects (will also have curve check in different script)
308
+ @model.getCoilHeatingDXSingleSpeeds.each do |component|
309
+ # get curve and evaluate
310
+ eir_function_of_flow_fraction_curve = component.energyInputRatioFunctionofFlowFractionCurve # why lowercase of here but not in CoilCoolingDX objects
311
+ curve_40_pct = eir_function_of_flow_fraction_curve.evaluate(0.4)
312
+ curve_80_pct = eir_function_of_flow_fraction_curve.evaluate(0.8)
313
+
314
+ # find ac properties
315
+ if use_old_gem_code
316
+ search_criteria = component.find_search_criteria(target_standard)
317
+ else
318
+ search_criteria = std.coil_dx_find_search_criteria(component)
319
+ end
320
+
321
+ if use_old_gem_code
322
+ capacity_w = component.find_capacity
323
+ else
324
+ capacity_w = std.coil_heating_dx_single_speed_find_capacity(component)
325
+ end
326
+ capacity_btu_per_hr = OpenStudio.convert(capacity_w, 'W', 'Btu/hr').get
327
+
328
+ if use_old_gem_code
329
+ ac_props = component.model.find_object($os_standards['heat_pumps_heating'], search_criteria, capacity_btu_per_hr, Date.today)
330
+ else
331
+ ac_props = std.model_find_object(std.standards_data['heat_pumps_heating'], search_criteria, capacity_btu_per_hr, Date.today)
332
+ end
333
+ if ac_props.nil?
334
+ target_curve_name = nil
335
+ else
336
+ target_curve_name = ac_props['heat_eir_fflow']
337
+ end
338
+
339
+ # temp model to hold temp curve
340
+ model_temp = OpenStudio::Model::Model.new
341
+
342
+ # create temp curve
343
+ if target_curve_name.nil?
344
+ check_elems << OpenStudio::Attribute.new('flag', "Can't find target curve for #{component.name}")
345
+ next # don't go past here in loop if can't find curve
346
+ end
347
+ if use_old_gem_code
348
+ temp_curve = model_temp.add_curve(target_curve_name)
349
+ else
350
+ temp_curve = std.model_add_curve(model_temp, target_curve_name)
351
+ end
352
+
353
+ # Ensure that the curve was found in standards before attempting to evaluate
354
+ if temp_curve.nil?
355
+ check_elems << OpenStudio::Attribute.new('flag', "Can't find coefficients of curve called #{target_curve_name} for #{component.name}, cannot check part-load performance.")
356
+ next
357
+ end
358
+
359
+ target_curve_40_pct = temp_curve.evaluate(0.4)
360
+ target_curve_80_pct = temp_curve.evaluate(0.8)
361
+
362
+ # check curve at two points
363
+ if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
364
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
365
+ elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
366
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
367
+ end
368
+ if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
369
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
370
+ elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
371
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
372
+ end
373
+ end
374
+
375
+ # check
376
+ @model.getFanVariableVolumes.each do |component|
377
+ # skip if not on multi-zone system.
378
+ if component.airLoopHVAC.is_initialized
379
+ airloop = component.airLoopHVAC.get
380
+
381
+ next unless airloop.thermalZones.size > 1.0
382
+ end
383
+
384
+ # skip of brake horsepower is 0
385
+ if use_old_gem_code
386
+ next if component.brake_horsepower == 0.0
387
+ else
388
+ next if std.fan_brake_horsepower(component) == 0.0
389
+ end
390
+
391
+ # temp model for use by temp model and target curve
392
+ model_temp = OpenStudio::Model::Model.new
393
+
394
+ # get coeficents for fan
395
+ model_fan_coefs = []
396
+ model_fan_coefs << component.fanPowerCoefficient1.get
397
+ model_fan_coefs << component.fanPowerCoefficient2.get
398
+ model_fan_coefs << component.fanPowerCoefficient3.get
399
+ model_fan_coefs << component.fanPowerCoefficient4.get
400
+ model_fan_coefs << component.fanPowerCoefficient5.get
401
+
402
+ # make model curve
403
+ model_curve = OpenStudio::Model::CurveQuartic.new(model_temp)
404
+ model_curve.setCoefficient1Constant(model_fan_coefs[0])
405
+ model_curve.setCoefficient2x(model_fan_coefs[1])
406
+ model_curve.setCoefficient3xPOW2(model_fan_coefs[2])
407
+ model_curve.setCoefficient4xPOW3(model_fan_coefs[3])
408
+ model_curve.setCoefficient5xPOW4(model_fan_coefs[4])
409
+ curve_40_pct = model_curve.evaluate(0.4)
410
+ curve_80_pct = model_curve.evaluate(0.8)
411
+
412
+ # get target coefs
413
+ target_fan = OpenStudio::Model::FanVariableVolume.new(model_temp)
414
+ if use_old_gem_code
415
+ target_fan.set_control_type('Multi Zone VAV with Static Pressure Reset')
416
+ else
417
+ std.fan_variable_volume_set_control_type(target_fan, 'Multi Zone VAV with VSD and Static Pressure Reset')
418
+ end
419
+
420
+ # get coeficents for fan
421
+ target_fan_coefs = []
422
+ target_fan_coefs << target_fan.fanPowerCoefficient1.get
423
+ target_fan_coefs << target_fan.fanPowerCoefficient2.get
424
+ target_fan_coefs << target_fan.fanPowerCoefficient3.get
425
+ target_fan_coefs << target_fan.fanPowerCoefficient4.get
426
+ target_fan_coefs << target_fan.fanPowerCoefficient5.get
427
+
428
+ # make model curve
429
+ target_curve = OpenStudio::Model::CurveQuartic.new(model_temp)
430
+ target_curve.setCoefficient1Constant(target_fan_coefs[0])
431
+ target_curve.setCoefficient2x(target_fan_coefs[1])
432
+ target_curve.setCoefficient3xPOW2(target_fan_coefs[2])
433
+ target_curve.setCoefficient4xPOW3(target_fan_coefs[3])
434
+ target_curve.setCoefficient5xPOW4(target_fan_coefs[4])
435
+ target_curve_40_pct = target_curve.evaluate(0.4)
436
+ target_curve_80_pct = target_curve.evaluate(0.8)
437
+
438
+ # check curve at two points
439
+ if curve_40_pct < target_curve_40_pct * (1.0 - min_pass)
440
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
441
+ elsif curve_40_pct > target_curve_40_pct * (1.0 + max_pass)
442
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 40% of #{curve_40_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_40_pct.round(2)} for #{display_standard}.")
443
+ end
444
+ if curve_80_pct < target_curve_80_pct * (1.0 - min_pass)
445
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{min_pass * 100} % below the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
446
+ elsif curve_80_pct > target_curve_80_pct * (1.0 + max_pass)
447
+ check_elems << OpenStudio::Attribute.new('flag', "The curve value at 80% of #{curve_80_pct.round(2)} for #{component.name} is more than #{max_pass * 100} % above the typical value of #{target_curve_80_pct.round(2)} for #{display_standard}.")
448
+ end
449
+ end
450
+ rescue StandardError => e
451
+ # brief description of ruby error
452
+ check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
453
+
454
+ # backtrace of ruby error for diagnostic use
455
+ if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
456
+ end
457
+
458
+ # add check_elms to new attribute
459
+ check_elem = OpenStudio::Attribute.new('check', check_elems)
460
+
461
+ return check_elem
462
+ # note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
463
+ end
464
+ end