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,214 +1,214 @@
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
- require "#{File.dirname(__FILE__)}/os_lib_schedules"
37
-
38
- # load OpenStudio measure libraries
39
- module OsLib_LightingAndEquipment
40
- include OsLib_Schedules
41
-
42
- # return min ans max from array
43
- def self.getExteriorLightsValue(model)
44
- facility = model.getFacility
45
- exterior_lights_power = 0
46
- exterior_lights = facility.exteriorLights
47
- exterior_lights.each do |exterior_light|
48
- exterior_light_multiplier = exterior_light.multiplier
49
- exterior_light_def = exterior_light.exteriorLightsDefinition
50
- exterior_light_base_power = exterior_light_def.designLevel
51
- exterior_lights_power += exterior_light_base_power * exterior_light_multiplier
52
- end
53
-
54
- result = { 'exterior_lighting_power' => exterior_lights_power, 'exterior_lights' => exterior_lights }
55
- return result
56
- end
57
-
58
- # return min ans max from array
59
- def self.removeAllExteriorLights(model, runner)
60
- facility = model.getFacility
61
- lightsRemoved = false
62
- exterior_lights = facility.exteriorLights
63
- exterior_lights.each do |exterior_light|
64
- runner.registerInfo("Removed exterior light named #{exterior_light.name}.")
65
- exterior_light.remove
66
- lightsRemoved = true
67
- end
68
-
69
- result = lightsRemoved
70
- return result
71
- end
72
-
73
- # return min ans max from array
74
- def self.addExteriorLights(model, runner, options = {})
75
- # set defaults to use if user inputs not passed in
76
- defaults = {
77
- 'name' => 'Exterior Light',
78
- 'power' => 0,
79
- 'subCategory' => 'Exterior Lighting',
80
- 'controlOption' => 'AstronomicalClock',
81
- 'setbackStartTime' => 0,
82
- 'setbackEndTime' => 0,
83
- 'setbackFraction' => 0.25
84
- }
85
-
86
- # merge user inputs with defaults
87
- options = defaults.merge(options)
88
-
89
- ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(model)
90
- ext_lights_def.setName("#{options['name']} Definition")
91
- runner.registerInfo("Setting #{ext_lights_def.name} to a design power of #{options['power']} Watts")
92
- ext_lights_def.setDesignLevel(options['power'])
93
-
94
- # creating schedule type limits for the exterior lights schedule
95
- ext_lights_sch_type_limits = OpenStudio::Model::ScheduleTypeLimits.new(model)
96
- ext_lights_sch_type_limits.setName("#{options['name']} Fractional")
97
- ext_lights_sch_type_limits.setLowerLimitValue(0)
98
- ext_lights_sch_type_limits.setUpperLimitValue(1)
99
- ext_lights_sch_type_limits.setNumericType('Continuous')
100
-
101
- # prepare values for profile
102
- if options['setbackStartTime'] == 24
103
- profile = { options['setbackEndTime'] => options['setbackFraction'], 24.0 => 1.0 }
104
- elsif options['setbackStartTime'] == 0
105
- profile = { options['setbackEndTime'] => options['setbackFraction'], 24.0 => 1.0 }
106
- elsif options['setbackStartTime'] > options['setbackEndTime']
107
- profile = { options['setbackEndTime'] => options['setbackFraction'], options['setbackStartTime'] => 1.0, 24.0 => options['setbackFraction'] }
108
- else
109
- profile = { options['setbackStartTime'] => 1.0, options['setbackEndTime'] => options['setbackFraction'] }
110
- end
111
-
112
- # inputs
113
- createSimpleSchedule_inputs = {
114
- 'name' => options['name'],
115
- 'winterTimeValuePairs' => profile,
116
- 'summerTimeValuePairs' => profile,
117
- 'defaultTimeValuePairs' => profile
118
- }
119
-
120
- # create a ruleset schedule with a basic profile
121
- ext_lights_sch = OsLib_Schedules.createSimpleSchedule(model, createSimpleSchedule_inputs)
122
-
123
- # creating exterior lights object
124
- ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def, ext_lights_sch)
125
- ext_lights.setName("#{options['power']} w Exterior Light")
126
- ext_lights.setControlOption(options['controlOption'])
127
- ext_lights.setEndUseSubcategory(options['subCategory'])
128
-
129
- result = ext_lights
130
- return result
131
- end
132
-
133
- # add daylight sensor
134
- def self.addDaylightSensor(model, options = {})
135
- # set defaults to use if user inputs not passed in
136
- defaults = {
137
- 'name' => nil,
138
- 'space' => nil,
139
- 'position' => nil,
140
- 'phiRotationAroundZAxis' => nil,
141
- 'illuminanceSetpoint' => nil,
142
- 'lightingControlType' => '1', # 1 = Continuous
143
- 'minInputPowerFractionContinuous' => nil,
144
- 'minOutputPowerFractionContinuous' => nil,
145
- 'numberOfSteppedControlSteps' => nil
146
- }
147
-
148
- # merge user inputs with defaults
149
- options = defaults.merge(options)
150
-
151
- pri_light_sensor = OpenStudio::Model::DaylightingControl.new(model)
152
- if !options['name'].nil? then pri_light_sensor.setName(options['name']) end
153
- if !options['space'].nil? then pri_light_sensor.setSpace(options['space']) end
154
- if !options['position'].nil? then pri_light_sensor.setPosition(options['position']) end
155
- if !options['phiRotationAroundZAxis'].nil? then pri_light_sensor.setPhiRotationAroundZAxis(options['phiRotationAroundZAxis']) end
156
- if !options['illuminanceSetpoint'].nil? then pri_light_sensor.setIlluminanceSetpoint(options['illuminanceSetpoint']) end
157
- if !options['lightingControlType'].nil? then pri_light_sensor.setLightingControlType(options['lightingControlType']) end
158
-
159
- result = pri_light_sensor
160
- return result
161
- end
162
-
163
- # make hash of hard assigned schedules with lighting levels
164
- def self.createHashOfInternalLoadWithHardAssignedSchedules(internalLoadArray)
165
- hardAssignedScheduleHash = {}
166
- internalLoadArray.each do |load|
167
- if !load.isScheduleDefaulted
168
- # add to schedule hash
169
- if !load.schedule.empty?
170
- if !(hardAssignedScheduleHash[load.schedule.get])
171
- hardAssignedScheduleHash[load.schedule.get] = 1
172
- else
173
- hardAssignedScheduleHash[load.schedule.get] += 1 # this is to catch multiple objects instances using the same hard assigned schedule
174
- end
175
- end
176
- end
177
- end
178
-
179
- result = hardAssignedScheduleHash
180
- return result
181
- end
182
-
183
- # get value per area for electric equipment loads in space array.
184
- def self.getEpdForSpaceArray(spaceArray)
185
- floorArea = 0
186
- electricEquipmentPower = 0
187
-
188
- spaceArray.each do |space|
189
- floorArea += space.floorArea * space.multiplier
190
- electricEquipmentPower += space.electricEquipmentPower * space.multiplier
191
- end
192
-
193
- epd = electricEquipmentPower / floorArea
194
-
195
- result = epd
196
- return result
197
- end
198
-
199
- # get value per area for electric equipment loads in space array.
200
- def self.getLpdForSpaceArray(spaceArray)
201
- floorArea = 0
202
- lightingPower = 0
203
-
204
- spaceArray.each do |space|
205
- floorArea += space.floorArea * space.multiplier
206
- lightingPower += space.lightingPower * space.multiplier
207
- end
208
-
209
- lpd = lightingPower / floorArea
210
-
211
- result = lpd
212
- return result
213
- end
214
- 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
+ require "#{File.dirname(__FILE__)}/os_lib_schedules"
37
+
38
+ # load OpenStudio measure libraries
39
+ module OsLib_LightingAndEquipment
40
+ include OsLib_Schedules
41
+
42
+ # return min ans max from array
43
+ def self.getExteriorLightsValue(model)
44
+ facility = model.getFacility
45
+ exterior_lights_power = 0
46
+ exterior_lights = facility.exteriorLights
47
+ exterior_lights.each do |exterior_light|
48
+ exterior_light_multiplier = exterior_light.multiplier
49
+ exterior_light_def = exterior_light.exteriorLightsDefinition
50
+ exterior_light_base_power = exterior_light_def.designLevel
51
+ exterior_lights_power += exterior_light_base_power * exterior_light_multiplier
52
+ end
53
+
54
+ result = { 'exterior_lighting_power' => exterior_lights_power, 'exterior_lights' => exterior_lights }
55
+ return result
56
+ end
57
+
58
+ # return min ans max from array
59
+ def self.removeAllExteriorLights(model, runner)
60
+ facility = model.getFacility
61
+ lightsRemoved = false
62
+ exterior_lights = facility.exteriorLights
63
+ exterior_lights.each do |exterior_light|
64
+ runner.registerInfo("Removed exterior light named #{exterior_light.name}.")
65
+ exterior_light.remove
66
+ lightsRemoved = true
67
+ end
68
+
69
+ result = lightsRemoved
70
+ return result
71
+ end
72
+
73
+ # return min ans max from array
74
+ def self.addExteriorLights(model, runner, options = {})
75
+ # set defaults to use if user inputs not passed in
76
+ defaults = {
77
+ 'name' => 'Exterior Light',
78
+ 'power' => 0,
79
+ 'subCategory' => 'Exterior Lighting',
80
+ 'controlOption' => 'AstronomicalClock',
81
+ 'setbackStartTime' => 0,
82
+ 'setbackEndTime' => 0,
83
+ 'setbackFraction' => 0.25
84
+ }
85
+
86
+ # merge user inputs with defaults
87
+ options = defaults.merge(options)
88
+
89
+ ext_lights_def = OpenStudio::Model::ExteriorLightsDefinition.new(model)
90
+ ext_lights_def.setName("#{options['name']} Definition")
91
+ runner.registerInfo("Setting #{ext_lights_def.name} to a design power of #{options['power']} Watts")
92
+ ext_lights_def.setDesignLevel(options['power'])
93
+
94
+ # creating schedule type limits for the exterior lights schedule
95
+ ext_lights_sch_type_limits = OpenStudio::Model::ScheduleTypeLimits.new(model)
96
+ ext_lights_sch_type_limits.setName("#{options['name']} Fractional")
97
+ ext_lights_sch_type_limits.setLowerLimitValue(0)
98
+ ext_lights_sch_type_limits.setUpperLimitValue(1)
99
+ ext_lights_sch_type_limits.setNumericType('Continuous')
100
+
101
+ # prepare values for profile
102
+ if options['setbackStartTime'] == 24
103
+ profile = { options['setbackEndTime'] => options['setbackFraction'], 24.0 => 1.0 }
104
+ elsif options['setbackStartTime'] == 0
105
+ profile = { options['setbackEndTime'] => options['setbackFraction'], 24.0 => 1.0 }
106
+ elsif options['setbackStartTime'] > options['setbackEndTime']
107
+ profile = { options['setbackEndTime'] => options['setbackFraction'], options['setbackStartTime'] => 1.0, 24.0 => options['setbackFraction'] }
108
+ else
109
+ profile = { options['setbackStartTime'] => 1.0, options['setbackEndTime'] => options['setbackFraction'] }
110
+ end
111
+
112
+ # inputs
113
+ createSimpleSchedule_inputs = {
114
+ 'name' => options['name'],
115
+ 'winterTimeValuePairs' => profile,
116
+ 'summerTimeValuePairs' => profile,
117
+ 'defaultTimeValuePairs' => profile
118
+ }
119
+
120
+ # create a ruleset schedule with a basic profile
121
+ ext_lights_sch = OsLib_Schedules.createSimpleSchedule(model, createSimpleSchedule_inputs)
122
+
123
+ # creating exterior lights object
124
+ ext_lights = OpenStudio::Model::ExteriorLights.new(ext_lights_def, ext_lights_sch)
125
+ ext_lights.setName("#{options['power']} w Exterior Light")
126
+ ext_lights.setControlOption(options['controlOption'])
127
+ ext_lights.setEndUseSubcategory(options['subCategory'])
128
+
129
+ result = ext_lights
130
+ return result
131
+ end
132
+
133
+ # add daylight sensor
134
+ def self.addDaylightSensor(model, options = {})
135
+ # set defaults to use if user inputs not passed in
136
+ defaults = {
137
+ 'name' => nil,
138
+ 'space' => nil,
139
+ 'position' => nil,
140
+ 'phiRotationAroundZAxis' => nil,
141
+ 'illuminanceSetpoint' => nil,
142
+ 'lightingControlType' => '1', # 1 = Continuous
143
+ 'minInputPowerFractionContinuous' => nil,
144
+ 'minOutputPowerFractionContinuous' => nil,
145
+ 'numberOfSteppedControlSteps' => nil
146
+ }
147
+
148
+ # merge user inputs with defaults
149
+ options = defaults.merge(options)
150
+
151
+ pri_light_sensor = OpenStudio::Model::DaylightingControl.new(model)
152
+ if !options['name'].nil? then pri_light_sensor.setName(options['name']) end
153
+ if !options['space'].nil? then pri_light_sensor.setSpace(options['space']) end
154
+ if !options['position'].nil? then pri_light_sensor.setPosition(options['position']) end
155
+ if !options['phiRotationAroundZAxis'].nil? then pri_light_sensor.setPhiRotationAroundZAxis(options['phiRotationAroundZAxis']) end
156
+ if !options['illuminanceSetpoint'].nil? then pri_light_sensor.setIlluminanceSetpoint(options['illuminanceSetpoint']) end
157
+ if !options['lightingControlType'].nil? then pri_light_sensor.setLightingControlType(options['lightingControlType']) end
158
+
159
+ result = pri_light_sensor
160
+ return result
161
+ end
162
+
163
+ # make hash of hard assigned schedules with lighting levels
164
+ def self.createHashOfInternalLoadWithHardAssignedSchedules(internalLoadArray)
165
+ hardAssignedScheduleHash = {}
166
+ internalLoadArray.each do |load|
167
+ if !load.isScheduleDefaulted
168
+ # add to schedule hash
169
+ if !load.schedule.empty?
170
+ if !(hardAssignedScheduleHash[load.schedule.get])
171
+ hardAssignedScheduleHash[load.schedule.get] = 1
172
+ else
173
+ hardAssignedScheduleHash[load.schedule.get] += 1 # this is to catch multiple objects instances using the same hard assigned schedule
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ result = hardAssignedScheduleHash
180
+ return result
181
+ end
182
+
183
+ # get value per area for electric equipment loads in space array.
184
+ def self.getEpdForSpaceArray(spaceArray)
185
+ floorArea = 0
186
+ electricEquipmentPower = 0
187
+
188
+ spaceArray.each do |space|
189
+ floorArea += space.floorArea * space.multiplier
190
+ electricEquipmentPower += space.electricEquipmentPower * space.multiplier
191
+ end
192
+
193
+ epd = electricEquipmentPower / floorArea
194
+
195
+ result = epd
196
+ return result
197
+ end
198
+
199
+ # get value per area for electric equipment loads in space array.
200
+ def self.getLpdForSpaceArray(spaceArray)
201
+ floorArea = 0
202
+ lightingPower = 0
203
+
204
+ spaceArray.each do |space|
205
+ floorArea += space.floorArea * space.multiplier
206
+ lightingPower += space.lightingPower * space.multiplier
207
+ end
208
+
209
+ lpd = lightingPower / floorArea
210
+
211
+ result = lpd
212
+ return result
213
+ end
214
+ end
@@ -1,817 +1,817 @@
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_ModelGeneration
37
- # simple list of building types that are valid for get_space_types_from_building_type
38
- def get_building_types
39
- array = OpenStudio::StringVector.new
40
- array << 'SecondarySchool'
41
- array << 'PrimarySchool'
42
- array << 'SmallOffice'
43
- array << 'MediumOffice'
44
- array << 'LargeOffice'
45
- array << 'SmallHotel'
46
- array << 'LargeHotel'
47
- array << 'Warehouse'
48
- array << 'RetailStandalone'
49
- array << 'RetailStripmall'
50
- array << 'QuickServiceRestaurant'
51
- array << 'FullServiceRestaurant'
52
- array << 'MidriseApartment'
53
- array << 'HighriseApartment'
54
- array << 'Hospital'
55
- array << 'Outpatient'
56
- array << 'SuperMarket'
57
-
58
- return array
59
- end
60
-
61
- # simple list of templates that are valid for get_space_types_from_building_type
62
- def get_templates
63
- array = OpenStudio::StringVector.new
64
- array << 'DOE Ref Pre-1980'
65
- array << 'DOE Ref 1980-2004'
66
- array << '90.1-2004'
67
- array << '90.1-2007'
68
- # array << '189.1-2009' # if turn this on need to update space_type_array for RetailStripmall
69
- array << '90.1-2010'
70
- array << '90.1-2013'
71
- array << 'NREL ZNE Ready 2017'
72
-
73
- return array
74
- end
75
-
76
- # calculate aspect ratio from area and perimeter
77
- def calc_aspect_ratio(a, p)
78
- l = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
79
- w = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
80
- aspect_ratio = l / w
81
-
82
- return aspect_ratio
83
- end
84
-
85
- # Building Form Defaults from Table 4.2 in Achieving the 30% Goal: Energy and Cost Savings Analysis of ASHRAE Standard 90.1-2010
86
- # aspect ratio for NA replaced with floor area to perimeter ratio from prototype model
87
- def building_form_defaults(building_type)
88
- hash = {}
89
-
90
- # calculate aspect ratios not represented on Table 4.2
91
- primary_aspet_ratio = calc_aspect_ratio(73958.0, 2060.0)
92
- secondary_aspet_ratio = calc_aspect_ratio(128112.0, 2447.0)
93
- outpatient_aspet_ratio = calc_aspect_ratio(14782.0, 588.0)
94
- supermarket_a = 45001.0
95
- supermarket_p = 866.0
96
- supermarket_wwr = 1880.0 / (supermarket_p * 20.0)
97
- supermarket_aspet_ratio = calc_aspect_ratio(supermarket_a, supermarket_p)
98
-
99
- hash['SmallOffice'] = { aspect_ratio: 1.5, wwr: 0.15, typical_story: 10.0 }
100
- hash['MediumOffice'] = { aspect_ratio: 1.5, wwr: 0.33, typical_story: 13.0 }
101
- hash['LargeOffice'] = { aspect_ratio: 1.5, wwr: 0.15, typical_story: 13.0 }
102
- hash['RetailStandalone'] = { aspect_ratio: 1.28, wwr: 0.07, typical_story: 20.0 }
103
- hash['RetailStripmall'] = { aspect_ratio: 4.0, wwr: 0.11, typical_story: 17.0 }
104
- hash['PrimarySchool'] = { aspect_ratio: primary_aspet_ratio.round(1), wwr: 0.35, typical_story: 13.0 }
105
- hash['SecondarySchool'] = { aspect_ratio: secondary_aspet_ratio.round(1), wwr: 0.33, typical_story: 13.0 }
106
- hash['Outpatient'] = { aspect_ratio: outpatient_aspet_ratio.round(1), wwr: 0.20, typical_story: 10.0 }
107
- hash['Hospital'] = { aspect_ratio: 1.33, wwr: 0.16, typical_story: 14.0 }
108
- hash['SmallHotel'] = { aspect_ratio: 3.0, wwr: 0.11, typical_story: 9.0, first_story: 11.0 }
109
- hash['LargeHotel'] = { aspect_ratio: 5.1, wwr: 0.27, typical_story: 10.0, first_story: 13.0 }
110
-
111
- # code in get_space_types_from_building_type is used to override building wwr with space type specific wwr
112
- hash['Warehouse'] = { aspect_ratio: 2.2, wwr: 0.0, typical_story: 28.0}
113
-
114
- hash['QuickServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.14, typical_story: 10.0 }
115
- hash['FullServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.18, typical_story: 10.0 }
116
- hash['QuickServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.18, typical_story: 10.0 }
117
- hash['MidriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0 }
118
- hash['HighriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0 }
119
- # SuperMarket inputs come from prototype model
120
- hash['SuperMarket'] = { aspect_ratio: supermarket_aspet_ratio.round(1), wwr: supermarket_wwr.round(2), typical_story: 20.0 }
121
-
122
- return hash[building_type]
123
- end
124
-
125
- # create hash of space types and generic ratios of building floor area
126
- def get_space_types_from_building_type(building_type, template, whole_building = true)
127
- hash = {}
128
-
129
- # TODO: - Confirm that these work for all standards
130
-
131
- if building_type == 'SecondarySchool'
132
- hash['Auditorium'] = { ratio: 0.0504, space_type_gen: true, default: false }
133
- hash['Cafeteria'] = { ratio: 0.0319, space_type_gen: true, default: false }
134
- hash['Classroom'] = { ratio: 0.3528, space_type_gen: true, default: true }
135
- hash['Corridor'] = { ratio: 0.2144, space_type_gen: true, default: false }
136
- hash['Gym'] = { ratio: 0.1646, space_type_gen: true, default: false }
137
- hash['Kitchen'] = { ratio: 0.0110, space_type_gen: true, default: false }
138
- hash['Library'] = { ratio: 0.0429, space_type_gen: true, default: false } # not in prototype
139
- hash['Lobby'] = { ratio: 0.0214, space_type_gen: true, default: false }
140
- hash['Mechanical'] = { ratio: 0.0349, space_type_gen: true, default: false }
141
- hash['Office'] = { ratio: 0.0543, space_type_gen: true, default: false }
142
- hash['Restroom'] = { ratio: 0.0214, space_type_gen: true, default: false }
143
- elsif building_type == 'PrimarySchool'
144
- hash['Cafeteria'] = { ratio: 0.0458, space_type_gen: true, default: false }
145
- hash['Classroom'] = { ratio: 0.5610, space_type_gen: true, default: true }
146
- hash['Corridor'] = { ratio: 0.1633, space_type_gen: true, default: false }
147
- hash['Gym'] = { ratio: 0.0520, space_type_gen: true, default: false }
148
- hash['Kitchen'] = { ratio: 0.0244, space_type_gen: true, default: false }
149
- # TODO: - confirm if Library is 0.0 for all templates
150
- hash['Library'] = { ratio: 0.0, space_type_gen: true, default: false }
151
- hash['Lobby'] = { ratio: 0.0249, space_type_gen: true, default: false }
152
- hash['Mechanical'] = { ratio: 0.0367, space_type_gen: true, default: false }
153
- hash['Office'] = { ratio: 0.0642, space_type_gen: true, default: false }
154
- hash['Restroom'] = { ratio: 0.0277, space_type_gen: true, default: false }
155
- elsif building_type == 'SmallOffice'
156
- # TODO: - populate Small, Medium, and Large office for whole_building false
157
- if whole_building
158
- hash['WholeBuilding - Sm Office'] = { ratio: 1.0, space_type_gen: true, default: true }
159
- else
160
- hash['SmallOffice - Breakroom'] = { ratio: 0.99, space_type_gen: true, default: false }
161
- hash['SmallOffice - ClosedOffice'] = { ratio: 0.99, space_type_gen: true, default: false }
162
- hash['SmallOffice - Conference'] = { ratio: 0.99, space_type_gen: true, default: false }
163
- hash['SmallOffice - Corridor'] = { ratio: 0.99, space_type_gen: true, default: false }
164
- hash['SmallOffice - Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
165
- hash['SmallOffice - Lobby'] = { ratio: 0.99, space_type_gen: true, default: false }
166
- hash['SmallOffice - OpenOffice'] = { ratio: 0.99, space_type_gen: true, default: true }
167
- hash['SmallOffice - Restroom'] = { ratio: 0.99, space_type_gen: true, default: false }
168
- hash['SmallOffice - Stair'] = { ratio: 0.99, space_type_gen: true, default: false }
169
- hash['SmallOffice - Storage'] = { ratio: 0.99, space_type_gen: true, default: false }
170
- hash['SmallOffice - Classroom'] = { ratio: 0.99, space_type_gen: true, default: false }
171
- hash['SmallOffice - Dining'] = { ratio: 0.99, space_type_gen: true, default: false }
172
- hash['WholeBuilding - Sm Office'] = { ratio: 0.0, space_type_gen: true, default: false }
173
- end
174
- elsif building_type == 'MediumOffice'
175
- if whole_building
176
- hash['WholeBuilding - Md Office'] = { ratio: 1.0, space_type_gen: true, default: true }
177
- else
178
- hash['MediumOffice - Breakroom'] = { ratio: 0.99, space_type_gen: true, default: false }
179
- hash['MediumOffice - ClosedOffice'] = { ratio: 0.99, space_type_gen: true, default: false }
180
- hash['MediumOffice - Conference'] = { ratio: 0.99, space_type_gen: true, default: false }
181
- hash['MediumOffice - Corridor'] = { ratio: 0.99, space_type_gen: true, default: false }
182
- hash['MediumOffice - Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
183
- hash['MediumOffice - Lobby'] = { ratio: 0.99, space_type_gen: true, default: false }
184
- hash['MediumOffice - OpenOffice'] = { ratio: 0.99, space_type_gen: true, default: true }
185
- hash['MediumOffice - Restroom'] = { ratio: 0.99, space_type_gen: true, default: false }
186
- hash['MediumOffice - Stair'] = { ratio: 0.99, space_type_gen: true, default: false }
187
- hash['MediumOffice - Storage'] = { ratio: 0.99, space_type_gen: true, default: false }
188
- hash['MediumOffice - Classroom'] = { ratio: 0.99, space_type_gen: true, default: false }
189
- hash['MediumOffice - Dining'] = { ratio: 0.99, space_type_gen: true, default: false }
190
- hash['WholeBuilding - Md Office'] = { ratio: 0.0, space_type_gen: true, default: false }
191
- end
192
- elsif building_type == 'LargeOffice'
193
- if ['DOE Ref Pre-1980', 'DOE Ref 1980-2004'].include?(template)
194
- if whole_building
195
- hash['WholeBuilding - Lg Office'] = { ratio: 1.0, space_type_gen: true, default: true }
196
- else
197
- hash['BreakRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
198
- hash['ClosedOffice'] = { ratio: 0.99, space_type_gen: true, default: false }
199
- hash['Conference'] = { ratio: 0.99, space_type_gen: true, default: false }
200
- hash['Corridor'] = { ratio: 0.99, space_type_gen: true, default: false }
201
- hash['Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
202
- hash['IT_Room'] = { ratio: 0.99, space_type_gen: true, default: false }
203
- hash['Lobby'] = { ratio: 0.99, space_type_gen: true, default: false }
204
- hash['OpenOffice'] = { ratio: 0.99, space_type_gen: true, default: true }
205
- hash['PrintRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
206
- hash['Restroom'] = { ratio: 0.99, space_type_gen: true, default: false }
207
- hash['Stair'] = { ratio: 0.99, space_type_gen: true, default: false }
208
- hash['Storage'] = { ratio: 0.99, space_type_gen: true, default: false }
209
- hash['Vending'] = { ratio: 0.99, space_type_gen: true, default: false }
210
- hash['WholeBuilding - Lg Office'] = { ratio: 0.0, space_type_gen: true, default: false }
211
- end
212
- else
213
- if whole_building
214
- hash['WholeBuilding - Lg Office'] = { ratio: 0.9737, space_type_gen: true, default: true }
215
- hash['OfficeLarge Data Center'] = { ratio: 0.0094, space_type_gen: true, default: false }
216
- hash['OfficeLarge Main Data Center'] = { ratio: 0.0169, space_type_gen: true, default: false }
217
- else
218
- hash['BreakRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
219
- hash['ClosedOffice'] = { ratio: 0.99, space_type_gen: true, default: false }
220
- hash['Conference'] = { ratio: 0.99, space_type_gen: true, default: false }
221
- hash['Corridor'] = { ratio: 0.99, space_type_gen: true, default: false }
222
- hash['Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
223
- hash['IT_Room'] = { ratio: 0.99, space_type_gen: true, default: false }
224
- hash['Lobby'] = { ratio: 0.99, space_type_gen: true, default: false }
225
- hash['OpenOffice'] = { ratio: 0.99, space_type_gen: true, default: true }
226
- hash['PrintRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
227
- hash['Restroom'] = { ratio: 0.99, space_type_gen: true, default: false }
228
- hash['Stair'] = { ratio: 0.99, space_type_gen: true, default: false }
229
- hash['Storage'] = { ratio: 0.99, space_type_gen: true, default: false }
230
- hash['Vending'] = { ratio: 0.99, space_type_gen: true, default: false }
231
- hash['WholeBuilding - Lg Office'] = { ratio: 0.0, space_type_gen: true, default: false }
232
- hash['OfficeLarge Data Center'] = { ratio: 0.0, space_type_gen: true, default: false }
233
- hash['OfficeLarge Main Data Center'] = { ratio: 0.0, space_type_gen: true, default: false }
234
- end
235
- end
236
- elsif building_type == 'SmallHotel'
237
- if ['DOE Ref Pre-1980', 'DOE Ref 1980-2004'].include?(template)
238
- hash['Corridor'] = { ratio: 0.1313, space_type_gen: true, default: false }
239
- hash['Elec/MechRoom'] = { ratio: 0.0038, space_type_gen: true, default: false }
240
- hash['ElevatorCore'] = { ratio: 0.0113, space_type_gen: true, default: false }
241
- hash['Exercise'] = { ratio: 0.0081, space_type_gen: true, default: false }
242
- hash['GuestLounge'] = { ratio: 0.0406, space_type_gen: true, default: false }
243
- hash['GuestRoom'] = { ratio: 0.6313, space_type_gen: true, default: true }
244
- hash['Laundry'] = { ratio: 0.0244, space_type_gen: true, default: false }
245
- hash['Mechanical'] = { ratio: 0.0081, space_type_gen: true, default: false }
246
- hash['Meeting'] = { ratio: 0.0200, space_type_gen: true, default: false }
247
- hash['Office'] = { ratio: 0.0325, space_type_gen: true, default: false }
248
- hash['PublicRestroom'] = { ratio: 0.0081, space_type_gen: true, default: false }
249
- hash['StaffLounge'] = { ratio: 0.0081, space_type_gen: true, default: false }
250
- hash['Stair'] = { ratio: 0.0400, space_type_gen: true, default: false }
251
- hash['Storage'] = { ratio: 0.0325, space_type_gen: true, default: false }
252
- else
253
- hash['Corridor'] = { ratio: 0.1313, space_type_gen: true, default: false }
254
- hash['Elec/MechRoom'] = { ratio: 0.0038, space_type_gen: true, default: false }
255
- hash['ElevatorCore'] = { ratio: 0.0113, space_type_gen: true, default: false }
256
- hash['Exercise'] = { ratio: 0.0081, space_type_gen: true, default: false }
257
- hash['GuestLounge'] = { ratio: 0.0406, space_type_gen: true, default: false }
258
- hash['GuestRoom123Occ'] = { ratio: 0.4081, space_type_gen: true, default: true }
259
- hash['GuestRoom123Vac'] = { ratio: 0.2231, space_type_gen: true, default: false }
260
- hash['Laundry'] = { ratio: 0.0244, space_type_gen: true, default: false }
261
- hash['Mechanical'] = { ratio: 0.0081, space_type_gen: true, default: false }
262
- hash['Meeting'] = { ratio: 0.0200, space_type_gen: true, default: false }
263
- hash['Office'] = { ratio: 0.0325, space_type_gen: true, default: false }
264
- hash['PublicRestroom'] = { ratio: 0.0081, space_type_gen: true, default: false }
265
- hash['StaffLounge'] = { ratio: 0.0081, space_type_gen: true, default: false }
266
- hash['Stair'] = { ratio: 0.0400, space_type_gen: true, default: false }
267
- hash['Storage'] = { ratio: 0.0325, space_type_gen: true, default: false }
268
- end
269
- elsif building_type == 'LargeHotel'
270
- hash['Banquet'] = { ratio: 0.0585, space_type_gen: true, default: false }
271
- hash['Basement'] = { ratio: 0.1744, space_type_gen: false, default: false }
272
- hash['Cafe'] = { ratio: 0.0166, space_type_gen: true, default: false }
273
- hash['Corridor'] = { ratio: 0.1736, space_type_gen: true, default: false }
274
- hash['GuestRoom'] = { ratio: 0.4099, space_type_gen: true, default: true }
275
- hash['Kitchen'] = { ratio: 0.0091, space_type_gen: true, default: false }
276
- hash['Laundry'] = { ratio: 0.0069, space_type_gen: true, default: false }
277
- hash['Lobby'] = { ratio: 0.1153, space_type_gen: true, default: false }
278
- hash['Mechanical'] = { ratio: 0.0145, space_type_gen: true, default: false }
279
- hash['Retail'] = { ratio: 0.0128, space_type_gen: true, default: false }
280
- hash['Storage'] = { ratio: 0.0084, space_type_gen: true, default: false }
281
- elsif building_type == 'Warehouse'
282
- hash['Bulk'] = { ratio: 0.6628, space_type_gen: true, default: true }
283
- hash['Fine'] = { ratio: 0.2882, space_type_gen: true, default: false }
284
- hash['Office'] = { ratio: 0.0490, space_type_gen: true, default: false, wwr: 0.71 }
285
- elsif building_type == 'RetailStandalone'
286
- hash['Back_Space'] = { ratio: 0.1656, space_type_gen: true, default: false }
287
- hash['Entry'] = { ratio: 0.0052, space_type_gen: true, default: false }
288
- hash['Point_of_Sale'] = { ratio: 0.0657, space_type_gen: true, default: false }
289
- hash['Retail'] = { ratio: 0.7635, space_type_gen: true, default: true }
290
- elsif building_type == 'RetailStripmall'
291
- hash['Strip mall - type 1'] = { ratio: 0.25, space_type_gen: true, default: false }
292
- hash['Strip mall - type 2'] = { ratio: 0.25, space_type_gen: true, default: false }
293
- hash['Strip mall - type 3'] = { ratio: 0.50, space_type_gen: true, default: true }
294
- elsif building_type == 'QuickServiceRestaurant'
295
- hash['Dining'] = { ratio: 0.5, space_type_gen: true, default: true }
296
- hash['Kitchen'] = { ratio: 0.5, space_type_gen: true, default: false }
297
- elsif building_type == 'FullServiceRestaurant'
298
- hash['Dining'] = { ratio: 0.7272, space_type_gen: true, default: true }
299
- hash['Kitchen'] = { ratio: 0.2728, space_type_gen: true, default: false }
300
- elsif building_type == 'MidriseApartment'
301
- hash['Apartment'] = { ratio: 0.8727, space_type_gen: true, default: true }
302
- hash['Corridor'] = { ratio: 0.0991, space_type_gen: true, default: false }
303
- hash['Office'] = { ratio: 0.0282, space_type_gen: true, default: false }
304
- elsif building_type == 'HighriseApartment'
305
- hash['Apartment'] = { ratio: 0.8896, space_type_gen: true, default: true }
306
- hash['Corridor'] = { ratio: 0.0991, space_type_gen: true, default: false }
307
- hash['Office'] = { ratio: 0.0113, space_type_gen: true, default: false }
308
- elsif building_type == 'Hospital'
309
- hash['Basement'] = { ratio: 0.1667, space_type_gen: false, default: false }
310
- hash['Corridor'] = { ratio: 0.1741, space_type_gen: true, default: false }
311
- hash['Dining'] = { ratio: 0.0311, space_type_gen: true, default: false }
312
- hash['ER_Exam'] = { ratio: 0.0099, space_type_gen: true, default: false }
313
- hash['ER_NurseStn'] = { ratio: 0.0551, space_type_gen: true, default: false }
314
- hash['ER_Trauma'] = { ratio: 0.0025, space_type_gen: true, default: false }
315
- hash['ER_Triage'] = { ratio: 0.0050, space_type_gen: true, default: false }
316
- hash['ICU_NurseStn'] = { ratio: 0.0298, space_type_gen: true, default: false }
317
- hash['ICE_Open'] = { ratio: 0.0275, space_type_gen: true, default: false }
318
- hash['ICU_PatRm'] = { ratio: 0.0115, space_type_gen: true, default: false }
319
- hash['Kitchen'] = { ratio: 0.0414, space_type_gen: true, default: false }
320
- hash['Lab'] = { ratio: 0.0236, space_type_gen: true, default: false }
321
- hash['Lobby'] = { ratio: 0.0657, space_type_gen: true, default: false }
322
- hash['NurseStn'] = { ratio: 0.1723, space_type_gen: true, default: false }
323
- hash['Office'] = { ratio: 0.0286, space_type_gen: true, default: false }
324
- hash['OR'] = { ratio: 0.0273, space_type_gen: true, default: false }
325
- hash['PatCorridor'] = { ratio: 0.0, space_type_gen: true, default: false } # not in prototype
326
- hash['PatRoom'] = { ratio: 0.0845, space_type_gen: true, default: true }
327
- hash['PhysTherapy'] = { ratio: 0.0217, space_type_gen: true, default: false }
328
- hash['Radiology'] = { ratio: 0.0217, space_type_gen: true, default: false }
329
- elsif building_type == 'Outpatient'
330
- hash['Anesthesia'] = { ratio: 0.0026, space_type_gen: true, default: false }
331
- hash['BioHazard'] = { ratio: 0.0014, space_type_gen: true, default: false }
332
- hash['Cafe'] = { ratio: 0.0103, space_type_gen: true, default: false }
333
- hash['CleanWork'] = { ratio: 0.0071, space_type_gen: true, default: false }
334
- hash['Conference'] = { ratio: 0.0082, space_type_gen: true, default: false }
335
- hash['DresingRoom'] = { ratio: 0.0021, space_type_gen: true, default: false }
336
- hash['Elec/MechRoom'] = { ratio: 0.0109, space_type_gen: true, default: false }
337
- hash['ElevatorPumpRoom'] = { ratio: 0.0022, space_type_gen: true, default: false }
338
- hash['Exam'] = { ratio: 0.1029, space_type_gen: true, default: true }
339
- hash['Hall'] = { ratio: 0.1924, space_type_gen: true, default: false }
340
- hash['IT_Room'] = { ratio: 0.0027, space_type_gen: true, default: false }
341
- hash['Janitor'] = { ratio: 0.0672, space_type_gen: true, default: false }
342
- hash['Lobby'] = { ratio: 0.0152, space_type_gen: true, default: false }
343
- hash['LockerRoom'] = { ratio: 0.0190, space_type_gen: true, default: false }
344
- hash['Lounge'] = { ratio: 0.0293, space_type_gen: true, default: false }
345
- hash['MedGas'] = { ratio: 0.0014, space_type_gen: true, default: false }
346
- hash['MRI'] = { ratio: 0.0107, space_type_gen: true, default: false }
347
- hash['MRI_Control'] = { ratio: 0.0041, space_type_gen: true, default: false }
348
- hash['NurseStation'] = { ratio: 0.0189, space_type_gen: true, default: false }
349
- hash['Office'] = { ratio: 0.1828, space_type_gen: true, default: false }
350
- hash['OR'] = { ratio: 0.0346, space_type_gen: true, default: false }
351
- hash['PACU'] = { ratio: 0.0232, space_type_gen: true, default: false }
352
- hash['PhysicalTherapy'] = { ratio: 0.0462, space_type_gen: true, default: false }
353
- hash['PreOp'] = { ratio: 0.0129, space_type_gen: true, default: false }
354
- hash['ProcedureRoom'] = { ratio: 0.0070, space_type_gen: true, default: false }
355
- hash['Reception'] = { ratio: 0.0365, space_type_gen: true, default: false }
356
- hash['Soil Work'] = { ratio: 0.0088, space_type_gen: true, default: false }
357
- hash['Stair'] = { ratio: 0.0146, space_type_gen: true, default: false }
358
- hash['Toilet'] = { ratio: 0.0193, space_type_gen: true, default: false }
359
- hash['Undeveloped'] = { ratio: 0.0835, space_type_gen: false, default: false }
360
- hash['Xray'] = { ratio: 0.0220, space_type_gen: true, default: false }
361
- elsif building_type == 'SuperMarket'
362
- # TODO: - populate ratios for SuperMarket
363
- hash['Bakery'] = { ratio: 0.99, space_type_gen: true, default: false }
364
- hash['Deli'] = { ratio: 0.99, space_type_gen: true, default: false }
365
- hash['DryStorage'] = { ratio: 0.99, space_type_gen: true, default: false }
366
- hash['Office'] = { ratio: 0.99, space_type_gen: true, default: false }
367
- hash['Produce'] = { ratio: 0.99, space_type_gen: true, default: true }
368
- hash['Sales'] = { ratio: 0.99, space_type_gen: true, default: true }
369
- hash['Corridor'] = { ratio: 0.99, space_type_gen: true, default: true }
370
- hash['Dining'] = { ratio: 0.99, space_type_gen: true, default: true }
371
- hash['Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: true }
372
- hash['Meeting'] = { ratio: 0.99, space_type_gen: true, default: true }
373
- hash['Restroom'] = { ratio: 0.99, space_type_gen: true, default: true }
374
- hash['Vestibule'] = { ratio: 0.99, space_type_gen: true, default: true }
375
- else
376
- return false
377
- end
378
-
379
- return hash
380
- end
381
-
382
- # remove existing non resource objects from the model
383
- # technically thermostats and building stories are resources but still want to remove them.
384
- def remove_non_resource_objects(runner, model, options = nil)
385
- if options.nil?
386
- options = {}
387
- options[:remove_building_stories] = true
388
- options[:remove_thermostats] = true
389
- options[:remove_air_loops] = true
390
- options[:remove_non_swh_plant_loops] = true
391
-
392
- # leave these in by default unless requsted when method called
393
- options[:remove_swh_plant_loops] = false
394
- options[:remove_exterior_lights] = false
395
- options[:remove_site_shading] = false
396
- end
397
-
398
- num_model_objects = model.objects.size
399
-
400
- # remove non-resource objects not removed by removing the building
401
- if options[:remove_building_stories] then model.getBuildingStorys.each(&:remove) end
402
- if options[:remove_thermostats] then model.getThermostats.each(&:remove) end
403
- if options[:remove_air_loops] then model.getAirLoopHVACs.each(&:remove) end
404
- if options[:remove_exterior_lights] then model.getFacility.exteriorLights.each(&:remove) end
405
- if options[:remove_site_shading] then model.getSite.shadingSurfaceGroups.each(&:remove) end
406
-
407
- # see if plant loop is swh or not and take proper action (booter loop doesn't have water use equipment)
408
- model.getPlantLoops.each do |plant_loop|
409
- is_swh_loop = false
410
- plant_loop.supplyComponents.each do |component|
411
- if component.to_WaterHeaterMixed.is_initialized
412
- is_swh_loop = true
413
- next
414
- end
415
- end
416
-
417
- if is_swh_loop
418
- if options[:remove_swh_plant_loops] then plant_loop.remove end
419
- else
420
- if options[:remove_non_swh_plant_loops] then plant_loop.remove end
421
- end
422
- end
423
-
424
- # remove water use connections (may be removed when loop is removed)
425
- if options[:remove_swh_plant_loops] then model.getWaterConnectionss.each(&:remove) end
426
- if options[:remove_swh_plant_loops] then model.getWaterUseEquipments.each(&:remove) end
427
-
428
- # remove building but reset fields on new building object.
429
- building_fields = []
430
- building = model.getBuilding
431
- num_fields = building.numFields
432
- num_fields.times.each do |i|
433
- building_fields << building.getString(i).get
434
- end
435
- # removes spaces, space's child objects, thermal zones, zone equipment, non site surfaces, building stories and water use connections.
436
- model.getBuilding.remove
437
- building = model.getBuilding
438
- num_fields.times.each do |i|
439
- next if i == 0 # don't try and set handle
440
- building_fields << building.setString(i, building_fields[i])
441
- end
442
-
443
- # other than optionally site shading and exterior lights not messing with site characteristics
444
-
445
- if num_model_objects - model.objects.size > 0
446
- runner.registerInfo("Removed #{num_model_objects - model.objects.size} non resource objects from the model.")
447
- end
448
-
449
- return true
450
- end
451
-
452
- # create_bar(runner,model,bar_hash)
453
- # measures using this method should include OsLibGeometry and OsLibHelperMethods
454
- def create_bar(runner, model, bar_hash, story_multiplier_method = 'Basements Ground Mid Top')
455
- # warn about site shading
456
- if !model.getSite.shadingSurfaceGroups.empty?
457
- runner.registerWarning('The model has one or more site shading surafces. New geometry may not be positioned where expected, it will be centered over the center of the original geometry.')
458
- end
459
-
460
- # make custom story hash when number of stories below grade > 0
461
- # todo - update this so have option basements are not below 0? (useful for simplifying existing model and maintaining z position relative to site shading)
462
- story_hash = {}
463
- eff_below = bar_hash[:num_stories_below_grade]
464
- eff_above = bar_hash[:num_stories_above_grade]
465
- footprint_origin = bar_hash[:center_of_footprint]
466
- typical_story_height = bar_hash[:floor_height]
467
-
468
- # flatten story_hash out to individual stories included in building area
469
- stories_flat = []
470
- stories_flat_counter = 0
471
- bar_hash[:stories].each_with_index do |(k, v), i|
472
- # k is invalid in some cases, old story object that has been removed, should be from low to high including basement
473
- # skip if source story insn't included in building area
474
- if v[:story_included_in_building_area].nil? || (v[:story_included_in_building_area] == true)
475
-
476
- # add to counter
477
- stories_flat_counter += v[:story_min_multiplier]
478
-
479
- flat_hash = {}
480
- flat_hash[:story_party_walls] = v[:story_party_walls]
481
- flat_hash[:below_partial_story] = v[:below_partial_story]
482
- flat_hash[:bottom_story_ground_exposed_floor] = v[:bottom_story_ground_exposed_floor]
483
- flat_hash[:top_story_exterior_exposed_roof] = v[:top_story_exterior_exposed_roof]
484
- if i < eff_below
485
- flat_hash[:story_type] = 'B'
486
- flat_hash[:multiplier] = 1
487
- elsif i == eff_below
488
- flat_hash[:story_type] = 'Ground'
489
- flat_hash[:multiplier] = 1
490
- elsif stories_flat_counter == eff_below + eff_above.ceil
491
- flat_hash[:story_type] = 'Top'
492
- flat_hash[:multiplier] = 1
493
- else
494
- flat_hash[:story_type] = 'Mid'
495
- flat_hash[:multiplier] = v[:story_min_multiplier]
496
- end
497
-
498
- compare_hash = {}
499
- if !stories_flat.empty?
500
- stories_flat.last.each { |k, v| compare_hash[k] = flat_hash[k] if flat_hash[k] != v }
501
- end
502
- if (story_multiplier_method != 'None' && stories_flat.last == flat_hash) || (story_multiplier_method != 'None' && compare_hash.size == 1 && compare_hash.include?(:multiplier))
503
- stories_flat.last[:multiplier] += v[:story_min_multiplier]
504
- else
505
- stories_flat << flat_hash
506
- end
507
- end
508
- end
509
-
510
- if bar_hash[:num_stories_below_grade] > 0
511
-
512
- # add in below grade levels (may want to add below grade multipliers at some point if we start running deep basements)
513
- eff_below.times do |i|
514
- story_hash["B#{i + 1}"] = { space_origin_z: footprint_origin.z - typical_story_height * (i + 1), space_height: typical_story_height, multiplier: 1 }
515
- end
516
- end
517
-
518
- # add in above grade levels
519
- if eff_above > 2
520
- story_hash['Ground'] = { space_origin_z: footprint_origin.z, space_height: typical_story_height, multiplier: 1 }
521
-
522
- footprint_counter = 0
523
- effective_stories_counter = 1
524
- stories_flat.each do |hash|
525
- next if hash[:story_type] != 'Mid'
526
- if footprint_counter == 0
527
- string = 'Mid'
528
- else
529
- string = "Mid#{footprint_counter + 1}"
530
- end
531
- story_hash[string] = { space_origin_z: footprint_origin.z + typical_story_height * effective_stories_counter + typical_story_height * (hash[:multiplier] - 1) / 2.0, space_height: typical_story_height, multiplier: hash[:multiplier] }
532
- footprint_counter += 1
533
- effective_stories_counter += hash[:multiplier]
534
- end
535
-
536
- story_hash['Top'] = { space_origin_z: footprint_origin.z + typical_story_height * (eff_above.ceil - 1), space_height: typical_story_height, multiplier: 1 }
537
- elsif eff_above > 1
538
- story_hash['Ground'] = { space_origin_z: footprint_origin.z, space_height: typical_story_height, multiplier: 1 }
539
- story_hash['Top'] = { space_origin_z: footprint_origin.z + typical_story_height * (eff_above.ceil - 1), space_height: typical_story_height, multiplier: 1 }
540
- else # one story only
541
- story_hash['Ground'] = { space_origin_z: footprint_origin.z, space_height: typical_story_height, multiplier: 1 }
542
- end
543
-
544
- # create footprints
545
- if bar_hash[:bar_division_method] == 'Multiple Space Types - Simple Sliced'
546
- footprints = []
547
- story_hash.size.times do |i|
548
- # adjust size of bar of top story is not a full story
549
- if i + 1 == story_hash.size
550
- area_multiplier = (1.0 - bar_hash[:num_stories_above_grade].ceil + bar_hash[:num_stories_above_grade])
551
- edge_multiplier = Math.sqrt(area_multiplier)
552
- length = bar_hash[:length] * edge_multiplier
553
- width = bar_hash[:width] * edge_multiplier
554
- else
555
- length = bar_hash[:length]
556
- width = bar_hash[:width]
557
- end
558
- footprints << OsLib_Geometry.make_sliced_bar_simple_polygons(runner, bar_hash[:space_types], length, width, bar_hash[:center_of_footprint])
559
- end
560
-
561
- elsif bar_hash[:bar_division_method] == 'Multiple Space Types - Individual Stories Sliced'
562
-
563
- # update story_hash for partial_story_above
564
- story_hash.each_with_index do |(k, v), i|
565
- # adjust size of bar of top story is not a full story
566
- if i + 1 == story_hash.size
567
- story_hash[k][:partial_story_multiplier] = (1.0 - bar_hash[:num_stories_above_grade].ceil + bar_hash[:num_stories_above_grade])
568
- end
569
- end
570
-
571
- footprints = OsLib_Geometry.make_sliced_bar_multi_polygons(runner, bar_hash[:space_types], bar_hash[:length], bar_hash[:width], bar_hash[:center_of_footprint], story_hash)
572
-
573
- else
574
- footprints = []
575
- story_hash.size.times do |i|
576
- # adjust size of bar of top story is not a full story
577
- if i + 1 == story_hash.size
578
- area_multiplier = (1.0 - bar_hash[:num_stories_above_grade].ceil + bar_hash[:num_stories_above_grade])
579
- edge_multiplier = Math.sqrt(area_multiplier)
580
- length = bar_hash[:length] * edge_multiplier
581
- width = bar_hash[:width] * edge_multiplier
582
- else
583
- length = bar_hash[:length]
584
- width = bar_hash[:width]
585
- end
586
- footprints << OsLib_Geometry.make_core_and_perimeter_polygons(runner, length, width, bar_hash[:center_of_footprint]) # perimeter defaults to 15'
587
- end
588
-
589
- # set primary space type to building default space type
590
- space_types = bar_hash[:space_types].sort_by { |k, v| v[:floor_area] }
591
- model.getBuilding.setSpaceType(space_types.last.first)
592
-
593
- end
594
-
595
- # makeSpacesFromPolygons
596
- OsLib_Geometry.makeSpacesFromPolygons(runner, model, footprints, bar_hash[:floor_height], bar_hash[:num_stories], bar_hash[:center_of_footprint], story_hash)
597
-
598
- # put all of the spaces in the model into a vector for intersection and surface matching
599
- spaces = OpenStudio::Model::SpaceVector.new
600
- model.getSpaces.sort.each do |space|
601
- spaces << space
602
- end
603
-
604
- # only intersect if make_mid_story_surfaces_adiabatic false
605
- if !(bar_hash[:make_mid_story_surfaces_adiabatic])
606
- # intersect surfaces
607
- # (when bottom floor has many space types and one above doesn't will end up with heavily subdivided floor. Maybe use adiabatic and don't intersect floor/ceilings)
608
- intersect_surfaces = true
609
- if intersect_surfaces
610
- OpenStudio::Model.intersectSurfaces(spaces)
611
- runner.registerInfo('Intersecting surfaces, this will create additional geometry.')
612
- end
613
- end
614
-
615
- # match surfaces for each space in the vector
616
- OpenStudio::Model.matchSurfaces(spaces)
617
-
618
- # set boundary conditions if not already set when geometry was created
619
- # todo - update this to use space original z value vs. story name
620
- if bar_hash[:num_stories_below_grade] > 0
621
- model.getBuildingStorys.each do |story|
622
- next if !story.name.to_s.include?('Story B')
623
- story.spaces.each do |space|
624
- space.surfaces.each do |surface|
625
- next if surface.surfaceType != 'Wall'
626
- next if surface.outsideBoundaryCondition != 'Outdoors'
627
- surface.setOutsideBoundaryCondition('Ground')
628
- end
629
- end
630
- end
631
- end
632
-
633
- # sort stories (by name for now but need better way)
634
- # todo - need to change this so doesn't create issues when models have existing stories and spaces. Should be able to run it multiple times
635
- sorted_stories = {}
636
- model.getBuildingStorys.each do |story|
637
- sorted_stories[story.name.to_s] = story
638
- end
639
-
640
- # flag space types that have wwr overrides
641
- space_type_wwr_overrides = {}
642
-
643
- # loop through building stories, spaces, and surfaces
644
- sorted_stories.sort.each_with_index do |(key, story), i|
645
- # flag for adiabatic floor if building doesn't have ground exposed floor
646
- if stories_flat[i][:bottom_story_ground_exposed_floor] == false
647
- adiabatic_floor = true
648
- end
649
- # flag for adiabatic roof if building doesn't have exterior exposed roof
650
- if stories_flat[i][:top_story_exterior_exposed_roof] == false
651
- adiabatic_ceiling = true
652
- end
653
-
654
- # make all mid story floor and celings adiabiatc if requested
655
- if bar_hash[:make_mid_story_surfaces_adiabatic]
656
- if i > 0
657
- adiabatic_floor = true
658
- end
659
- if i < sorted_stories.size - 1
660
- adiabatic_ceiling = true
661
- end
662
- end
663
-
664
- # flag orientations for this story to recieve party walls
665
- party_wall_facades = stories_flat[i][:story_party_walls]
666
-
667
- story.spaces.each do |space|
668
- space.surfaces. each do |surface|
669
- # set floor to adiabatic if requited
670
- if adiabatic_floor && surface.surfaceType == 'Floor'
671
- make_surfaces_adiabatic([surface])
672
- elsif adiabatic_ceiling && surface.surfaceType == 'RoofCeiling'
673
- make_surfaces_adiabatic([surface])
674
- end
675
-
676
- # skip of not exterior wall
677
- next if surface.surfaceType != 'Wall'
678
- next if surface.outsideBoundaryCondition != 'Outdoors'
679
-
680
- # get the absoluteAzimuth for the surface so we can categorize it
681
- absoluteAzimuth = OpenStudio.convert(surface.azimuth, 'rad', 'deg').get + surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis
682
- absoluteAzimuth = absoluteAzimuth % 360.0 # should result in value between 0 and 360
683
- absoluteAzimuth = absoluteAzimuth.round(5) # this was creating issues at 45 deg angles with opposing facades
684
-
685
- # target wwr values that may be changed for specific space types
686
- wwr_n = bar_hash[:building_wwr_n]
687
- wwr_e = bar_hash[:building_wwr_e]
688
- wwr_s = bar_hash[:building_wwr_s]
689
- wwr_w = bar_hash[:building_wwr_w]
690
-
691
- # look for space type specific wwr values
692
- if surface.space.is_initialized && surface.space.get.spaceType.is_initialized
693
- space_type = surface.space.get.spaceType.get
694
-
695
- # see if space type has wwr value
696
- bar_hash[:space_types].each do |k,v|
697
- if v.has_key?(:space_type) && space_type == v[:space_type]
698
-
699
- # if matching space type specifies a wwr then override the orientaiton specific recommendations for this surface.
700
- if v.has_key?(:wwr)
701
- wwr_n = v[:wwr]
702
- wwr_e = v[:wwr]
703
- wwr_s = v[:wwr]
704
- wwr_w = v[:wwr]
705
- space_type_wwr_overrides[space_type] = v[:wwr]
706
- end
707
- end
708
- end
709
- end
710
-
711
- # add fenestration (wwr for now, maybe overhang and overhead doors later)
712
- if (absoluteAzimuth >= 315.0) || (absoluteAzimuth < 45.0)
713
- if party_wall_facades.include?('north')
714
- make_surfaces_adiabatic([surface])
715
- else
716
- surface.setWindowToWallRatio(wwr_n)
717
- end
718
- elsif (absoluteAzimuth >= 45.0) && (absoluteAzimuth < 135.0)
719
- if party_wall_facades.include?('east')
720
- make_surfaces_adiabatic([surface])
721
- else
722
- surface.setWindowToWallRatio(wwr_e)
723
- end
724
- elsif (absoluteAzimuth >= 135.0) && (absoluteAzimuth < 225.0)
725
- if party_wall_facades.include?('south')
726
- make_surfaces_adiabatic([surface])
727
- else
728
- surface.setWindowToWallRatio(wwr_s)
729
- end
730
- elsif (absoluteAzimuth >= 225.0) && (absoluteAzimuth < 315.0)
731
- if party_wall_facades.include?('west')
732
- make_surfaces_adiabatic([surface])
733
- else
734
- surface.setWindowToWallRatio(wwr_w)
735
- end
736
- else
737
- runner.registerError('Unexpected value of facade: ' + absoluteAzimuth + '.')
738
- return false
739
- end
740
- end
741
- end
742
- end
743
-
744
- # report space types with custom wwr values
745
- space_type_wwr_overrides.each do |space_type,wwr|
746
- runner.registerInfo("For #{space_type.name} the default building wwr was replaced with a space type specifc value of #{wwr}")
747
- end
748
-
749
- final_floor_area_ip = OpenStudio.convert(model.getBuilding.floorArea, 'm^2', 'ft^2').get
750
- runner.registerInfo("Created Bar envlope with floor area of #{OpenStudio.toNeatString(final_floor_area_ip, 0, true)} (ft^2)")
751
- end
752
-
753
- # make selected surfaces adiabatic
754
- def make_surfaces_adiabatic(surfaces)
755
- surfaces.each do |surface|
756
- if surface.construction.is_initialized
757
- surface.setConstruction(surface.construction.get)
758
- end
759
- surface.setOutsideBoundaryCondition('Adiabatic')
760
- end
761
- end
762
-
763
- # get length and width of rectangle matching bounding box aspect ratio will maintaining proper floor area
764
- def calc_bar_reduced_bounding_box(envelope_data_hash)
765
- bar = {}
766
-
767
- bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
768
- bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
769
- bounding_area = bounding_length * bounding_width
770
- footprint_area = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective__num_stories].to_f
771
- area_multiplier = footprint_area / bounding_area
772
- edge_multiplier = Math.sqrt(area_multiplier)
773
- bar[:length] = bounding_length * edge_multiplier
774
- bar[:width] = bounding_width * edge_multiplier
775
-
776
- return bar
777
- end
778
-
779
- # get length and width of rectangle matching longer of two edges, and reducing the other way until floor area matches
780
- def calc_bar_reduced_width(envelope_data_hash)
781
- bar = {}
782
-
783
- bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
784
- bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
785
- footprint_area = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective__num_stories].to_f
786
-
787
- if bounding_length >= bounding_width
788
- bar[:length] = bounding_length
789
- bar[:width] = footprint_area / bounding_length
790
- else
791
- bar[:width] = bounding_width
792
- bar[:length] = footprint_area / bounding_width
793
- end
794
-
795
- return bar
796
- end
797
-
798
- # get length and width of rectangle by stretching it until both floor area and exterior wall area or perimeter match
799
- def calc_bar_stretched(envelope_data_hash)
800
- bar = {}
801
-
802
- bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
803
- bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
804
- a = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective__num_stories].to_f
805
- p = envelope_data_hash[:building_perimeter]
806
-
807
- if bounding_length >= bounding_width
808
- bar[:length] = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
809
- bar[:width] = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
810
- else
811
- bar[:length] = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
812
- bar[:width] = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
813
- end
814
-
815
- return bar
816
- end
817
- 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_ModelGeneration
37
+ # simple list of building types that are valid for get_space_types_from_building_type
38
+ def get_building_types
39
+ array = OpenStudio::StringVector.new
40
+ array << 'SecondarySchool'
41
+ array << 'PrimarySchool'
42
+ array << 'SmallOffice'
43
+ array << 'MediumOffice'
44
+ array << 'LargeOffice'
45
+ array << 'SmallHotel'
46
+ array << 'LargeHotel'
47
+ array << 'Warehouse'
48
+ array << 'RetailStandalone'
49
+ array << 'RetailStripmall'
50
+ array << 'QuickServiceRestaurant'
51
+ array << 'FullServiceRestaurant'
52
+ array << 'MidriseApartment'
53
+ array << 'HighriseApartment'
54
+ array << 'Hospital'
55
+ array << 'Outpatient'
56
+ array << 'SuperMarket'
57
+
58
+ return array
59
+ end
60
+
61
+ # simple list of templates that are valid for get_space_types_from_building_type
62
+ def get_templates
63
+ array = OpenStudio::StringVector.new
64
+ array << 'DOE Ref Pre-1980'
65
+ array << 'DOE Ref 1980-2004'
66
+ array << '90.1-2004'
67
+ array << '90.1-2007'
68
+ # array << '189.1-2009' # if turn this on need to update space_type_array for RetailStripmall
69
+ array << '90.1-2010'
70
+ array << '90.1-2013'
71
+ array << 'NREL ZNE Ready 2017'
72
+
73
+ return array
74
+ end
75
+
76
+ # calculate aspect ratio from area and perimeter
77
+ def calc_aspect_ratio(a, p)
78
+ l = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
79
+ w = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
80
+ aspect_ratio = l / w
81
+
82
+ return aspect_ratio
83
+ end
84
+
85
+ # Building Form Defaults from Table 4.2 in Achieving the 30% Goal: Energy and Cost Savings Analysis of ASHRAE Standard 90.1-2010
86
+ # aspect ratio for NA replaced with floor area to perimeter ratio from prototype model
87
+ def building_form_defaults(building_type)
88
+ hash = {}
89
+
90
+ # calculate aspect ratios not represented on Table 4.2
91
+ primary_aspet_ratio = calc_aspect_ratio(73958.0, 2060.0)
92
+ secondary_aspet_ratio = calc_aspect_ratio(128112.0, 2447.0)
93
+ outpatient_aspet_ratio = calc_aspect_ratio(14782.0, 588.0)
94
+ supermarket_a = 45001.0
95
+ supermarket_p = 866.0
96
+ supermarket_wwr = 1880.0 / (supermarket_p * 20.0)
97
+ supermarket_aspet_ratio = calc_aspect_ratio(supermarket_a, supermarket_p)
98
+
99
+ hash['SmallOffice'] = { aspect_ratio: 1.5, wwr: 0.15, typical_story: 10.0 }
100
+ hash['MediumOffice'] = { aspect_ratio: 1.5, wwr: 0.33, typical_story: 13.0 }
101
+ hash['LargeOffice'] = { aspect_ratio: 1.5, wwr: 0.15, typical_story: 13.0 }
102
+ hash['RetailStandalone'] = { aspect_ratio: 1.28, wwr: 0.07, typical_story: 20.0 }
103
+ hash['RetailStripmall'] = { aspect_ratio: 4.0, wwr: 0.11, typical_story: 17.0 }
104
+ hash['PrimarySchool'] = { aspect_ratio: primary_aspet_ratio.round(1), wwr: 0.35, typical_story: 13.0 }
105
+ hash['SecondarySchool'] = { aspect_ratio: secondary_aspet_ratio.round(1), wwr: 0.33, typical_story: 13.0 }
106
+ hash['Outpatient'] = { aspect_ratio: outpatient_aspet_ratio.round(1), wwr: 0.20, typical_story: 10.0 }
107
+ hash['Hospital'] = { aspect_ratio: 1.33, wwr: 0.16, typical_story: 14.0 }
108
+ hash['SmallHotel'] = { aspect_ratio: 3.0, wwr: 0.11, typical_story: 9.0, first_story: 11.0 }
109
+ hash['LargeHotel'] = { aspect_ratio: 5.1, wwr: 0.27, typical_story: 10.0, first_story: 13.0 }
110
+
111
+ # code in get_space_types_from_building_type is used to override building wwr with space type specific wwr
112
+ hash['Warehouse'] = { aspect_ratio: 2.2, wwr: 0.0, typical_story: 28.0}
113
+
114
+ hash['QuickServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.14, typical_story: 10.0 }
115
+ hash['FullServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.18, typical_story: 10.0 }
116
+ hash['QuickServiceRestaurant'] = { aspect_ratio: 1.0, wwr: 0.18, typical_story: 10.0 }
117
+ hash['MidriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0 }
118
+ hash['HighriseApartment'] = { aspect_ratio: 2.75, wwr: 0.15, typical_story: 10.0 }
119
+ # SuperMarket inputs come from prototype model
120
+ hash['SuperMarket'] = { aspect_ratio: supermarket_aspet_ratio.round(1), wwr: supermarket_wwr.round(2), typical_story: 20.0 }
121
+
122
+ return hash[building_type]
123
+ end
124
+
125
+ # create hash of space types and generic ratios of building floor area
126
+ def get_space_types_from_building_type(building_type, template, whole_building = true)
127
+ hash = {}
128
+
129
+ # TODO: - Confirm that these work for all standards
130
+
131
+ if building_type == 'SecondarySchool'
132
+ hash['Auditorium'] = { ratio: 0.0504, space_type_gen: true, default: false }
133
+ hash['Cafeteria'] = { ratio: 0.0319, space_type_gen: true, default: false }
134
+ hash['Classroom'] = { ratio: 0.3528, space_type_gen: true, default: true }
135
+ hash['Corridor'] = { ratio: 0.2144, space_type_gen: true, default: false }
136
+ hash['Gym'] = { ratio: 0.1646, space_type_gen: true, default: false }
137
+ hash['Kitchen'] = { ratio: 0.0110, space_type_gen: true, default: false }
138
+ hash['Library'] = { ratio: 0.0429, space_type_gen: true, default: false } # not in prototype
139
+ hash['Lobby'] = { ratio: 0.0214, space_type_gen: true, default: false }
140
+ hash['Mechanical'] = { ratio: 0.0349, space_type_gen: true, default: false }
141
+ hash['Office'] = { ratio: 0.0543, space_type_gen: true, default: false }
142
+ hash['Restroom'] = { ratio: 0.0214, space_type_gen: true, default: false }
143
+ elsif building_type == 'PrimarySchool'
144
+ hash['Cafeteria'] = { ratio: 0.0458, space_type_gen: true, default: false }
145
+ hash['Classroom'] = { ratio: 0.5610, space_type_gen: true, default: true }
146
+ hash['Corridor'] = { ratio: 0.1633, space_type_gen: true, default: false }
147
+ hash['Gym'] = { ratio: 0.0520, space_type_gen: true, default: false }
148
+ hash['Kitchen'] = { ratio: 0.0244, space_type_gen: true, default: false }
149
+ # TODO: - confirm if Library is 0.0 for all templates
150
+ hash['Library'] = { ratio: 0.0, space_type_gen: true, default: false }
151
+ hash['Lobby'] = { ratio: 0.0249, space_type_gen: true, default: false }
152
+ hash['Mechanical'] = { ratio: 0.0367, space_type_gen: true, default: false }
153
+ hash['Office'] = { ratio: 0.0642, space_type_gen: true, default: false }
154
+ hash['Restroom'] = { ratio: 0.0277, space_type_gen: true, default: false }
155
+ elsif building_type == 'SmallOffice'
156
+ # TODO: - populate Small, Medium, and Large office for whole_building false
157
+ if whole_building
158
+ hash['WholeBuilding - Sm Office'] = { ratio: 1.0, space_type_gen: true, default: true }
159
+ else
160
+ hash['SmallOffice - Breakroom'] = { ratio: 0.99, space_type_gen: true, default: false }
161
+ hash['SmallOffice - ClosedOffice'] = { ratio: 0.99, space_type_gen: true, default: false }
162
+ hash['SmallOffice - Conference'] = { ratio: 0.99, space_type_gen: true, default: false }
163
+ hash['SmallOffice - Corridor'] = { ratio: 0.99, space_type_gen: true, default: false }
164
+ hash['SmallOffice - Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
165
+ hash['SmallOffice - Lobby'] = { ratio: 0.99, space_type_gen: true, default: false }
166
+ hash['SmallOffice - OpenOffice'] = { ratio: 0.99, space_type_gen: true, default: true }
167
+ hash['SmallOffice - Restroom'] = { ratio: 0.99, space_type_gen: true, default: false }
168
+ hash['SmallOffice - Stair'] = { ratio: 0.99, space_type_gen: true, default: false }
169
+ hash['SmallOffice - Storage'] = { ratio: 0.99, space_type_gen: true, default: false }
170
+ hash['SmallOffice - Classroom'] = { ratio: 0.99, space_type_gen: true, default: false }
171
+ hash['SmallOffice - Dining'] = { ratio: 0.99, space_type_gen: true, default: false }
172
+ hash['WholeBuilding - Sm Office'] = { ratio: 0.0, space_type_gen: true, default: false }
173
+ end
174
+ elsif building_type == 'MediumOffice'
175
+ if whole_building
176
+ hash['WholeBuilding - Md Office'] = { ratio: 1.0, space_type_gen: true, default: true }
177
+ else
178
+ hash['MediumOffice - Breakroom'] = { ratio: 0.99, space_type_gen: true, default: false }
179
+ hash['MediumOffice - ClosedOffice'] = { ratio: 0.99, space_type_gen: true, default: false }
180
+ hash['MediumOffice - Conference'] = { ratio: 0.99, space_type_gen: true, default: false }
181
+ hash['MediumOffice - Corridor'] = { ratio: 0.99, space_type_gen: true, default: false }
182
+ hash['MediumOffice - Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
183
+ hash['MediumOffice - Lobby'] = { ratio: 0.99, space_type_gen: true, default: false }
184
+ hash['MediumOffice - OpenOffice'] = { ratio: 0.99, space_type_gen: true, default: true }
185
+ hash['MediumOffice - Restroom'] = { ratio: 0.99, space_type_gen: true, default: false }
186
+ hash['MediumOffice - Stair'] = { ratio: 0.99, space_type_gen: true, default: false }
187
+ hash['MediumOffice - Storage'] = { ratio: 0.99, space_type_gen: true, default: false }
188
+ hash['MediumOffice - Classroom'] = { ratio: 0.99, space_type_gen: true, default: false }
189
+ hash['MediumOffice - Dining'] = { ratio: 0.99, space_type_gen: true, default: false }
190
+ hash['WholeBuilding - Md Office'] = { ratio: 0.0, space_type_gen: true, default: false }
191
+ end
192
+ elsif building_type == 'LargeOffice'
193
+ if ['DOE Ref Pre-1980', 'DOE Ref 1980-2004'].include?(template)
194
+ if whole_building
195
+ hash['WholeBuilding - Lg Office'] = { ratio: 1.0, space_type_gen: true, default: true }
196
+ else
197
+ hash['BreakRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
198
+ hash['ClosedOffice'] = { ratio: 0.99, space_type_gen: true, default: false }
199
+ hash['Conference'] = { ratio: 0.99, space_type_gen: true, default: false }
200
+ hash['Corridor'] = { ratio: 0.99, space_type_gen: true, default: false }
201
+ hash['Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
202
+ hash['IT_Room'] = { ratio: 0.99, space_type_gen: true, default: false }
203
+ hash['Lobby'] = { ratio: 0.99, space_type_gen: true, default: false }
204
+ hash['OpenOffice'] = { ratio: 0.99, space_type_gen: true, default: true }
205
+ hash['PrintRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
206
+ hash['Restroom'] = { ratio: 0.99, space_type_gen: true, default: false }
207
+ hash['Stair'] = { ratio: 0.99, space_type_gen: true, default: false }
208
+ hash['Storage'] = { ratio: 0.99, space_type_gen: true, default: false }
209
+ hash['Vending'] = { ratio: 0.99, space_type_gen: true, default: false }
210
+ hash['WholeBuilding - Lg Office'] = { ratio: 0.0, space_type_gen: true, default: false }
211
+ end
212
+ else
213
+ if whole_building
214
+ hash['WholeBuilding - Lg Office'] = { ratio: 0.9737, space_type_gen: true, default: true }
215
+ hash['OfficeLarge Data Center'] = { ratio: 0.0094, space_type_gen: true, default: false }
216
+ hash['OfficeLarge Main Data Center'] = { ratio: 0.0169, space_type_gen: true, default: false }
217
+ else
218
+ hash['BreakRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
219
+ hash['ClosedOffice'] = { ratio: 0.99, space_type_gen: true, default: false }
220
+ hash['Conference'] = { ratio: 0.99, space_type_gen: true, default: false }
221
+ hash['Corridor'] = { ratio: 0.99, space_type_gen: true, default: false }
222
+ hash['Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
223
+ hash['IT_Room'] = { ratio: 0.99, space_type_gen: true, default: false }
224
+ hash['Lobby'] = { ratio: 0.99, space_type_gen: true, default: false }
225
+ hash['OpenOffice'] = { ratio: 0.99, space_type_gen: true, default: true }
226
+ hash['PrintRoom'] = { ratio: 0.99, space_type_gen: true, default: false }
227
+ hash['Restroom'] = { ratio: 0.99, space_type_gen: true, default: false }
228
+ hash['Stair'] = { ratio: 0.99, space_type_gen: true, default: false }
229
+ hash['Storage'] = { ratio: 0.99, space_type_gen: true, default: false }
230
+ hash['Vending'] = { ratio: 0.99, space_type_gen: true, default: false }
231
+ hash['WholeBuilding - Lg Office'] = { ratio: 0.0, space_type_gen: true, default: false }
232
+ hash['OfficeLarge Data Center'] = { ratio: 0.0, space_type_gen: true, default: false }
233
+ hash['OfficeLarge Main Data Center'] = { ratio: 0.0, space_type_gen: true, default: false }
234
+ end
235
+ end
236
+ elsif building_type == 'SmallHotel'
237
+ if ['DOE Ref Pre-1980', 'DOE Ref 1980-2004'].include?(template)
238
+ hash['Corridor'] = { ratio: 0.1313, space_type_gen: true, default: false }
239
+ hash['Elec/MechRoom'] = { ratio: 0.0038, space_type_gen: true, default: false }
240
+ hash['ElevatorCore'] = { ratio: 0.0113, space_type_gen: true, default: false }
241
+ hash['Exercise'] = { ratio: 0.0081, space_type_gen: true, default: false }
242
+ hash['GuestLounge'] = { ratio: 0.0406, space_type_gen: true, default: false }
243
+ hash['GuestRoom'] = { ratio: 0.6313, space_type_gen: true, default: true }
244
+ hash['Laundry'] = { ratio: 0.0244, space_type_gen: true, default: false }
245
+ hash['Mechanical'] = { ratio: 0.0081, space_type_gen: true, default: false }
246
+ hash['Meeting'] = { ratio: 0.0200, space_type_gen: true, default: false }
247
+ hash['Office'] = { ratio: 0.0325, space_type_gen: true, default: false }
248
+ hash['PublicRestroom'] = { ratio: 0.0081, space_type_gen: true, default: false }
249
+ hash['StaffLounge'] = { ratio: 0.0081, space_type_gen: true, default: false }
250
+ hash['Stair'] = { ratio: 0.0400, space_type_gen: true, default: false }
251
+ hash['Storage'] = { ratio: 0.0325, space_type_gen: true, default: false }
252
+ else
253
+ hash['Corridor'] = { ratio: 0.1313, space_type_gen: true, default: false }
254
+ hash['Elec/MechRoom'] = { ratio: 0.0038, space_type_gen: true, default: false }
255
+ hash['ElevatorCore'] = { ratio: 0.0113, space_type_gen: true, default: false }
256
+ hash['Exercise'] = { ratio: 0.0081, space_type_gen: true, default: false }
257
+ hash['GuestLounge'] = { ratio: 0.0406, space_type_gen: true, default: false }
258
+ hash['GuestRoom123Occ'] = { ratio: 0.4081, space_type_gen: true, default: true }
259
+ hash['GuestRoom123Vac'] = { ratio: 0.2231, space_type_gen: true, default: false }
260
+ hash['Laundry'] = { ratio: 0.0244, space_type_gen: true, default: false }
261
+ hash['Mechanical'] = { ratio: 0.0081, space_type_gen: true, default: false }
262
+ hash['Meeting'] = { ratio: 0.0200, space_type_gen: true, default: false }
263
+ hash['Office'] = { ratio: 0.0325, space_type_gen: true, default: false }
264
+ hash['PublicRestroom'] = { ratio: 0.0081, space_type_gen: true, default: false }
265
+ hash['StaffLounge'] = { ratio: 0.0081, space_type_gen: true, default: false }
266
+ hash['Stair'] = { ratio: 0.0400, space_type_gen: true, default: false }
267
+ hash['Storage'] = { ratio: 0.0325, space_type_gen: true, default: false }
268
+ end
269
+ elsif building_type == 'LargeHotel'
270
+ hash['Banquet'] = { ratio: 0.0585, space_type_gen: true, default: false }
271
+ hash['Basement'] = { ratio: 0.1744, space_type_gen: false, default: false }
272
+ hash['Cafe'] = { ratio: 0.0166, space_type_gen: true, default: false }
273
+ hash['Corridor'] = { ratio: 0.1736, space_type_gen: true, default: false }
274
+ hash['GuestRoom'] = { ratio: 0.4099, space_type_gen: true, default: true }
275
+ hash['Kitchen'] = { ratio: 0.0091, space_type_gen: true, default: false }
276
+ hash['Laundry'] = { ratio: 0.0069, space_type_gen: true, default: false }
277
+ hash['Lobby'] = { ratio: 0.1153, space_type_gen: true, default: false }
278
+ hash['Mechanical'] = { ratio: 0.0145, space_type_gen: true, default: false }
279
+ hash['Retail'] = { ratio: 0.0128, space_type_gen: true, default: false }
280
+ hash['Storage'] = { ratio: 0.0084, space_type_gen: true, default: false }
281
+ elsif building_type == 'Warehouse'
282
+ hash['Bulk'] = { ratio: 0.6628, space_type_gen: true, default: true }
283
+ hash['Fine'] = { ratio: 0.2882, space_type_gen: true, default: false }
284
+ hash['Office'] = { ratio: 0.0490, space_type_gen: true, default: false, wwr: 0.71 }
285
+ elsif building_type == 'RetailStandalone'
286
+ hash['Back_Space'] = { ratio: 0.1656, space_type_gen: true, default: false }
287
+ hash['Entry'] = { ratio: 0.0052, space_type_gen: true, default: false }
288
+ hash['Point_of_Sale'] = { ratio: 0.0657, space_type_gen: true, default: false }
289
+ hash['Retail'] = { ratio: 0.7635, space_type_gen: true, default: true }
290
+ elsif building_type == 'RetailStripmall'
291
+ hash['Strip mall - type 1'] = { ratio: 0.25, space_type_gen: true, default: false }
292
+ hash['Strip mall - type 2'] = { ratio: 0.25, space_type_gen: true, default: false }
293
+ hash['Strip mall - type 3'] = { ratio: 0.50, space_type_gen: true, default: true }
294
+ elsif building_type == 'QuickServiceRestaurant'
295
+ hash['Dining'] = { ratio: 0.5, space_type_gen: true, default: true }
296
+ hash['Kitchen'] = { ratio: 0.5, space_type_gen: true, default: false }
297
+ elsif building_type == 'FullServiceRestaurant'
298
+ hash['Dining'] = { ratio: 0.7272, space_type_gen: true, default: true }
299
+ hash['Kitchen'] = { ratio: 0.2728, space_type_gen: true, default: false }
300
+ elsif building_type == 'MidriseApartment'
301
+ hash['Apartment'] = { ratio: 0.8727, space_type_gen: true, default: true }
302
+ hash['Corridor'] = { ratio: 0.0991, space_type_gen: true, default: false }
303
+ hash['Office'] = { ratio: 0.0282, space_type_gen: true, default: false }
304
+ elsif building_type == 'HighriseApartment'
305
+ hash['Apartment'] = { ratio: 0.8896, space_type_gen: true, default: true }
306
+ hash['Corridor'] = { ratio: 0.0991, space_type_gen: true, default: false }
307
+ hash['Office'] = { ratio: 0.0113, space_type_gen: true, default: false }
308
+ elsif building_type == 'Hospital'
309
+ hash['Basement'] = { ratio: 0.1667, space_type_gen: false, default: false }
310
+ hash['Corridor'] = { ratio: 0.1741, space_type_gen: true, default: false }
311
+ hash['Dining'] = { ratio: 0.0311, space_type_gen: true, default: false }
312
+ hash['ER_Exam'] = { ratio: 0.0099, space_type_gen: true, default: false }
313
+ hash['ER_NurseStn'] = { ratio: 0.0551, space_type_gen: true, default: false }
314
+ hash['ER_Trauma'] = { ratio: 0.0025, space_type_gen: true, default: false }
315
+ hash['ER_Triage'] = { ratio: 0.0050, space_type_gen: true, default: false }
316
+ hash['ICU_NurseStn'] = { ratio: 0.0298, space_type_gen: true, default: false }
317
+ hash['ICE_Open'] = { ratio: 0.0275, space_type_gen: true, default: false }
318
+ hash['ICU_PatRm'] = { ratio: 0.0115, space_type_gen: true, default: false }
319
+ hash['Kitchen'] = { ratio: 0.0414, space_type_gen: true, default: false }
320
+ hash['Lab'] = { ratio: 0.0236, space_type_gen: true, default: false }
321
+ hash['Lobby'] = { ratio: 0.0657, space_type_gen: true, default: false }
322
+ hash['NurseStn'] = { ratio: 0.1723, space_type_gen: true, default: false }
323
+ hash['Office'] = { ratio: 0.0286, space_type_gen: true, default: false }
324
+ hash['OR'] = { ratio: 0.0273, space_type_gen: true, default: false }
325
+ hash['PatCorridor'] = { ratio: 0.0, space_type_gen: true, default: false } # not in prototype
326
+ hash['PatRoom'] = { ratio: 0.0845, space_type_gen: true, default: true }
327
+ hash['PhysTherapy'] = { ratio: 0.0217, space_type_gen: true, default: false }
328
+ hash['Radiology'] = { ratio: 0.0217, space_type_gen: true, default: false }
329
+ elsif building_type == 'Outpatient'
330
+ hash['Anesthesia'] = { ratio: 0.0026, space_type_gen: true, default: false }
331
+ hash['BioHazard'] = { ratio: 0.0014, space_type_gen: true, default: false }
332
+ hash['Cafe'] = { ratio: 0.0103, space_type_gen: true, default: false }
333
+ hash['CleanWork'] = { ratio: 0.0071, space_type_gen: true, default: false }
334
+ hash['Conference'] = { ratio: 0.0082, space_type_gen: true, default: false }
335
+ hash['DresingRoom'] = { ratio: 0.0021, space_type_gen: true, default: false }
336
+ hash['Elec/MechRoom'] = { ratio: 0.0109, space_type_gen: true, default: false }
337
+ hash['ElevatorPumpRoom'] = { ratio: 0.0022, space_type_gen: true, default: false }
338
+ hash['Exam'] = { ratio: 0.1029, space_type_gen: true, default: true }
339
+ hash['Hall'] = { ratio: 0.1924, space_type_gen: true, default: false }
340
+ hash['IT_Room'] = { ratio: 0.0027, space_type_gen: true, default: false }
341
+ hash['Janitor'] = { ratio: 0.0672, space_type_gen: true, default: false }
342
+ hash['Lobby'] = { ratio: 0.0152, space_type_gen: true, default: false }
343
+ hash['LockerRoom'] = { ratio: 0.0190, space_type_gen: true, default: false }
344
+ hash['Lounge'] = { ratio: 0.0293, space_type_gen: true, default: false }
345
+ hash['MedGas'] = { ratio: 0.0014, space_type_gen: true, default: false }
346
+ hash['MRI'] = { ratio: 0.0107, space_type_gen: true, default: false }
347
+ hash['MRI_Control'] = { ratio: 0.0041, space_type_gen: true, default: false }
348
+ hash['NurseStation'] = { ratio: 0.0189, space_type_gen: true, default: false }
349
+ hash['Office'] = { ratio: 0.1828, space_type_gen: true, default: false }
350
+ hash['OR'] = { ratio: 0.0346, space_type_gen: true, default: false }
351
+ hash['PACU'] = { ratio: 0.0232, space_type_gen: true, default: false }
352
+ hash['PhysicalTherapy'] = { ratio: 0.0462, space_type_gen: true, default: false }
353
+ hash['PreOp'] = { ratio: 0.0129, space_type_gen: true, default: false }
354
+ hash['ProcedureRoom'] = { ratio: 0.0070, space_type_gen: true, default: false }
355
+ hash['Reception'] = { ratio: 0.0365, space_type_gen: true, default: false }
356
+ hash['Soil Work'] = { ratio: 0.0088, space_type_gen: true, default: false }
357
+ hash['Stair'] = { ratio: 0.0146, space_type_gen: true, default: false }
358
+ hash['Toilet'] = { ratio: 0.0193, space_type_gen: true, default: false }
359
+ hash['Undeveloped'] = { ratio: 0.0835, space_type_gen: false, default: false }
360
+ hash['Xray'] = { ratio: 0.0220, space_type_gen: true, default: false }
361
+ elsif building_type == 'SuperMarket'
362
+ # TODO: - populate ratios for SuperMarket
363
+ hash['Bakery'] = { ratio: 0.99, space_type_gen: true, default: false }
364
+ hash['Deli'] = { ratio: 0.99, space_type_gen: true, default: false }
365
+ hash['DryStorage'] = { ratio: 0.99, space_type_gen: true, default: false }
366
+ hash['Office'] = { ratio: 0.99, space_type_gen: true, default: false }
367
+ hash['Produce'] = { ratio: 0.99, space_type_gen: true, default: true }
368
+ hash['Sales'] = { ratio: 0.99, space_type_gen: true, default: true }
369
+ hash['Corridor'] = { ratio: 0.99, space_type_gen: true, default: true }
370
+ hash['Dining'] = { ratio: 0.99, space_type_gen: true, default: true }
371
+ hash['Elec/MechRoom'] = { ratio: 0.99, space_type_gen: true, default: true }
372
+ hash['Meeting'] = { ratio: 0.99, space_type_gen: true, default: true }
373
+ hash['Restroom'] = { ratio: 0.99, space_type_gen: true, default: true }
374
+ hash['Vestibule'] = { ratio: 0.99, space_type_gen: true, default: true }
375
+ else
376
+ return false
377
+ end
378
+
379
+ return hash
380
+ end
381
+
382
+ # remove existing non resource objects from the model
383
+ # technically thermostats and building stories are resources but still want to remove them.
384
+ def remove_non_resource_objects(runner, model, options = nil)
385
+ if options.nil?
386
+ options = {}
387
+ options[:remove_building_stories] = true
388
+ options[:remove_thermostats] = true
389
+ options[:remove_air_loops] = true
390
+ options[:remove_non_swh_plant_loops] = true
391
+
392
+ # leave these in by default unless requsted when method called
393
+ options[:remove_swh_plant_loops] = false
394
+ options[:remove_exterior_lights] = false
395
+ options[:remove_site_shading] = false
396
+ end
397
+
398
+ num_model_objects = model.objects.size
399
+
400
+ # remove non-resource objects not removed by removing the building
401
+ if options[:remove_building_stories] then model.getBuildingStorys.each(&:remove) end
402
+ if options[:remove_thermostats] then model.getThermostats.each(&:remove) end
403
+ if options[:remove_air_loops] then model.getAirLoopHVACs.each(&:remove) end
404
+ if options[:remove_exterior_lights] then model.getFacility.exteriorLights.each(&:remove) end
405
+ if options[:remove_site_shading] then model.getSite.shadingSurfaceGroups.each(&:remove) end
406
+
407
+ # see if plant loop is swh or not and take proper action (booter loop doesn't have water use equipment)
408
+ model.getPlantLoops.each do |plant_loop|
409
+ is_swh_loop = false
410
+ plant_loop.supplyComponents.each do |component|
411
+ if component.to_WaterHeaterMixed.is_initialized
412
+ is_swh_loop = true
413
+ next
414
+ end
415
+ end
416
+
417
+ if is_swh_loop
418
+ if options[:remove_swh_plant_loops] then plant_loop.remove end
419
+ else
420
+ if options[:remove_non_swh_plant_loops] then plant_loop.remove end
421
+ end
422
+ end
423
+
424
+ # remove water use connections (may be removed when loop is removed)
425
+ if options[:remove_swh_plant_loops] then model.getWaterConnectionss.each(&:remove) end
426
+ if options[:remove_swh_plant_loops] then model.getWaterUseEquipments.each(&:remove) end
427
+
428
+ # remove building but reset fields on new building object.
429
+ building_fields = []
430
+ building = model.getBuilding
431
+ num_fields = building.numFields
432
+ num_fields.times.each do |i|
433
+ building_fields << building.getString(i).get
434
+ end
435
+ # removes spaces, space's child objects, thermal zones, zone equipment, non site surfaces, building stories and water use connections.
436
+ model.getBuilding.remove
437
+ building = model.getBuilding
438
+ num_fields.times.each do |i|
439
+ next if i == 0 # don't try and set handle
440
+ building_fields << building.setString(i, building_fields[i])
441
+ end
442
+
443
+ # other than optionally site shading and exterior lights not messing with site characteristics
444
+
445
+ if num_model_objects - model.objects.size > 0
446
+ runner.registerInfo("Removed #{num_model_objects - model.objects.size} non resource objects from the model.")
447
+ end
448
+
449
+ return true
450
+ end
451
+
452
+ # create_bar(runner,model,bar_hash)
453
+ # measures using this method should include OsLibGeometry and OsLibHelperMethods
454
+ def create_bar(runner, model, bar_hash, story_multiplier_method = 'Basements Ground Mid Top')
455
+ # warn about site shading
456
+ if !model.getSite.shadingSurfaceGroups.empty?
457
+ runner.registerWarning('The model has one or more site shading surafces. New geometry may not be positioned where expected, it will be centered over the center of the original geometry.')
458
+ end
459
+
460
+ # make custom story hash when number of stories below grade > 0
461
+ # todo - update this so have option basements are not below 0? (useful for simplifying existing model and maintaining z position relative to site shading)
462
+ story_hash = {}
463
+ eff_below = bar_hash[:num_stories_below_grade]
464
+ eff_above = bar_hash[:num_stories_above_grade]
465
+ footprint_origin = bar_hash[:center_of_footprint]
466
+ typical_story_height = bar_hash[:floor_height]
467
+
468
+ # flatten story_hash out to individual stories included in building area
469
+ stories_flat = []
470
+ stories_flat_counter = 0
471
+ bar_hash[:stories].each_with_index do |(k, v), i|
472
+ # k is invalid in some cases, old story object that has been removed, should be from low to high including basement
473
+ # skip if source story insn't included in building area
474
+ if v[:story_included_in_building_area].nil? || (v[:story_included_in_building_area] == true)
475
+
476
+ # add to counter
477
+ stories_flat_counter += v[:story_min_multiplier]
478
+
479
+ flat_hash = {}
480
+ flat_hash[:story_party_walls] = v[:story_party_walls]
481
+ flat_hash[:below_partial_story] = v[:below_partial_story]
482
+ flat_hash[:bottom_story_ground_exposed_floor] = v[:bottom_story_ground_exposed_floor]
483
+ flat_hash[:top_story_exterior_exposed_roof] = v[:top_story_exterior_exposed_roof]
484
+ if i < eff_below
485
+ flat_hash[:story_type] = 'B'
486
+ flat_hash[:multiplier] = 1
487
+ elsif i == eff_below
488
+ flat_hash[:story_type] = 'Ground'
489
+ flat_hash[:multiplier] = 1
490
+ elsif stories_flat_counter == eff_below + eff_above.ceil
491
+ flat_hash[:story_type] = 'Top'
492
+ flat_hash[:multiplier] = 1
493
+ else
494
+ flat_hash[:story_type] = 'Mid'
495
+ flat_hash[:multiplier] = v[:story_min_multiplier]
496
+ end
497
+
498
+ compare_hash = {}
499
+ if !stories_flat.empty?
500
+ stories_flat.last.each { |k, v| compare_hash[k] = flat_hash[k] if flat_hash[k] != v }
501
+ end
502
+ if (story_multiplier_method != 'None' && stories_flat.last == flat_hash) || (story_multiplier_method != 'None' && compare_hash.size == 1 && compare_hash.include?(:multiplier))
503
+ stories_flat.last[:multiplier] += v[:story_min_multiplier]
504
+ else
505
+ stories_flat << flat_hash
506
+ end
507
+ end
508
+ end
509
+
510
+ if bar_hash[:num_stories_below_grade] > 0
511
+
512
+ # add in below grade levels (may want to add below grade multipliers at some point if we start running deep basements)
513
+ eff_below.times do |i|
514
+ story_hash["B#{i + 1}"] = { space_origin_z: footprint_origin.z - typical_story_height * (i + 1), space_height: typical_story_height, multiplier: 1 }
515
+ end
516
+ end
517
+
518
+ # add in above grade levels
519
+ if eff_above > 2
520
+ story_hash['Ground'] = { space_origin_z: footprint_origin.z, space_height: typical_story_height, multiplier: 1 }
521
+
522
+ footprint_counter = 0
523
+ effective_stories_counter = 1
524
+ stories_flat.each do |hash|
525
+ next if hash[:story_type] != 'Mid'
526
+ if footprint_counter == 0
527
+ string = 'Mid'
528
+ else
529
+ string = "Mid#{footprint_counter + 1}"
530
+ end
531
+ story_hash[string] = { space_origin_z: footprint_origin.z + typical_story_height * effective_stories_counter + typical_story_height * (hash[:multiplier] - 1) / 2.0, space_height: typical_story_height, multiplier: hash[:multiplier] }
532
+ footprint_counter += 1
533
+ effective_stories_counter += hash[:multiplier]
534
+ end
535
+
536
+ story_hash['Top'] = { space_origin_z: footprint_origin.z + typical_story_height * (eff_above.ceil - 1), space_height: typical_story_height, multiplier: 1 }
537
+ elsif eff_above > 1
538
+ story_hash['Ground'] = { space_origin_z: footprint_origin.z, space_height: typical_story_height, multiplier: 1 }
539
+ story_hash['Top'] = { space_origin_z: footprint_origin.z + typical_story_height * (eff_above.ceil - 1), space_height: typical_story_height, multiplier: 1 }
540
+ else # one story only
541
+ story_hash['Ground'] = { space_origin_z: footprint_origin.z, space_height: typical_story_height, multiplier: 1 }
542
+ end
543
+
544
+ # create footprints
545
+ if bar_hash[:bar_division_method] == 'Multiple Space Types - Simple Sliced'
546
+ footprints = []
547
+ story_hash.size.times do |i|
548
+ # adjust size of bar of top story is not a full story
549
+ if i + 1 == story_hash.size
550
+ area_multiplier = (1.0 - bar_hash[:num_stories_above_grade].ceil + bar_hash[:num_stories_above_grade])
551
+ edge_multiplier = Math.sqrt(area_multiplier)
552
+ length = bar_hash[:length] * edge_multiplier
553
+ width = bar_hash[:width] * edge_multiplier
554
+ else
555
+ length = bar_hash[:length]
556
+ width = bar_hash[:width]
557
+ end
558
+ footprints << OsLib_Geometry.make_sliced_bar_simple_polygons(runner, bar_hash[:space_types], length, width, bar_hash[:center_of_footprint])
559
+ end
560
+
561
+ elsif bar_hash[:bar_division_method] == 'Multiple Space Types - Individual Stories Sliced'
562
+
563
+ # update story_hash for partial_story_above
564
+ story_hash.each_with_index do |(k, v), i|
565
+ # adjust size of bar of top story is not a full story
566
+ if i + 1 == story_hash.size
567
+ story_hash[k][:partial_story_multiplier] = (1.0 - bar_hash[:num_stories_above_grade].ceil + bar_hash[:num_stories_above_grade])
568
+ end
569
+ end
570
+
571
+ footprints = OsLib_Geometry.make_sliced_bar_multi_polygons(runner, bar_hash[:space_types], bar_hash[:length], bar_hash[:width], bar_hash[:center_of_footprint], story_hash)
572
+
573
+ else
574
+ footprints = []
575
+ story_hash.size.times do |i|
576
+ # adjust size of bar of top story is not a full story
577
+ if i + 1 == story_hash.size
578
+ area_multiplier = (1.0 - bar_hash[:num_stories_above_grade].ceil + bar_hash[:num_stories_above_grade])
579
+ edge_multiplier = Math.sqrt(area_multiplier)
580
+ length = bar_hash[:length] * edge_multiplier
581
+ width = bar_hash[:width] * edge_multiplier
582
+ else
583
+ length = bar_hash[:length]
584
+ width = bar_hash[:width]
585
+ end
586
+ footprints << OsLib_Geometry.make_core_and_perimeter_polygons(runner, length, width, bar_hash[:center_of_footprint]) # perimeter defaults to 15'
587
+ end
588
+
589
+ # set primary space type to building default space type
590
+ space_types = bar_hash[:space_types].sort_by { |k, v| v[:floor_area] }
591
+ model.getBuilding.setSpaceType(space_types.last.first)
592
+
593
+ end
594
+
595
+ # makeSpacesFromPolygons
596
+ OsLib_Geometry.makeSpacesFromPolygons(runner, model, footprints, bar_hash[:floor_height], bar_hash[:num_stories], bar_hash[:center_of_footprint], story_hash)
597
+
598
+ # put all of the spaces in the model into a vector for intersection and surface matching
599
+ spaces = OpenStudio::Model::SpaceVector.new
600
+ model.getSpaces.sort.each do |space|
601
+ spaces << space
602
+ end
603
+
604
+ # only intersect if make_mid_story_surfaces_adiabatic false
605
+ if !(bar_hash[:make_mid_story_surfaces_adiabatic])
606
+ # intersect surfaces
607
+ # (when bottom floor has many space types and one above doesn't will end up with heavily subdivided floor. Maybe use adiabatic and don't intersect floor/ceilings)
608
+ intersect_surfaces = true
609
+ if intersect_surfaces
610
+ OpenStudio::Model.intersectSurfaces(spaces)
611
+ runner.registerInfo('Intersecting surfaces, this will create additional geometry.')
612
+ end
613
+ end
614
+
615
+ # match surfaces for each space in the vector
616
+ OpenStudio::Model.matchSurfaces(spaces)
617
+
618
+ # set boundary conditions if not already set when geometry was created
619
+ # todo - update this to use space original z value vs. story name
620
+ if bar_hash[:num_stories_below_grade] > 0
621
+ model.getBuildingStorys.each do |story|
622
+ next if !story.name.to_s.include?('Story B')
623
+ story.spaces.each do |space|
624
+ space.surfaces.each do |surface|
625
+ next if surface.surfaceType != 'Wall'
626
+ next if surface.outsideBoundaryCondition != 'Outdoors'
627
+ surface.setOutsideBoundaryCondition('Ground')
628
+ end
629
+ end
630
+ end
631
+ end
632
+
633
+ # sort stories (by name for now but need better way)
634
+ # todo - need to change this so doesn't create issues when models have existing stories and spaces. Should be able to run it multiple times
635
+ sorted_stories = {}
636
+ model.getBuildingStorys.each do |story|
637
+ sorted_stories[story.name.to_s] = story
638
+ end
639
+
640
+ # flag space types that have wwr overrides
641
+ space_type_wwr_overrides = {}
642
+
643
+ # loop through building stories, spaces, and surfaces
644
+ sorted_stories.sort.each_with_index do |(key, story), i|
645
+ # flag for adiabatic floor if building doesn't have ground exposed floor
646
+ if stories_flat[i][:bottom_story_ground_exposed_floor] == false
647
+ adiabatic_floor = true
648
+ end
649
+ # flag for adiabatic roof if building doesn't have exterior exposed roof
650
+ if stories_flat[i][:top_story_exterior_exposed_roof] == false
651
+ adiabatic_ceiling = true
652
+ end
653
+
654
+ # make all mid story floor and celings adiabiatc if requested
655
+ if bar_hash[:make_mid_story_surfaces_adiabatic]
656
+ if i > 0
657
+ adiabatic_floor = true
658
+ end
659
+ if i < sorted_stories.size - 1
660
+ adiabatic_ceiling = true
661
+ end
662
+ end
663
+
664
+ # flag orientations for this story to recieve party walls
665
+ party_wall_facades = stories_flat[i][:story_party_walls]
666
+
667
+ story.spaces.each do |space|
668
+ space.surfaces. each do |surface|
669
+ # set floor to adiabatic if requited
670
+ if adiabatic_floor && surface.surfaceType == 'Floor'
671
+ make_surfaces_adiabatic([surface])
672
+ elsif adiabatic_ceiling && surface.surfaceType == 'RoofCeiling'
673
+ make_surfaces_adiabatic([surface])
674
+ end
675
+
676
+ # skip of not exterior wall
677
+ next if surface.surfaceType != 'Wall'
678
+ next if surface.outsideBoundaryCondition != 'Outdoors'
679
+
680
+ # get the absoluteAzimuth for the surface so we can categorize it
681
+ absoluteAzimuth = OpenStudio.convert(surface.azimuth, 'rad', 'deg').get + surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis
682
+ absoluteAzimuth = absoluteAzimuth % 360.0 # should result in value between 0 and 360
683
+ absoluteAzimuth = absoluteAzimuth.round(5) # this was creating issues at 45 deg angles with opposing facades
684
+
685
+ # target wwr values that may be changed for specific space types
686
+ wwr_n = bar_hash[:building_wwr_n]
687
+ wwr_e = bar_hash[:building_wwr_e]
688
+ wwr_s = bar_hash[:building_wwr_s]
689
+ wwr_w = bar_hash[:building_wwr_w]
690
+
691
+ # look for space type specific wwr values
692
+ if surface.space.is_initialized && surface.space.get.spaceType.is_initialized
693
+ space_type = surface.space.get.spaceType.get
694
+
695
+ # see if space type has wwr value
696
+ bar_hash[:space_types].each do |k,v|
697
+ if v.has_key?(:space_type) && space_type == v[:space_type]
698
+
699
+ # if matching space type specifies a wwr then override the orientaiton specific recommendations for this surface.
700
+ if v.has_key?(:wwr)
701
+ wwr_n = v[:wwr]
702
+ wwr_e = v[:wwr]
703
+ wwr_s = v[:wwr]
704
+ wwr_w = v[:wwr]
705
+ space_type_wwr_overrides[space_type] = v[:wwr]
706
+ end
707
+ end
708
+ end
709
+ end
710
+
711
+ # add fenestration (wwr for now, maybe overhang and overhead doors later)
712
+ if (absoluteAzimuth >= 315.0) || (absoluteAzimuth < 45.0)
713
+ if party_wall_facades.include?('north')
714
+ make_surfaces_adiabatic([surface])
715
+ else
716
+ surface.setWindowToWallRatio(wwr_n)
717
+ end
718
+ elsif (absoluteAzimuth >= 45.0) && (absoluteAzimuth < 135.0)
719
+ if party_wall_facades.include?('east')
720
+ make_surfaces_adiabatic([surface])
721
+ else
722
+ surface.setWindowToWallRatio(wwr_e)
723
+ end
724
+ elsif (absoluteAzimuth >= 135.0) && (absoluteAzimuth < 225.0)
725
+ if party_wall_facades.include?('south')
726
+ make_surfaces_adiabatic([surface])
727
+ else
728
+ surface.setWindowToWallRatio(wwr_s)
729
+ end
730
+ elsif (absoluteAzimuth >= 225.0) && (absoluteAzimuth < 315.0)
731
+ if party_wall_facades.include?('west')
732
+ make_surfaces_adiabatic([surface])
733
+ else
734
+ surface.setWindowToWallRatio(wwr_w)
735
+ end
736
+ else
737
+ runner.registerError('Unexpected value of facade: ' + absoluteAzimuth + '.')
738
+ return false
739
+ end
740
+ end
741
+ end
742
+ end
743
+
744
+ # report space types with custom wwr values
745
+ space_type_wwr_overrides.each do |space_type,wwr|
746
+ runner.registerInfo("For #{space_type.name} the default building wwr was replaced with a space type specifc value of #{wwr}")
747
+ end
748
+
749
+ final_floor_area_ip = OpenStudio.convert(model.getBuilding.floorArea, 'm^2', 'ft^2').get
750
+ runner.registerInfo("Created Bar envlope with floor area of #{OpenStudio.toNeatString(final_floor_area_ip, 0, true)} (ft^2)")
751
+ end
752
+
753
+ # make selected surfaces adiabatic
754
+ def make_surfaces_adiabatic(surfaces)
755
+ surfaces.each do |surface|
756
+ if surface.construction.is_initialized
757
+ surface.setConstruction(surface.construction.get)
758
+ end
759
+ surface.setOutsideBoundaryCondition('Adiabatic')
760
+ end
761
+ end
762
+
763
+ # get length and width of rectangle matching bounding box aspect ratio will maintaining proper floor area
764
+ def calc_bar_reduced_bounding_box(envelope_data_hash)
765
+ bar = {}
766
+
767
+ bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
768
+ bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
769
+ bounding_area = bounding_length * bounding_width
770
+ footprint_area = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective__num_stories].to_f
771
+ area_multiplier = footprint_area / bounding_area
772
+ edge_multiplier = Math.sqrt(area_multiplier)
773
+ bar[:length] = bounding_length * edge_multiplier
774
+ bar[:width] = bounding_width * edge_multiplier
775
+
776
+ return bar
777
+ end
778
+
779
+ # get length and width of rectangle matching longer of two edges, and reducing the other way until floor area matches
780
+ def calc_bar_reduced_width(envelope_data_hash)
781
+ bar = {}
782
+
783
+ bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
784
+ bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
785
+ footprint_area = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective__num_stories].to_f
786
+
787
+ if bounding_length >= bounding_width
788
+ bar[:length] = bounding_length
789
+ bar[:width] = footprint_area / bounding_length
790
+ else
791
+ bar[:width] = bounding_width
792
+ bar[:length] = footprint_area / bounding_width
793
+ end
794
+
795
+ return bar
796
+ end
797
+
798
+ # get length and width of rectangle by stretching it until both floor area and exterior wall area or perimeter match
799
+ def calc_bar_stretched(envelope_data_hash)
800
+ bar = {}
801
+
802
+ bounding_length = envelope_data_hash[:building_max_xyz][0] - envelope_data_hash[:building_min_xyz][0]
803
+ bounding_width = envelope_data_hash[:building_max_xyz][1] - envelope_data_hash[:building_min_xyz][1]
804
+ a = envelope_data_hash[:building_floor_area] / envelope_data_hash[:effective__num_stories].to_f
805
+ p = envelope_data_hash[:building_perimeter]
806
+
807
+ if bounding_length >= bounding_width
808
+ bar[:length] = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
809
+ bar[:width] = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
810
+ else
811
+ bar[:length] = 0.25 * (p - Math.sqrt(p**2 - 16 * a))
812
+ bar[:width] = 0.25 * (p + Math.sqrt(p**2 - 16 * a))
813
+ end
814
+
815
+ return bar
816
+ end
817
+ end