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