Class: Honeybee::Room

Inherits:
ModelObject show all
Defined in:
lib/honeybee/geometry/room.rb,
lib/to_openstudio/geometry/room.rb,
lib/from_openstudio/geometry/room.rb

Instance Attribute Summary collapse

Attributes inherited from ModelObject

#errors, #openstudio_object, #warnings

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ModelObject

#allowable_types, clean_identifier, clean_name, #initialize, #method_missing, read_from_disk, truncate

Constructor Details

This class inherits a constructor from Honeybee::ModelObject

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Honeybee::ModelObject

Instance Attribute Details

#unique_space_typeObject (readonly)

Returns the value of attribute unique_space_type.



39
40
41
# File 'lib/to_openstudio/geometry/room.rb', line 39

def unique_space_type
  @unique_space_type
end

Class Method Details

.energy_properties_from_space(space) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/from_openstudio/geometry/room.rb', line 76

def self.energy_properties_from_space(space)
  hash = {}
  hash[:type] = 'RoomEnergyPropertiesAbridged'
  # set room energy properties
  unless space.defaultConstructionSet.empty?
    const_set = space.defaultConstructionSet.get
    c_set_name = const_set.nameString
    unless c_set_name == 'Default Generic Construction Set'
      hash[:construction_set] = const_set.nameString
    end
  end
  unless space.spaceType.empty?
    space_type = space.spaceType.get
    hash[:program_type] = space_type.nameString
  end
  # TODO: These are only loads assigned to the space directly.
  # Duplicates and other definitions created in programtype should be summed 
  unless space.people.empty?
    space.people.each do |people|
      people_def = people.peopleDefinition
      # Only translate if people per floor area is specified
      # Check if schedule exists and is of the correct type
      if !people_def.peopleperSpaceFloorArea.empty? && !people.numberofPeopleSchedule.empty?
        sch = people.numberofPeopleSchedule.get
        if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
          hash[:people] = Honeybee::PeopleAbridged.from_load(people)
          break
        end
      end
    end
  end

  unless space.lights.empty?
    space.lights.each do |light|
      light_def = light.lightsDefinition
      # Only translate if watts per floor area is specified
      # Check if schedule exists and is of the correct type
      if !light_def.wattsperSpaceFloorArea.empty? && !light.schedule.empty?
        sch = light.schedule.get
        if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
          hash[:lighting] = Honeybee::LightingAbridged.from_load(light)
          break
        end
      end
    end
  end

  unless space.electricEquipment.empty?
    space.electricEquipment.each do |electric_eq|
      electric_eq_def = electric_eq.electricEquipmentDefinition
      # Only translate if watts per floor area is specified
      # Check if schedule exists and is of the correct type
      if !electric_eq_def.wattsperSpaceFloorArea.empty? && !electric_eq.schedule.empty?
        sch = electric_eq.schedule.get
        if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
          hash[:electric_equipment] = Honeybee::ElectricEquipmentAbridged.from_load(electric_eq)
          break
        end
      end
    end
  end

  unless space.gasEquipment.empty?
    space.gasEquipment.each do |gas_eq|
      gas_eq_def = gas_eq.gasEquipmentDefinition
      # Only translate if watts per floor area is specified
      # Check if schedule exists and is of the correct type
      if !gas_eq_def.wattsperSpaceFloorArea.empty? && !gas_eq.schedule.empty?
        sch = gas_eq.schedule.get
        if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
          hash[:gas_equipment] = Honeybee::GasEquipmentAbridged.from_load(gas_eq)
          break
        end
      end
    end
  end

  unless space.spaceInfiltrationDesignFlowRates.empty?
    space.spaceInfiltrationDesignFlowRates.each do |infiltration|
      # Only translate if flow per exterior area is specified
      # Check if schedule exists and is of the correct type
      if !infiltration.flowperExteriorSurfaceArea.empty? && !infiltration.schedule.empty?
        sch = infiltration.schedule.get
        if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
          hash[:infiltration] = Honeybee::InfiltrationAbridged.from_load(infiltration)
          break
        end
      end
    end
  end

  unless space.waterUseEquipment.empty?
    space.waterUseEquipment.each do |shw_equipment|
      # Check if schedule exists and is of the correct type
      unless shw_equipment.flowRateFractionSchedule.empty?
        sch = shw_equipment.flowRateFractionSchedule.get
        if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
          floor_area = space.floorArea  # Get floor area 
          hash[:service_hot_water] = Honeybee::ServiceHotWaterAbridged.from_load(shw_equipment, floor_area)
        end
      end
    end
  end

  unless space.designSpecificationOutdoorAir.empty?
    hash[:ventilation] = Honeybee::VentilationAbridged.from_load(space.designSpecificationOutdoorAir.get)
  end

  unless space.otherEquipment.empty?
    hash[:process_loads] = []
    space.otherEquipment.each do |other_eq|
      other_eq_def = other_eq.otherEquipmentDefinition
      if !other_eq_def.designLevel.empty? && !other_eq.schedule.empty?
        sch = other_eq.schedule.get
        if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
          hash[:process_loads] << Honeybee::ProcessAbridged.from_load(other_eq)
        end
      end
    end
  end

  unless space.daylightingControls.empty?
    hash[:daylighting_control] = Honeybee::DaylightingControl.from_load(space.daylightingControls[0])
  end
 
  thermal_zone = space.thermalZone
  unless thermal_zone.empty?
    thermal_zone = space.thermalZone.get
    # Create ideal_air_system if present
    unless thermal_zone.equipment.nil?
      thermal_zone.equipment.each do |equipment|
        if equipment.to_ZoneHVACIdealLoadsAirSystem.is_initialized
          ideal_air_hash = Honeybee::IdealAirSystemAbridged.from_hvac(
            equipment.to_ZoneHVACIdealLoadsAirSystem.get)
          $hvacs << ideal_air_hash
          hash[:hvac] = ideal_air_hash[:identifier]
        end
      end
    end
    unless thermal_zone.thermostatSetpointDualSetpoint.empty?
      hash[:setpoint] = {}
      hash[:setpoint][:type] = 'SetpointAbridged'
      thermostat = thermal_zone.thermostatSetpointDualSetpoint.get
      hash[:setpoint][:identifier] = thermostat.nameString
      unless thermostat.displayName.empty?
        hash[:display_name] = (thermostat.displayName.get).force_encoding("UTF-8")
      end
      sch = thermostat.heatingSetpointTemperatureSchedule
      if sch.empty? or !sch.get.to_ScheduleRuleset.is_initialized
        # if heating setpoint schedule is not specified create a new setpoint schedule and assign to HB thermostat object.
        # first check if schedule is already created
        if $heating_setpoint_schedule.nil?
          openstudio_model = OpenStudio::Model::Model.new
          openstudio_schedule = OpenStudio::Model::ScheduleRuleset.new(openstudio_model)
          openstudio_schedule.setName('Heating Schedule Default')
          openstudio_sch_type_lim = OpenStudio::Model::ScheduleTypeLimits.new(openstudio_model)
          openstudio_sch_type_lim.setName('Temperature')
          openstudio_sch_type_lim.setNumericType('Temperature')
          openstudio_schedule.defaultDaySchedule.setName('Heating Day Default')
          openstudio_schedule.defaultDaySchedule.addValue(OpenStudio::Time.new(0,24,0,0), -100)
          openstudio_schedule.defaultDaySchedule.setScheduleTypeLimits(openstudio_sch_type_lim)
          $heating_setpoint_schedule = Honeybee::ScheduleRulesetAbridged.from_schedule_ruleset(openstudio_schedule)
        end
        hash[:setpoint][:heating_schedule] = $heating_setpoint_schedule[:identifier]
      else
        heating_schedule = sch.get
        hash[:setpoint][:heating_schedule] = heating_schedule.nameString
      end
      sch = thermostat.coolingSetpointTemperatureSchedule
      if sch.empty? or !sch.get.to_ScheduleRuleset.is_initialized
        # if cooling setpoint schedule is not specified create a new setpoint schedule and assign to HB thermostat object
        # first check if schedule is already created
        if $cooling_setpoint_schedule.nil?
          openstudio_model = OpenStudio::Model::Model.new
          openstudio_schedule = OpenStudio::Model::ScheduleRuleset.new(openstudio_model)
          openstudio_schedule.setName('Cooling Schedule Default')
          openstudio_sch_type_lim = OpenStudio::Model::ScheduleTypeLimits.new(openstudio_model)
          openstudio_sch_type_lim.setName('Temperature')
          openstudio_sch_type_lim.setNumericType('Temperature')
          openstudio_schedule.defaultDaySchedule.setName('Cooling Day Default')
          openstudio_schedule.defaultDaySchedule.addValue(OpenStudio::Time.new(0,24,0,0), 100)
          openstudio_schedule.defaultDaySchedule.setScheduleTypeLimits(openstudio_sch_type_lim)
          $cooling_setpoint_schedule = Honeybee::ScheduleRulesetAbridged.from_schedule_ruleset(openstudio_schedule)
        end
        hash[:setpoint][:cooling_schedule] = $cooling_setpoint_schedule[:identifier]
      else
        cooling_schedule = sch.get
        hash[:setpoint][:cooling_schedule] = cooling_schedule.nameString
      end
    end
    unless thermal_zone.zoneControlHumidistat.empty?
      humidistat = thermal_zone.zoneControlHumidistat.get
      unless humidistat.humidifyingRelativeHumiditySetpointSchedule.empty?
        sch = humidistat.humidifyingRelativeHumiditySetpointSchedule.get
        if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
          hash[:setpoint][:humidifying_schedule] = sch.nameString
        end
      end
      unless humidistat.dehumidifyingRelativeHumiditySetpointSchedule.empty?
        sch = humidistat.dehumidifyingRelativeHumiditySetpointSchedule.get
        if sch.to_ScheduleRuleset.is_initialized or sch.to_ScheduleFixedInterval.is_initialized
          hash[:setpoint][:dehumidifying_schedule] = sch.nameString
        end
      end
    end
  end 
  hash
