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,453 @@
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
+
39
+ # checks the number of unmet hours in the model
40
+ # todo - do I need unique tolerance ranges for conductance, reflectance, and shgc
41
+ def check_envelope_conductance(category, target_standard, min_pass, max_pass, name_only = false)
42
+ # summary of the check
43
+ check_elems = OpenStudio::AttributeVector.new
44
+ check_elems << OpenStudio::Attribute.new('name', 'Envelope R-Value')
45
+ check_elems << OpenStudio::Attribute.new('category', category)
46
+ if target_standard == 'ICC IECC 2015'
47
+ dislay_standard = target_standard
48
+ check_elems << OpenStudio::Attribute.new('description', "Check envelope against Table R402.1.2 and R402.1.4 in #{dislay_standard} Residential Provisions.")
49
+ elsif target_standard.include?('90.1-2013')
50
+ display_standard = "ASHRAE #{target_standard}"
51
+ check_elems << OpenStudio::Attribute.new('description', "Check envelope against #{display_standard} Table 5.5.2, Table G2.1.5 b,c,d,e, Section 5.5.3.1.1a. Roof reflectance of 55%, wall relfectance of 30%.")
52
+ else
53
+ # TODO: - could add more elsifs if want to dsiplay tables and sections for additional 90.1 standards
54
+ if target_standard.include?('90.1')
55
+ display_standard = "ASHRAE #{target_standard}"
56
+ else
57
+ display_standard = target_standard
58
+ end
59
+ check_elems << OpenStudio::Attribute.new('description', "Check envelope against #{display_standard}. Roof reflectance of 55%, wall relfectance of 30%.")
60
+ end
61
+
62
+ # stop here if only name is requested this is used to populate display name for arguments
63
+ if name_only == true
64
+ results = []
65
+ check_elems.each do |elem|
66
+ results << elem.valueAsString
67
+ end
68
+ return results
69
+ end
70
+
71
+ # list of surface types to identify for each space type for surfaces and sub-surfaces
72
+ construction_type_array = []
73
+ construction_type_array << ['ExteriorWall', 'SteelFramed']
74
+ construction_type_array << ['ExteriorRoof', 'IEAD']
75
+ construction_type_array << ['ExteriorFloor', 'Mass']
76
+ construction_type_array << ['ExteriorDoor', 'Swinging']
77
+ construction_type_array << ['ExteriorWindow', 'Metal framing (all other)']
78
+ construction_type_array << ['Skylight', 'Glass with Curb']
79
+ # overhead door doesn't show in list, or glass door
80
+
81
+ # Versions of OpenStudio greater than 2.4.0 use a modified version of
82
+ # openstudio-standards with different method calls. These methods
83
+ # require a "Standard" object instead of the standard being passed into method calls.
84
+ # This Standard object is used throughout the QAQC check.
85
+ if OpenStudio::VersionString.new(OpenStudio.openStudioVersion) < OpenStudio::VersionString.new('2.4.3')
86
+ use_old_gem_code = true
87
+ else
88
+ use_old_gem_code = false
89
+ std = Standard.build(target_standard)
90
+ end
91
+
92
+ begin
93
+ # loop through all space types used in the model
94
+ @model.getSpaceTypes.each do |space_type|
95
+ next if space_type.floorArea <= 0
96
+ space_type_const_properties = {}
97
+ construction_type_array.each do |const_type|
98
+ # gather data for exterior wall
99
+ intended_surface_type = const_type[0]
100
+ standards_construction_type = const_type[1]
101
+ space_type_const_properties[intended_surface_type] = {}
102
+ if use_old_gem_code
103
+ data = space_type.get_construction_properties(target_standard, intended_surface_type, standards_construction_type)
104
+ else
105
+ data = std.space_type_get_construction_properties(space_type, intended_surface_type, standards_construction_type)
106
+ end
107
+ if data.nil?
108
+ puts "lookup for #{target_standard},#{intended_surface_type},#{standards_construction_type}"
109
+ check_elems << OpenStudio::Attribute.new('flag', "Didn't find construction for #{standards_construction_type} #{intended_surface_type} for #{space_type.name}.")
110
+ elsif intended_surface_type.include? 'ExteriorWall' || 'ExteriorFloor' || 'ExteriorDoor'
111
+ space_type_const_properties[intended_surface_type]['u_value'] = data['assembly_maximum_u_value']
112
+ space_type_const_properties[intended_surface_type]['reflectance'] = 0.30 # hard coded value
113
+ elsif intended_surface_type.include? 'ExteriorRoof'
114
+ space_type_const_properties[intended_surface_type]['u_value'] = data['assembly_maximum_u_value']
115
+ space_type_const_properties[intended_surface_type]['reflectance'] = 0.55 # hard coded value
116
+ else
117
+ space_type_const_properties[intended_surface_type]['u_value'] = data['assembly_maximum_u_value']
118
+ space_type_const_properties[intended_surface_type]['shgc'] = data['assembly_maximum_solar_heat_gain_coefficient']
119
+ end
120
+ end
121
+
122
+ # make array of construction details for surfaces
123
+ surface_details = []
124
+ missing_surface_constructions = []
125
+ sub_surface_details = []
126
+ missing_sub_surface_constructions = []
127
+
128
+ # loop through spaces
129
+ space_type.spaces.each do |space|
130
+ space.surfaces.each do |surface|
131
+ next if surface.outsideBoundaryCondition != 'Outdoors'
132
+ if surface.construction.is_initialized
133
+ surface_details << { boundary_condition: surface.outsideBoundaryCondition, surface_type: surface.surfaceType, construction: surface.construction.get }
134
+ else
135
+ missing_constructions << surface.name.get
136
+ end
137
+
138
+ # make array of construction details for sub_surfaces
139
+ surface.subSurfaces.each do |sub_surface|
140
+ if sub_surface.construction.is_initialized
141
+ sub_surface_details << { boundary_condition: sub_surface.outsideBoundaryCondition, surface_type: sub_surface.subSurfaceType, construction: sub_surface.construction.get }
142
+ else
143
+ missing_constructions << sub_surface.name.get
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ if !missing_surface_constructions.empty?
150
+ check_elems << OpenStudio::Attribute.new('flag', "#{missing_constructions.size} surfaces are missing constructions in #{space_type.name}. Spaces and can't be checked.")
151
+ end
152
+
153
+ if !missing_sub_surface_constructions.empty?
154
+ check_elems << OpenStudio::Attribute.new('flag', "#{missing_constructions.size} sub surfaces are missing constructions in #{space_type.name}. Spaces and can't be checked.")
155
+ end
156
+
157
+ # gather targer values for this space type
158
+ # todo - address support for other surface types e.g. overhead door glass door
159
+ target_r_value_ip = {}
160
+ target_reflectance = {}
161
+ target_u_value_ip = {}
162
+ target_shgc = {}
163
+ target_r_value_ip['Wall'] = 1.0 / space_type_const_properties['ExteriorWall']['u_value'].to_f
164
+ target_reflectance['Wall'] = space_type_const_properties['ExteriorWall']['reflectance'].to_f
165
+ target_r_value_ip['RoofCeiling'] = 1.0 / space_type_const_properties['ExteriorRoof']['u_value'].to_f
166
+ target_reflectance['RoofCeiling'] = space_type_const_properties['ExteriorRoof']['reflectance'].to_f
167
+ target_r_value_ip['Floor'] = 1.0 / space_type_const_properties['ExteriorFloor']['u_value'].to_f
168
+ target_reflectance['Floor'] = space_type_const_properties['ExteriorFloor']['reflectance'].to_f
169
+ target_r_value_ip['Door'] = 1.0 / space_type_const_properties['ExteriorDoor']['u_value'].to_f
170
+ target_reflectance['Door'] = space_type_const_properties['ExteriorDoor']['reflectance'].to_f
171
+ target_u_value_ip['FixedWindow'] = space_type_const_properties['ExteriorWindow']['u_value'].to_f
172
+ target_shgc['FixedWindow'] = space_type_const_properties['ExteriorWindow']['shgc'].to_f
173
+ target_u_value_ip['OperableWindow'] = space_type_const_properties['ExteriorWindow']['u_value'].to_f
174
+ target_shgc['OperableWindow'] = space_type_const_properties['ExteriorWindow']['shgc'].to_f
175
+ target_u_value_ip['Skylight'] = space_type_const_properties['Skylight']['u_value'].to_f
176
+ target_shgc['Skylight'] = space_type_const_properties['Skylight']['shgc'].to_f
177
+
178
+ # loop through unique construction arary combinations
179
+ surface_details.uniq.each do |surface_detail|
180
+ if surface_detail[:construction].thermalConductance.is_initialized
181
+
182
+ # don't use intened surface type of construction, look map based on surface type and boundary condition
183
+ boundary_condition = surface_detail[:boundary_condition]
184
+ surface_type = surface_detail[:surface_type]
185
+ intended_surface_type = ''
186
+ if boundary_condition.to_s == 'Outdoors'
187
+ if surface_type.to_s == 'Wall' then intended_surface_type = 'ExteriorWall' end
188
+ if surface_type == 'RoofCeiling' then intended_surface_type = 'ExteriorRoof' end
189
+ if surface_type == 'Floor' then intended_surface_type = 'ExteriorFloor' end
190
+ else
191
+ # currently only used for surfaces with outdoor boundary condition
192
+ end
193
+ if use_old_gem_code
194
+ film_coefficients_r_value = surface_detail[:construction].to_LayeredConstruction.get.to_Construction.get.film_coefficients_r_value(intended_surface_type)
195
+ else
196
+ film_coefficients_r_value = std.film_coefficients_r_value(intended_surface_type, includes_int_film = true, includes_ext_film = true)
197
+ end
198
+ thermal_conductance = surface_detail[:construction].thermalConductance.get
199
+ r_value_with_film = 1 / thermal_conductance + film_coefficients_r_value
200
+ source_units = 'm^2*K/W'
201
+ target_units = 'ft^2*h*R/Btu'
202
+ r_value_ip = OpenStudio.convert(r_value_with_film, source_units, target_units).get
203
+ solar_reflectance = surface_detail[:construction].to_LayeredConstruction.get.layers[0].to_OpaqueMaterial.get.solarReflectance .get # TODO: - check optional first does what happens with ext. air wall
204
+
205
+ # stop if didn't find values (0 or infinity)
206
+ next if target_r_value_ip[surface_detail[:surface_type]] == 0.0
207
+ next if target_r_value_ip[surface_detail[:surface_type]] == Float::INFINITY
208
+
209
+ # check r avlues
210
+ if r_value_ip < target_r_value_ip[surface_detail[:surface_type]] * (1.0 - min_pass)
211
+ check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{surface_detail[:construction].name} in #{space_type.name} is more than #{min_pass * 100} % below the expected value of #{target_r_value_ip[surface_detail[:surface_type]].round(2)} (#{target_units}) for #{display_standard}.")
212
+ elsif r_value_ip > target_r_value_ip[surface_detail[:surface_type]] * (1.0 + max_pass)
213
+ check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{surface_detail[:construction].name} in #{space_type.name} is more than #{max_pass * 100} % above the expected value of #{target_r_value_ip[surface_detail[:surface_type]].round(2)} (#{target_units}) for #{display_standard}.")
214
+ end
215
+
216
+ # check solar reflectance
217
+ if (solar_reflectance < target_reflectance[surface_detail[:surface_type]] * (1.0 - min_pass)) && (target_standard != 'ICC IECC 2015')
218
+ check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{surface_detail[:construction].name} in #{space_type.name} is more than #{min_pass * 100} % below the expected value of #{(target_reflectance[surface_detail[:surface_type]] * 100).round} %.")
219
+ elsif (solar_reflectance > target_reflectance[surface_detail[:surface_type]] * (1.0 + max_pass)) && (target_standard != 'ICC IECC 2015')
220
+ check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{surface_detail[:construction].name} in #{space_type.name} is more than #{max_pass * 100} % above the expected value of #{(target_reflectance[surface_detail[:surface_type]] * 100).round} %.")
221
+ end
222
+
223
+ else
224
+ check_elems << OpenStudio::Attribute.new('flag', "Can't calculate R value for #{surface_detail[:construction].name}.")
225
+ end
226
+ end
227
+
228
+ # loop through unique construction arary combinations
229
+ sub_surface_details.uniq.each do |sub_surface_detail|
230
+ if sub_surface_detail[:surface_type] == 'FixedWindow' || sub_surface_detail[:surface_type] == 'OperableWindow' || sub_surface_detail[:surface_type] == 'Skylight'
231
+ # check for non opaque sub surfaces
232
+ source_units = 'W/m^2*K'
233
+ target_units = 'Btu/ft^2*h*R'
234
+
235
+ if use_old_gem_code
236
+ u_factor_si = sub_surface_detail[:construction].to_LayeredConstruction.get.to_Construction.get.calculated_u_factor
237
+ else
238
+ u_factor_si = std.construction_calculated_u_factor(sub_surface_detail[:construction].to_LayeredConstruction.get.to_Construction.get)
239
+ end
240
+ u_factor_ip = OpenStudio.convert(u_factor_si, source_units, target_units).get
241
+ if use_old_gem_code
242
+ shgc = sub_surface_detail[:construction].to_LayeredConstruction.get.to_Construction.get.calculated_solar_heat_gain_coefficient
243
+ else
244
+ shgc = std.construction_calculated_solar_heat_gain_coefficient(sub_surface_detail[:construction].to_LayeredConstruction.get.to_Construction.get)
245
+ end
246
+
247
+ # stop if didn't find values (0 or infinity)
248
+ next if target_u_value_ip[sub_surface_detail[:surface_type]] == 0.0
249
+ next if target_u_value_ip[sub_surface_detail[:surface_type]] == Float::INFINITY
250
+
251
+ # check u avlues
252
+ if u_factor_ip < target_u_value_ip[sub_surface_detail[:surface_type]] * (1.0 - min_pass)
253
+ check_elems << OpenStudio::Attribute.new('flag', "U value of #{u_factor_ip.round(2)} (#{target_units}) for #{sub_surface_detail[:construction].name} in #{space_type.name} is more than #{min_pass * 100} % below the expected value of #{target_u_value_ip[sub_surface_detail[:surface_type]].round(2)} (#{target_units}) for #{display_standard}.")
254
+ elsif u_factor_ip > target_u_value_ip[sub_surface_detail[:surface_type]] * (1.0 + max_pass)
255
+ check_elems << OpenStudio::Attribute.new('flag', "U value of #{u_factor_ip.round(2)} (#{target_units}) for #{sub_surface_detail[:construction].name} in #{space_type.name} is more than #{max_pass * 100} % above the expected value of #{target_u_value_ip[sub_surface_detail[:surface_type]].round(2)} (#{target_units}) for #{display_standard}.")
256
+ end
257
+
258
+ # check shgc
259
+ if shgc < target_shgc[sub_surface_detail[:surface_type]] * (1.0 - min_pass)
260
+ check_elems << OpenStudio::Attribute.new('flag', "SHGC of #{shgc.round(2)} % for #{sub_surface_detail[:construction].name} in #{space_type.name} is more than #{min_pass * 100} % below the expected value of #{target_shgc[sub_surface_detail[:surface_type]].round(2)} %.")
261
+ elsif shgc > target_shgc[sub_surface_detail[:surface_type]] * (1.0 + max_pass)
262
+ check_elems << OpenStudio::Attribute.new('flag', "SHGC of #{shgc.round(2)} % for #{sub_surface_detail[:construction].name} in #{space_type.name} is more than #{max_pass * 100} % above the expected value of #{target_shgc[sub_surface_detail[:surface_type]].round(2)} %.")
263
+ end
264
+
265
+ else
266
+ # check for opaque sub surfaces
267
+ if sub_surface_detail[:construction].thermalConductance.is_initialized
268
+
269
+ # don't use intened surface type of construction, look map based on surface type and boundary condition
270
+ boundary_condition = sub_surface_detail[:boundary_condition]
271
+ surface_type = sub_surface_detail[:surface_type]
272
+ intended_surface_type = ''
273
+ if boundary_condition.to_s == 'Outdoors'
274
+ # TODO: add additional intended surface types
275
+ if surface_type.to_s == 'Door' then intended_surface_type = 'ExteriorDoor' end
276
+ else
277
+ # currently only used for surfaces with outdoor boundary condition
278
+ end
279
+ if use_old_gem_code
280
+ film_coefficients_r_value = sub_surface_detail[:construction].to_LayeredConstruction.get.to_Construction.get.film_coefficients_r_value(intended_surface_type)
281
+ else
282
+ film_coefficients_r_value = std.film_coefficients_r_value(intended_surface_type, includes_int_film = true, includes_ext_film = true)
283
+ end
284
+
285
+ thermal_conductance = sub_surface_detail[:construction].thermalConductance.get
286
+ r_value_with_film = 1 / thermal_conductance + film_coefficients_r_value
287
+ source_units = 'm^2*K/W'
288
+ target_units = 'ft^2*h*R/Btu'
289
+ r_value_ip = OpenStudio.convert(r_value_with_film, source_units, target_units).get
290
+ solar_reflectance = sub_surface_detail[:construction].to_LayeredConstruction.get.layers[0].to_OpaqueMaterial.get.solarReflectance .get # TODO: - check optional first does what happens with ext. air wall
291
+
292
+ # stop if didn't find values (0 or infinity)
293
+ next if target_r_value_ip[sub_surface_detail[:surface_type]] == 0.0
294
+ next if target_r_value_ip[sub_surface_detail[:surface_type]] == Float::INFINITY
295
+
296
+ # check r avlues
297
+ if r_value_ip < target_r_value_ip[sub_surface_detail[:surface_type]] * (1.0 - min_pass)
298
+ check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{sub_surface_detail[:construction].name} in #{space_type.name} is more than #{min_pass * 100} % below the expected value of #{target_r_value_ip[sub_surface_detail[:surface_type]].round(2)} (#{target_units}) for #{display_standard}.")
299
+ elsif r_value_ip > target_r_value_ip[sub_surface_detail[:surface_type]] * (1.0 + max_pass)
300
+ check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{sub_surface_detail[:construction].name} in #{space_type.name} is more than #{max_pass * 100} % above the expected value of #{target_r_value_ip[sub_surface_detail[:surface_type]].round(2)} (#{target_units}) for #{display_standard}.")
301
+ end
302
+
303
+ # check solar reflectance
304
+ if (solar_reflectance < target_reflectance[sub_surface_detail[:surface_type]] * (1.0 - min_pass)) && (target_standard != 'ICC IECC 2015')
305
+ check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{sub_surface_detail[:construction].name} in #{space_type.name} is more than #{min_pass * 100} % below the expected value of #{(target_reflectance[sub_surface_detail[:surface_type]] * 100).round} %.")
306
+ elsif (solar_reflectance > target_reflectance[sub_surface_detail[:surface_type]] * (1.0 + max_pass)) && (target_standard != 'ICC IECC 2015')
307
+ check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{sub_surface_detail[:construction].name} in #{space_type.name} is more than #{max_pass * 100} % above the expected value of #{(target_reflectance[sub_surface_detail[:surface_type]] * 100).round} %.")
308
+ end
309
+
310
+ else
311
+ check_elems << OpenStudio::Attribute.new('flag', "Can't calculate R value for #{sub_surface_detail[:construction].name}.")
312
+ end
313
+
314
+ end
315
+ end
316
+ end
317
+
318
+ # check spaces without space types against Nonresidential for this climate zone
319
+ @model.getSpaces.each do |space|
320
+ if !space.spaceType.is_initialized
321
+
322
+ # make array of construction details for surfaces
323
+ surface_details = []
324
+ missing_surface_constructions = []
325
+ sub_surface_details = []
326
+ missing_sub_surface_constructions = []
327
+
328
+ space.surfaces.each do |surface|
329
+ next if surface.outsideBoundaryCondition != 'Outdoors'
330
+ if surface.construction.is_initialized
331
+ surface_details << { boundary_condition: surface.outsideBoundaryCondition, surface_type: surface.surfaceType, construction: surface.construction.get }
332
+ else
333
+ missing_constructions << surface.name.get
334
+ end
335
+
336
+ # make array of construction details for sub_surfaces
337
+ surface.subSurfaces.each do |sub_surface|
338
+ if sub_surface.construction.is_initialized
339
+ sub_surface_details << { boundary_condition: sub_surface.outsideBoundaryCondition, surface_type: sub_surface.subSurfaceType, construction: sub_surface.construction.get }
340
+ else
341
+ missing_constructions << sub_surface.name.get
342
+ end
343
+ end
344
+ end
345
+
346
+ if !missing_surface_constructions.empty?
347
+ check_elems << OpenStudio::Attribute.new('flag', "#{missing_constructions.size} surfaces are missing constructions in #{space_type.name}. Spaces and can't be checked.")
348
+ end
349
+
350
+ if !missing_sub_surface_constructions.empty?
351
+ check_elems << OpenStudio::Attribute.new('flag', "#{missing_constructions.size} sub surfaces are missing constructions in #{space_type.name}. Spaces and can't be checked.")
352
+ end
353
+
354
+ surface_details.uniq.each do |surface_detail|
355
+ if surface_detail[:construction].thermalConductance.is_initialized
356
+ # don't use intened surface type of construction, look map based on surface type and boundary condition
357
+ boundary_condition = surface_detail[:boundary_condition]
358
+ surface_type = surface_detail[:surface_type]
359
+ intended_surface_type = ''
360
+ if boundary_condition.to_s == 'Outdoors'
361
+ if surface_type.to_s == 'Wall'
362
+ intended_surface_type = 'ExteriorWall'
363
+ standards_construction_type = 'SteelFramed'
364
+ elsif surface_type == 'RoofCeiling'
365
+ intended_surface_type = 'ExteriorRoof'
366
+ standards_construction_type = 'IEAD'
367
+ else surface_type == 'Floor'
368
+ intended_surface_type = 'ExteriorFloor'
369
+ standards_construction_type = 'Mass'
370
+ end
371
+ else
372
+ # currently only used for surfaces with outdoor boundary condition
373
+ end
374
+ if use_old_gem_code
375
+ film_coefficients_r_value = surface_detail[:construction].to_LayeredConstruction.get.to_Construction.get.film_coefficients_r_value(intended_surface_type)
376
+ else
377
+ film_coefficients_r_value = std.film_coefficients_r_value(intended_surface_type, includes_int_film = true, includes_ext_film = true)
378
+ end
379
+
380
+ thermal_conductance = surface_detail[:construction].thermalConductance.get
381
+ r_value_with_film = 1 / thermal_conductance + film_coefficients_r_value
382
+ source_units = 'm^2*K/W'
383
+ target_units = 'ft^2*h*R/Btu'
384
+ r_value_ip = OpenStudio.convert(r_value_with_film, source_units, target_units).get
385
+ solar_reflectance = surface_detail[:construction].to_LayeredConstruction.get.layers[0].to_OpaqueMaterial.get.solarReflectance .get # TODO: - check optional first does what happens with ext. air wall
386
+
387
+ # calculate target_r_value_ip
388
+ target_reflectance = nil
389
+ if use_old_gem_code
390
+ data = @model.get_construction_properties(target_standard, intended_surface_type, standards_construction_type)
391
+ else
392
+ data = std.model_get_construction_properties(@model, intended_surface_type, standards_construction_type)
393
+ end
394
+
395
+ if data.nil?
396
+ check_elems << OpenStudio::Attribute.new('flag', "Didn't find construction for #{standards_construction_type} #{intended_surface_type} for #{space.name}.")
397
+ next
398
+ elsif intended_surface_type.include? 'ExteriorWall' || 'ExteriorFloor' || 'ExteriorDoor'
399
+ assembly_maximum_u_value = data['assembly_maximum_u_value']
400
+ target_reflectance = 0.30
401
+ elsif intended_surface_type.include? 'ExteriorRoof'
402
+ assembly_maximum_u_value = data['assembly_maximum_u_value']
403
+ target_reflectance = 0.55
404
+ else
405
+ assembly_maximum_u_value = data['assembly_maximum_u_value']
406
+ assembly_maximum_solar_heat_gain_coefficient = data['assembly_maximum_solar_heat_gain_coefficient']
407
+ end
408
+ assembly_maximum_r_value_ip = 1 / assembly_maximum_u_value
409
+
410
+ # stop if didn't find values (0 or infinity)
411
+ next if assembly_maximum_r_value_ip == 0.0
412
+ next if assembly_maximum_r_value_ip == Float::INFINITY
413
+
414
+ # check r avlues
415
+ if r_value_ip < assembly_maximum_r_value_ip * (1.0 - min_pass)
416
+ check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{surface_detail[:construction].name} in #{space.name} is more than #{min_pass * 100} % below the expected value of #{assembly_maximum_r_value_ip.round(2)} (#{target_units}) for #{display_standard}.")
417
+ elsif r_value_ip > assembly_maximum_r_value_ip * (1.0 + max_pass)
418
+ check_elems << OpenStudio::Attribute.new('flag', "R value of #{r_value_ip.round(2)} (#{target_units}) for #{surface_detail[:construction].name} in #{space.name} is more than #{max_pass * 100} % above the expected value of #{assembly_maximum_r_value_ip.round(2)} (#{target_units}) for #{display_standard}.")
419
+ end
420
+
421
+ # check solar reflectance
422
+ if (solar_reflectance < target_reflectance * (1.0 - min_pass)) && (target_standard != 'ICC IECC 2015')
423
+ check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{surface_detail[:construction].name} in #{space.name} is more than #{min_pass * 100} % below the expected value of #{(target_reflectance * 100).round} %.")
424
+ elsif (solar_reflectance > target_reflectance * (1.0 + max_pass)) && (target_standard != 'ICC IECC 2015')
425
+ check_elems << OpenStudio::Attribute.new('flag', "Solar Reflectance of #{(solar_reflectance * 100).round} % for #{surface_detail[:construction].name} in #{space.name} is more than #{max_pass * 100} % above the expected value of #{(target_reflectance * 100).round} %.")
426
+ end
427
+ else
428
+ check_elems << OpenStudio::Attribute.new('flag', "Can't calculate R value for #{surface_detail[:construction].name}.")
429
+ end
430
+ end
431
+
432
+ sub_surface_details.uniq.each do |sub_surface_detail|
433
+ # TODO: update this so it works for doors and windows
434
+ check_elems << OpenStudio::Attribute.new('flag', "Not setup to check sub-surfaces of spaces without space types. Can't check properties for #{sub_surface_detail[:construction].name}.")
435
+ end
436
+
437
+ end
438
+ end
439
+ rescue StandardError => e
440
+ # brief description of ruby error
441
+ check_elems << OpenStudio::Attribute.new('flag', "Error prevented QAQC check from running (#{e}).")
442
+
443
+ # backtrace of ruby error for diagnostic use
444
+ if @error_backtrace then check_elems << OpenStudio::Attribute.new('flag', e.backtrace.join("\n").to_s) end
445
+ end
446
+
447
+ # add check_elms to new attribute
448
+ check_elem = OpenStudio::Attribute.new('check', check_elems)
449
+
450
+ return check_elem
451
+ # note: registerWarning and registerValue will be added for checks downstream using os_lib_reporting_qaqc.rb
452
+ end
453
+ end