honeybee-openstudio 1.8.1 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +5 -5
  2. data/.coveralls.yml +1 -1
  3. data/.gitignore +32 -32
  4. data/.releaserc.json +7 -7
  5. data/.travis.yml +42 -41
  6. data/Gemfile +18 -18
  7. data/LICENSE.md +23 -23
  8. data/README.md +95 -95
  9. data/Rakefile +20 -20
  10. data/doc_templates/LICENSE.md +23 -23
  11. data/doc_templates/copyright_erb.txt +31 -31
  12. data/doc_templates/copyright_js.txt +4 -4
  13. data/doc_templates/copyright_ruby.txt +29 -29
  14. data/honeybee-openstudio.gemspec +35 -35
  15. data/lib/files/Honeybee.rb +112 -112
  16. data/lib/files/honeybee_workflow.osw +47 -47
  17. data/lib/files/urbanopt_Gemfile +32 -32
  18. data/lib/from_honeybee.rb +86 -86
  19. data/lib/from_honeybee/_openapi/model.json +8126 -8126
  20. data/lib/from_honeybee/_openapi/simulation-parameter.json +842 -842
  21. data/lib/from_honeybee/construction/air.rb +64 -64
  22. data/lib/from_honeybee/construction/opaque.rb +76 -76
  23. data/lib/from_honeybee/construction/shade.rb +107 -107
  24. data/lib/from_honeybee/construction/window.rb +80 -80
  25. data/lib/from_honeybee/construction_set.rb +278 -278
  26. data/lib/from_honeybee/extension.rb +109 -109
  27. data/lib/from_honeybee/geometry/aperture.rb +167 -167
  28. data/lib/from_honeybee/geometry/door.rb +160 -160
  29. data/lib/from_honeybee/geometry/face.rb +163 -163
  30. data/lib/from_honeybee/geometry/room.rb +392 -392
  31. data/lib/from_honeybee/geometry/shade.rb +89 -89
  32. data/lib/from_honeybee/hvac/ideal_air.rb +150 -150
  33. data/lib/from_honeybee/load/electric_equipment.rb +95 -95
  34. data/lib/from_honeybee/load/gas_equipment.rb +97 -97
  35. data/lib/from_honeybee/load/infiltration.rb +94 -94
  36. data/lib/from_honeybee/load/lighting.rb +98 -98
  37. data/lib/from_honeybee/load/people.rb +99 -99
  38. data/lib/from_honeybee/load/setpoint_humidistat.rb +74 -74
  39. data/lib/from_honeybee/load/setpoint_thermostat.rb +71 -71
  40. data/lib/from_honeybee/load/ventilation.rb +95 -95
  41. data/lib/from_honeybee/material/opaque.rb +94 -94
  42. data/lib/from_honeybee/material/opaque_no_mass.rb +94 -94
  43. data/lib/from_honeybee/material/window_blind.rb +238 -238
  44. data/lib/from_honeybee/material/window_gas.rb +76 -76
  45. data/lib/from_honeybee/material/window_gas_custom.rb +118 -118
  46. data/lib/from_honeybee/material/window_gas_mixture.rb +79 -79
  47. data/lib/from_honeybee/material/window_glazing.rb +166 -166
  48. data/lib/from_honeybee/material/window_shade.rb +160 -160
  49. data/lib/from_honeybee/material/window_simpleglazsys.rb +73 -73
  50. data/lib/from_honeybee/model.rb +434 -434
  51. data/lib/from_honeybee/model_object.rb +110 -110
  52. data/lib/from_honeybee/program_type.rb +124 -124
  53. data/lib/from_honeybee/schedule/fixed_interval.rb +115 -115
  54. data/lib/from_honeybee/schedule/ruleset.rb +164 -164
  55. data/lib/from_honeybee/schedule/type_limit.rb +88 -88
  56. data/lib/from_honeybee/simulation/designday.rb +105 -105
  57. data/lib/from_honeybee/simulation/extension.rb +46 -46
  58. data/lib/from_honeybee/simulation/parameter.rb +277 -277
  59. data/lib/from_honeybee/version.rb +34 -34
  60. data/lib/measures/from_honeybee_model/LICENSE.md +26 -26
  61. data/lib/measures/from_honeybee_model/README.md +32 -32
  62. data/lib/measures/from_honeybee_model/measure.rb +91 -91
  63. data/lib/measures/from_honeybee_model/measure.xml +103 -103
  64. data/lib/measures/from_honeybee_model/tests/from_honeybee_model_test.rb +126 -126
  65. data/lib/measures/from_honeybee_simulation_parameter/LICENSE.md +26 -26
  66. data/lib/measures/from_honeybee_simulation_parameter/README.md +32 -32
  67. data/lib/measures/from_honeybee_simulation_parameter/measure.rb +95 -95
  68. data/lib/measures/from_honeybee_simulation_parameter/measure.xml +91 -91
  69. data/lib/measures/from_honeybee_simulation_parameter/tests/from_honeybee_simulation_parameter_test.rb +109 -109
  70. metadata +2 -2