end

.exclude_floor_area_from_space(space) ⇒ Object



321
322
323
324
325
326
327
# File 'lib/from_openstudio/geometry/room.rb', line 321

def self.exclude_floor_area_from_space(space)
  incl_floor = space.partofTotalFloorArea
  if incl_floor
    return false
  end
  true
end

.faces_from_space(space, site_transformation) ⇒ Object



285
286
287
288
289
290
291
# File 'lib/from_openstudio/geometry/room.rb', line 285

def self.faces_from_space(space, site_transformation)
  result = []
  space.surfaces.each do |surface|
    result << Face.from_surface(surface, site_transformation)
  end
  result
end

.from_space(space) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/from_openstudio/geometry/room.rb', line 37

def self.from_space(space)
  hash = {}
  hash[:type] = 'Room'
  hash[:identifier] = clean_identifier(space.nameString)
  unless space.displayName.empty?
    hash[:display_name] = (space.displayName.get).force_encoding("UTF-8")
  end
  hash[:user_data] = {space: space.handle.to_s}
  hash[:properties] = properties_from_space(space)

  site_transformation = space.siteTransformation
  hash[:faces] = faces_from_space(space, site_transformation)

  indoor_shades = indoor_shades_from_space(space)
  hash[:indoor_shades] = indoor_shades if !indoor_shades.empty?

  outdoor_shades = outdoor_shades_from_space(space)
  hash[:outdoor_shades] = outdoor_shades if !outdoor_shades.empty?

  multiplier = multiplier_from_space(space)
  hash[:multiplier] = multiplier if multiplier

  exclude_floor_area = exclude_floor_area_from_space(space)
  hash[:exclude_floor_area] = exclude_floor_area

  story = story_from_space(space)
  hash[:story] = story if story

  hash
