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,378 +1,378 @@
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_Constructions
37
- # infer insulation layer from a construction
38
- def self.inferInsulationLayer(construction, minThermalResistance)
39
- construction_layers = construction.layers
40
- counter = 0
41
- max_resistance = 0
42
- thermal_resistance_array = []
43
-
44
- # loop through construction layers and infer insulation layer/material
45
- construction_layers.each do |construction_layer|
46
- construction_thermal_resistance = construction_layer.to_OpaqueMaterial.get.thermalResistance
47
- if !thermal_resistance_array.empty?
48
- if construction_thermal_resistance > max_resistance
49
- thermal_resistance_array = [construction_layer, counter, construction_thermal_resistance]
50
- max_resistance = construction_thermal_resistance
51
- end
52
- else
53
- thermal_resistance_array = [construction_layer, counter, construction_thermal_resistance]
54
- end
55
- counter += 1
56
- end
57
-
58
- # test against minimum
59
- if max_resistance > minThermalResistance
60
- minTestPass = true
61
- else
62
- minTestPass = false
63
- end
64
-
65
- result = {
66
- 'construction' => construction,
67
- 'construction_layer' => thermal_resistance_array[0],
68
- 'layer_index' => thermal_resistance_array[1],
69
- 'construction_thermal_resistance' => thermal_resistance_array[2],
70
- 'insulationFound' => minTestPass
71
- }
72
-
73
- return result
74
- end
75
-
76
- # change thermal resistance of opaque materials
77
- def self.setMaterialThermalResistance(material, thermalResistance, options = {})
78
- # set defaults to use if user inputs not passed in
79
- defaults = {
80
- 'cloneMaterial' => true, # in future give user option to clone or change live material
81
- 'name' => "#{material.name} - adj"
82
- }
83
-
84
- # merge user inputs with defaults
85
- options = defaults.merge(options)
86
-
87
- # clone input material
88
- new_material = material.clone(material.model)
89
- new_material = new_material.to_OpaqueMaterial.get
90
- new_material.setName(options['name'])
91
-
92
- # edit insulation material
93
- new_material_matt = new_material.to_Material
94
- if !new_material_matt.empty?
95
- starting_thickness = new_material_matt.get.thickness
96
- target_thickness = starting_thickness * thermalResistance / material.to_OpaqueMaterial.get.thermalResistance
97
- final_thickness = new_material_matt.get.setThickness(target_thickness)
98
- end
99
- new_material_massless = new_material.to_MasslessOpaqueMaterial
100
- if !new_material_massless.empty?
101
- final_thermal_resistance = new_material_massless.get.setThermalResistance(thermalResistance)
102
- end
103
- new_material_airgap = new_material.to_AirGap
104
- if !new_material_airgap.empty?
105
- final_thermal_resistance = new_material_airgap.get.setThermalResistance(thermalResistance)
106
- end
107
-
108
- result = new_material
109
- return result
110
- end
111
-
112
- # add new material to outside of a construction
113
- def self.addNewLayerToConstruction(construction, options = {})
114
- # set defaults to use if user inputs not passed in
115
- defaults = {
116
- 'cloneConstruction' => false, # in future give user option to clone or change live construction
117
- 'layerIndex' => 0, # 0 will be outside. Measure writer should validate any non 0 layerIndex passed in.
118
- 'name' => "#{construction.name} - new material",
119
- 'roughness' => nil,
120
- 'thickness' => nil,
121
- 'conductivity' => nil,
122
- 'density' => nil,
123
- 'specificHeat' => nil,
124
- 'thermalAbsorptance' => nil,
125
- 'solarAbsorptance' => nil,
126
- 'visibleAbsorptance' => nil
127
- }
128
-
129
- # merge user inputs with defaults
130
- options = defaults.merge(options)
131
-
132
- # TODO: - would be nice to grab surface properties from previous exposed material
133
-
134
- # make new material
135
- exposedMaterialNew = OpenStudio::Model::StandardOpaqueMaterial.new(construction.model)
136
- exposedMaterialNew.setName(options['name'])
137
-
138
- # set requested material properties
139
- if !options['roughness'].nil? then exposedMaterialNew.setRoughness(options['roughness']) end
140
- if !options['thickness'].nil? then exposedMaterialNew.setThickness(options['thickness']) end
141
- if !options['conductivity'].nil? then exposedMaterialNew.setConductivity(options['conductivity']) end
142
- if !options['density'].nil? then exposedMaterialNew.setDensity(options['density']) end
143
- if !options['specificHeat'].nil? then exposedMaterialNew.setSpecificHeat(options['specificHeat']) end
144
- if !options['thermalAbsorptance'].nil? then exposedMaterialNew.setThermalAbsorptance(options['thermalAbsorptance']) end
145
- if !options['solarAbsorptance'].nil? then exposedMaterialNew.setSolarAbsorptance(options['solarAbsorptance']) end
146
- if !options['visibleAbsorptance'].nil? then exposedMaterialNew.setVisibleAbsorptance(options['visibleAbsorptance']) end
147
-
148
- # add material to construction
149
- construction.insertLayer(options['layerIndex'], exposedMaterialNew)
150
-
151
- result = exposedMaterialNew
152
- return result
153
- end
154
-
155
- # set material surface properties for specific layer in construction. this should work on OS:Material and OS:MasslessOpaqueMaterial
156
- def self.setConstructionSurfaceProperties(construction, options = {})
157
- # set defaults to use if user inputs not passed in
158
- defaults = {
159
- 'cloneConstruction' => false, # in future give user option to clone or change live construction
160
- 'nameConstruction' => "#{construction.name} - adj", # not currently used
161
- 'cloneMaterial' => true,
162
- 'roughness' => nil,
163
- 'thermalAbsorptance' => nil,
164
- 'solarAbsorptance' => nil,
165
- 'visibleAbsorptance' => nil
166
- }
167
-
168
- # merge user inputs with defaults
169
- options = defaults.merge(options)
170
-
171
- exposedMaterial = construction.to_LayeredConstruction.get.getLayer(0)
172
- if options['cloneMaterial']
173
-
174
- # clone material
175
- exposedMaterialNew = exposedMaterial.clone(construction.model).to_StandardOpaqueMaterial.get # to_StandardOpaqueMaterial is needed to access roughness, otherwise to_OpaqueMaterial would have been fine.
176
- exposedMaterialNew.setName("#{exposedMaterial.name} - adj")
177
-
178
- # connect new material to original construction
179
- construction.eraseLayer(0)
180
- construction.insertLayer(0, exposedMaterialNew)
181
-
182
- else
183
- exposedMaterialNew = exposedMaterial.to_StandardOpaqueMaterial.get # not being cloned but still want to rename
184
- exposedMaterialNew.setName("#{exposedMaterial.name} - adj")
185
- end
186
-
187
- # TODO: - need to test with MasslessOpaqueMaterial. Add test if doesn't return anything when use to_StandardOpaqueMaterial.get
188
-
189
- # set requested material properties
190
- if !options['roughness'].nil? then exposedMaterialNew.setRoughness(options['roughness']) end
191
- if !options['thermalAbsorptance'].nil? then exposedMaterialNew.setThermalAbsorptance(options['thermalAbsorptance']) end
192
- if !options['solarAbsorptance'].nil? then exposedMaterialNew.setSolarAbsorptance(options['solarAbsorptance']) end
193
- if !options['visibleAbsorptance'].nil? then exposedMaterialNew.setVisibleAbsorptance(options['visibleAbsorptance']) end
194
-
195
- result = { 'exposedMaterial' => exposedMaterial, 'exposedMaterialNew' => exposedMaterialNew }
196
- return result
197
- end
198
-
199
- # similar to setMaterialSurfaceProperties but I just pass a material in. Needed this to set material properties for interior walls and both sides of interior partitions.
200
- def self.setMaterialSurfaceProperties(material, options = {})
201
- # set defaults to use if user inputs not passed in
202
- defaults = {
203
- 'cloneMaterial' => true,
204
- 'roughness' => nil,
205
- 'thermalAbsorptance' => nil,
206
- 'solarAbsorptance' => nil,
207
- 'visibleAbsorptance' => nil
208
- }
209
-
210
- # merge user inputs with defaults
211
- options = defaults.merge(options)
212
-
213
- if options['cloneMaterial']
214
- # clone material
215
- materialNew = exposedMaterial.clone(construction.model).get
216
- materialNew.setName("#{materialNew.name} - adj")
217
- else
218
- materialNew = material # not being cloned
219
- materialNew.setName("#{materialNew.name} - adj")
220
- end
221
-
222
- # to_StandardOpaqueMaterial is needed to access roughness, otherwise to_OpaqueMaterial would have been fine.
223
- if !materialNew.to_StandardOpaqueMaterial.empty?
224
- materialNew = materialNew.to_StandardOpaqueMaterial.get
225
- elsif !materialNew.to_MasslessOpaqueMaterial.empty?
226
- materialNew = materialNew.to_MasslessOpaqueMaterial.get
227
- end
228
-
229
- # set requested material properties
230
- if !options['roughness'].nil? then materialNew.setRoughness(options['roughness']) end
231
- if !options['thermalAbsorptance'].nil? then materialNew.setThermalAbsorptance(options['thermalAbsorptance']) end
232
- if !options['solarAbsorptance'].nil? then materialNew.setSolarAbsorptance(options['solarAbsorptance']) end
233
- if !options['visibleAbsorptance'].nil? then materialNew.setVisibleAbsorptance(options['visibleAbsorptance']) end
234
-
235
- result = { 'material' => material, 'materialNew' => materialNew }
236
- return result
237
- end
238
-
239
- # sri of exposed surface of a construction (calculation from K-12 AEDG, derived from ASTM E1980 assuming medium wind speed)
240
- def self.getConstructionSRI(construction)
241
- exposedMaterial = construction.to_LayeredConstruction.get.getLayer(0)
242
- solarAbsorptance = exposedMaterial.to_OpaqueMaterial.get.solarAbsorptance
243
- thermalEmissivity = exposedMaterial.to_OpaqueMaterial.get.thermalAbsorptance
244
- # lines below just for testing
245
- # solarAbsorptance = 1 - 0.65
246
- # thermalEmissivity = 0.86
247
-
248
- x = (20.797 * solarAbsorptance - 0.603 * thermalEmissivity) / (9.5205 * thermalEmissivity + 12.0)
249
- sri = 123.97 - 141.35 * x + 9.6555 * x * x
250
-
251
- result = sri
252
- return result
253
- end
254
-
255
- # create simple glazing material
256
- def self.createConstructionWithSimpleGlazing(model, runner = nil, options = {})
257
- # set defaults to use if user inputs not passed in
258
- defaults = {
259
- 'constructionName' => nil,
260
- 'materialName' => nil,
261
- 'uFactor' => nil,
262
- 'solarHeatGainCoef' => nil,
263
- 'visibleTransmittance' => nil
264
- }
265
-
266
- # merge user inputs with defaults
267
- options = defaults.merge(options)
268
-
269
- # create construction and material and link them together
270
- construction = OpenStudio::Model::Construction.new(model)
271
- if !options['constructionName'].nil? then construction.setName(options['constructionName'].to_s) end
272
- material = OpenStudio::Model::SimpleGlazing.new(model)
273
- if !options['materialName'].nil? then material.setName(options['materialName'].to_s) end
274
-
275
- # add material to construction
276
- construction.insertLayer(0, material)
277
-
278
- # set material properties
279
- if !options['uFactor'].nil? then material.setUFactor(options['uFactor']) end
280
- if !options['solarHeatGainCoef'].nil? then material.setSolarHeatGainCoefficient(options['solarHeatGainCoef']) end
281
- if !options['visibleTransmittance'].nil? then material.setVisibleTransmittance(options['visibleTransmittance']) end
282
-
283
- # create info message
284
- if !runner.nil? # todo - need to look for bad visible transmittance here
285
- uFactorSiToIpConversion = OpenStudio.convert(material.uFactor, 'W/m^2*K', 'Btu/ft^2*h*R').get
286
- runner.registerInfo("Created #{construction.name} construction. U-factor: #{OpenStudio.toNeatString(uFactorSiToIpConversion, 2, true)}(Btu/ft^2*h*R), SHGC: #{material.getSolarHeatGainCoefficient}, VT: #{material.getVisibleTransmittance.get}.")
287
- end
288
-
289
- result = construction
290
- return result
291
- end
292
-
293
- # get cost of selected constructions
294
- def self.getTotalCostOfSelectedConstructions(constructionArray)
295
- envelope_cost = 0
296
-
297
- # loop through selected constructions
298
- constructionArray.each do |construction|
299
- next if construction.getNetArea == 0
300
- const_llcs = construction.lifeCycleCosts
301
- const_llcs.each do |const_llc|
302
- if const_llc.category == 'Construction'
303
- envelope_cost += const_llc.totalCost
304
- end
305
- end
306
- end
307
-
308
- result = envelope_cost
309
- return result
310
- end
311
-
312
- # report names of constructions in a construction set
313
- def self.reportConstructionSetConstructions(constructionSet)
314
- constructionArray = []
315
-
316
- # populate exterior surfaces
317
- if constructionSet.defaultExteriorSurfaceConstructions.is_initialized
318
- surfaceSet = constructionSet.defaultExteriorSurfaceConstructions.get
319
- if surfaceSet.floorConstruction.is_initialized then constructionArray << surfaceSet.floorConstruction.get end
320
- if surfaceSet.wallConstruction.is_initialized then constructionArray << surfaceSet.wallConstruction.get end
321
- if surfaceSet.roofCeilingConstruction.is_initialized then constructionArray << surfaceSet.roofCeilingConstruction.get end
322
- end
323
- # populate interior surfaces
324
- if constructionSet.defaultInteriorSurfaceConstructions.is_initialized
325
- surfaceSet = constructionSet.defaultInteriorSurfaceConstructions.get
326
- if surfaceSet.floorConstruction.is_initialized then constructionArray << surfaceSet.floorConstruction.get end
327
- if surfaceSet.wallConstruction.is_initialized then constructionArray << surfaceSet.wallConstruction.get end
328
- if surfaceSet.roofCeilingConstruction.is_initialized then constructionArray << surfaceSet.roofCeilingConstruction.get end
329
- end
330
- # populate ground surfaces
331
- if constructionSet.defaultGroundContactSurfaceConstructions.is_initialized
332
- surfaceSet = constructionSet.defaultGroundContactSurfaceConstructions.get
333
- if surfaceSet.floorConstruction.is_initialized then constructionArray << surfaceSet.floorConstruction.get end
334
- if surfaceSet.wallConstruction.is_initialized then constructionArray << surfaceSet.wallConstruction.get end
335
- if surfaceSet.roofCeilingConstruction.is_initialized then constructionArray << surfaceSet.roofCeilingConstruction.get end
336
- end
337
- # populate exterior sub-surfaces
338
- if constructionSet.defaultExteriorSubSurfaceConstructions.is_initialized
339
- subSurfaceSet = constructionSet.defaultExteriorSubSurfaceConstructions.get
340
- if subSurfaceSet.fixedWindowConstruction.is_initialized then constructionArray << subSurfaceSet.fixedWindowConstruction.get end
341
- if subSurfaceSet.operableWindowConstruction.is_initialized then constructionArray << subSurfaceSet.operableWindowConstruction.get end
342
- if subSurfaceSet.doorConstruction.is_initialized then constructionArray << subSurfaceSet.doorConstruction.get end
343
- if subSurfaceSet.glassDoorConstruction.is_initialized then constructionArray << subSurfaceSet.glassDoorConstruction.get end
344
- if subSurfaceSet.overheadDoorConstruction.is_initialized then constructionArray << subSurfaceSet.overheadDoorConstruction.get end
345
- if subSurfaceSet.skylightConstruction.is_initialized then constructionArray << subSurfaceSet.skylightConstruction.get end
346
- if subSurfaceSet.tubularDaylightDomeConstruction.is_initialized then constructionArray << subSurfaceSet.tubularDaylightDomeConstruction.get end
347
- if subSurfaceSet.tubularDaylightDiffuserConstruction.is_initialized then constructionArray << subSurfaceSet.tubularDaylightDiffuserConstruction.get end
348
- end
349
- # populate interior sub-surfaces
350
- if constructionSet.defaultInteriorSubSurfaceConstructions.is_initialized
351
- subSurfaceSet = constructionSet.defaultInteriorSubSurfaceConstructions.get
352
- if subSurfaceSet.fixedWindowConstruction.is_initialized then constructionArray << subSurfaceSet.fixedWindowConstruction.get end
353
- if subSurfaceSet.operableWindowConstruction.is_initialized then constructionArray << subSurfaceSet.operableWindowConstruction.get end
354
- if subSurfaceSet.doorConstruction.is_initialized then constructionArray << subSurfaceSet.doorConstruction.get end
355
- if subSurfaceSet.glassDoorConstruction.is_initialized then constructionArray << subSurfaceSet.glassDoorConstruction.get end
356
- if subSurfaceSet.overheadDoorConstruction.is_initialized then constructionArray << subSurfaceSet.overheadDoorConstruction.get end
357
- if subSurfaceSet.skylightConstruction.is_initialized then constructionArray << subSurfaceSet.skylightConstruction.get end
358
- if subSurfaceSet.tubularDaylightDomeConstruction.is_initialized then constructionArray << subSurfaceSet.tubularDaylightDomeConstruction.get end
359
- if subSurfaceSet.tubularDaylightDiffuserConstruction.is_initialized then constructionArray << subSurfaceSet.tubularDaylightDiffuserConstruction.get end
360
- end
361
- # populate misc surfaces
362
- if constructionSet.interiorPartitionConstruction.is_initialized
363
- constructionArray << constructionSet.interiorPartitionConstruction.get
364
- end
365
- if constructionSet.spaceShadingConstruction.is_initialized
366
- constructionArray << constructionSet.spaceShadingConstruction.get
367
- end
368
- if constructionSet.buildingShadingConstruction.is_initialized
369
- constructionArray << constructionSet.buildingShadingConstruction.get
370
- end
371
- if constructionSet.siteShadingConstruction.is_initialized
372
- constructionArray << constructionSet.siteShadingConstruction.get
373
- end
374
-
375
- result = constructionArray
376
- return result
377
- end
378
- 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_Constructions
37
+ # infer insulation layer from a construction
38
+ def self.inferInsulationLayer(construction, minThermalResistance)
39
+ construction_layers = construction.layers
40
+ counter = 0
41
+ max_resistance = 0
42
+ thermal_resistance_array = []
43
+
44
+ # loop through construction layers and infer insulation layer/material
45
+ construction_layers.each do |construction_layer|
46
+ construction_thermal_resistance = construction_layer.to_OpaqueMaterial.get.thermalResistance
47
+ if !thermal_resistance_array.empty?
48
+ if construction_thermal_resistance > max_resistance
49
+ thermal_resistance_array = [construction_layer, counter, construction_thermal_resistance]
50
+ max_resistance = construction_thermal_resistance
51
+ end
52
+ else
53
+ thermal_resistance_array = [construction_layer, counter, construction_thermal_resistance]
54
+ end
55
+ counter += 1
56
+ end
57
+
58
+ # test against minimum
59
+ if max_resistance > minThermalResistance
60
+ minTestPass = true
61
+ else
62
+ minTestPass = false
63
+ end
64
+
65
+ result = {
66
+ 'construction' => construction,
67
+ 'construction_layer' => thermal_resistance_array[0],
68
+ 'layer_index' => thermal_resistance_array[1],
69
+ 'construction_thermal_resistance' => thermal_resistance_array[2],
70
+ 'insulationFound' => minTestPass
71
+ }
72
+
73
+ return result
74
+ end
75
+
76
+ # change thermal resistance of opaque materials
77
+ def self.setMaterialThermalResistance(material, thermalResistance, options = {})
78
+ # set defaults to use if user inputs not passed in
79
+ defaults = {
80
+ 'cloneMaterial' => true, # in future give user option to clone or change live material
81
+ 'name' => "#{material.name} - adj"
82
+ }
83
+
84
+ # merge user inputs with defaults
85
+ options = defaults.merge(options)
86
+
87
+ # clone input material
88
+ new_material = material.clone(material.model)
89
+ new_material = new_material.to_OpaqueMaterial.get
90
+ new_material.setName(options['name'])
91
+
92
+ # edit insulation material
93
+ new_material_matt = new_material.to_Material
94
+ if !new_material_matt.empty?
95
+ starting_thickness = new_material_matt.get.thickness
96
+ target_thickness = starting_thickness * thermalResistance / material.to_OpaqueMaterial.get.thermalResistance
97
+ final_thickness = new_material_matt.get.setThickness(target_thickness)
98
+ end
99
+ new_material_massless = new_material.to_MasslessOpaqueMaterial
100
+ if !new_material_massless.empty?
101
+ final_thermal_resistance = new_material_massless.get.setThermalResistance(thermalResistance)
102
+ end
103
+ new_material_airgap = new_material.to_AirGap
104
+ if !new_material_airgap.empty?
105
+ final_thermal_resistance = new_material_airgap.get.setThermalResistance(thermalResistance)
106
+ end
107
+
108
+ result = new_material
109
+ return result
110
+ end
111
+
112
+ # add new material to outside of a construction
113
+ def self.addNewLayerToConstruction(construction, options = {})
114
+ # set defaults to use if user inputs not passed in
115
+ defaults = {
116
+ 'cloneConstruction' => false, # in future give user option to clone or change live construction
117
+ 'layerIndex' => 0, # 0 will be outside. Measure writer should validate any non 0 layerIndex passed in.
118
+ 'name' => "#{construction.name} - new material",
119
+ 'roughness' => nil,
120
+ 'thickness' => nil,
121
+ 'conductivity' => nil,
122
+ 'density' => nil,
123
+ 'specificHeat' => nil,
124
+ 'thermalAbsorptance' => nil,
125
+ 'solarAbsorptance' => nil,
126
+ 'visibleAbsorptance' => nil
127
+ }
128
+
129
+ # merge user inputs with defaults
130
+ options = defaults.merge(options)
131
+
132
+ # TODO: - would be nice to grab surface properties from previous exposed material
133
+
134
+ # make new material
135
+ exposedMaterialNew = OpenStudio::Model::StandardOpaqueMaterial.new(construction.model)
136
+ exposedMaterialNew.setName(options['name'])
137
+
138
+ # set requested material properties
139
+ if !options['roughness'].nil? then exposedMaterialNew.setRoughness(options['roughness']) end
140
+ if !options['thickness'].nil? then exposedMaterialNew.setThickness(options['thickness']) end
141
+ if !options['conductivity'].nil? then exposedMaterialNew.setConductivity(options['conductivity']) end
142
+ if !options['density'].nil? then exposedMaterialNew.setDensity(options['density']) end
143
+ if !options['specificHeat'].nil? then exposedMaterialNew.setSpecificHeat(options['specificHeat']) end
144
+ if !options['thermalAbsorptance'].nil? then exposedMaterialNew.setThermalAbsorptance(options['thermalAbsorptance']) end
145
+ if !options['solarAbsorptance'].nil? then exposedMaterialNew.setSolarAbsorptance(options['solarAbsorptance']) end
146
+ if !options['visibleAbsorptance'].nil? then exposedMaterialNew.setVisibleAbsorptance(options['visibleAbsorptance']) end
147
+
148
+ # add material to construction
149
+ construction.insertLayer(options['layerIndex'], exposedMaterialNew)
150
+
151
+ result = exposedMaterialNew
152
+ return result
153
+ end
154
+
155
+ # set material surface properties for specific layer in construction. this should work on OS:Material and OS:MasslessOpaqueMaterial
156
+ def self.setConstructionSurfaceProperties(construction, options = {})
157
+ # set defaults to use if user inputs not passed in
158
+ defaults = {
159
+ 'cloneConstruction' => false, # in future give user option to clone or change live construction
160
+ 'nameConstruction' => "#{construction.name} - adj", # not currently used
161
+ 'cloneMaterial' => true,
162
+ 'roughness' => nil,
163
+ 'thermalAbsorptance' => nil,
164
+ 'solarAbsorptance' => nil,
165
+ 'visibleAbsorptance' => nil
166
+ }
167
+
168
+ # merge user inputs with defaults
169
+ options = defaults.merge(options)
170
+
171
+ exposedMaterial = construction.to_LayeredConstruction.get.getLayer(0)
172
+ if options['cloneMaterial']
173
+
174
+ # clone material
175
+ exposedMaterialNew = exposedMaterial.clone(construction.model).to_StandardOpaqueMaterial.get # to_StandardOpaqueMaterial is needed to access roughness, otherwise to_OpaqueMaterial would have been fine.
176
+ exposedMaterialNew.setName("#{exposedMaterial.name} - adj")
177
+
178
+ # connect new material to original construction
179
+ construction.eraseLayer(0)
180
+ construction.insertLayer(0, exposedMaterialNew)
181
+
182
+ else
183
+ exposedMaterialNew = exposedMaterial.to_StandardOpaqueMaterial.get # not being cloned but still want to rename
184
+ exposedMaterialNew.setName("#{exposedMaterial.name} - adj")
185
+ end
186
+
187
+ # TODO: - need to test with MasslessOpaqueMaterial. Add test if doesn't return anything when use to_StandardOpaqueMaterial.get
188
+
189
+ # set requested material properties
190
+ if !options['roughness'].nil? then exposedMaterialNew.setRoughness(options['roughness']) end
191
+ if !options['thermalAbsorptance'].nil? then exposedMaterialNew.setThermalAbsorptance(options['thermalAbsorptance']) end
192
+ if !options['solarAbsorptance'].nil? then exposedMaterialNew.setSolarAbsorptance(options['solarAbsorptance']) end
193
+ if !options['visibleAbsorptance'].nil? then exposedMaterialNew.setVisibleAbsorptance(options['visibleAbsorptance']) end
194
+
195
+ result = { 'exposedMaterial' => exposedMaterial, 'exposedMaterialNew' => exposedMaterialNew }
196
+ return result
197
+ end
198
+
199
+ # similar to setMaterialSurfaceProperties but I just pass a material in. Needed this to set material properties for interior walls and both sides of interior partitions.
200
+ def self.setMaterialSurfaceProperties(material, options = {})
201
+ # set defaults to use if user inputs not passed in
202
+ defaults = {
203
+ 'cloneMaterial' => true,
204
+ 'roughness' => nil,
205
+ 'thermalAbsorptance' => nil,
206
+ 'solarAbsorptance' => nil,
207
+ 'visibleAbsorptance' => nil
208
+ }
209
+
210
+ # merge user inputs with defaults
211
+ options = defaults.merge(options)
212
+
213
+ if options['cloneMaterial']
214
+ # clone material
215
+ materialNew = exposedMaterial.clone(construction.model).get
216
+ materialNew.setName("#{materialNew.name} - adj")
217
+ else
218
+ materialNew = material # not being cloned
219
+ materialNew.setName("#{materialNew.name} - adj")
220
+ end
221
+
222
+ # to_StandardOpaqueMaterial is needed to access roughness, otherwise to_OpaqueMaterial would have been fine.
223
+ if !materialNew.to_StandardOpaqueMaterial.empty?
224
+ materialNew = materialNew.to_StandardOpaqueMaterial.get
225
+ elsif !materialNew.to_MasslessOpaqueMaterial.empty?
226
+ materialNew = materialNew.to_MasslessOpaqueMaterial.get
227
+ end
228
+
229
+ # set requested material properties
230
+ if !options['roughness'].nil? then materialNew.setRoughness(options['roughness']) end
231
+ if !options['thermalAbsorptance'].nil? then materialNew.setThermalAbsorptance(options['thermalAbsorptance']) end
232
+ if !options['solarAbsorptance'].nil? then materialNew.setSolarAbsorptance(options['solarAbsorptance']) end
233
+ if !options['visibleAbsorptance'].nil? then materialNew.setVisibleAbsorptance(options['visibleAbsorptance']) end
234
+
235
+ result = { 'material' => material, 'materialNew' => materialNew }
236
+ return result
237
+ end
238
+
239
+ # sri of exposed surface of a construction (calculation from K-12 AEDG, derived from ASTM E1980 assuming medium wind speed)
240
+ def self.getConstructionSRI(construction)
241
+ exposedMaterial = construction.to_LayeredConstruction.get.getLayer(0)
242
+ solarAbsorptance = exposedMaterial.to_OpaqueMaterial.get.solarAbsorptance
243
+ thermalEmissivity = exposedMaterial.to_OpaqueMaterial.get.thermalAbsorptance
244
+ # lines below just for testing
245
+ # solarAbsorptance = 1 - 0.65
246
+ # thermalEmissivity = 0.86
247
+
248
+ x = (20.797 * solarAbsorptance - 0.603 * thermalEmissivity) / (9.5205 * thermalEmissivity + 12.0)
249
+ sri = 123.97 - 141.35 * x + 9.6555 * x * x
250
+
251
+ result = sri
252
+ return result
253
+ end
254
+
255
+ # create simple glazing material
256
+ def self.createConstructionWithSimpleGlazing(model, runner = nil, options = {})
257
+ # set defaults to use if user inputs not passed in
258
+ defaults = {
259
+ 'constructionName' => nil,
260
+ 'materialName' => nil,
261
+ 'uFactor' => nil,
262
+ 'solarHeatGainCoef' => nil,
263
+ 'visibleTransmittance' => nil
264
+ }
265
+
266
+ # merge user inputs with defaults
267
+ options = defaults.merge(options)
268
+
269
+ # create construction and material and link them together
270
+ construction = OpenStudio::Model::Construction.new(model)
271
+ if !options['constructionName'].nil? then construction.setName(options['constructionName'].to_s) end
272
+ material = OpenStudio::Model::SimpleGlazing.new(model)
273
+ if !options['materialName'].nil? then material.setName(options['materialName'].to_s) end
274
+
275
+ # add material to construction
276
+ construction.insertLayer(0, material)
277
+
278
+ # set material properties
279
+ if !options['uFactor'].nil? then material.setUFactor(options['uFactor']) end
280
+ if !options['solarHeatGainCoef'].nil? then material.setSolarHeatGainCoefficient(options['solarHeatGainCoef']) end
281
+ if !options['visibleTransmittance'].nil? then material.setVisibleTransmittance(options['visibleTransmittance']) end
282
+
283
+ # create info message
284
+ if !runner.nil? # todo - need to look for bad visible transmittance here
285
+ uFactorSiToIpConversion = OpenStudio.convert(material.uFactor, 'W/m^2*K', 'Btu/ft^2*h*R').get
286
+ runner.registerInfo("Created #{construction.name} construction. U-factor: #{OpenStudio.toNeatString(uFactorSiToIpConversion, 2, true)}(Btu/ft^2*h*R), SHGC: #{material.getSolarHeatGainCoefficient}, VT: #{material.getVisibleTransmittance.get}.")
287
+ end
288
+
289
+ result = construction
290
+ return result
291
+ end
292
+
293
+ # get cost of selected constructions
294
+ def self.getTotalCostOfSelectedConstructions(constructionArray)
295
+ envelope_cost = 0
296
+
297
+ # loop through selected constructions
298
+ constructionArray.each do |construction|
299
+ next if construction.getNetArea == 0
300
+ const_llcs = construction.lifeCycleCosts
301
+ const_llcs.each do |const_llc|
302
+ if const_llc.category == 'Construction'
303
+ envelope_cost += const_llc.totalCost
304
+ end
305
+ end
306
+ end
307
+
308
+ result = envelope_cost
309
+ return result
310
+ end
311
+
312
+ # report names of constructions in a construction set
313
+ def self.reportConstructionSetConstructions(constructionSet)
314
+ constructionArray = []
315
+
316
+ # populate exterior surfaces
317
+ if constructionSet.defaultExteriorSurfaceConstructions.is_initialized
318
+ surfaceSet = constructionSet.defaultExteriorSurfaceConstructions.get
319
+ if surfaceSet.floorConstruction.is_initialized then constructionArray << surfaceSet.floorConstruction.get end
320
+ if surfaceSet.wallConstruction.is_initialized then constructionArray << surfaceSet.wallConstruction.get end
321
+ if surfaceSet.roofCeilingConstruction.is_initialized then constructionArray << surfaceSet.roofCeilingConstruction.get end
322
+ end
323
+ # populate interior surfaces
324
+ if constructionSet.defaultInteriorSurfaceConstructions.is_initialized
325
+ surfaceSet = constructionSet.defaultInteriorSurfaceConstructions.get
326
+ if surfaceSet.floorConstruction.is_initialized then constructionArray << surfaceSet.floorConstruction.get end
327
+ if surfaceSet.wallConstruction.is_initialized then constructionArray << surfaceSet.wallConstruction.get end
328
+ if surfaceSet.roofCeilingConstruction.is_initialized then constructionArray << surfaceSet.roofCeilingConstruction.get end
329
+ end
330
+ # populate ground surfaces
331
+ if constructionSet.defaultGroundContactSurfaceConstructions.is_initialized
332
+ surfaceSet = constructionSet.defaultGroundContactSurfaceConstructions.get
333
+ if surfaceSet.floorConstruction.is_initialized then constructionArray << surfaceSet.floorConstruction.get end
334
+ if surfaceSet.wallConstruction.is_initialized then constructionArray << surfaceSet.wallConstruction.get end
335
+ if surfaceSet.roofCeilingConstruction.is_initialized then constructionArray << surfaceSet.roofCeilingConstruction.get end
336
+ end
337
+ # populate exterior sub-surfaces
338
+ if constructionSet.defaultExteriorSubSurfaceConstructions.is_initialized
339
+ subSurfaceSet = constructionSet.defaultExteriorSubSurfaceConstructions.get
340
+ if subSurfaceSet.fixedWindowConstruction.is_initialized then constructionArray << subSurfaceSet.fixedWindowConstruction.get end
341
+ if subSurfaceSet.operableWindowConstruction.is_initialized then constructionArray << subSurfaceSet.operableWindowConstruction.get end
342
+ if subSurfaceSet.doorConstruction.is_initialized then constructionArray << subSurfaceSet.doorConstruction.get end
343
+ if subSurfaceSet.glassDoorConstruction.is_initialized then constructionArray << subSurfaceSet.glassDoorConstruction.get end
344
+ if subSurfaceSet.overheadDoorConstruction.is_initialized then constructionArray << subSurfaceSet.overheadDoorConstruction.get end
345
+ if subSurfaceSet.skylightConstruction.is_initialized then constructionArray << subSurfaceSet.skylightConstruction.get end
346
+ if subSurfaceSet.tubularDaylightDomeConstruction.is_initialized then constructionArray << subSurfaceSet.tubularDaylightDomeConstruction.get end
347
+ if subSurfaceSet.tubularDaylightDiffuserConstruction.is_initialized then constructionArray << subSurfaceSet.tubularDaylightDiffuserConstruction.get end
348
+ end
349
+ # populate interior sub-surfaces
350
+ if constructionSet.defaultInteriorSubSurfaceConstructions.is_initialized
351
+ subSurfaceSet = constructionSet.defaultInteriorSubSurfaceConstructions.get
352
+ if subSurfaceSet.fixedWindowConstruction.is_initialized then constructionArray << subSurfaceSet.fixedWindowConstruction.get end
353
+ if subSurfaceSet.operableWindowConstruction.is_initialized then constructionArray << subSurfaceSet.operableWindowConstruction.get end
354
+ if subSurfaceSet.doorConstruction.is_initialized then constructionArray << subSurfaceSet.doorConstruction.get end
355
+ if subSurfaceSet.glassDoorConstruction.is_initialized then constructionArray << subSurfaceSet.glassDoorConstruction.get end
356
+ if subSurfaceSet.overheadDoorConstruction.is_initialized then constructionArray << subSurfaceSet.overheadDoorConstruction.get end
357
+ if subSurfaceSet.skylightConstruction.is_initialized then constructionArray << subSurfaceSet.skylightConstruction.get end
358
+ if subSurfaceSet.tubularDaylightDomeConstruction.is_initialized then constructionArray << subSurfaceSet.tubularDaylightDomeConstruction.get end
359
+ if subSurfaceSet.tubularDaylightDiffuserConstruction.is_initialized then constructionArray << subSurfaceSet.tubularDaylightDiffuserConstruction.get end
360
+ end
361
+ # populate misc surfaces
362
+ if constructionSet.interiorPartitionConstruction.is_initialized
363
+ constructionArray << constructionSet.interiorPartitionConstruction.get
364
+ end
365
+ if constructionSet.spaceShadingConstruction.is_initialized
366
+ constructionArray << constructionSet.spaceShadingConstruction.get
367
+ end
368
+ if constructionSet.buildingShadingConstruction.is_initialized
369
+ constructionArray << constructionSet.buildingShadingConstruction.get
370
+ end
371
+ if constructionSet.siteShadingConstruction.is_initialized
372
+ constructionArray << constructionSet.siteShadingConstruction.get
373
+ end
374
+
375
+ result = constructionArray
376
+ return result
377
+ end
378
+ end