openstudio-extension 0.1.0 → 0.1.1

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