end

.indoor_shades_from_space(space) ⇒ Object



293
294
295
# File 'lib/from_openstudio/geometry/room.rb', line 293

def self.indoor_shades_from_space(space)
  []
end

.multiplier_from_space(space) ⇒ Object



313
314
315
316
317
318
319
# File 'lib/from_openstudio/geometry/room.rb', line 313

def self.multiplier_from_space(space)
  multiplier = space.multiplier
  if multiplier != 1
    return multiplier
  end
  nil
end

.outdoor_shades_from_space(space) ⇒ Object



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/from_openstudio/geometry/room.rb', line 297

def self.outdoor_shades_from_space(space)
  result = []
  space.shadingSurfaceGroups.each do |shading_surface_group|
    # skip if attached to a surface or sub_surface
    if !shading_surface_group.shadedSurface.empty? || !shading_surface_group.shadedSubSurface.empty?
      next
    end

    site_transformation = shading_surface_group.siteTransformation
    shading_surface_group.shadingSurfaces.each do |shading_surface|
      result << Shade.from_shading_surface(shading_surface, site_transformation)
    end
  end
  result
end

.properties_from_space(space) ⇒ Object



68
69
70
71
72
73
74
# File 'lib/from_openstudio/geometry/room.rb', line 68

def self.properties_from_space(space)
  hash = {}
  hash[:type] = 'RoomPropertiesAbridged'
  hash[:energy] = self.energy_properties_from_space(space)

  hash
end