@@ -1,392 +1,392 @@
1
- # *******************************************************************************
2
- # Honeybee OpenStudio Gem, Copyright (c) 2020, Alliance for Sustainable
3
- # Energy, LLC, Ladybug Tools LLC and other contributors. All rights reserved.
4
- #
5
- # Redistribution and use in source and binary forms, with or without
6
- # modification, are permitted provided that the following conditions are met:
7
- #
8
- # (1) Redistributions of source code must retain the above copyright notice,
9
- # this list of conditions and the following disclaimer.
10
- #
11
- # (2) Redistributions in binary form must reproduce the above copyright notice,
12
- # this list of conditions and the following disclaimer in the documentation
13
- # and/or other materials provided with the distribution.
14
- #
15
- # (3) Neither the name of the copyright holder nor the names of any contributors
16
- # may be used to endorse or promote products derived from this software without
17
- # specific prior written permission from the respective party.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
20
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
- # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
23
- # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
24
- # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26
- # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
- # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
- # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
- # *******************************************************************************
31
-
32
- require 'from_honeybee/model_object'
33
-
34
- require 'from_honeybee/geometry/face'
35
- require 'from_honeybee/geometry/shade'
36
-
37
- require 'from_honeybee/load/people'
38
- require 'from_honeybee/load/lighting'
39
- require 'from_honeybee/load/electric_equipment'
40
- require 'from_honeybee/load/gas_equipment'
41
- require 'from_honeybee/load/infiltration'
42
- require 'from_honeybee/load/ventilation'
43
- require 'from_honeybee/load/setpoint_thermostat'
44
- require 'from_honeybee/load/setpoint_humidistat'
45
-
46
- require 'openstudio'
47
-
48
- module FromHoneybee
49
- class Room < ModelObject
50
- attr_reader :errors, :warnings
51
-
52
- def initialize(hash = {})
53
- super(hash)
54
- raise "Incorrect model type '#{@type}'" unless @type == 'Room'
55
- end
56
-
57
- def defaults
58
- @@schema[:components][:schemas][:RoomEnergyPropertiesAbridged][:properties]
59
- end
60
-
61
- def find_existing_openstudio_object(openstudio_model)
62
- model_space = openstudio_model.getSpaceByName(@hash[:identifier])
63
- return model_space.get unless model_space.empty?
64
- nil
65
- end
66
-
67
- def to_openstudio(openstudio_model)
68
- # create the space and thermal zone
69
- os_space = OpenStudio::Model::Space.new(openstudio_model)
70
- os_space.setName(@hash[:identifier])
71
- os_thermal_zone = OpenStudio::Model::ThermalZone.new(openstudio_model)
72
- os_thermal_zone.setName(@hash[:identifier])
73
- os_space.setThermalZone(os_thermal_zone)
74
-
75
- # assign the programtype
76
- if @hash[:properties][:energy][:program_type]
77
- space_type = openstudio_model.getSpaceTypeByName(@hash[:properties][:energy][:program_type])
78
- unless space_type.empty?
79
- space_type_object = space_type.get
80
- os_space.setSpaceType(space_type_object)
81
- end
82
- end
83
-
84
- # assign the constructionset
85
- if @hash[:properties][:energy][:construction_set]
86
- construction_set_identifier = @hash[:properties][:energy][:construction_set]
87
- # gets default construction set assigned to room from openstudio_model
88
- construction_set = openstudio_model.getDefaultConstructionSetByName(construction_set_identifier)
89
- unless construction_set.empty?
90
- default_construction_set = construction_set.get
91
- os_space.setDefaultConstructionSet(default_construction_set)
92
- end
93
- end
94
-
95
- # assign the multiplier
96
- if @hash[:multiplier] and @hash[:multiplier] != 1
97
- os_thermal_zone.setMultiplier(@hash[:multiplier])
98
- end
99
-
100
- # assign the story
101
- if @hash[:story] # the users has specified the name of the story
102
- story = openstudio_model.getBuildingStoryByName(@hash[:story])
103
- if story.empty? # first time that this story has been referenced
104
- story = OpenStudio::Model::BuildingStory.new(openstudio_model)
105
- story.setName(@hash[:story])
106
- else
107
- story = story.get
108
- end
109
- else # give the room a dummy story so that it works with David's measures
110
- story = openstudio_model.getBuildingStoryByName('UndefiniedStory')
111
- if story.empty? # first time that this story has been referenced
112
- story = OpenStudio::Model::BuildingStory.new(openstudio_model)
113
- story.setName('UndefiniedStory')
114
- else
115
- story = story.get
116
- end
117
- end
118
- os_space.setBuildingStory(story)
119
-
120
- # assign all of the faces to the room
121
- @hash[:faces].each do |face|
122
- ladybug_face = Face.new(face)
123
- os_surface = ladybug_face.to_openstudio(openstudio_model)
124
- os_surface.setSpace(os_space)
125
-
126
- # assign face-level shades if they exist
127
- if face[:outdoor_shades]
128
- os_shd_group = make_shade_group(openstudio_model, os_surface, os_space)
129
- face[:outdoor_shades].each do |outdoor_shade|
130
- add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
131
- end
132
- end
133
-
134
- # assign aperture-level shades if they exist
135
- if face[:apertures]
136
- face[:apertures].each do |aperture|
137
- if aperture[:outdoor_shades]
138
- unless os_shd_group
139
- os_shd_group = make_shade_group(openstudio_model, os_surface, os_space)
140
- end
141
- aperture[:outdoor_shades].each do |outdoor_shade|
142
- add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
143
- end
144
- end
145
- end
146
- end
147
-
148
- # assign door-level shades if they exist
149
- if face[:doors]
150
- face[:doors].each do |door|
151
- if door[:outdoor_shades]
152
- unless os_shd_group
153
- os_shd_group = make_shade_group(openstudio_model, os_surface, os_space)
154
- end
155
- door[:outdoor_shades].each do |outdoor_shade|
156
- add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
157
- end
158
- end
159
- end
160
- end
161
-
162
- if !face[:properties][:energy][:construction]
163
- if face[:boundary_condition][:type] == 'Adiabatic'
164
- # assign default interior construciton for Adiabatic Faces
165
- if face[:face_type] != 'Wall'
166
- interior_construction = closest_interior_construction(openstudio_model, os_space, face[:face_type])
167
- unless interior_construction.nil?
168
- os_surface.setConstruction(interior_construction)
169
- end
170
- end
171
- elsif face[:face_type] == 'AirBoundary'
172
- # assign default air boundary construciton for AirBoundary face types
173
- air_construction = closest_air_construction(openstudio_model, os_space)
174
- unless air_construction.nil?
175
- os_surface.setConstruction(air_construction)
176
- end
177
- # add air mixing properties to the global list that tracks them
178
- air_hash = $air_boundary_hash[air_construction.name.to_s]
179
- if air_hash[:air_mixing_per_area]
180
- air_mix_area = air_hash[:air_mixing_per_area]
181
- else
182
- air_default = @@schema[:components][:schemas][:AirBoundaryConstructionAbridged]
183
- air_mix_area = air_default[:properties][:air_mixing_per_area][:default]
184
- end
185
- flow_rate = os_surface.netArea * air_mix_area
186
- flow_sch_id = air_hash[:air_mixing_schedule]
187
- adj_zone_id = face[:boundary_condition][:boundary_condition_objects][-1]
188
- $air_mxing_array << [os_thermal_zone, flow_rate, flow_sch_id, adj_zone_id]
189
- end
190
- end
191
- end
192
-
193
- # assign any room-level outdoor shades if they exist
194
- if @hash[:outdoor_shades]
195
- os_shd_group = OpenStudio::Model::ShadingSurfaceGroup.new(openstudio_model)
196
- os_shd_group.setSpace(os_space)
197
- os_shd_group.setShadingSurfaceType("Space")
198
- @hash[:outdoor_shades].each do |outdoor_shade|
199
- add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
200
- end
201
- end
202
-
203
- #check whether there are any load objects on the room overriding the programtype
204
- if @hash[:properties][:energy][:people]
205
- people = openstudio_model.getPeopleByName(@hash[:properties][:energy][:people][:identifier])
206
- unless people.empty?
207
- people_object = people.get
208
- people_object.setSpace(os_space)
209
- else
210
- people_space = PeopleAbridged.new(@hash[:properties][:energy][:people])
211
- os_people_space = people_space.to_openstudio(openstudio_model)
212
- os_people_space.setSpace(os_space)
213
- end
214
- end
215
-
216
- # assign lighting if it exists
217
- if @hash[:properties][:energy][:lighting]
218
- lighting = openstudio_model.getLightsByName(@hash[:properties][:energy][:lighting][:identifier])
219
- unless lighting.empty?
220
- lighting_object = lighting.get
221
- lighting_object.setSpace(os_space)
222
- else
223
- lighting_space = LightingAbridged.new(@hash[:properties][:energy][:lighting])
224
- os_lighting_space = lighting_space.to_openstudio(openstudio_model)
225
- os_lighting_space.setSpace(os_space)
226
- end
227
- end
228
-
229
- # assign electric equipment if it exists
230
- if @hash[:properties][:energy][:electric_equipment]
231
- electric_equipment = openstudio_model.getElectricEquipmentByName(
232
- @hash[:properties][:energy][:electric_equipment][:identifier])
233
- unless electric_equipment.empty?
234
- electric_equipment_object = electric_equipment.get
235
- electric_equipment_object.setSpace(os_space)
236
- else
237
- electric_equipment_space = ElectricEquipmentAbridged.new(@hash[:properties][:energy][:electric_equipment])
238
- os_electric_equipment_space = electric_equipment_space.to_openstudio(openstudio_model)
239
- os_electric_equipment_space.setSpace(os_space)
240
- end
241
- end
242
-
243
- # assign gas equipment if it exists
244
- if @hash[:properties][:energy][:gas_equipment]
245
- gas_equipment = openstudio_model.getGasEquipmentByName(
246
- @hash[:properties][:energy][:gas_equipment][:identifier])
247
- unless gas_equipment.empty?
248
- gas_equipment_object = gas_equipment.get
249
- gas_equipment_object.setSpace(os_space)
250
- else
251
- gas_equipment_space = GasEquipmentAbridged.new(@hash[:properties][:energy][:gas_equipment])
252
- os_gas_equipment_space = gas_equipment_space.to_openstudio(openstudio_model)
253
- os_gas_equipment_space.setSpace(os_space)
254
- end
255
- end
256
-
257
- # assign infiltration if it exists
258
- if @hash[:properties][:energy][:infiltration]
259
- infiltration = openstudio_model.getSpaceInfiltrationDesignFlowRateByName(
260
- @hash[:properties][:energy][:infiltration][:identifier])
261
- unless infiltration.empty?
262
- infiltration_object = infiltration.get
263
- infiltration_object.setSpace(os_space)
264
- else
265
- infiltration_space = InfiltrationAbridged.new(@hash[:properties][:energy][:infiltration])
266
- os_infiltration_space = infiltration_space.to_openstudio(openstudio_model)
267
- os_infiltration_space.setSpace(os_space)
268
- end
269
- end
270
-
271
- # assign ventilation if it exists
272
- if @hash[:properties][:energy][:ventilation]
273
- ventilation = openstudio_model.getDesignSpecificationOutdoorAirByName(
274
- @hash[:properties][:energy][:ventilation][:identifier])
275
- unless ventilation.empty?
276
- ventilation_object = ventilation.get
277
- ventilation_object.setSpace(os_space)
278
- else
279
- ventilation_space = VentilationAbridged.new(@hash[:properties][:energy][:ventilation])
280
- os_ventilation_space = ventilation_space.to_openstudio(openstudio_model)
281
- os_space.setDesignSpecificationOutdoorAir(os_ventilation_space)
282
- end
283
- end
284
-
285
- # assign setpoint if it exists
286
- if @hash[:properties][:energy][:setpoint]
287
- #thermostat object is created because heating and cooling schedule are required
288
- #fields.
289
- setpoint_thermostat_space = SetpointThermostat.new(@hash[:properties][:energy][:setpoint])
290
- os_setpoint_thermostat_space = setpoint_thermostat_space.to_openstudio(openstudio_model)
291
- #set thermostat to thermal zone
292
- os_thermal_zone.setThermostatSetpointDualSetpoint(os_setpoint_thermostat_space)
293
- #humidistat object is created if humidifying or dehumidifying schedule is
294
- #specified.
295
- if @hash[:properties][:energy][:setpoint][:humidifying_schedule] or @hash[:properties][:energy][:setpoint][:dehumidifying_schedule]
296
- setpoint_humidistat_space = SetpointHumidistat.new(@hash[:properties][:energy][:setpoint])
297
- os_setpoint_humidistat_space = setpoint_humidistat_space.to_openstudio(openstudio_model)
298
- os_thermal_zone.setZoneControlHumidistat(os_setpoint_humidistat_space)
299
- end
300
- end
301
-
302
- os_space
303
- end
304
-
305
- # method to make a space-assigned Shade group for shades assigned to parent objects
306
- def make_shade_group(openstudio_model, os_surface, os_space)
307
- os_shd_group = OpenStudio::Model::ShadingSurfaceGroup.new(openstudio_model)
308
- os_shd_group.setShadedSurface(os_surface)
309
- os_shd_group.setSpace(os_space)
310
- os_shd_group.setShadingSurfaceType("Space")
311
-
312
- os_shd_group
313
- end
314
-
315
- # method to create a Shade and add it to a shade group
316
- def add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
317
- hb_outdoor_shade = Shade.new(outdoor_shade)
318
- os_outdoor_shade = hb_outdoor_shade.to_openstudio(openstudio_model)
319
- os_outdoor_shade.setShadingSurfaceGroup(os_shd_group)
320
- end
321
-
322
- # method to check for the closest-assigned interior ceiling or floor construction
323
- def closest_interior_construction(openstudio_model, os_space, surface_type)
324
- # first check the space-assigned construction set
325
- constr_set_space = os_space.defaultConstructionSet
326
- unless constr_set_space.empty?
327
- constr_set_space_object = constr_set_space.get
328
- default_interior_srf_set = constr_set_space_object.defaultInteriorSurfaceConstructions
329
- unless default_interior_srf_set.empty?
330
- default_interior_srf_set = default_interior_srf_set.get
331
- if surface_type == 'RoofCeiling'
332
- interior_construction = default_interior_srf_set.roofCeilingConstruction
333
- else
334
- interior_construction = default_interior_srf_set.floorConstruction
335
- end
336
- unless interior_construction.empty?
337
- return interior_construction.get
338
- end
339
- end
340
- end
341
- # if no construction was found, check the building-assigned construction set
342
- building = openstudio_model.building
343
- unless building.empty?
344
- building = building.get
345
- construction_set_bldg = building.defaultConstructionSet
346
- unless construction_set_bldg.empty?
347
- construction_set_bldg_object = construction_set_bldg.get
348
- default_interior_srf_set = construction_set_bldg_object.defaultInteriorSurfaceConstructions
349
- unless default_interior_srf_set.empty?
350
- default_interior_srf_set = default_interior_srf_set.get
351
- if surface_type == 'RoofCeiling'
352
- interior_construction = default_interior_srf_set.roofCeilingConstruction
353
- else
354
- interior_construction = default_interior_srf_set.floorConstruction
355
- end
356
- unless interior_construction.empty?
357
- return interior_construction.get
358
- end
359
- end
360
- end
361
- end
362
- nil # no construction was found
363
- end
364
-
365
- # method to check for the closest-assigned air boundary construction
366
- def closest_air_construction(openstudio_model, os_space)
367
- # first check the space-assigned construction set
368
- constr_set_ref = os_space.defaultConstructionSet
369
- unless constr_set_ref.empty?
370
- constr_set_space = constr_set_ref.get
371
- air_constr_ref = constr_set_space.interiorPartitionConstruction
372
- unless air_constr_ref.empty?
373
- return air_constr_ref.get
374
- end
375
- end
376
- # if no construction was found, check the building-assigned construction set
377
- building_ref = openstudio_model.building
378
- unless building_ref.empty?
379
- building = building_ref.get
380
- constr_set_bldg_ref = building.defaultConstructionSet
381
- unless constr_set_bldg_ref.empty?
382
- constr_set_bldg = constr_set_bldg_ref.get
383
- air_constr_ref = constr_set_bldg.interiorPartitionConstruction
384
- unless air_constr_ref.empty?
385
- return air_constr_ref.get
386
- end
387
- end
388
- end
389
- end
390
-
391
- end #Room
392
- end #FromHoneybee
1
+ # *******************************************************************************
2
+ # Honeybee OpenStudio Gem, Copyright (c) 2020, Alliance for Sustainable
3
+ # Energy, LLC, Ladybug Tools LLC and other contributors. All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # (1) Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # (2) Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # (3) Neither the name of the copyright holder nor the names of any contributors
16
+ # may be used to endorse or promote products derived from this software without
17
+ # specific prior written permission from the respective party.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
20
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
23
+ # UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
24
+ # THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26
+ # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
+ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ # *******************************************************************************
31
+
32
+ require 'from_honeybee/model_object'
33
+
34
+ require 'from_honeybee/geometry/face'
35
+ require 'from_honeybee/geometry/shade'
36
+
37
+ require 'from_honeybee/load/people'
38
+ require 'from_honeybee/load/lighting'
39
+ require 'from_honeybee/load/electric_equipment'
40
+ require 'from_honeybee/load/gas_equipment'
41
+ require 'from_honeybee/load/infiltration'
42
+ require 'from_honeybee/load/ventilation'
43
+ require 'from_honeybee/load/setpoint_thermostat'
44
+ require 'from_honeybee/load/setpoint_humidistat'
45
+
46
+ require 'openstudio'
47
+
48
+ module FromHoneybee
49
+ class Room < ModelObject
50
+ attr_reader :errors, :warnings
51
+
52
+ def initialize(hash = {})
53
+ super(hash)
54
+ raise "Incorrect model type '#{@type}'" unless @type == 'Room'
55
+ end
56
+
57
+ def defaults
58
+ @@schema[:components][:schemas][:RoomEnergyPropertiesAbridged][:properties]
59
+ end
60
+
61
+ def find_existing_openstudio_object(openstudio_model)
62
+ model_space = openstudio_model.getSpaceByName(@hash[:identifier])
63
+ return model_space.get unless model_space.empty?
64
+ nil
65
+ end
66
+
67
+ def to_openstudio(openstudio_model)
68
+ # create the space and thermal zone
69
+ os_space = OpenStudio::Model::Space.new(openstudio_model)
70
+ os_space.setName(@hash[:identifier])
71
+ os_thermal_zone = OpenStudio::Model::ThermalZone.new(openstudio_model)
72
+ os_thermal_zone.setName(@hash[:identifier])
73
+ os_space.setThermalZone(os_thermal_zone)
74
+
75
+ # assign the programtype
76
+ if @hash[:properties][:energy][:program_type]
77
+ space_type = openstudio_model.getSpaceTypeByName(@hash[:properties][:energy][:program_type])
78
+ unless space_type.empty?
79
+ space_type_object = space_type.get
80
+ os_space.setSpaceType(space_type_object)
81
+ end
82
+ end
83
+
84
+ # assign the constructionset
85
+ if @hash[:properties][:energy][:construction_set]
86
+ construction_set_identifier = @hash[:properties][:energy][:construction_set]
87
+ # gets default construction set assigned to room from openstudio_model
88
+ construction_set = openstudio_model.getDefaultConstructionSetByName(construction_set_identifier)
89
+ unless construction_set.empty?
90
+ default_construction_set = construction_set.get
91
+ os_space.setDefaultConstructionSet(default_construction_set)
92
+ end
93
+ end
94
+
95
+ # assign the multiplier
96
+ if @hash[:multiplier] and @hash[:multiplier] != 1
97
+ os_thermal_zone.setMultiplier(@hash[:multiplier])
98
+ end
99
+
100
+ # assign the story
101
+ if @hash[:story] # the users has specified the name of the story
102
+ story = openstudio_model.getBuildingStoryByName(@hash[:story])
103
+ if story.empty? # first time that this story has been referenced
104
+ story = OpenStudio::Model::BuildingStory.new(openstudio_model)
105
+ story.setName(@hash[:story])
106
+ else
107
+ story = story.get
108
+ end
109
+ else # give the room a dummy story so that it works with David's measures
110
+ story = openstudio_model.getBuildingStoryByName('UndefiniedStory')
111
+ if story.empty? # first time that this story has been referenced
112
+ story = OpenStudio::Model::BuildingStory.new(openstudio_model)
113
+ story.setName('UndefiniedStory')
114
+ else
115
+ story = story.get
116
+ end
117
+ end
118
+ os_space.setBuildingStory(story)
119
+
120
+ # assign all of the faces to the room
121
+ @hash[:faces].each do |face|
122
+ ladybug_face = Face.new(face)
123
+ os_surface = ladybug_face.to_openstudio(openstudio_model)
124
+ os_surface.setSpace(os_space)
125
+
126
+ # assign face-level shades if they exist
127
+ if face[:outdoor_shades]
128
+ os_shd_group = make_shade_group(openstudio_model, os_surface, os_space)
129
+ face[:outdoor_shades].each do |outdoor_shade|
130
+ add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
131
+ end
132
+ end
133
+
134
+ # assign aperture-level shades if they exist
135
+ if face[:apertures]
136
+ face[:apertures].each do |aperture|
137
+ if aperture[:outdoor_shades]
138
+ unless os_shd_group
139
+ os_shd_group = make_shade_group(openstudio_model, os_surface, os_space)
140
+ end
141
+ aperture[:outdoor_shades].each do |outdoor_shade|
142
+ add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ # assign door-level shades if they exist
149
+ if face[:doors]
150
+ face[:doors].each do |door|
151
+ if door[:outdoor_shades]
152
+ unless os_shd_group
153
+ os_shd_group = make_shade_group(openstudio_model, os_surface, os_space)
154
+ end
155
+ door[:outdoor_shades].each do |outdoor_shade|
156
+ add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ if !face[:properties][:energy][:construction]
163
+ if face[:boundary_condition][:type] == 'Adiabatic'
164
+ # assign default interior construciton for Adiabatic Faces
165
+ if face[:face_type] != 'Wall'
166
+ interior_construction = closest_interior_construction(openstudio_model, os_space, face[:face_type])
167
+ unless interior_construction.nil?
168
+ os_surface.setConstruction(interior_construction)
169
+ end
170
+ end
171
+ elsif face[:face_type] == 'AirBoundary'
172
+ # assign default air boundary construciton for AirBoundary face types
173
+ air_construction = closest_air_construction(openstudio_model, os_space)
174
+ unless air_construction.nil?
175
+ os_surface.setConstruction(air_construction)
176
+ end
177
+ # add air mixing properties to the global list that tracks them
178
+ air_hash = $air_boundary_hash[air_construction.name.to_s]
179
+ if air_hash[:air_mixing_per_area]
180
+ air_mix_area = air_hash[:air_mixing_per_area]
181
+ else
182
+ air_default = @@schema[:components][:schemas][:AirBoundaryConstructionAbridged]
183
+ air_mix_area = air_default[:properties][:air_mixing_per_area][:default]
184
+ end
185
+ flow_rate = os_surface.netArea * air_mix_area
186
+ flow_sch_id = air_hash[:air_mixing_schedule]
187
+ adj_zone_id = face[:boundary_condition][:boundary_condition_objects][-1]
188
+ $air_mxing_array << [os_thermal_zone, flow_rate, flow_sch_id, adj_zone_id]
189
+ end
190
+ end
191
+ end
192
+
193
+ # assign any room-level outdoor shades if they exist
194
+ if @hash[:outdoor_shades]
195
+ os_shd_group = OpenStudio::Model::ShadingSurfaceGroup.new(openstudio_model)
196
+ os_shd_group.setSpace(os_space)
197
+ os_shd_group.setShadingSurfaceType("Space")
198
+ @hash[:outdoor_shades].each do |outdoor_shade|
199
+ add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
200
+ end
201
+ end
202
+
203
+ #check whether there are any load objects on the room overriding the programtype
204
+ if @hash[:properties][:energy][:people]
205
+ people = openstudio_model.getPeopleByName(@hash[:properties][:energy][:people][:identifier])
206
+ unless people.empty?
207
+ people_object = people.get
208
+ people_object.setSpace(os_space)
209
+ else
210
+ people_space = PeopleAbridged.new(@hash[:properties][:energy][:people])
211
+ os_people_space = people_space.to_openstudio(openstudio_model)
212
+ os_people_space.setSpace(os_space)
213
+ end
214
+ end
215
+
216
+ # assign lighting if it exists
217
+ if @hash[:properties][:energy][:lighting]
218
+ lighting = openstudio_model.getLightsByName(@hash[:properties][:energy][:lighting][:identifier])
219
+ unless lighting.empty?
220
+ lighting_object = lighting.get
221
+ lighting_object.setSpace(os_space)
222
+ else
223
+ lighting_space = LightingAbridged.new(@hash[:properties][:energy][:lighting])
224
+ os_lighting_space = lighting_space.to_openstudio(openstudio_model)
225
+ os_lighting_space.setSpace(os_space)
226
+ end
227
+ end
228
+
229
+ # assign electric equipment if it exists
230
+ if @hash[:properties][:energy][:electric_equipment]
231
+ electric_equipment = openstudio_model.getElectricEquipmentByName(
232
+ @hash[:properties][:energy][:electric_equipment][:identifier])
233
+ unless electric_equipment.empty?
234
+ electric_equipment_object = electric_equipment.get
235
+ electric_equipment_object.setSpace(os_space)
236
+ else
237
+ electric_equipment_space = ElectricEquipmentAbridged.new(@hash[:properties][:energy][:electric_equipment])
238
+ os_electric_equipment_space = electric_equipment_space.to_openstudio(openstudio_model)
239
+ os_electric_equipment_space.setSpace(os_space)
240
+ end
241
+ end
242
+
243
+ # assign gas equipment if it exists
244
+ if @hash[:properties][:energy][:gas_equipment]
245
+ gas_equipment = openstudio_model.getGasEquipmentByName(
246
+ @hash[:properties][:energy][:gas_equipment][:identifier])
247
+ unless gas_equipment.empty?
248
+ gas_equipment_object = gas_equipment.get
249
+ gas_equipment_object.setSpace(os_space)
250
+ else
251
+ gas_equipment_space = GasEquipmentAbridged.new(@hash[:properties][:energy][:gas_equipment])
252
+ os_gas_equipment_space = gas_equipment_space.to_openstudio(openstudio_model)
253
+ os_gas_equipment_space.setSpace(os_space)
254
+ end
255
+ end
256
+
257
+ # assign infiltration if it exists
258
+ if @hash[:properties][:energy][:infiltration]
259
+ infiltration = openstudio_model.getSpaceInfiltrationDesignFlowRateByName(
260
+ @hash[:properties][:energy][:infiltration][:identifier])
261
+ unless infiltration.empty?
262
+ infiltration_object = infiltration.get
263
+ infiltration_object.setSpace(os_space)
264
+ else
265
+ infiltration_space = InfiltrationAbridged.new(@hash[:properties][:energy][:infiltration])
266
+ os_infiltration_space = infiltration_space.to_openstudio(openstudio_model)
267
+ os_infiltration_space.setSpace(os_space)
268
+ end
269
+ end
270
+
271
+ # assign ventilation if it exists
272
+ if @hash[:properties][:energy][:ventilation]
273
+ ventilation = openstudio_model.getDesignSpecificationOutdoorAirByName(
274
+ @hash[:properties][:energy][:ventilation][:identifier])
275
+ unless ventilation.empty?
276
+ ventilation_object = ventilation.get
277
+ ventilation_object.setSpace(os_space)
278
+ else
279
+ ventilation_space = VentilationAbridged.new(@hash[:properties][:energy][:ventilation])
280
+ os_ventilation_space = ventilation_space.to_openstudio(openstudio_model)
281
+ os_space.setDesignSpecificationOutdoorAir(os_ventilation_space)
282
+ end
283
+ end
284
+
285
+ # assign setpoint if it exists
286
+ if @hash[:properties][:energy][:setpoint]
287
+ #thermostat object is created because heating and cooling schedule are required
288
+ #fields.
289
+ setpoint_thermostat_space = SetpointThermostat.new(@hash[:properties][:energy][:setpoint])
290
+ os_setpoint_thermostat_space = setpoint_thermostat_space.to_openstudio(openstudio_model)
291
+ #set thermostat to thermal zone
292
+ os_thermal_zone.setThermostatSetpointDualSetpoint(os_setpoint_thermostat_space)
293
+ #humidistat object is created if humidifying or dehumidifying schedule is
294
+ #specified.
295
+ if @hash[:properties][:energy][:setpoint][:humidifying_schedule] or @hash[:properties][:energy][:setpoint][:dehumidifying_schedule]
296
+ setpoint_humidistat_space = SetpointHumidistat.new(@hash[:properties][:energy][:setpoint])
297
+ os_setpoint_humidistat_space = setpoint_humidistat_space.to_openstudio(openstudio_model)
298
+ os_thermal_zone.setZoneControlHumidistat(os_setpoint_humidistat_space)
299
+ end
300
+ end
301
+
302
+ os_space
303
+ end
304
+
305
+ # method to make a space-assigned Shade group for shades assigned to parent objects
306
+ def make_shade_group(openstudio_model, os_surface, os_space)
307
+ os_shd_group = OpenStudio::Model::ShadingSurfaceGroup.new(openstudio_model)
308
+ os_shd_group.setShadedSurface(os_surface)
309
+ os_shd_group.setSpace(os_space)
310
+ os_shd_group.setShadingSurfaceType("Space")
311
+
312
+ os_shd_group
313
+ end
314
+
315
+ # method to create a Shade and add it to a shade group
316
+ def add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
317
+ hb_outdoor_shade = Shade.new(outdoor_shade)
318
+ os_outdoor_shade = hb_outdoor_shade.to_openstudio(openstudio_model)
319
+ os_outdoor_shade.setShadingSurfaceGroup(os_shd_group)
320
+ end
321
+
322
+ # method to check for the closest-assigned interior ceiling or floor construction
323
+ def closest_interior_construction(openstudio_model, os_space, surface_type)
324
+ # first check the space-assigned construction set
325
+ constr_set_space = os_space.defaultConstructionSet
326
+ unless constr_set_space.empty?
327
+ constr_set_space_object = constr_set_space.get
328
+ default_interior_srf_set = constr_set_space_object.defaultInteriorSurfaceConstructions
329
+ unless default_interior_srf_set.empty?
330
+ default_interior_srf_set = default_interior_srf_set.get
331
+ if surface_type == 'RoofCeiling'
332
+ interior_construction = default_interior_srf_set.roofCeilingConstruction
333
+ else
334
+ interior_construction = default_interior_srf_set.floorConstruction
335
+ end
336
+ unless interior_construction.empty?
337
+ return interior_construction.get
338
+ end
339
+ end
340
+ end
341
+ # if no construction was found, check the building-assigned construction set
342
+ building = openstudio_model.building
343
+ unless building.empty?
344
+ building = building.get
345
+ construction_set_bldg = building.defaultConstructionSet
346
+ unless construction_set_bldg.empty?
347
+ construction_set_bldg_object = construction_set_bldg.get
348
+ default_interior_srf_set = construction_set_bldg_object.defaultInteriorSurfaceConstructions
349
+ unless default_interior_srf_set.empty?
350
+ default_interior_srf_set = default_interior_srf_set.get
351
+ if surface_type == 'RoofCeiling'
352
+ interior_construction = default_interior_srf_set.roofCeilingConstruction
353
+ else
354
+ interior_construction = default_interior_srf_set.floorConstruction
355
+ end
356
+ unless interior_construction.empty?
357
+ return interior_construction.get
358
+ end
359
+ end
360
+ end
361
+ end
362
+ nil # no construction was found
363
+ end
364
+
365
+ # method to check for the closest-assigned air boundary construction
366
+ def closest_air_construction(openstudio_model, os_space)
367
+ # first check the space-assigned construction set
368
+ constr_set_ref = os_space.defaultConstructionSet
369
+ unless constr_set_ref.empty?
370
+ constr_set_space = constr_set_ref.get
371
+ air_constr_ref = constr_set_space.interiorPartitionConstruction
372
+ unless air_constr_ref.empty?
373
+ return air_constr_ref.get
374
+ end
375
+ end
376
+ # if no construction was found, check the building-assigned construction set
377
+ building_ref = openstudio_model.building
378
+ unless building_ref.empty?
379
+ building = building_ref.get
380
+ constr_set_bldg_ref = building.defaultConstructionSet
381
+ unless constr_set_bldg_ref.empty?
382
+ constr_set_bldg = constr_set_bldg_ref.get
383
+ air_constr_ref = constr_set_bldg.interiorPartitionConstruction
384
+ unless air_constr_ref.empty?
385
+ return air_constr_ref.get
386
+ end
387
+ end
388
+ end
389
+ end
390
+
391
+ end #Room
392
+ end #FromHoneybee