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,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