.story_from_space(space) ⇒ Object



329
330
331
332
333
334
335
# File 'lib/from_openstudio/geometry/room.rb', line 329

def self.story_from_space(space)
  story = space.buildingStory
  if !story.empty?
    return clean_identifier(story.get.nameString)
  end
  nil
end

Instance Method Details

#add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade) ⇒ Object

method to create a Shade and add it to a shade group



480
481
482
483
484
# File 'lib/to_openstudio/geometry/room.rb', line 480

def add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
  hb_outdoor_shade = Shade.new(outdoor_shade)
  os_outdoor_shade = hb_outdoor_shade.to_openstudio(openstudio_model)
  os_outdoor_shade.setShadingSurfaceGroup(os_shd_group)
end

#closest_air_construction(openstudio_model, os_space) ⇒ Object

method to check for the closest-assigned air boundary construction



530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
# File 'lib/to_openstudio/geometry/room.rb', line 530

def closest_air_construction(openstudio_model, os_space)
  # first check the space-assigned construction set
  constr_set_ref = os_space.defaultConstructionSet
  unless constr_set_ref.empty?
    constr_set_space = constr_set_ref.get
    air_constr_ref = constr_set_space.interiorPartitionConstruction
    unless air_constr_ref.empty?
      return air_constr_ref.get
    end
  end
  # if no construction was found, check the building-assigned construction set
  building_ref = openstudio_model.building
  unless building_ref.empty?
    building = building_ref.get
    constr_set_bldg_ref = building.defaultConstructionSet
    unless constr_set_bldg_ref.empty?
      constr_set_bldg = constr_set_bldg_ref.get
      air_constr_ref = constr_set_bldg.interiorPartitionConstruction
      unless air_constr_ref.empty?
        return air_constr_ref.get
      end
    end
  end
end

#closest_interior_construction(openstudio_model, os_space, surface_type) ⇒ Object

method to check for the closest-assigned interior ceiling or floor construction



487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/to_openstudio/geometry/room.rb', line 487

def closest_interior_construction(openstudio_model, os_space, surface_type)
  # first check the space-assigned construction set
  constr_set_space = os_space.defaultConstructionSet
  unless constr_set_space.empty?
    constr_set_space_object = constr_set_space.get
    default_interior_srf_set = constr_set_space_object.defaultInteriorSurfaceConstructions
    unless default_interior_srf_set.empty?
      default_interior_srf_set = default_interior_srf_set.get
      if surface_type == 'RoofCeiling'
        interior_construction = default_interior_srf_set.roofCeilingConstruction
      else
        interior_construction = default_interior_srf_set.floorConstruction
      end
      unless interior_construction.empty?
        return interior_construction.get
      end
    end
  end
  # if no construction was found, check the building-assigned construction set
  building = openstudio_model.building
  unless building.empty?
    building = building.get
    construction_set_bldg = building.defaultConstructionSet
    unless construction_set_bldg.empty?
      construction_set_bldg_object = construction_set_bldg.get
      default_interior_srf_set = construction_set_bldg_object.defaultInteriorSurfaceConstructions
      unless default_interior_srf_set.empty?
        default_interior_srf_set = default_interior_srf_set.get
        if surface_type == 'RoofCeiling'
          interior_construction = default_interior_srf_set.roofCeilingConstruction
        else
          interior_construction = default_interior_srf_set.floorConstruction
        end
        unless interior_construction.empty?
          return interior_construction.get
        end
      end
    end
  end
  nil  # no construction was found
end

#defaultsObject



51
52
53
# File 'lib/honeybee/geometry/room.rb', line 51

def defaults
  @@schema[:components][:schemas][:RoomEnergyPropertiesAbridged][:properties]
end

#find_existing_openstudio_object(openstudio_model) ⇒ Object



41
42
43
44
45
# File 'lib/to_openstudio/geometry/room.rb', line 41

def find_existing_openstudio_object(openstudio_model)
  model_space = openstudio_model.getSpaceByName(@hash[:identifier] + '_Space')
  return model_space.get unless model_space.empty?
  nil
end

#get_unique_space_type(openstudio_model, os_space) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/to_openstudio/geometry/room.rb', line 47

