openstudio-extension 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -0
  3. data/.rubocop.yml +9 -0
  4. data/Gemfile +3 -1
  5. data/Jenkinsfile +10 -0
  6. data/README.md +230 -12
  7. data/Rakefile +88 -3
  8. data/bin/console +3 -3
  9. data/doc_templates/LICENSE.md +27 -0
  10. data/doc_templates/README.md.erb +42 -0
  11. data/doc_templates/copyright_erb.txt +36 -0
  12. data/doc_templates/copyright_js.txt +4 -0
  13. data/doc_templates/copyright_ruby.txt +34 -0
  14. data/init_templates/README.md +37 -0
  15. data/init_templates/gemspec.txt +32 -0
  16. data/init_templates/openstudio_module.rb +50 -0
  17. data/init_templates/spec.rb +47 -0
  18. data/init_templates/spec_helper.rb +49 -0
  19. data/init_templates/template_gemfile.txt +17 -0
  20. data/init_templates/template_rakefile.txt +15 -0
  21. data/init_templates/version.rb +40 -0
  22. data/lib/files/openstudio-extension-gem-test.ddy +536 -0
  23. data/lib/files/openstudio-extension-gem-test.epw +8768 -0
  24. data/lib/files/openstudio-extension-gem-test.stat +554 -0
  25. data/lib/measures/openstudio_extension_test_measure/LICENSE.md +27 -0
  26. data/lib/measures/openstudio_extension_test_measure/README.md +26 -0
  27. data/lib/measures/openstudio_extension_test_measure/README.md.erb +42 -0
  28. data/lib/measures/openstudio_extension_test_measure/measure.rb +72 -0
  29. data/lib/measures/openstudio_extension_test_measure/measure.xml +83 -0
  30. data/lib/measures/openstudio_extension_test_measure/resources/os_lib_helper_methods.rb +399 -0
  31. data/lib/measures/openstudio_extension_test_measure/tests/OpenStudioExtensionTestMeasure_Test.rb +75 -0
  32. data/lib/openstudio/extension.rb +220 -0
  33. data/lib/openstudio/extension/core/CreateResults.rb +879 -0
  34. data/lib/openstudio/extension/core/check_air_sys_temps.rb +190 -0
  35. data/lib/openstudio/extension/core/check_calibration.rb +155 -0
  36. data/lib/openstudio/extension/core/check_cond_zns.rb +84 -0
  37. data/lib/openstudio/extension/core/check_domestic_hot_water.rb +334 -0
  38. data/lib/openstudio/extension/core/check_envelope_conductance.rb +453 -0
  39. data/lib/openstudio/extension/core/check_eui_by_end_use.rb +162 -0
  40. data/lib/openstudio/extension/core/check_eui_reasonableness.rb +135 -0
  41. data/lib/openstudio/extension/core/check_fan_pwr.rb +98 -0
  42. data/lib/openstudio/extension/core/check_internal_loads.rb +393 -0
  43. data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +226 -0
  44. data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +326 -0
  45. data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +464 -0
  46. data/lib/openstudio/extension/core/check_mech_sys_type.rb +139 -0
  47. data/lib/openstudio/extension/core/check_part_loads.rb +451 -0
  48. data/lib/openstudio/extension/core/check_placeholder.rb +75 -0
  49. data/lib/openstudio/extension/core/check_plant_cap.rb +123 -0
  50. data/lib/openstudio/extension/core/check_plant_temps.rb +159 -0
  51. data/lib/openstudio/extension/core/check_plenum_loads.rb +87 -0
  52. data/lib/openstudio/extension/core/check_pump_pwr.rb +108 -0
  53. data/lib/openstudio/extension/core/check_sch_coord.rb +241 -0
  54. data/lib/openstudio/extension/core/check_schedules.rb +311 -0
  55. data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +158 -0
  56. data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +148 -0
  57. data/lib/openstudio/extension/core/check_weather_files.rb +132 -0
  58. data/lib/openstudio/extension/core/deer_vintages.rb +311 -0
  59. data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +491 -0
  60. data/lib/openstudio/extension/core/os_lib_cofee.rb +259 -0
  61. data/lib/openstudio/extension/core/os_lib_constructions.rb +378 -0
  62. data/lib/openstudio/extension/core/os_lib_geometry.rb +1022 -0
  63. data/lib/openstudio/extension/core/os_lib_helper_methods.rb +399 -0
  64. data/lib/openstudio/extension/core/os_lib_hvac.rb +2171 -0
  65. data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +214 -0
  66. data/lib/openstudio/extension/core/os_lib_model_generation.rb +817 -0
  67. data/lib/openstudio/extension/core/os_lib_model_simplification.rb +1049 -0
  68. data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +165 -0
  69. data/lib/openstudio/extension/core/os_lib_reporting.rb +4652 -0
  70. data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +200 -0
  71. data/lib/openstudio/extension/core/os_lib_schedules.rb +963 -0
  72. data/lib/openstudio/extension/rake_task.rb +149 -0
  73. data/lib/openstudio/extension/runner.rb +644 -0
  74. data/lib/openstudio/extension/version.rb +40 -0
  75. data/openstudio-extension.gemspec +20 -15
  76. metadata +150 -14
  77. data/.travis.yml +0 -7
  78. data/lib/OpenStudio/Extension/rake_task.rb +0 -84
  79. data/lib/OpenStudio/Extension/version.rb +0 -33
  80. data/lib/OpenStudio/extension.rb +0 -65
