openstudio-extension 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +29 -26
  3. data/.rspec +3 -3
  4. data/.rubocop.yml +10 -9
  5. data/CHANGELOG.md +16 -0
  6. data/Gemfile +6 -6
  7. data/Jenkinsfile +10 -10
  8. data/README.md +251 -250
  9. data/Rakefile +119 -119
  10. data/bin/console +14 -14
  11. data/bin/setup +8 -8
  12. data/doc_templates/LICENSE.md +26 -26
  13. data/doc_templates/README.md.erb +41 -41
  14. data/doc_templates/copyright_erb.txt +35 -35
  15. data/doc_templates/copyright_js.txt +3 -3
  16. data/doc_templates/copyright_ruby.txt +33 -33
  17. data/init_templates/README.md +37 -37
  18. data/init_templates/gemspec.txt +32 -32
  19. data/init_templates/openstudio_module.rb +50 -50
  20. data/init_templates/spec.rb +47 -47
  21. data/init_templates/spec_helper.rb +49 -49
  22. data/init_templates/template_gemfile.txt +17 -17
  23. data/init_templates/template_rakefile.txt +15 -15
  24. data/init_templates/version.rb +40 -40
  25. data/lib/files/openstudio-extension-gem-test.ddy +536 -536
  26. data/lib/files/openstudio-extension-gem-test.epw +8768 -8768
  27. data/lib/files/openstudio-extension-gem-test.stat +554 -554
  28. data/lib/measures/openstudio_extension_test_measure/LICENSE.md +26 -26
  29. data/lib/measures/openstudio_extension_test_measure/README.md +26 -26
  30. data/lib/measures/openstudio_extension_test_measure/README.md.erb +41 -41
  31. data/lib/measures/openstudio_extension_test_measure/measure.rb +72 -72
  32. data/lib/measures/openstudio_extension_test_measure/measure.xml +83 -83
  33. data/lib/measures/openstudio_extension_test_measure/resources/os_lib_helper_methods.rb +399 -399
  34. data/lib/measures/openstudio_extension_test_measure/tests/{OpenStudioExtensionTestMeasure_Test.rb → openstudio_extension_test_measure_test.rb} +74 -75
  35. data/lib/openstudio-extension.rb +1 -1
  36. data/lib/openstudio/extension.rb +234 -229
  37. data/lib/openstudio/extension/core/CreateResults.rb +886 -886
  38. data/lib/openstudio/extension/core/check_air_sys_temps.rb +190 -190
  39. data/lib/openstudio/extension/core/check_calibration.rb +155 -155
  40. data/lib/openstudio/extension/core/check_cond_zns.rb +84 -84
  41. data/lib/openstudio/extension/core/check_domestic_hot_water.rb +334 -334
  42. data/lib/openstudio/extension/core/check_envelope_conductance.rb +453 -453
  43. data/lib/openstudio/extension/core/check_eui_by_end_use.rb +162 -162
  44. data/lib/openstudio/extension/core/check_eui_reasonableness.rb +135 -135
  45. data/lib/openstudio/extension/core/check_fan_pwr.rb +98 -98
  46. data/lib/openstudio/extension/core/check_internal_loads.rb +393 -393
  47. data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +226 -226
  48. data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +326 -326
  49. data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +464 -464
  50. data/lib/openstudio/extension/core/check_mech_sys_type.rb +139 -139
  51. data/lib/openstudio/extension/core/check_part_loads.rb +451 -451
  52. data/lib/openstudio/extension/core/check_placeholder.rb +75 -75
  53. data/lib/openstudio/extension/core/check_plant_cap.rb +123 -123
  54. data/lib/openstudio/extension/core/check_plant_temps.rb +159 -159
  55. data/lib/openstudio/extension/core/check_plenum_loads.rb +87 -87
  56. data/lib/openstudio/extension/core/check_pump_pwr.rb +108 -108
  57. data/lib/openstudio/extension/core/check_sch_coord.rb +241 -241
  58. data/lib/openstudio/extension/core/check_schedules.rb +311 -311
  59. data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +158 -158
  60. data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +148 -148
  61. data/lib/openstudio/extension/core/check_weather_files.rb +132 -132
  62. data/lib/openstudio/extension/core/deer_vintages.rb +311 -311
  63. data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +491 -491
  64. data/lib/openstudio/extension/core/os_lib_cofee.rb +258 -258
  65. data/lib/openstudio/extension/core/os_lib_constructions.rb +378 -378
  66. data/lib/openstudio/extension/core/os_lib_geometry.rb +1022 -1022
  67. data/lib/openstudio/extension/core/os_lib_helper_methods.rb +399 -399
  68. data/lib/openstudio/extension/core/os_lib_hvac.rb +2171 -2171
  69. data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +214 -214
  70. data/lib/openstudio/extension/core/os_lib_model_generation.rb +817 -817
  71. data/lib/openstudio/extension/core/os_lib_model_simplification.rb +1049 -1049
  72. data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +165 -165
  73. data/lib/openstudio/extension/core/os_lib_reporting.rb +4651 -4651
  74. data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +200 -200
  75. data/lib/openstudio/extension/core/os_lib_schedules.rb +963 -963
  76. data/lib/openstudio/extension/rake_task.rb +159 -149
  77. data/lib/openstudio/extension/runner.rb +667 -644
  78. data/lib/openstudio/extension/runner_config.rb +114 -0
  79. data/lib/openstudio/extension/version.rb +40 -40
  80. data/openstudio-extension.gemspec +34 -33
  81. metadata +39 -37
@@ -1,453 +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
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