def get_unique_space_type(openstudio_model, os_space)
  # get a space type that is unique to the room
  if @unique_space_type.nil?
    space_type = os_space.spaceType
    unless space_type.empty?
      # copy the space type that is already assigned to the room
      space_type_object = space_type.get
      space_type_mod_obj = space_type_object.clone(openstudio_model)
      new_space_type = space_type_mod_obj.to_SpaceType.get
      # give the space type a new unique name and assign it to the room
      st_name = space_type_object.name
      unless space_type.empty?
        st_name = st_name.get
      else
        st_name = 'CustomSpaceType'
      end
    else
      # create a new space type as there is currently none assigned to the room
      new_space_type = OpenStudio::Model::SpaceType.new(openstudio_model)
      st_name = 'CustomSpaceType'
    end
    new_space_type.setName(st_name + '_' + @hash[:identifier])
    os_space.setSpaceType(new_space_type)
    @unique_space_type = new_space_type
  end
  @unique_space_type
end

#make_shade_group(openstudio_model, os_surface, os_space) ⇒ Object

method to make a space-assigned Shade group for shades assigned to parent objects



470
471
472
473
474
475
476
477
# File 'lib/to_openstudio/geometry/room.rb', line 470

def make_shade_group(openstudio_model, os_surface, os_space)
  os_shd_group = OpenStudio::Model::ShadingSurfaceGroup.new(openstudio_model)
  os_shd_group.setShadedSurface(os_surface)
  os_shd_group.setSpace(os_space)
  os_shd_group.setShadingSurfaceType("Space")

  os_shd_group
end

#to_openstudio(openstudio_model) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/to_openstudio/geometry/room.rb', line 75

