openstudio-extension 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +14 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +2 -0
  6. data/lib/openstudio/extension/runner.rb +12 -8
  7. data/lib/openstudio/extension/runner_config.rb +33 -6
  8. data/lib/openstudio/extension/version.rb +1 -1
  9. data/openstudio-extension.gemspec +6 -6
  10. metadata +15 -66
  11. data/lib/openstudio/extension/core/CreateResults.rb +0 -1033
  12. data/lib/openstudio/extension/core/check_air_sys_temps.rb +0 -160
  13. data/lib/openstudio/extension/core/check_calibration.rb +0 -125
  14. data/lib/openstudio/extension/core/check_cond_zns.rb +0 -54
  15. data/lib/openstudio/extension/core/check_domestic_hot_water.rb +0 -304
  16. data/lib/openstudio/extension/core/check_envelope_conductance.rb +0 -423
  17. data/lib/openstudio/extension/core/check_eui_by_end_use.rb +0 -132
  18. data/lib/openstudio/extension/core/check_eui_reasonableness.rb +0 -105
  19. data/lib/openstudio/extension/core/check_fan_pwr.rb +0 -68
  20. data/lib/openstudio/extension/core/check_internal_loads.rb +0 -363
  21. data/lib/openstudio/extension/core/check_mech_sys_capacity.rb +0 -196
  22. data/lib/openstudio/extension/core/check_mech_sys_efficiency.rb +0 -296
  23. data/lib/openstudio/extension/core/check_mech_sys_part_load_eff.rb +0 -434
  24. data/lib/openstudio/extension/core/check_mech_sys_type.rb +0 -109
  25. data/lib/openstudio/extension/core/check_part_loads.rb +0 -421
  26. data/lib/openstudio/extension/core/check_placeholder.rb +0 -45
  27. data/lib/openstudio/extension/core/check_plant_cap.rb +0 -93
  28. data/lib/openstudio/extension/core/check_plant_temps.rb +0 -129
  29. data/lib/openstudio/extension/core/check_plenum_loads.rb +0 -57
  30. data/lib/openstudio/extension/core/check_pump_pwr.rb +0 -78
  31. data/lib/openstudio/extension/core/check_sch_coord.rb +0 -211
  32. data/lib/openstudio/extension/core/check_schedules.rb +0 -281
  33. data/lib/openstudio/extension/core/check_simultaneous_heating_and_cooling.rb +0 -128
  34. data/lib/openstudio/extension/core/check_supply_air_and_thermostat_temp_difference.rb +0 -118
  35. data/lib/openstudio/extension/core/check_weather_files.rb +0 -102
  36. data/lib/openstudio/extension/core/deer_vintages.rb +0 -281
  37. data/lib/openstudio/extension/core/os_lib_aedg_measures.rb +0 -461
  38. data/lib/openstudio/extension/core/os_lib_constructions.rb +0 -353
  39. data/lib/openstudio/extension/core/os_lib_geometry.rb +0 -1169
  40. data/lib/openstudio/extension/core/os_lib_helper_methods.rb +0 -383
  41. data/lib/openstudio/extension/core/os_lib_hvac.rb +0 -2163
  42. data/lib/openstudio/extension/core/os_lib_lighting_and_equipment.rb +0 -184
  43. data/lib/openstudio/extension/core/os_lib_model_generation.rb +0 -3584
  44. data/lib/openstudio/extension/core/os_lib_model_simplification.rb +0 -1019
  45. data/lib/openstudio/extension/core/os_lib_outdoorair_and_infiltration.rb +0 -135
  46. data/lib/openstudio/extension/core/os_lib_reporting_qaqc.rb +0 -170
  47. data/lib/openstudio/extension/core/os_lib_schedules.rb +0 -933