@@ -0,0 +1,259 @@
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_Cofee
37
+
38
+ # create def to use later to make bar
39
+ def OsLib_Cofee.createBar(model, spaceTypeHash,lengthXTarget,lengthYTarget,totalFloorArea,numStories,midFloorMultiplier,xmin,ymin,lengthX,lengthY,zmin,zmax,endZones)
40
+
41
+ # floor to floor height
42
+ floor_to_floor_height = (zmax-zmin)/numStories
43
+
44
+ # perimeter depth
45
+ perimeterDepth = OpenStudio::convert(12,"ft","m").get
46
+ perimeterBufferFactor = 1.5 # this is a margin below which I won't bother splitting the two largest spaces
47
+
48
+ # create an array to control sort order of spaces in bar
49
+ customSpaceTypeBar = []
50
+ counter = 0
51
+ spaceTypeHash.sort_by {|key, value| value}.reverse.each do |k,v|
52
+ next if v == 0 # this line adds support for fractional values of 0
53
+ if counter == 1
54
+ if lengthXTarget*(v/totalFloorArea) > perimeterDepth * perimeterBufferFactor and endZones
55
+ customSpaceTypeBar << [k,totalFloorArea * (perimeterDepth/lengthXTarget)]
56
+ customSpaceTypeBar << [k,v - (totalFloorArea * (perimeterDepth/lengthXTarget))]
57
+ else
58
+ customSpaceTypeBar << [k,v]
59
+ end
60
+ elsif counter > 1
61
+ customSpaceTypeBar << [k,v]
62
+ end
63
+ counter += 1
64
+ end
65
+
66
+ # add the largest space type to the end
67
+ counter = 0
68
+ spaceTypeHash.sort_by {|key, value| value}.reverse.each do |k,v|
69
+ if counter == 0
70
+ # if width is greater than 1.5x perimeter depth then split in half
71
+ if lengthXTarget*(v/totalFloorArea) > perimeterDepth * perimeterBufferFactor and endZones
72
+ customSpaceTypeBar << [k,v - (totalFloorArea * (perimeterDepth/lengthXTarget))]
73
+ customSpaceTypeBar << [k,totalFloorArea * (perimeterDepth/lengthXTarget)]
74
+ else
75
+ customSpaceTypeBar << [k,v]
76
+ end
77
+ end
78
+ break
79
+ end
80
+
81
+ # starting z level
82
+ z = zmin
83
+ storyCounter = 0
84
+ barSpaceArray = []
85
+
86
+ # create new stories and then add spaces
87
+ [numStories,3].min.times do # no more than tree loops through this
88
+ story = OpenStudio::Model::BuildingStory.new(model)
89
+ story.setNominalFloortoFloorHeight(floor_to_floor_height)
90
+ story.setNominalZCoordinate(z)
91
+
92
+ # starting position for first space
93
+ x = (lengthX - lengthXTarget)*0.5 + xmin
94
+ y = (lengthY - lengthYTarget)*0.5 + ymin
95
+
96
+ # temp array of spaces (this is to change floor boundary when there is mid floor multiplier)
97
+ tempSpaceArray = []
98
+
99
+ # loop through space types making diagram and spaces.
100
+ #spaceTypeHash.sort_by {|key, value| value}.reverse.each do |k,v|
101
+ customSpaceTypeBar.each do |object|
102
+
103
+ # get values from what was hash
104
+ k = object[0]
105
+ v = object[1]
106
+
107
+ # get proper zone multiplier value
108
+ if storyCounter == 1 and midFloorMultiplier > 1
109
+ thermalZoneMultiplier = midFloorMultiplier
110
+ else
111
+ thermalZoneMultiplier = 1
112
+ end
113
+
114
+ options = {
115
+ "name" => nil,
116
+ "spaceType" => k,
117
+ "story" => story,
118
+ "makeThermalZone" => true,
119
+ "thermalZone" => nil,
120
+ "thermalZoneMultiplier" => thermalZoneMultiplier,
121
+ "floor_to_floor_height" => floor_to_floor_height,
122
+ }
123
+
124
+ # three paths for spaces depending upon building depth (3, 2 or one cross slices)
125
+ if lengthYTarget > perimeterDepth * 3 # slice into core and perimeter
126
+
127
+ # perimeter polygon a
128
+ perim_polygon_a = OpenStudio::Point3dVector.new
129
+ perim_origin_a = OpenStudio::Point3d.new(x,y,z)
130
+ perim_polygon_a << perim_origin_a
131
+ perim_polygon_a << OpenStudio::Point3d.new(x,y + perimeterDepth,z)
132
+ perim_polygon_a << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y + perimeterDepth,z)
133
+ perim_polygon_a << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y,z)
134
+
135
+ # create core polygon
136
+ core_polygon = OpenStudio::Point3dVector.new
137
+ core_origin = OpenStudio::Point3d.new(x,y + perimeterDepth,z)
138
+ core_polygon << core_origin
139
+ core_polygon << OpenStudio::Point3d.new(x,y + lengthYTarget - perimeterDepth,z)
140
+ core_polygon << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y + lengthYTarget - perimeterDepth,z)
141
+ core_polygon << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y + perimeterDepth,z)
142
+
143
+ # perimeter polygon b w
144
+ perim_polygon_b = OpenStudio::Point3dVector.new
145
+ perim_origin_b = OpenStudio::Point3d.new(x,y + lengthYTarget - perimeterDepth,z)
146
+ perim_polygon_b << perim_origin_b
147
+ perim_polygon_b << OpenStudio::Point3d.new(x,y + lengthYTarget,z)
148
+ perim_polygon_b << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y + lengthYTarget,z)
149
+ perim_polygon_b << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y + lengthYTarget - perimeterDepth,z)
150
+
151
+ # run method to make spaces
152
+ tempSpaceArray << OsLib_Geometry.makeSpaceFromPolygon(model,perim_origin_a,perim_polygon_a,options) # model, origin, polygon, options
153
+ tempSpaceArray << OsLib_Geometry.makeSpaceFromPolygon(model,core_origin,core_polygon,options) # model, origin, polygon, options
154
+ tempSpaceArray << OsLib_Geometry.makeSpaceFromPolygon(model,perim_origin_b,perim_polygon_b,options) # model, origin, polygon, options
155
+
156
+ elsif lengthYTarget > perimeterDepth * 2 # slice into two peremeter zones but no core
157
+
158
+ # perimeter polygon a
159
+ perim_polygon_a = OpenStudio::Point3dVector.new
160
+ perim_origin_a = OpenStudio::Point3d.new(x,y,z)
161
+ perim_polygon_a << perim_origin_a
162
+ perim_polygon_a << OpenStudio::Point3d.new(x,y + lengthYTarget/2,z)
163
+ perim_polygon_a << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y + lengthYTarget/2,z)
164
+ perim_polygon_a << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y,z)
165
+
166
+ # perimeter polygon b
167
+ perim_polygon_b = OpenStudio::Point3dVector.new
168
+ perim_origin_b = OpenStudio::Point3d.new(x,y + lengthYTarget/2,z)
169
+ perim_polygon_b << perim_origin_b
170
+ perim_polygon_b << OpenStudio::Point3d.new(x,y + lengthYTarget,z)
171
+ perim_polygon_b << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y + lengthYTarget,z)
172
+ perim_polygon_b << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y + lengthYTarget/2,z)
173
+
174
+ # run method to make spaces
175
+ tempSpaceArray << OsLib_Geometry.makeSpaceFromPolygon(model,perim_origin_a,perim_polygon_a,options) # model, origin, polygon, options
176
+ tempSpaceArray << OsLib_Geometry.makeSpaceFromPolygon(model,perim_origin_b,perim_polygon_b,options) # model, origin, polygon, options
177
+
178
+ else # don't slice into core and perimeter
179
+
180
+ # create polygon
181
+ core_polygon = OpenStudio::Point3dVector.new
182
+ core_origin = OpenStudio::Point3d.new(x,y,z)
183
+ core_polygon << core_origin
184
+ core_polygon << OpenStudio::Point3d.new(x,y + lengthYTarget,z)
185
+ core_polygon << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y + lengthYTarget,z)
186
+ core_polygon << OpenStudio::Point3d.new(x + lengthXTarget*(v/totalFloorArea),y,z)
187
+
188
+ # run method to make space
189
+ tempSpaceArray << OsLib_Geometry.makeSpaceFromPolygon(model,core_origin,core_polygon,options) # model, origin, polygon, options
190
+
191
+ end
192
+
193
+ # update points for next run
194
+ x += lengthXTarget*(v/totalFloorArea)
195
+
196
+ end
197
+
198
+ # set flags for adiabatic surfaces
199
+ floorAdiabatic = false
200
+ ceilingAdiabatic = false
201
+
202
+ # update z
203
+ if midFloorMultiplier == 1
204
+ z += floor_to_floor_height
205
+ else
206
+ z += floor_to_floor_height * midFloorMultiplier - floor_to_floor_height
207
+
208
+ if storyCounter == 0
209
+ ceilingAdiabatic = true
210
+ elsif storyCounter == 1
211
+ floorAdiabatic = true
212
+ ceilingAdiabatic = true
213
+ else
214
+ floorAdiabatic = true
215
+ end
216
+
217
+ # alter surfaces boundary conditions and constructions as described above
218
+ tempSpaceArray.each do |space|
219
+ space.surfaces.each do |surface|
220
+ if surface.surfaceType == "RoofCeiling" and ceilingAdiabatic
221
+ construction = surface.construction # todo - this isn't really the construction I want since it wasn't an interior one, but will work for now
222
+ surface.setOutsideBoundaryCondition("Adiabatic")
223
+ if not construction.empty?
224
+ surface.setConstruction(construction.get)
225
+ end
226
+ end
227
+ if surface.surfaceType == "Floor" and floorAdiabatic
228
+ construction = surface.construction # todo - this isn't really the construction I want since it wasn't an interior one, but will work for now
229
+ surface.setOutsideBoundaryCondition("Adiabatic")
230
+ if not construction.empty?
231
+ surface.setConstruction(construction.get)
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ # populate bar space array from temp array
238
+ barSpaceArray << tempSpaceArray
239
+
240
+ end
241
+
242
+ # update storyCounter
243
+ storyCounter += 1
244
+
245
+ end
246
+
247
+ # surface matching (seems more complex than necessary)
248
+ spaces = OpenStudio::Model::SpaceVector.new
249
+ model.getSpaces.each do |space|
250
+ spaces << space
251
+ end
252
+ OpenStudio::Model.matchSurfaces(spaces)
253
+
254
+ result = barSpaceArray
255
+ return result
256
+
257
+ end
258
+
259
+ end
@@ -0,0 +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