def to_openstudio(openstudio_model)
  # create the space and thermal zone
  os_space = OpenStudio::Model::Space.new(openstudio_model)
  os_space.setName(@hash[:identifier] + '_Space')
  os_thermal_zone = OpenStudio::Model::ThermalZone.new(openstudio_model)
  os_thermal_zone.setName(@hash[:identifier])
  os_space.setThermalZone(os_thermal_zone)
  unless @hash[:display_name].nil?
    os_space.setDisplayName(@hash[:display_name])
    os_thermal_zone.setDisplayName(@hash[:display_name])
  end

  # assign the multiplier
  if @hash[:multiplier] and @hash[:multiplier] != 1
    os_thermal_zone.setMultiplier(@hash[:multiplier])
  end

  # assign the exclude_floor_area
  if @hash[:exclude_floor_area]
    os_space.setPartofTotalFloorArea(false)
  end

  # assign the geometry properties if they exist
  if @hash[:ceiling_height]
    os_thermal_zone.setCeilingHeight(@hash[:ceiling_height])
  end
  if @hash[:volume]
    os_thermal_zone.setVolume(@hash[:volume])
  end

  # assign the story
  if @hash[:story]  # the users has specified the name of the story
    story = openstudio_model.getBuildingStoryByName(@hash[:story])
    if story.empty?  # first time that this story has been referenced
      story = OpenStudio::Model::BuildingStory.new(openstudio_model)
      story.setName(@hash[:story])
    else
      story = story.get
    end
  else  # give the room a dummy story so that it works with David's measures
    story = openstudio_model.getBuildingStoryByName('UndefiniedStory')
    if story.empty?  # first time that this story has been referenced
      story = OpenStudio::Model::BuildingStory.new(openstudio_model)
      story.setName('UndefiniedStory')
    else
      story = story.get
    end
  end
  os_space.setBuildingStory(story)

  if @hash[:properties].key?(:energy)
    # assign the programtype
    if @hash[:properties][:energy][:program_type]
      space_type = openstudio_model.getSpaceTypeByName(@hash[:properties][:energy][:program_type])
      unless space_type.empty?
        space_type_object = space_type.get
        os_space.setSpaceType(space_type_object)
      end
    end
  
    # assign the constructionset
    if @hash[:properties][:energy][:construction_set]
      construction_set_identifier = @hash[:properties][:energy][:construction_set]
      # gets default construction set assigned to room from openstudio_model
      construction_set = openstudio_model.getDefaultConstructionSetByName(construction_set_identifier)
      unless construction_set.empty?
        default_construction_set = construction_set.get
        os_space.setDefaultConstructionSet(default_construction_set)
      end
    end
  end

  # keep track of all window ventilation objects
  window_vent = {}

  # assign all of the faces to the room
  @hash[:faces].each do |face|
    ladybug_face = Face.new(face)
    os_surface = ladybug_face.to_openstudio(openstudio_model)
    os_surface.setSpace(os_space)

    # assign face-level shades if they exist
    if face[:outdoor_shades]
      os_shd_group = make_shade_group(openstudio_model, os_surface, os_space)
      face[:outdoor_shades].each do |outdoor_shade|
        add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
      end
    end

    # assign aperture-level shades if they exist
    if face[:apertures]
      face[:apertures].each do |aperture|
        if aperture[:properties].key?(:energy)
          if aperture[:properties][:energy][:vent_opening]
            window_vent[aperture[:identifier]] = \
              [aperture[:properties][:energy][:vent_opening], aperture[:boundary_condition][:type]]
          end
        end
        if aperture[:outdoor_shades]
          unless os_shd_group
            os_shd_group = make_shade_group(openstudio_model, os_surface, os_space)
          end
          aperture[:outdoor_shades].each do |outdoor_shade|
            add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
          end
        end
      end
    end

    # assign door-level shades if they exist
    if face[:doors]
      face[:doors].each do |door|
        if door[:properties].key?(:energy)
          if door[:properties][:energy][:vent_opening]
            window_vent[door[:identifier]] = \
              [door[:properties][:energy][:vent_opening], door[:boundary_condition][:type]]
          end
        end
        if door[:outdoor_shades]
          unless os_shd_group
            os_shd_group = make_shade_group(openstudio_model, os_surface, os_space)
          end
          door[:outdoor_shades].each do |outdoor_shade|
            add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
          end
        end
      end
    end

    # assign interior constructions for adiabatic Faces
    if face[:properties].key?(:energy) && !face[:properties][:energy][:construction]
      if face[:boundary_condition][:type] == 'Adiabatic'
        # assign default interior construction for Adiabatic Faces
        if face[:face_type] != 'Wall'
          interior_construction = closest_interior_construction(openstudio_model, os_space, face[:face_type])
          unless interior_construction.nil?
            os_surface.setConstruction(interior_construction)
          end
        end
      elsif face[:boundary_condition][:type] == 'OtherSideTemperature'
        interior_construction = closest_interior_construction(openstudio_model, os_space, face[:face_type])
        unless interior_construction.nil?
          os_surface.setConstruction(interior_construction)
        end
      end
    end

    # overwrite the face type if E+ is going to flip the surface on us and make the geometry incorrect
    face_tilt = os_surface.tilt
    if face[:face_type] == 'RoofCeiling' && face_tilt > 1.57079632679
      if !face[:properties][:energy][:construction]
        orig_construction = os_surface.construction
        unless orig_construction.empty?
          orig_construction = orig_construction.get
          os_surface.setConstruction(orig_construction)
        end
      end
      os_surface.setSurfaceType('Wall')
    elsif face[:face_type] == 'Floor' && face_tilt < 1.57079632679
      if !face[:properties][:energy][:construction]
        orig_construction = os_surface.construction
        unless orig_construction.empty?
          orig_construction = orig_construction.get
          os_surface.setConstruction(orig_construction)
        end
      end
      os_surface.setSurfaceType('Wall')
    end

    # assign air boundaries
    if face[:face_type] == 'AirBoundary'
      # assign default air boundary construction for AirBoundary face types
      air_construction = closest_air_construction(openstudio_model, os_space)
      if face[:properties].key?(:energy) && !face[:properties][:energy][:construction]
        unless air_construction.nil?
          os_surface.setConstruction(air_construction)
        end
      end
      # add air mixing properties to the global list that tracks them
      if $use_simple_vent  # only use air mixing objects when simple ventilation is requested
        air_hash = $air_boundary_hash[air_construction.name.to_s]
        if air_hash && air_hash[:air_mixing_per_area]
          air_mix_area = air_hash[:air_mixing_per_area]
        else
          air_default = @@schema[:components][:schemas][:AirBoundaryConstructionAbridged]
          air_mix_area = air_default[:properties][:air_mixing_per_area][:default]
        end
        flow_rate = os_surface.netArea * air_mix_area
        if air_hash
          flow_sch_id = air_hash[:air_mixing_schedule]
        else
          flow_sch_id = 'Always On'
        end
        begin
          adj_zone_id = face[:boundary_condition][:boundary_condition_objects][-1]
        rescue Exception
          msg = 'ERROR: Air Boundary Faces must be adjacent to a neighboring Room and have a Surface boundary condition.'
          puts msg
        else
          $air_mxing_array << [os_thermal_zone, flow_rate, flow_sch_id, adj_zone_id]
        end
      end
    end
  
  end

  # assign any room-level outdoor shades if they exist
  if @hash[:outdoor_shades]
    os_shd_group = OpenStudio::Model::ShadingSurfaceGroup.new(openstudio_model)
    os_shd_group.setSpace(os_space)
    os_shd_group.setShadingSurfaceType("Space")
    @hash[:outdoor_shades].each do |outdoor_shade|
      add_shade_to_group(openstudio_model, os_shd_group, outdoor_shade)
    end
  end

  #check whether there are any load objects on the room overriding the programtype
  if @hash[:properties].key?(:energy)
    if @hash[:properties][:energy][:people]
      unique_program = get_unique_space_type(openstudio_model, os_space)
      unique_program_ppl = unique_program.people
      unless unique_program_ppl.empty?  # remove the previous load definition
        unique_program_ppl[0].remove()
      end
      custom_people = PeopleAbridged.new(@hash[:properties][:energy][:people])
      os_custom_people = custom_people.to_openstudio(openstudio_model)
      os_custom_people.setSpaceType(unique_program)  # assign the new load definition
    end

    # assign lighting if it exists
    if @hash[:properties][:energy][:lighting]
      unique_program = get_unique_space_type(openstudio_model, os_space)
      unique_program_lght = unique_program.lights
      unless unique_program_lght.empty?  # remove the previous load definition
        unique_program_lght[0].remove()
      end
      custom_lighting = LightingAbridged.new(@hash[:properties][:energy][:lighting])
      os_custom_lighting = custom_lighting.to_openstudio(openstudio_model)
      os_custom_lighting.setSpaceType(unique_program)  # assign the new load definition
    end

    # assign electric equipment if it exists
    if @hash[:properties][:energy][:electric_equipment]
      unique_program = get_unique_space_type(openstudio_model, os_space)
      unique_program_ele = unique_program.electricEquipment
      unless unique_program_ele.empty?  # remove the previous load definition
        unique_program_ele[0].remove()
      end
      custom_electric_equipment = ElectricEquipmentAbridged.new(@hash[:properties][:energy][:electric_equipment])
      os_custom_electric_equipment = custom_electric_equipment.to_openstudio(openstudio_model)
      os_custom_electric_equipment.setSpaceType(unique_program)  # assign the new load definition
    end

    # assign gas equipment if it exists
    if @hash[:properties][:energy][:gas_equipment]
      unique_program = get_unique_space_type(openstudio_model, os_space)
      unique_program_gas = unique_program.gasEquipment
      unless unique_program_gas.empty?  # remove the previous load definition
        unique_program_gas[0].remove()
      end
      custom_gas_equipment = GasEquipmentAbridged.new(@hash[:properties][:energy][:gas_equipment])
      os_custom_gas_equipment = custom_gas_equipment.to_openstudio(openstudio_model)
      os_custom_gas_equipment.setSpaceType(unique_program)  # assign the new load definition
    end

    # assign service hot water if it exists
    if @hash[:properties][:energy][:service_hot_water]
      if @hash[:properties][:energy][:service_hot_water][:flow_per_area].to_f != 0
        shw_space = ServiceHotWaterAbridged.new(@hash[:properties][:energy][:service_hot_water])
        os_shw_space = shw_space.to_openstudio(
          openstudio_model, os_space, @hash[:properties][:energy][:shw])
        $shw_for_plant = shw_space
      end
    end

    # assign infiltration if it exists
    if @hash[:properties][:energy][:infiltration] && $use_simple_vent  # only use infiltration with simple ventilation
      unique_program = get_unique_space_type(openstudio_model, os_space)
      unique_program_inf = unique_program.spaceInfiltrationDesignFlowRates
      unless unique_program_inf.empty?  # remove the previous load definition
        unique_program_inf[0].remove()
      end
      custom_infiltration = InfiltrationAbridged.new(@hash[:properties][:energy][:infiltration])
      os_custom_infiltration = custom_infiltration.to_openstudio(openstudio_model)
      os_custom_infiltration.setSpaceType(unique_program)  # assign the new load definition
    end

    # assign ventilation if it exists
    if @hash[:properties][:energy][:ventilation]
      unique_program = get_unique_space_type(openstudio_model, os_space)
      unique_program.resetDesignSpecificationOutdoorAir()
      custom_ventilation = VentilationAbridged.new(@hash[:properties][:energy][:ventilation])
      os_custom_ventilation = custom_ventilation.to_openstudio(openstudio_model)
      unique_program.setDesignSpecificationOutdoorAir(os_custom_ventilation)
    end

    # assign setpoint if it exists
    if @hash[:properties][:energy][:setpoint]
      # thermostat object is created because heating and cooling schedule are required
      setpoint_thermostat_space = SetpointThermostat.new(@hash[:properties][:energy][:setpoint])
      os_setpoint_thermostat_space = setpoint_thermostat_space.to_openstudio(openstudio_model)
      #set thermostat to thermal zone
      os_thermal_zone.setThermostatSetpointDualSetpoint(os_setpoint_thermostat_space)
      # humidistat object is created if humidifying or dehumidifying schedule is specified
      if @hash[:properties][:energy][:setpoint][:humidifying_schedule] or @hash[:properties][:energy][:setpoint][:dehumidifying_schedule]
        setpoint_humidistat_space = SetpointHumidistat.new(@hash[:properties][:energy][:setpoint])
        os_setpoint_humidistat_space = setpoint_humidistat_space.to_openstudio(openstudio_model)
        os_thermal_zone.setZoneControlHumidistat(os_setpoint_humidistat_space)
      end
    end

    # assign daylight control if it exists
    if @hash[:properties][:energy][:daylighting_control]
      dl_control = DaylightingControl.new(@hash[:properties][:energy][:daylighting_control])
      os_dl_control = dl_control.to_openstudio(openstudio_model, os_thermal_zone, os_space)
    end

    # assign window ventilation objects if they exist
    if $use_simple_vent && !window_vent.empty?  # write simple WindAndStack ventilation
      window_vent.each do |sub_f_id, open_prop|
        opening = open_prop[0]
        bc = open_prop[1]
        if bc == 'Outdoors'
          opt_sub_f = openstudio_model.getSubSurfaceByName(sub_f_id)
          unless opt_sub_f.empty?
            sub_f = opt_sub_f.get
            vent_open = VentilationOpening.new(opening)
            os_vent_open = vent_open.to_openstudio(
              openstudio_model, sub_f, @hash[:properties][:energy][:window_vent_control])
            os_vent_open.addToThermalZone(os_thermal_zone)
          end
        end
      end
    elsif !$use_simple_vent  # we're using the AFN!
      # write an AirflowNetworkZone object in for the Room
      os_afn_room_node = os_thermal_zone.getAirflowNetworkZone
      os_afn_room_node.setVentilationControlMode('NoVent')
      # write the opening objects for each Aperture / Door
      operable_subfs = []  # collect the sub-face objects for the EMS
      opening_factors = []  # collect the maximum opening factors for the EMS
      window_vent.each do |sub_f_id, open_prop|
        opening = open_prop[0]
        opt_sub_f = openstudio_model.getSubSurfaceByName(sub_f_id)
        unless opt_sub_f.empty?
          sub_f = opt_sub_f.get
          if sub_f.adjacentSubSurface.empty?  # not an interior window that's already in the AFN
            vent_open = VentilationOpening.new(opening)
            open_fac = vent_open.to_openstudio_afn(openstudio_model, sub_f)
            unless open_fac.nil?  # nil is used for horizontal exterior skylights
              operable_subfs << sub_f
              opening_factors << open_fac
            end
          end
        end
      end
      # add the control startegy of the ventilation openings using the EMS
      if @hash[:properties][:energy][:window_vent_control]
        vent_control = VentilationControlAbridged.new(@hash[:properties][:energy][:window_vent_control])
        vent_control.to_openstudio(
          openstudio_model, os_thermal_zone, operable_subfs, opening_factors)
      end
    end

    # assign any ventilation fan loads if specified
    if @hash[:properties][:energy][:fans]
      @hash[:properties][:energy][:fans].each do |fan|
        hb_fan = VentilationFanAbridged.new(fan)
        os_fan = hb_fan.to_openstudio(openstudio_model, os_thermal_zone)
        os_fan.addToThermalZone(os_thermal_zone)
      end
    end

    # assign any internal masses if specified
    if @hash[:properties][:energy][:internal_masses]
      @hash[:properties][:energy][:internal_masses].each do |int_mass|
        hb_int_mass = InternalMassAbridged.new(int_mass)
        os_int_mass = hb_int_mass.to_openstudio(openstudio_model, os_space)
        os_int_mass.setSpace(os_space)
      end
    end

    # assign any process loads if specified
    if @hash[:properties][:energy][:process_loads]
      @hash[:properties][:energy][:process_loads].each do |p_load|
        hb_p_load = ProcessAbridged.new(p_load)
        os_p_load = hb_p_load.to_openstudio(openstudio_model)
        os_p_load.setSpace(os_space)
      end
    end
  end

  os_space
end