@@ -1,1019 +0,0 @@
1
- # *******************************************************************************
2
- # OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
3
- # See also https://openstudio.net/license
4
- # *******************************************************************************
5
-
6
- module OsLib_ModelSimplification
7
- # get all loads for a space_or_space_type and place in hash by type
8
- def gather_internal_loads(space_or_space_type)
9
- internal_load_hash = {}
10
-
11
- # gather different load types (all vectors except dsoa which will be turned into an array)
12
- internal_load_hash[:internal_mass] = space_or_space_type.internalMass
13
- internal_load_hash[:people] = space_or_space_type.people
14
- internal_load_hash[:lights] = space_or_space_type.lights
15
- internal_load_hash[:luminaires] = space_or_space_type.luminaires
16
- internal_load_hash[:electric_equipment] = space_or_space_type.electricEquipment
17
- internal_load_hash[:gas_equipment] = space_or_space_type.gasEquipment
18
- internal_load_hash[:hot_water_equipment] = space_or_space_type.hotWaterEquipment
19
- internal_load_hash[:steam_equipment] = space_or_space_type.steamEquipment
20
- internal_load_hash[:other_equipment] = space_or_space_type.otherEquipment
21
- internal_load_hash[:space_infiltration_design_flow_rates] = space_or_space_type.spaceInfiltrationDesignFlowRates
22
- internal_load_hash[:space_infiltration_effective_leakage_areas] = space_or_space_type.spaceInfiltrationEffectiveLeakageAreas
23
- if space_or_space_type.designSpecificationOutdoorAir.nil?
24
- internal_load_hash[:design_specification_outdoor_air] = []
25
- else
26
- internal_load_hash[:design_specification_outdoor_air] = [space_or_space_type.designSpecificationOutdoorAir]
27
- end
28
- if space_or_space_type.class.to_s == 'OpenStudio::Model::Space'
29
- internal_load_hash[:water_use_equipment] = space_or_space_type.waterUseEquipment # don't think this reports
30
- internal_load_hash[:daylighting_controls] = space_or_space_type.daylightingControls
31
- end
32
-
33
- # TODO: - warn if daylighting controls in spaces (should I alter fraction controled based on lighting per area ratio)
34
-
35
- return internal_load_hash
36
- end
37
-
38
- # blend_space_types_from_floor_area_ratio used when working from space type ratio and un-assigned space types
39
- def blend_space_types_from_floor_area_ratio(runner, model, space_type_ratio_hash)
40
- # create stub blended space type
41
- blended_space_type = OpenStudio::Model::SpaceType.new(model)
42
- blended_space_type.setName('Blended Space Type')
43
-
44
- # TODO: - inspect people instances and see if any defs are not normalized per area. If find any issue warning
45
-
46
- # gather inputs
47
- sum_of_num_people_per_m_2 = 0.0
48
- space_type_ratio_hash.each do |space_type, ratios|
49
- # get number of peple per m 2 for space type. Can do this without looking at instances
50
- sum_of_num_people_per_m_2 += space_type.getPeoplePerFloorArea(1.0)
51
- end
52
-
53
- # raw num_people_ratios
54
- sum_area_adj_num_people_ratio = 0.0
55
- space_type_ratio_hash.each do |space_type, ratios|
56
- # calculate num_people_ratios
57
- area_adj_num_people_ratio = (space_type.getPeoplePerFloorArea(1.0) / sum_of_num_people_per_m_2) * ratios[:floor_area_ratio]
58
- sum_area_adj_num_people_ratio += area_adj_num_people_ratio
59
- end
60
-
61
- # set ratios
62
- largest_space_type = nil
63
- largest_space_type_ratio = 0.00
64
- space_type_ratio_hash.each do |space_type, ratios|
65
- # calculate num_people_ratios
66
- area_adj_num_people_ratio = (space_type.getPeoplePerFloorArea(1.0) / sum_of_num_people_per_m_2) * ratios[:floor_area_ratio]
67
- normalized_area_adj_num_people_ratio = area_adj_num_people_ratio / sum_area_adj_num_people_ratio
68
-
69
- # ratios[:floor_area_ratio] is already defined
70
- ratios[:num_people_ratio] = normalized_area_adj_num_people_ratio.round(4)
71
- ratios[:ext_surface_area_ratio] = ratios[:floor_area_ratio]
72
- ratios[:ext_wall_area_ratio] = ratios[:floor_area_ratio]
73
- ratios[:volume_ratio] = ratios[:floor_area_ratio]
74
-
75
- # update largest space type values
76
- if largest_space_type.nil?
77
- largest_space_type = space_type
78
- largest_space_type_ratio = ratios[:floor_area_ratio]
79
- elsif ratios[:floor_area_ratio] > largest_space_type_ratio
80
- largest_space_type = space_type
81
- largest_space_type_ratio = ratios[:floor_area_ratio]
82
- end
83
- end
84
-
85
- if largest_space_type.nil?
86
- runner.registerError("Didn't find any space types in model matching user argument string.")
87
- return nil
88
- end
89
-
90
- # set standards info for space type based on largest ratio (for use to apply HVAC system)
91
- standards_building_type = largest_space_type.standardsBuildingType
92
- standards_space_type = largest_space_type.standardsSpaceType
93
- if standards_building_type.is_initialized
94
- blended_space_type.setStandardsBuildingType(standards_building_type.get)
95
- end
96
- if standards_space_type.is_initialized
97
- blended_space_type.setStandardsSpaceType(standards_space_type.get)
98
- end
99
-
100
- # loop therough space types to get instances from and then remove
101
- space_type_ratio_hash.each do |space_type, ratios|
102
- # blend internal loads (nil is space_hash)
103
- space_type_load_instances = blend_internal_loads(runner, model, space_type, blended_space_type, ratios, model.getBuilding.floorArea, nil)
104
- runner.registerInfo("Blending #{space_type.name.get} with floor area ratio of #{ratios[:floor_area_ratio]} and number of people ratio of #{ratios[:num_people_ratio]}.")
105
-
106
- # delete space type. Don't want to leave in model since internal loads have been removed from it
107
- space_type.remove
108
- end
109
-
110
- return blended_space_type
111
- end
112
-
113
- # takes in space type hash where each hash value is a colleciton of space types. Each collection is blended into it's own space type
114
- # If key for any collection is "Building" it will also opererate on spaces that don't have space type assigned
115
- # where a space assigned to a space type from a collection has space loads, those space loads are normalized and added to the blended space type
116
- # load instances are maintained so that they can haave unique schedules, and can have EE measures selectivly applied.
117
- def blend_space_type_collections(runner, model, space_type_hash)
118
- # loop through building type hash to create multiple blends
119
- space_type_hash.each do |collection_name, space_types|
120
- if collection_name == 'Building'
121
- space_array = model.getSpaces.sort # use all space types, not just space types passed in
122
- else
123
- space_array = []
124
- space_types.each do |space_type|
125
- space_array.concat(space_type.spaces)
126
- end
127
- end
128
-
129
- # calculate metrics for all spaces included in building area to pass into space_type and space hash
130
- # note: in the future this may be a subset of spaces if blending into multiple space types vs. just one.
131
- collection_totals = {}
132
- collection_totals[:floor_area] = 0.0
133
- collection_totals[:num_people] = 0.0
134
- collection_totals[:ext_surface_area] = 0.0
135
- collection_totals[:ext_wall_area] = 0.0
136
- collection_totals[:volume] = 0.0
137
- space_array.each do |space|
138
- next if !space.partofTotalFloorArea
139
- collection_totals[:floor_area] += space.floorArea * space.multiplier
140
- collection_totals[:num_people] += space.numberOfPeople * space.multiplier
141
- collection_totals[:ext_surface_area] += space.exteriorArea * space.multiplier
142
- collection_totals[:ext_wall_area] += space.exteriorWallArea * space.multiplier
143
- collection_totals[:volume] += space.volume * space.multiplier
144
- end
145
- area_ip = OpenStudio.convert(collection_totals[:floor_area], 'm^2', 'ft^2').get
146
- area_ip_neat = OpenStudio.toNeatString(area_ip, 2, true)
147
- runner.registerInfo("#{collection_name} area is #{area_ip_neat} ft^2, number of people is #{collection_totals[:num_people].round(0)}.")
148
-
149
- # create hash of space types and floor area for all space types with area > 0 when spaces included in floor area
150
- # code to gather space type areas came from openstudio_results measure.
151
- space_type_hash = {}
152
- largest_space_type = nil
153
- largest_space_type_ratio = 0.00
154
- space_types.each do |space_type|
155
- next if space_type.floorArea == 0
156
- space_type_totals = {}
157
- space_type_totals[:floor_area] = 0.0
158
- space_type_totals[:num_people] = 0.0
159
- space_type_totals[:ext_surface_area] = 0.0
160
- space_type_totals[:ext_wall_area] = 0.0
161
- space_type_totals[:volume] = 0.0
162
- # loop through spaces so I can skip if not included in floor area
163
- space_type.spaces.each do |space|
164
- next if !space.partofTotalFloorArea
165
- space_type_totals[:floor_area] += space.floorArea * space.multiplier
166
- space_type_totals[:num_people] += space.numberOfPeople * space.multiplier
167
- space_type_totals[:ext_surface_area] += space.exteriorArea * space.multiplier
168
- space_type_totals[:ext_wall_area] += space.exteriorWallArea * space.multiplier
169
- space_type_totals[:volume] += space.volume * space.multiplier
170
- end
171
-
172
- # update largest space type values
173
- if largest_space_type.nil?
174
- largest_space_type = space_type
175
- largest_space_type_ratio = space_type_totals[:floor_area]
176
- elsif space_type_totals[:floor_area] > largest_space_type_ratio
177
- largest_space_type = space_type
178
- largest_space_type_ratio = space_type_totals[:floor_area]
179
- end
180
-
181
- # gather internal loads
182
- space_type_loads_hash = gather_internal_loads(space_type)
183
-
184
- # don't add to hash if no spaces used for space type are included in building area (e.g. plenum and attic)
185
- # todo - log these and decide what to do for them. Leave loads alone or remove, do they add to blend at all?
186
- next if space_type_totals[:floor_area] == 0
187
-
188
- if !space_type_totals[:floor_area] = space_type.floorArea # TODO: - not sure if these would ever show as different
189
- runner.registerWarning("Some but not all spaces of #{space_type.name} space type are not included in the building floor area. May have unexpected results")
190
- end
191
-
192
- # populate space type hash
193
- space_type_hash[space_type] = { int_loads: space_type_loads_hash, totals: space_type_totals }
194
- end
195
-
196
- # report initial condition of model
197
- runner.registerInfo("#{collection_name} accounts for #{space_type_hash.size} space types.")
198
-
199
- if collection_name == 'Building'
200
- # count area of spaces that have no space type
201
- no_space_type_area_counter = 0
202
- model.getSpaces.sort.each do |space|
203
- if space.spaceType.empty?
204
- next if !space.partofTotalFloorArea
205
- no_space_type_area_counter += space.floorArea * space.multiplier
206
- end
207
- end
208
- floor_area_ratio = no_space_type_area_counter / collection_totals[:floor_area]
209
- if floor_area_ratio > 0
210
- runner.registerInfo("#{floor_area_ratio} fraction of building area is composed of spaces without space type assignments.")
211
- end
212
- end
213
-
214
- # report the space ratio for hard spaces
215
- space_hash = {}
216
- space_array.each do |space|
217
- next if !space.partofTotalFloorArea
218
- space_loads_hash = gather_internal_loads(space)
219
- space_totals = {}
220
- space_totals[:floor_area] = space.floorArea * space.multiplier
221
- space_totals[:num_people] = space.numberOfPeople * space.multiplier
222
- space_totals[:ext_surface_area] = space.exteriorArea * space.multiplier
223
- space_totals[:ext_wall_area] = space.exteriorWallArea * space.multiplier
224
- space_totals[:volume] = space.volume * space.multiplier
225
- if !space_loads_hash[:daylighting_controls].empty?
226
- runner.registerWarning("#{space.name} has one or more daylighting controls. Lighting loads from blended space type may affect lighting reduction from daylighting controls.")
227
- end
228
- if !space_loads_hash[:water_use_equipment].empty?
229
- runner.registerInfo("One ore more water use equipment objects are associated with space #{space.name}. This can't be moved to a space type.")
230
- end
231
- # note: If generating ratios without geometry can calculate people_ratio given space_types floor_area_ratio
232
- space_hash[space] = { int_loads: space_loads_hash, totals: space_totals }
233
- end
234
-
235
- # create stub blended space type
236
- blended_space_type = OpenStudio::Model::SpaceType.new(model)
237
- blended_space_type.setName("#{collection_name} Blended Space Type")
238
-
239
- # set standards info for space type based on largest ratio (for use to apply HVAC system)
240
- standards_building_type = largest_space_type.standardsBuildingType
241
- standards_space_type = largest_space_type.standardsSpaceType
242
- if standards_building_type.is_initialized
243
- blended_space_type.setStandardsBuildingType(standards_building_type.get)
244
- end
245
- if standards_space_type.is_initialized
246
- blended_space_type.setStandardsSpaceType(standards_space_type.get)
247
- end
248
-
249
- # values from collection hash
250
- collection_floor_area = collection_totals[:floor_area]
251
- collection_num_people = collection_totals[:num_people]
252
- collection_ext_surface_area = collection_totals[:ext_surface_area]
253
- collection_ext_wall_area = collection_totals[:ext_wall_area]
254
- collection_volume = collection_totals[:volume]
255
-
256
- # loop through space that have one or more spaces included in the building area
257
- space_type_hash.each do |space_type, hash|
258
- # hard assign space load schedules before re-assign instances to blended space type
259
- space_type.hardApplySpaceLoadSchedules
260
-
261
- # vaules from space or space_type
262
- floor_area = hash[:totals][:floor_area]
263
- num_people = hash[:totals][:num_people]
264
- ext_surface_area = hash[:totals][:ext_surface_area]
265
- ext_wall_area = hash[:totals][:ext_wall_area]
266
- volume = hash[:totals][:volume]
267
-
268
- # ratios
269
- ratios = {}
270
- if collection_floor_area > 0
271
- ratios[:floor_area_ratio] = floor_area / collection_floor_area
272
- else
273
- ratios[:floor_area_ratio] = 0.0
274
- end
275
- if collection_num_people > 0
276
- ratios[:num_people_ratio] = num_people / collection_num_people
277
- else
278
- ratios[:num_people_ratio] = 0.0
279
- end
280
- if collection_ext_surface_area > 0
281
- ratios[:ext_surface_area_ratio] = ext_surface_area / collection_ext_surface_area
282
- else
283
- ratios[:ext_surface_area_ratio] = 0.0
284
- end
285
- if collection_ext_wall_area > 0
286
- ratios[:ext_wall_area_ratio] = ext_wall_area / collection_ext_wall_area
287
- else
288
- ratios[:ext_wall_area_ratio] = 0.0
289
- end
290
- if collection_volume > 0
291
- ratios[:volume_ratio] = volume / collection_volume
292
- else
293
- ratios[:volume_ratio] = 0.0
294
- end
295
-
296
- # populate blended space type with space type loads
297
- space_type_load_instances = blend_internal_loads(runner, model, space_type, blended_space_type, ratios, collection_floor_area, space_hash)
298
- runner.registerInfo("Blending space type #{space_type.name}. Floor area ratio is #{(hash[:totals][:floor_area] / collection_totals[:floor_area]).round(3)}. People ratio is #{(hash[:totals][:num_people] / collection_totals[:num_people]).round(3)}")
299
-
300
- # hard assign any constructions assigned by space types, except for space not included in the building area
301
- if space_type.defaultConstructionSet.is_initialized
302
- runner.registerInfo("Hard assigning constructions for #{space_type.name}.")
303
- space_type.spaces.each(&:hardApplyConstructions)
304
- end
305
-
306
- # remove all space type assignments, except for spaces not included in building area.
307
- space_type.spaces.each do |space|
308
- next if !space.partofTotalFloorArea
309
- space.resetSpaceType
310
- end
311
-
312
- # delete space type. Don't want to leave in model since internal loads have been removed from it
313
- space_type.remove
314
- end
315
-
316
- # loop through spaces that are included in building area
317
- space_hash.each do |space, hash|
318
- # hard assign space load schedules before re-assign instances to blended space type
319
- space.hardApplySpaceLoadSchedules
320
-
321
- # vaules from space or space_type
322
- floor_area = hash[:totals][:floor_area]
323
- num_people = hash[:totals][:num_people]
324
- ext_surface_area = hash[:totals][:ext_surface_area]
325
- ext_wall_area = hash[:totals][:ext_wall_area]
326
- volume = hash[:totals][:volume]
327
-
328
- # ratios
329
- ratios = {}
330
- if collection_floor_area > 0
331
- ratios[:floor_area_ratio] = floor_area / collection_floor_area
332
- else
333
- ratios[:floor_area_ratio] = 0.0
334
- end
335
- if collection_num_people > 0
336
- ratios[:num_people_ratio] = num_people / collection_num_people
337
- else
338
- ratios[:num_people_ratio] = 0.0
339
- end
340
- if collection_ext_surface_area > 0
341
- ratios[:ext_surface_area_ratio] = ext_surface_area / collection_ext_surface_area
342
- else
343
- ratios[:ext_surface_area_ratio] = 0.0
344
- end
345
- if collection_ext_wall_area > 0
346
- ratios[:ext_wall_area_ratio] = ext_wall_area / collection_ext_wall_area
347
- else
348
- ratios[:ext_wall_area_ratio] = 0.0
349
- end
350
- if collection_volume > 0
351
- ratios[:volume_ratio] = volume / collection_volume
352
- else
353
- ratios[:volume_ratio] = 0.0
354
- end
355
-
356
- # populate blended space type with space loads
357
- space_load_instances = blend_internal_loads(runner, model, space, blended_space_type, ratios, collection_floor_area, space_hash)
358
- next if space_load_instances.empty?
359
- runner.registerInfo("Blending space #{space.name}. Floor area ratio is #{(hash[:totals][:floor_area] / collection_totals[:floor_area]).round(3)}. People ratio is #{(hash[:totals][:num_people] / collection_totals[:num_people]).round(3)}")
360
- end
361
-
362
- if collection_name == 'Building'
363
- # assign blended space type to building
364
- model.getBuilding.setSpaceType(blended_space_type)
365
- building_space_type = model.getBuilding.spaceType
366
- else
367
- space_array.each do |space|
368
- space.setSpaceType(blended_space_type)
369
- end
370
- end
371
- end
372
-
373
- return model.getSpaceTypes.sort
374
- end
375
-
376
- # blend internal loads used when working from existing model
377
- def blend_internal_loads(runner, model, source_space_or_space_type, target_space_type, ratios, collection_floor_area, space_hash)
378
- # ratios
379
- floor_area_ratio = ratios[:floor_area_ratio]
380
- num_people_ratio = ratios[:num_people_ratio]
381
- ext_surface_area_ratio = ratios[:ext_surface_area_ratio]
382
- ext_wall_area_ratio = ratios[:ext_wall_area_ratio]
383
- volume_ratio = ratios[:volume_ratio]
384
-
385
- # for normalizing design level loads I need to know effective number of spaces instance is applied to
386
- if source_space_or_space_type.to_Space.is_initialized
387
- eff_num_spaces = source_space_or_space_type.multiplier
388
- else
389
- eff_num_spaces = 0
390
- source_space_or_space_type.spaces.each do |space|
391
- eff_num_spaces += space.multiplier
392
- end
393
- end
394
-
395
- # array of load instacnes re-assigned to blended space
396
- instances_array = []
397
-
398
- # internal_mass
399
- source_space_or_space_type.internalMass.each do |load_inst|
400
- load_def = load_inst.definition.to_InternalMassDefinition.get
401
- if load_def.surfaceArea.is_initialized
402
- # edit and assign a clone of definition and normalize per area based on floor area ratio
403
- if collection_floor_area == 0
404
- runner.registerWarning("Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
405
- else
406
- cloned_load_def = load_def.clone(model).to_InternalMass.get
407
- orig_design_level = cloned_load_def.surfaceArea.get
408
- cloned_load_def.setSurfaceAreaperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
409
- cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} m^2.")
410
- load_inst.setInternalMassDefinition(cloned_load_def)
411
- end
412
- elsif load_def.surfaceAreaperSpaceFloorArea.is_initialized
413
- load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
414
- elsif load_def.surfaceAreaperPerson.is_initialized
415
- if num_people_ratio.nil?
416
- runner.registerError("#{load_def} has value defined per person, but people ratio wasn't passed in")
417
- return false
418
- else
419
- load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
420
- end
421
- else
422
- runner.registerError("Unexpected value type for #{load_def.name}")
423
- return false
424
- end
425
- load_inst.setSpaceType(target_space_type)
426
- instances_array << load_inst
427
- end
428
-
429
- # people
430
- source_space_or_space_type.people.each do |load_inst|
431
- load_def = load_inst.definition.to_PeopleDefinition.get
432
- if load_def.numberofPeople.is_initialized
433
- # edit and assign a clone of definition and normalize per area based on floor area ratio
434
- if collection_floor_area == 0
435
- runner.registerWarning("Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
436
- else
437
- cloned_load_def = load_def.clone(model).to_PeopleDefinition.get
438
- orig_design_level = cloned_load_def.numberofPeople.get
439
- cloned_load_def.setPeopleperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
440
- cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} people.")
441
- load_inst.setPeopleDefinition(cloned_load_def)
442
- end
443
- elsif load_def.peopleperSpaceFloorArea.is_initialized
444
- load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
445
- elsif load_def.spaceFloorAreaperPerson.is_initialized
446
- load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
447
- else
448
- runner.registerError("Unexpected value type for #{load_def.name}")
449
- return false
450
- end
451
- load_inst.setSpaceType(target_space_type)
452
- instances_array << load_inst
453
- end
454
-
455
- # lights
456
- source_space_or_space_type.lights.each do |load_inst|
457
- load_def = load_inst.definition.to_LightsDefinition.get
458
- if load_def.lightingLevel.is_initialized
459
- # edit and assign a clone of definition and normalize per area based on floor area ratio
460
- if collection_floor_area == 0
461
- runner.registerWarning("Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
462
- else
463
- cloned_load_def = load_def.clone(model).to_LightsDefinition.get
464
- orig_design_level = cloned_load_def.lightingLevel.get
465
- cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
466
- cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
467
- load_inst.setLightsDefinition(cloned_load_def)
468
- end
469
- elsif load_def.wattsperSpaceFloorArea.is_initialized
470
- load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
471
- elsif load_def.wattsperPerson.is_initialized
472
- if num_people_ratio.nil?
473
- runner.registerError("#{load_def} has value defined per person, but people ratio wasn't passed in")
474
- return false
475
- else
476
- load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
477
- end
478
- else
479
- runner.registerError("Unexpected value type for #{load_def.name}")
480
- return false
481
- end
482
- load_inst.setSpaceType(target_space_type)
483
- instances_array << load_inst
484
- end
485
-
486
- # luminaires
487
- source_space_or_space_type.luminaires.each do |load_inst|
488
- # TODO: - can't normalize luminaire. Replace it with similar normalized lights def and instance
489
- runner.registerWarning("Can't area normalize luminaire. Instance will be applied to every space using the blended space type")
490
- instances_array << load_inst
491
- end
492
-
493
- # electric_equipment
494
- source_space_or_space_type.electricEquipment.each do |load_inst|
495
- load_def = load_inst.definition.to_ElectricEquipmentDefinition.get
496
- if load_def.designLevel.is_initialized
497
- # edit and assign a clone of definition and normalize per area based on floor area ratio
498
- if collection_floor_area == 0
499
- runner.registerWarning("Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
500
- else
501
- cloned_load_def = load_def.clone(model).to_ElectricEquipmentDefinition.get
502
- orig_design_level = cloned_load_def.designLevel.get
503
- cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
504
- cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
505
- load_inst.setElectricEquipmentDefinition(cloned_load_def)
506
- end
507
- elsif load_def.wattsperSpaceFloorArea.is_initialized
508
- load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
509
- elsif load_def.wattsperPerson.is_initialized
510
- if num_people_ratio.nil?
511
- runner.registerError("#{load_def} has value defined per person, but people ratio wasn't passed in")
512
- return false
513
- else
514
- load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
515
- end
516
- else
517
- runner.registerError("Unexpected value type for #{load_def.name}")
518
- return false
519
- end
520
- load_inst.setSpaceType(target_space_type)
521
- instances_array << load_inst
522
- end
523
-
524
- # gas_equipment
525
- source_space_or_space_type.gasEquipment.each do |load_inst|
526
- load_def = load_inst.definition.to_GasEquipmentDefinition.get
527
- if load_def.designLevel.is_initialized
528
- # edit and assign a clone of definition and normalize per area based on floor area ratio
529
- if collection_floor_area == 0
530
- runner.registerWarning("Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
531
- else
532
- cloned_load_def = load_def.clone(model).to_GasEquipmentDefinition.get
533
- orig_design_level = cloned_load_def.designLevel.get
534
- cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
535
- cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
536
- load_inst.setGasEquipmentDefinition(cloned_load_def)
537
- end
538
- elsif load_def.wattsperSpaceFloorArea.is_initialized
539
- load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
540
- elsif load_def.wattsperPerson.is_initialized
541
- if num_people_ratio.nil?
542
- runner.registerError("#{load_def} has value defined per person, but people ratio wasn't passed in")
543
- return false
544
- else
545
- load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
546
- end
547
- else
548
- runner.registerError("Unexpected value type for #{load_def.name}")
549
- return false
550
- end
551
- load_inst.setSpaceType(target_space_type)
552
- instances_array << load_inst
553
- end
554
-
555
- # hot_water_equipment
556
- source_space_or_space_type.hotWaterEquipment.each do |load_inst|
557
- load_def = load_inst.definition.to_HotWaterDefinition.get
558
- if load_def.designLevel.is_initialized
559
- # edit and assign a clone of definition and normalize per area based on floor area ratio
560
- if collection_floor_area == 0
561
- runner.registerWarning("Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
562
- else
563
- cloned_load_def = load_def.clone(model).to_HotWaterEquipmentDefinition.get
564
- orig_design_level = cloned_load_def.designLevel.get
565
- cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
566
- cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
567
- load_inst.setHotWaterEquipmentDefinition(cloned_load_def)
568
- end
569
- elsif load_def.wattsperSpaceFloorArea.is_initialized
570
- load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
571
- elsif load_def.wattsperPerson.is_initialized
572
- if num_people_ratio.nil?
573
- runner.registerError("#{load_def} has value defined per person, but people ratio wasn't passed in")
574
- return false
575
- else
576
- load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
577
- end
578
- else
579
- runner.registerError("Unexpected value type for #{load_def.name}")
580
- return false
581
- end
582
- load_inst.setSpaceType(target_space_type)
583
- instances_array << load_inst
584
- end
585
-
586
- # steam_equipment
587
- source_space_or_space_type.steamEquipment.each do |load_inst|
588
- load_def = load_inst.definition.to_SteamDefinition.get
589
- if load_def.designLevel.is_initialized
590
- # edit and assign a clone of definition and normalize per area based on floor area ratio
591
- if collection_floor_area == 0
592
- runner.registerWarning("Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
593
- else
594
- cloned_load_def = load_def.clone(model).to_SteamEquipmentDefinition.get
595
- orig_design_level = cloned_load_def.designLevel.get
596
- cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
597
- cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
598
- load_inst.setSteamEquipmentDefinition(cloned_load_def)
599
- end
600
- elsif load_def.wattsperSpaceFloorArea.is_initialized
601
- load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
602
- elsif load_def.wattsperPerson.is_initialized
603
- if num_people_ratio.nil?
604
- runner.registerError("#{load_def} has value defined per person, but people ratio wasn't passed in")
605
- return false
606
- else
607
- load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
608
- end
609
- else
610
- runner.registerError("Unexpected value type for #{load_def.name}")
611
- return false
612
- end
613
- load_inst.setSpaceType(target_space_type)
614
- instances_array << load_inst
615
- end
616
-
617
- # other_equipment
618
- source_space_or_space_type.otherEquipment.each do |load_inst|
619
- load_def = load_inst.definition.to_OtherDefinition.get
620
- if load_def.designLevel.is_initialized
621
- # edit and assign a clone of definition and normalize per area based on floor area ratio
622
- if collection_floor_area == 0
623
- runner.registerWarning("Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
624
- else
625
- cloned_load_def = load_def.clone(model).to_OtherEquipmentDefinition.get
626
- orig_design_level = cloned_load_def.designLevel.get
627
- cloned_load_def.setWattsperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
628
- cloned_load_def.setName("#{cloned_load_def.name} - pre-normalized value was #{orig_design_level.round} W.")
629
- load_inst.setOtherEquipmentDefinition(cloned_load_def)
630
- end
631
- elsif load_def.wattsperSpaceFloorArea.is_initialized
632
- load_inst.setMultiplier(load_inst.multiplier * floor_area_ratio)
633
- elsif load_def.wattsperPerson.is_initialized
634
- if num_people_ratio.nil?
635
- runner.registerError("#{load_def} has value defined per person, but people ratio wasn't passed in")
636
- return false
637
- else
638
- load_inst.setMultiplier(load_inst.multiplier * num_people_ratio)
639
- end
640
- else
641
- runner.registerError("Unexpected value type for #{load_def.name}")
642
- return false
643
- end
644
- load_inst.setSpaceType(target_space_type)
645
- instances_array << load_inst
646
- end
647
-
648
- # space_infiltration_design_flow_rates
649
- source_space_or_space_type.spaceInfiltrationDesignFlowRates.each do |load_inst|
650
- if load_inst.designFlowRateCalculationMethod == 'Flow/Space'
651
- # edit load so normalized for building area
652
- if collection_floor_area == 0
653
- runner.registerWarning("Can't determine building floor area to normalize #{load_def}. #{load_inst} will be asigned the the blended space without altering its values.")
654
- else
655
- orig_design_level = load_inst.designFlowRate.get
656
- load_inst.setFlowperSpaceFloorArea(eff_num_spaces * orig_design_level / collection_floor_area)
657
- load_inst.setName("#{load_inst.name} - pre-normalized value was #{orig_design_level} m^3/sec")
658
- end
659
- elsif load_inst.designFlowRateCalculationMethod == 'Flow/Area'
660
- load_inst.setFlowperSpaceFloorArea(load_inst.flowperSpaceFloorArea.get * floor_area_ratio)
661
- elsif load_inst.designFlowRateCalculationMethod == 'Flow/ExteriorArea'
662
- load_inst.setFlowperExteriorSurfaceArea(load_inst.flowperExteriorSurfaceArea.get * ext_surface_area_ratio)
663
- elsif load_inst.designFlowRateCalculationMethod == 'Flow/ExteriorWallArea'
664
- load_inst.setFlowperExteriorWallArea(load_inst.flowperExteriorWallArea.get * ext_wall_area_ratio)
665
- elsif load_inst.designFlowRateCalculationMethod == 'AirChanges/Hour'
666
- load_inst.setAirChangesperHour (load_inst.airChangesperHour.get * volume_ratio)
667
- else
668
- runner.registerError("Unexpected value type for #{load_inst.name}")
669
- return false
670
- end
671
- load_inst.setSpaceType(target_space_type)
672
- instances_array << load_inst
673
- end
674
-
675
- # space_infiltration_effective_leakage_areas
676
- source_space_or_space_type.spaceInfiltrationEffectiveLeakageAreas.each do |load|
677
- # TODO: - can't normalize space_infiltration_effective_leakage_areas. Come up with logic to address this
678
- runner.registerWarning("Can't area normalize space_infiltration_effective_leakage_areas. It will be applied to every space using the blended space type")
679
- load.setSpaceType(target_space_type)
680
- instances_array << load
681
- end
682
-
683
- # add OA object if it doesn't already exist
684
- if target_space_type.designSpecificationOutdoorAir.is_initialized
685
- blended_oa = target_space_type.designSpecificationOutdoorAir.get
686
- else
687
- blended_oa = OpenStudio::Model::DesignSpecificationOutdoorAir.new(model)
688
- blended_oa.setName('Blended OA')
689
- blended_oa.setOutdoorAirMethod('Sum')
690
- target_space_type.setDesignSpecificationOutdoorAir(blended_oa)
691
- instances_array << blended_oa
692
- end
693
-
694
- # update OA object
695
- if source_space_or_space_type.designSpecificationOutdoorAir.is_initialized
696
- oa = source_space_or_space_type.designSpecificationOutdoorAir.get
697
- oa_sch = nil
698
- if oa.outdoorAirFlowRateFractionSchedule.is_initialized
699
- # TODO: - improve logic to address multiple schedules
700
- runner.registerWarning("Schedule #{oa.outdoorAirFlowRateFractionSchedule.get.name} assigned to #{oa.name} will be ignored. New OA object will not have a schedule assigned")
701
- end
702
- if oa.outdoorAirMethod == 'Maximum'
703
- # TODO: - see if way to address this by pre-calculating the max and only entering that value for space type
704
- runner.registerWarning("Outdoor air method of Maximum will be ignored for #{oa.name}. New OA object will have outdoor air method of Sum.")
705
- end
706
- # adjusted ratios for oa (lowered for space type if there is hard assigned oa load for one or more spaces)
707
- oa_floor_area_ratio = floor_area_ratio
708
- oa_num_people_ratio = num_people_ratio
709
- if source_space_or_space_type.class.to_s == 'OpenStudio::Model::SpaceType'
710
- source_space_or_space_type.spaces.each do |space|
711
- if !space.isDesignSpecificationOutdoorAirDefaulted
712
- if space_hash.nil?
713
- runner.registerWarning('No space_hash passed in and model has OA designed at space level.')
714
- else
715
- oa_floor_area_ratio -= space_hash[space][:floor_area_ratio]
716
- oa_num_people_ratio -= space_hash[space][:num_people_ratio]
717
- end
718
- end
719
- end
720
- end
721
- # add to values of blended OA load
722
- if oa.outdoorAirFlowperPerson > 0
723
- blended_oa.setOutdoorAirFlowperPerson(blended_oa.outdoorAirFlowperPerson + oa.outdoorAirFlowperPerson * oa_num_people_ratio)
724
- end
725
- if oa.outdoorAirFlowperFloorArea > 0
726
- blended_oa.setOutdoorAirFlowperFloorArea(blended_oa.outdoorAirFlowperFloorArea + oa.outdoorAirFlowperFloorArea * oa_floor_area_ratio)
727
- end
728
- if oa.outdoorAirFlowRate > 0
729
-
730
- # calculate quantity for instance (doesn't exist as a method in api)
731
- if source_space_or_space_type.class.to_s == 'OpenStudio::Model::SpaceType'
732
- quantity = 0
733
- source_space_or_space_type.spaces.each do |space|
734
- if !space.isDesignSpecificationOutdoorAirDefaulted
735
- quantity += space.multiplier
736
- end
737
- end
738
- else
739
- quantity = source_space_or_space_type.multiplier
740
- end
741
-
742
- # can't normalize air flow rate, convert to air flow rate per floor area
743
- blended_oa.setOutdoorAirFlowperFloorArea(blended_oa.outdoorAirFlowperFloorArea + quantity * oa.outdoorAirFlowRate / collection_floor_area)
744
- end
745
- if oa.outdoorAirFlowAirChangesperHour > 0
746
- # floor area should be good approximation of area for multiplier
747
- blended_oa.setOutdoorAirFlowAirChangesperHour(blended_oa.outdoorAirFlowAirChangesperHour + oa.outdoorAirFlowAirChangesperHour * oa_floor_area_ratio)
748
- end
749
- end
750
-
751
- # note: water_use_equipment can't be assigned to a space type. Leave it as is, if assigned to space type
752
- # todo - if we use this measure with new geometry need to find a way to pull water use equipment loads into new model
753
-
754
- return instances_array
755
- end
756
-
757
- # sort building stories
758
- def sort_building_stories_and_get_min_multiplier(model)
759
- sorted_building_stories = {}
760
- # loop through stories
761
- model.getBuildingStorys.sort.each do |story|
762
- story_min_z = nil
763
- # loop through spaces in story.
764
- story.spaces.sort.each do |space|
765
- space_z_min = OsLib_Geometry.getSurfaceZValues(space.surfaces.to_a).min + space.zOrigin
766
- if story_min_z.nil? || (story_min_z > space_z_min)
767
- story_min_z = space_z_min
768
- end
769
- end
770
- sorted_building_stories[story] = story_min_z
771
- end
772
-
773
- return sorted_building_stories
774
- end
775
-
776
- # gather_envelope_data for envelope simplification
777
- def gather_envelope_data(runner, model)
778
- runner.registerInfo('Gathering envelope data.')
779
-
780
- # hash to contain envelope data
781
- envelope_data_hash = {}
782
-
783
- # used for overhang and party wall orientation catigorization
784
- facade_options = {
785
- 'northEast' => 45,
786
- 'southEast' => 125,
787
- 'southWest' => 225,
788
- 'northWest' => 315
789
- }
790
-
791
- # get building level inputs
792
- envelope_data_hash[:north_axis] = model.getBuilding.northAxis
793
- envelope_data_hash[:building_floor_area] = model.getBuilding.floorArea
794
- envelope_data_hash[:building_exterior_surface_area] = model.getBuilding.exteriorSurfaceArea
795
- envelope_data_hash[:building_exterior_wall_area] = model.getBuilding.exteriorWallArea
796
- envelope_data_hash[:building_exterior_roof_area] = envelope_data_hash[:building_exterior_surface_area] - envelope_data_hash[:building_exterior_wall_area]
797
- envelope_data_hash[:building_air_volume] = model.getBuilding.airVolume
798
- envelope_data_hash[:building_perimeter] = nil # will be applied for first story without ground walls
799
-
800
- # get bounding_box
801
- bounding_box = OpenStudio::BoundingBox.new
802
- model.getSpaces.sort.each do |space|
803
- space.surfaces.sort.each do |spaceSurface|
804
- bounding_box.addPoints(space.transformation * spaceSurface.vertices)
805
- end
806
- end
807
- min_x = bounding_box.minX.get
808
- min_y = bounding_box.minY.get
809
- min_z = bounding_box.minZ.get
810
- max_x = bounding_box.maxX.get
811
- max_y = bounding_box.maxY.get
812
- max_z = bounding_box.maxZ.get
813
- envelope_data_hash[:building_min_xyz] = [min_x, min_y, min_z]
814
- envelope_data_hash[:building_max_xyz] = [max_x, max_y, max_z]
815
-
816
- # add orientation specific wwr
817
- ext_surfaces_hash = OsLib_Geometry.getExteriorWindowAndWllAreaByOrientation(model, model.getSpaces.sort.to_a)
818
- envelope_data_hash[:building_wwr_n] = ext_surfaces_hash['northWindow'] / ext_surfaces_hash['northWall']
819
- envelope_data_hash[:building_wwr_s] = ext_surfaces_hash['southWindow'] / ext_surfaces_hash['southWall']
820
- envelope_data_hash[:building_wwr_e] = ext_surfaces_hash['eastWindow'] / ext_surfaces_hash['eastWall']
821
- envelope_data_hash[:building_wwr_w] = ext_surfaces_hash['westWindow'] / ext_surfaces_hash['westWall']
822
- envelope_data_hash[:stories] = {} # each entry will be hash with buildingStory as key and attributes has values
823
- envelope_data_hash[:space_types] = {} # each entry will be hash with spaceType as key and attributes has values
824
-
825
- # as rough estimate overhang area / glazing area should be close to projection factor assuming overhang is same width as windows
826
- # will only add building shading surfaces assoicated with a sub-surface.
827
- building_overhang_area_n = 0.0
828
- building_overhang_area_s = 0.0
829
- building_overhang_area_e = 0.0
830
- building_overhang_area_w = 0.0
831
-
832
- # loop through stories based on mine z height of surfaces.
833
- sorted_stories = sort_building_stories_and_get_min_multiplier(model).sort_by { |k, v| v }
834
- sorted_stories.each do |story, story_min_z|
835
- story_min_multiplier = nil
836
- story_footprint = nil
837
- story_multiplied_floor_area = OsLib_HelperMethods.getAreaOfSpacesInArray(model, story.spaces, 'floorArea')['totalArea']
838
- # goal of footprint calc is to count multiplier for hotel room on facade,but not to count what is intended as a story multiplier
839
- story_multiplied_exterior_surface_area = OsLib_HelperMethods.getAreaOfSpacesInArray(model, story.spaces, 'exteriorArea')['totalArea']
840
- story_multiplied_exterior_wall_area = OsLib_HelperMethods.getAreaOfSpacesInArray(model, story.spaces, 'exteriorWallArea')['totalArea']
841
- story_multiplied_exterior_roof_area = story_multiplied_exterior_surface_area - story_multiplied_exterior_wall_area
842
- story_has_ground_walls = []
843
- story_has_adiabatic_walls = []
844
- story_included_in_building_area = false # will be true if any spaces on story are inclued in building area
845
- story_max_z = nil
846
-
847
- # loop through spaces for story gathering information
848
- story.spaces.each do |space|
849
- # get min multiplier value
850
- multiplier = space.multiplier
851
- if story_min_multiplier.nil? || (story_min_multiplier > multiplier)
852
- story_min_multiplier = multiplier
853
- end
854
-
855
- # calculate footprint
856
- story_footprint = story_multiplied_floor_area / story_min_multiplier
857
-
858
- # see if part of floor area
859
- if space.partofTotalFloorArea
860
- story_included_in_building_area = true
861
-
862
- # add to space type ratio hash when space is included in building floor area
863
- if space.spaceType.is_initialized
864
- space_type = space.spaceType.get
865
- space_floor_area = space.floorArea * space.multiplier
866
- if envelope_data_hash[:space_types].key?(space_type)
867
- envelope_data_hash[:space_types][space_type][:floor_area] += space_floor_area
868
- else
869
- envelope_data_hash[:space_types][space_type] = {}
870
- envelope_data_hash[:space_types][space_type][:floor_area] = space_floor_area
871
-
872
- # make hash for heating and cooling setpoints
873
- envelope_data_hash[:space_types][space_type][:htg_setpoint] = {}
874
- envelope_data_hash[:space_types][space_type][:clg_setpoint] = {}
875
-
876
- end
877
-
878
- # add heating and cooling setpoints
879
- if space.thermalZone.is_initialized && space.thermalZone.get.thermostatSetpointDualSetpoint.is_initialized
880
- thermostat = space.thermalZone.get.thermostatSetpointDualSetpoint.get
881
-
882
- # log heating schedule
883
- if thermostat.heatingSetpointTemperatureSchedule.is_initialized
884
- htg_sch = thermostat.heatingSetpointTemperatureSchedule.get
885
- if envelope_data_hash[:space_types][space_type][:htg_setpoint].key?(htg_sch)
886
- envelope_data_hash[:space_types][space_type][:htg_setpoint][htg_sch] += space_floor_area
887
- else
888
- envelope_data_hash[:space_types][space_type][:htg_setpoint][htg_sch] = space_floor_area
889
- end
890
- else
891
- runner.registerWarning("#{space.thermalZone.get.name} containing #{space.name} doesn't have a heating setpoint schedule.")
892
- end
893
-
894
- # log cooling schedule
895
- if thermostat.coolingSetpointTemperatureSchedule.is_initialized
896
- clg_sch = thermostat.coolingSetpointTemperatureSchedule.get
897
- if envelope_data_hash[:space_types][space_type][:clg_setpoint].key?(clg_sch)
898
- envelope_data_hash[:space_types][space_type][:clg_setpoint][clg_sch] += space_floor_area
899
- else
900
- envelope_data_hash[:space_types][space_type][:clg_setpoint][clg_sch] = space_floor_area
901
- end
902
- else
903
- runner.registerWarning("#{space.thermalZone.get.name} containing #{space.name} doesn't have a heating setpoint schedule.")
904
- end
905
-
906
- else
907
- runner.registerWarning("#{space.name} either isn't in a thermal zone or doesn't have a thermostat assigned")
908
- end
909
-
910
- else
911
- runner.regsiterWarning("#{space.name} is included in the building floor area but isn't assigned a space type.")
912
- end
913
-
914
- end
915
-
916
- # check for walls with adiabatic and ground boundary condition
917
- space.surfaces.each do |surface|
918
- next if surface.surfaceType != 'Wall'
919
- if surface.outsideBoundaryCondition == 'Ground'
920
- story_has_ground_walls << surface
921
- elsif surface.outsideBoundaryCondition == 'Adiabatic'
922
- story_has_adiabatic_walls << surface
923
- end
924
- end
925
-
926
- # populate overhang values
927
- space.surfaces.each do |surface|
928
- surface.subSurfaces.each do |sub_surface|
929
- sub_surface.shadingSurfaceGroups.each do |shading_surface_group|
930
- shading_surface_group.shadingSurfaces.each do |shading_surface|
931
- absoluteAzimuth = OpenStudio.convert(sub_surface.azimuth, 'rad', 'deg').get + sub_surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis
932
- absoluteAzimuth -= 360.0 until absoluteAzimuth < 360.0
933
- # add to hash based on orientation
934
- if (facade_options['northEast'] <= absoluteAzimuth) && (absoluteAzimuth < facade_options['southEast']) # East overhang
935
- building_overhang_area_e += shading_surface.grossArea * space.multiplier
936
- elsif (facade_options['southEast'] <= absoluteAzimuth) && (absoluteAzimuth < facade_options['southWest']) # South overhang
937
- building_overhang_area_s += shading_surface.grossArea * space.multiplier
938
- elsif (facade_options['southWest'] <= absoluteAzimuth) && (absoluteAzimuth < facade_options['northWest']) # West overhang
939
- building_overhang_area_w += shading_surface.grossArea * space.multiplier
940
- else # North overhang
941
- building_overhang_area_n += shading_surface.grossArea * space.multiplier
942
- end
943
- end
944
- end
945
- end
946
- end
947
-
948
- # get max z
949
- space_z_max = OsLib_Geometry.getSurfaceZValues(space.surfaces.to_a).max + space.zOrigin
950
- if story_max_z.nil? || (story_max_z > space_z_max)
951
- story_max_z = space_z_max
952
- end
953
- end
954
-
955
- # populate hash for story data
956
- envelope_data_hash[:stories][story] = {}
957
- envelope_data_hash[:stories][story][:story_min_height] = story_min_z
958
- envelope_data_hash[:stories][story][:story_max_height] = story_max_z
959
- envelope_data_hash[:stories][story][:story_min_multiplier] = story_min_multiplier
960
- envelope_data_hash[:stories][story][:story_has_ground_walls] = story_has_ground_walls
961
- envelope_data_hash[:stories][story][:story_has_adiabatic_walls] = story_has_adiabatic_walls
962
- envelope_data_hash[:stories][story][:story_included_in_building_area] = story_included_in_building_area
963
- envelope_data_hash[:stories][story][:story_footprint] = story_footprint
964
- envelope_data_hash[:stories][story][:story_multiplied_floor_area] = story_multiplied_floor_area
965
- envelope_data_hash[:stories][story][:story_exterior_surface_area] = story_multiplied_exterior_surface_area
966
- envelope_data_hash[:stories][story][:story_multiplied_exterior_wall_area] = story_multiplied_exterior_wall_area
967
- envelope_data_hash[:stories][story][:story_multiplied_exterior_roof_area] = story_multiplied_exterior_roof_area
968
-
969
- # get perimeter and adiabatic walls that appear to be party walls
970
- perimeter_and_party_walls = OsLib_Geometry.calculate_story_exterior_wall_perimeter(runner, story, story_min_multiplier, ['Outdoors', 'Ground', 'Adiabatic'], bounding_box)
971
- envelope_data_hash[:stories][story][:story_perimeter] = perimeter_and_party_walls[:perimeter]
972
- envelope_data_hash[:stories][story][:story_party_walls] = []
973
- east = false
974
- south = false
975
- west = false
976
- north = false
977
- perimeter_and_party_walls[:party_walls].each do |surface|
978
- absoluteAzimuth = OpenStudio.convert(surface.azimuth, 'rad', 'deg').get + surface.space.get.directionofRelativeNorth + model.getBuilding.northAxis
979
- absoluteAzimuth -= 360.0 until absoluteAzimuth < 360.0
980
-
981
- # add to hash based on orientation (initially added array of sourfaces, but swtiched to just true/false flag)
982
- if (facade_options['northEast'] <= absoluteAzimuth) && (absoluteAzimuth < facade_options['southEast']) # East party walls
983
- east = true
984
- elsif (facade_options['southEast'] <= absoluteAzimuth) && (absoluteAzimuth < facade_options['southWest']) # South party walls
985
- south = true
986
- elsif (facade_options['southWest'] <= absoluteAzimuth) && (absoluteAzimuth < facade_options['northWest']) # West party walls
987
- west = true
988
- else # North party walls
989
- north = true
990
- end
991
- end
992
-
993
- if east then envelope_data_hash[:stories][story][:story_party_walls] << 'east' end
994
- if south then envelope_data_hash[:stories][story][:story_party_walls] << 'south' end
995
- if west then envelope_data_hash[:stories][story][:story_party_walls] << 'west' end
996
- if north then envelope_data_hash[:stories][story][:story_party_walls] << 'north' end
997
-
998
- # store perimeter from first story that doesn't have ground walls
999
- if story_has_ground_walls.empty? && envelope_data_hash[:building_perimeter].nil?
1000
- envelope_data_hash[:building_perimeter] = envelope_data_hash[:stories][story][:story_perimeter]
1001
- runner.registerInfo(" * #{story.name} is the first above grade story and will be used for the building perimeter.")
1002
- end
1003
- end
1004
-
1005
- envelope_data_hash[:building_overhang_proj_factor_n] = building_overhang_area_n / ext_surfaces_hash['northWindow']
1006
- envelope_data_hash[:building_overhang_proj_factor_s] = building_overhang_area_s / ext_surfaces_hash['southWindow']
1007
- envelope_data_hash[:building_overhang_proj_factor_e] = building_overhang_area_e / ext_surfaces_hash['eastWindow']
1008
- envelope_data_hash[:building_overhang_proj_factor_w] = building_overhang_area_w / ext_surfaces_hash['westWindow']
1009
-
1010
- # warn for spaces that are not on a story (in future could infer stories for these)
1011
- model.getSpaces.sort.each do |space|
1012
- if !space.buildingStory.is_initialized
1013
- runner.registerWarning("#{space.name} is not on a building story, may have unexpected results.")
1014
- end
1015
- end
1016
-
1017
- return envelope_data_hash
1018
- end
1019
- end