openstudio-standards 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. checksums.yaml +4 -4
  2. data/data/geometry/ASHRAEPrimarySchool.osm +36 -2
  3. data/data/geometry/ASHRAESecondarySchool.osm +19 -2
  4. data/data/standards/OpenStudio_Standards_elevators.json +10756 -0
  5. data/lib/openstudio-standards.rb +0 -2
  6. data/lib/openstudio-standards/hvac_sizing/Siz.HVACComponent.rb +36 -0
  7. data/lib/openstudio-standards/hvac_sizing/Siz.Model.rb +3 -0
  8. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.elevators.rb +175 -164
  9. data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.swh.rb +268 -476
  10. data/lib/openstudio-standards/prototypes/common/objects/Prototype.ServiceWaterHeating.rb +625 -116
  11. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -0
  12. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +2 -19
  13. data/lib/openstudio-standards/standards/Standards.ThermalZone.rb +112 -68
  14. data/lib/openstudio-standards/standards/Standards.WaterHeaterMixed.rb +10 -2
  15. data/lib/openstudio-standards/standards/necb/necb_2011/data/space_types.json +224 -224
  16. data/lib/openstudio-standards/standards/necb/necb_2011/service_water_heating.rb +8 -16
  17. data/lib/openstudio-standards/standards/necb/necb_2015/data/space_types.json +318 -318
  18. data/lib/openstudio-standards/standards/standard.rb +1 -0
  19. data/lib/openstudio-standards/version.rb +1 -1
  20. metadata +4 -4
  21. data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.hvac_systems.rb +0 -15
  22. data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.hvac_systems.rb +0 -15
@@ -286,13 +286,11 @@ module OpenstudioStandards
286
286
  # DOE Ref 1980-2004
287
287
  require_relative "#{proto}/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.AirTerminalSingleDuctVAVReheat"
288
288
  require_relative "#{proto}/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.Model.elevators"
289
- require_relative "#{proto}/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.hvac_systems"
290
289
  require_relative "#{proto}/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.refrigeration"
291
290
  # DOE Ref Pre-1980
292
291
  require_relative "#{proto}/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.AirTerminalSingleDuctVAVReheat"
293
292
  require_relative "#{proto}/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.CoilHeatingGas"
294
293
  require_relative "#{proto}/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.Model.elevators"
295
- require_relative "#{proto}/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.hvac_systems"
296
294
  require_relative "#{proto}/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.refrigeration"
297
295
  # NREL ZNE Ready 2017
298
296
  require_relative "#{proto}/ashrae_90_1/nrel_nze_ready_2017/nrel_zne_ready_2017.AirTerminalSingleDuctVAVReheat"
@@ -0,0 +1,36 @@
1
+
2
+ ###### IMPORTANT NOTE ######
3
+ # These methods should be done via extension to OS model objects
4
+ # directly in the C++ SDK.
5
+ ###### IMPORTANT NOTE ######
6
+
7
+ class OpenStudio::Model::HVACComponent
8
+ # Returns the number of individual pieces of equipment
9
+ # represented by a particular HVAC component. Pulls from
10
+ # the additionalProperties object attached to the component.
11
+ # This can be used during the application of efficiency
12
+ # levels that are based on component capacities, flowrates, etc.
13
+ # @return [Integer] the number of components, 1 if not set
14
+ def component_quantity
15
+ addl_props = self.additionalProperties
16
+ if addl_props.getFeatureAsInteger('component_quantity').is_initialized
17
+ comp_qty = addl_props.getFeatureAsInteger('component_quantity').get
18
+ else
19
+ comp_qty = 1
20
+ end
21
+
22
+ return comp_qty
23
+ end
24
+
25
+ # Sets the number of individual pieces of equipment
26
+ # represented by a particular HVAC component. Uses the
27
+ # additionalProperties object attached to the component.
28
+ # This can be used during the application of efficiency
29
+ # levels that are based on component capacities, flowrates, etc.
30
+ # @param comp_qty [Integer] the number of individual pieces of equipment
31
+ # represented by this HVAC component
32
+ # @return [Bool] true if successful, false if not
33
+ def set_component_quantity(comp_qty)
34
+ return self.additionalProperties.setFeature('component_quantity', comp_qty)
35
+ end
36
+ end
@@ -57,6 +57,9 @@ class OpenStudio::Model::Model
57
57
  # Heating and cooling fuel methods
58
58
  require_relative 'Siz.HeatingCoolingFuels'
59
59
 
60
+ # Component quantity methods
61
+ require_relative 'Siz.HVACComponent'
62
+
60
63
  # Takes the values calculated by the EnergyPlus sizing routines
61
64
  # and puts them into all objects model in place of the autosized fields.
62
65
  # Must have previously completed a run with sql output for this to work.
@@ -107,11 +107,11 @@ class Standard
107
107
  def model_elevator_lift_power(model, elevator_type, building_type)
108
108
  lift_pwr_w = 0
109
109
  if elevator_type == 'Traction'
110
- lift_pwr_w = 20_370.0
110
+ lift_pwr_w += 20_370.0
111
111
  elsif elevator_type == 'Hydraulic'
112
- lift_pwr_w = 16_055.0
112
+ lift_pwr_w += 16_055.0
113
113
  else
114
- lift_pwr_w = 16_055.0
114
+ lift_pwr_w += 16_055.0
115
115
  OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "Elevator type '#{elevator_type}', not recognized, will assume Hydraulic elevator, #{lift_pwr_w} W.")
116
116
  end
117
117
 
@@ -130,7 +130,7 @@ class Standard
130
130
  # Determines the power of the elevator ventilation fan.
131
131
  # Defaults to 90.1-2010, which had no requirement
132
132
  # for ventilation fan efficiency.
133
- # @return [Double] the ventilaton fan power (W)
133
+ # @return [Double] the ventilation fan power (W)
134
134
  def model_elevator_fan_pwr(model, vent_rate_cfm)
135
135
  vent_pwr_per_flow_w_per_cfm = 0.33
136
136
  vent_pwr_w = vent_pwr_per_flow_w_per_cfm * vent_rate_cfm
@@ -149,13 +149,14 @@ class Standard
149
149
 
150
150
  # determine elevator type
151
151
  # todo - add logic here or upstream to have some multi-story buildings without elevators (e.g. small multi-family and small hotels)
152
- elevator_type = nil
153
152
  if effective_num_stories[:below_grade] + effective_num_stories[:above_grade] < 2
154
153
  OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', 'The building only has 1 story, no elevators will be added.')
155
154
  return nil # don't add elevators
156
155
  elsif effective_num_stories[:below_grade] + effective_num_stories[:above_grade] < 6
156
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', 'The building has fewer than 6 effective stories; assuming Hydraulic elevators.')
157
157
  elevator_type = 'Hydraulic'
158
158
  else
159
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', 'The building has 6 or more effective stories; assuming Traction elevators.')
159
160
  elevator_type = 'Traction'
160
161
  end
161
162
 
@@ -169,6 +170,8 @@ class Standard
169
170
  end
170
171
  target_space = bottom_spaces.key(bottom_spaces.values.max)
171
172
 
173
+ building_types = []
174
+
172
175
  # determine number of elevators
173
176
  number_of_pass_elevators = 0.0
174
177
  number_of_freight_elevators = 0.0
@@ -184,98 +187,70 @@ class Standard
184
187
  building_type_hash[hash[:stds_bldg_type]] = hash[:floor_area]
185
188
  end
186
189
 
187
- # building type specific notes; prototype uses Beyer (2009) rules of thumb
188
- area_per_pass_elev_ft2 = nil
189
- units_per_pass_elevator = nil
190
- beds_per_pass_elevator = nil
191
- area_per_freight_elev_ft2 = nil
192
- units_per_freight_elevator = nil
193
- beds_per_freight_elevator = nil
194
- if ['Office', 'SmallOffice', 'MediumOffice', 'LargeOffice','SmallOfficeDetailed','MediumOfficeDetailed','LargeOfficeDetailed'].include?(hash[:stds_bldg_type])
195
- # The office buildings have one elevator for every 45,000 ft2 (4,181 m2),
196
- # plus one service elevator for the large office building (500,000 ft^2).
197
- area_per_pass_elev_ft2 = 45_000
198
- bldg_area_ft2 = OpenStudio.convert(model.getBuilding.floorArea, 'm^2', 'ft^2').get
199
- if bldg_area_ft2 > 500_000
200
- area_per_freight_elev_ft2 = 500_000
201
- end
202
- elsif ['SmallHotel', 'LargeHotel'].include?(hash[:stds_bldg_type])
203
- # The hotels have one elevator for every 75 rooms.
204
- if hash[:stds_space_type].include?('GuestRoom')
205
- units_per_pass_elevator = 75.0
206
- end
207
- # The large hotel includes one service elevator for every two public elevators,
208
- # plus one additional elevator for the dining and banquet facilities on the top floor.
209
- # None of the other space types generate elevators.
210
- if ['LargeHotel'].include?(hash[:stds_bldg_type]) && hash[:stds_space_type].include?('GuestRoom')
211
- units_per_freight_elevator = 150.0
212
- elsif ['LargeHotel'].include?(hash[:stds_bldg_type]) && ['Banquet', 'Cafe'].include?(hash[:stds_space_type])
213
- area_per_pass_elev_ft2 = 10_000
214
- end
215
- elsif ['MidriseApartment', 'HighriseApartment'].include?(hash[:stds_bldg_type]) && hash[:stds_space_type].include?('Apartment')
216
- # The apartment building has one elevator for every 90 units
217
- units_per_pass_elevator = 90.0
218
- elsif ['Hospital'].include?(hash[:stds_bldg_type])
219
- # The hospital has one public and one service elevator for every 100 beds (250 total),
220
- # plus two elevators for the offices and cafeteria on the top floor.
221
- # None of the other space types generate elevators.
222
- if ['PatRoom', 'ICU_PatRm', 'ICU_Open'].include?(hash[:stds_space_type])
223
- beds_per_pass_elevator = 100.0
224
- beds_per_freight_elevator = 100.0
225
- elsif ['Dining', 'Kitchen', 'Office'].include?(hash[:stds_space_type])
226
- area_per_pass_elev_ft2 = 12_500
227
- end
228
- elsif ['PrimarySchool', 'SecondarySchool'].include?(hash[:stds_bldg_type])
229
- # 210,887 ft^2 secondary school prototype has 2 elevators
230
- area_per_pass_elev_ft2 = 100_000
231
- elsif ['Outpatient'].include?(hash[:stds_bldg_type])
232
- # 40,946 Outpatient has 3 elevators
233
- area_per_pass_elev_ft2 = 15_000
234
- elsif ['Warehouse'].include?(hash[:stds_bldg_type])
235
- # Warehouse has no elevators, but assume some would be needed
236
- area_per_freight_elev_ft2 = 250_000
237
- else
238
- # TODO: - improve catchall for building types without elevator data, using same value as what Outpatient would be if not already in space type
239
- # includes RetailStandalone, RetailStripmall, QuickServiceRestaurant, FullServiceRestaurant, SuperMarket (made unique logic above for warehouse)
240
- area_per_pass_elev_ft2 = 15_000
241
- end
190
+ building_type = hash[:stds_bldg_type]
191
+ building_types << building_type
242
192
 
243
- # passenger elevators
244
- if area_per_pass_elev_ft2
245
- pass_elevs = hash[:floor_area] / OpenStudio.convert(area_per_pass_elev_ft2, 'ft^2', 'm^2').get
246
- number_of_pass_elevators += pass_elevs
247
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{pass_elevs.round(1)} passenger elevators at 1 per #{area_per_pass_elev_ft2.round} ft^2.")
248
- end
193
+ # store floor area ip
194
+ floor_area_ip = OpenStudio.convert(hash[:floor_area], 'm^2', 'ft^2').get
249
195
 
250
- if units_per_pass_elevator
251
- pass_elevs = hash[:num_units] / units_per_pass_elevator
252
- number_of_pass_elevators += pass_elevs
253
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{pass_elevs.round(1)} passenger elevators at 1 per #{units_per_pass_elevator} units.")
196
+ # load elevator_data
197
+ search_criteria = { 'building_type' => building_type }
198
+ elevator_data_lookup = model_find_object(standards_data['elevators'], search_criteria)
199
+ if elevator_data_lookup.nil?
200
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.prototype.elevators', "Could not find elevator data for #{building_type}.")
254
201
  end
255
202
 
256
- if beds_per_pass_elevator
257
- pass_elevs = hash[:num_beds] / beds_per_pass_elevator
258
- number_of_pass_elevators += pass_elevs
259
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{pass_elevs.round(1)} passenger elevators at 1 per #{beds_per_pass_elevator} beds.")
203
+ # determine number of passenger elevators
204
+ if !elevator_data_lookup['area_per_passenger_elevator'].nil?
205
+ pass_elevs = floor_area_ip / elevator_data_lookup['area_per_passenger_elevator'].to_f
206
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{pass_elevs.round(1)} passenger elevators at 1 per #{elevator_data_lookup['area_per_passenger_elevator']} ft^2.")
207
+ elsif !elevator_data_lookup['units_per_passenger_elevator'].nil?
208
+ pass_elevs = hash[:num_units] / elevator_data_lookup['units_per_passenger_elevator'].to_f
209
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{pass_elevs.round(1)} passenger elevators at 1 per #{elevator_data_lookup['units_per_passenger_elevator']} units.")
210
+ elsif !elevator_data_lookup['beds_per_passenger_elevator'].nil?
211
+ pass_elevs = hash[:num_beds] / elevator_data_lookup['beds_per_passenger_elevator'].to_f
212
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{pass_elevs.round(1)} passenger elevators at 1 per #{elevator_data_lookup['beds_per_passenger_elevator']} beds.")
213
+ else
214
+ pass_elevs = 0.0
215
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "Unexpected key, can't calculate number of passenger elevators from #{elevator_data_lookup.keys.first}.")
260
216
  end
261
217
 
262
- # freight or service elevators
263
- if area_per_freight_elev_ft2
264
- freight_elevs = hash[:floor_area] / OpenStudio.convert(area_per_freight_elev_ft2, 'ft^2', 'm^2').get
265
- number_of_freight_elevators += freight_elevs
266
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{freight_elevs.round(1)} freight/service elevators at 1 per #{area_per_freight_elev_ft2.round} ft^2.")
218
+ # determine number of freight elevators
219
+ if !elevator_data_lookup['area_per_freight_elevator'].nil?
220
+ freight_elevs = floor_area_ip / elevator_data_lookup['area_per_freight_elevator'].to_f
221
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{freight_elevs.round(1)} freight/service elevators at 1 per #{elevator_data_lookup['area_per_freight_elevator']} ft^2.")
222
+ elsif !elevator_data_lookup['units_per_freight_elevator'].nil?
223
+ freight_elevs = hash[:num_units] / elevator_data_lookup['units_per_freight_elevator'].to_f
224
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{freight_elevs.round(1)} freight/service elevators at 1 per #{elevator_data_lookup['units_per_freight_elevator']} units.")
225
+ elsif !elevator_data_lookup['beds_per_freight_elevator'].nil?
226
+ freight_elevs = hash[:num_beds] / elevator_data_lookup['beds_per_freight_elevator'].to_f
227
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{freight_elevs.round(1)} freight/service elevators at 1 per #{elevator_data_lookup['beds_per_freight_elevator']} beds.")
228
+ else
229
+ freight_elevs = 0.0
230
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "Unexpected key, can't calculate number of freight elevators from #{elevator_data_lookup.keys.first}.")
267
231
  end
232
+ number_of_pass_elevators += pass_elevs
233
+ number_of_freight_elevators += freight_elevs
234
+ end
268
235
 
269
- if units_per_freight_elevator
270
- freight_elevs = hash[:num_units] / units_per_freight_elevator
271
- number_of_freight_elevators += freight_elevs
272
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{freight_elevs.round(1)} freight/service elevators at 1 per #{units_per_freight_elevator} units.")
236
+ # additional passenger elevators (applicable for DOE LargeHotel and DOE Hospital only)
237
+ add_pass_elevs = 0.0
238
+ building_types.uniq.each do |building_type|
239
+ # load elevator_data
240
+ search_criteria = { 'building_type' => building_type }
241
+ elevator_data_lookup = model_find_object(standards_data['elevators'], search_criteria)
242
+ if elevator_data_lookup.nil?
243
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.prototype.elevators', "Could not find elevator data for #{building_type}.")
244
+ return nil
273
245
  end
274
246
 
275
- if beds_per_freight_elevator
276
- freight_elevs = hash[:num_beds] / beds_per_freight_elevator
277
- number_of_freight_elevators += freight_elevs
278
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "For #{space_type.name}, adding #{freight_elevs.round(1)} freight/service elevators at 1 per #{beds_per_freight_elevator} beds.")
247
+ # determine number of additional passenger elevators
248
+ if !elevator_data_lookup['additional_passenger_elevators'].nil?
249
+ add_pass_elevs += elevator_data_lookup['additional_passenger_elevators']
250
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', "Adding #{elevator_data_lookup['additional_passenger_elevators']} additional passenger elevators.")
251
+ else
252
+ add_pass_elevs += 0.0
253
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', 'No additional passenger elevators added to model.')
279
254
  end
280
255
  end
281
256
 
@@ -286,98 +261,134 @@ class Standard
286
261
  if (number_of_freight_elevators > 0.0) && (number_of_freight_elevators < 1.0)
287
262
  number_of_freight_elevators = 1.0
288
263
  end
264
+
265
+ # determine total number of elevators (rounding up to nearest whole number)
266
+ number_of_pass_elevators = number_of_pass_elevators.ceil + add_pass_elevs
267
+ number_of_freight_elevators = number_of_freight_elevators.ceil
289
268
  number_of_elevators = number_of_pass_elevators + number_of_freight_elevators
290
269
 
291
270
  building_type = building_type_hash.key(building_type_hash.values.max)
292
- # rename space types as needed
293
- if building_type == 'Office'
294
- building_type = model_remap_office(model, building_type_hash['Office'])
295
- end
296
- if building_type == 'SmallHotel' then building_type = 'LargeHotel' end # no elevator schedules for SmallHotel
297
- if building_type == 'PrimarySchool' then building_type = 'SecondarySchool' end # no elevator schedules for PrimarySchool
298
- if building_type == 'Retail' then building_type = 'RetailStandalone' end # no elevator schedules for PrimarySchool
299
- if building_type == 'StripMall' then building_type = 'RetailStripmall' end # no elevator schedules for PrimarySchool
300
- if building_type == 'Outpatient'
301
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.prototype.elevators', 'Outpatient ElevatorPumpRoom plug loads contain the elevator loads. Not adding extra elevator loads on top of it.')
302
- end
303
-
304
- # Retrieve the Prototype Inputs from JSON
305
- search_criteria = {
306
- 'template' => template,
307
- 'building_type' => building_type
308
- }
309
271
 
310
- prototype_input = model_find_object(standards_data['prototype_inputs'], search_criteria)
311
- if prototype_input.nil?
312
- OpenStudio.logFree(OpenStudio::Error, 'openstudio.prototype.elevators', "Could not find prototype inputs for #{search_criteria}, cannot add elevators.")
313
- return nil
272
+ # determine blended occupancy schedule
273
+ occ_schedule = spaces_get_occupancy_schedule(model.getSpaces)
274
+
275
+ # get total number of people in building
276
+ max_occ_in_spaces = 0
277
+ model.getSpaces.each do |space|
278
+ # From the space type
279
+ if space.spaceType.is_initialized
280
+ space.spaceType.get.people.each do |people|
281
+ num_ppl = people.getNumberOfPeople(space.floorArea)
282
+ max_occ_in_spaces += num_ppl
283
+ end
284
+ end
285
+ # From the space
286
+ space.people.each do |people|
287
+ num_ppl = people.getNumberOfPeople(space.floorArea)
288
+ max_occ_in_spaces += num_ppl
289
+ end
314
290
  end
315
291
 
316
- # assign schedules
317
- if ['Office', 'MediumOffice', 'MediumOfficeDetailed','MidriseApartment', 'HighriseApartment', 'SecondarySchool'].include?(building_type)
318
- elevator_schedule = prototype_input['elevator_schedule']
319
- elevator_fan_schedule = prototype_input['elevator_fan_schedule']
320
- elevator_lights_schedule = prototype_input['elevator_fan_schedule']
321
- elsif ['LargeHotel', 'LargeOfficeDetailed','Hospital', 'LargeOffice'].include?(building_type)
322
- elevator_schedule = prototype_input['exterior_fuel_equipment1_schedule']
323
- elevator_fan_schedule = prototype_input['exterior_fuel_equipment2_schedule']
324
- elevator_lights_schedule = prototype_input['exterior_fuel_equipment2_schedule']
325
- else
292
+ # make elevator schedule based on change in occupancy for each timestep
293
+ day_schedules = []
294
+ default_day_schedule = occ_schedule.defaultDaySchedule
295
+ day_schedules << default_day_schedule
296
+ occ_schedule.scheduleRules.each do |rule|
297
+ day_schedules << rule.daySchedule
298
+ end
299
+ day_schedules.each do |day_schedule|
300
+ elevator_hourly_fractions = []
301
+ (0..23).each do |hr|
302
+ t = OpenStudio::Time.new(0, hr, 0, 0)
303
+ value = day_schedule.getValue(t)
304
+ t_plus = OpenStudio::Time.new(0, hr + 1, 0, 0)
305
+ value_plus = day_schedule.getValue(t_plus)
306
+ change_occupancy_fraction = (value_plus - value).abs
307
+ change_num_people = change_occupancy_fraction * max_occ_in_spaces * 1.2
308
+ # multiplication factor or 1.2 to account for interfloor traffic
309
+
310
+ # determine time per ride based on number of floors and elevator type
311
+ if elevator_type == 'Hydraulic'
312
+ time_per_ride = 8.7 + (effective_num_stories[:above_grade] * 5.6)
313
+ elsif elevator_type == 'Traction'
314
+ time_per_ride = 5.6 + (effective_num_stories[:above_grade] * 2.1)
315
+ else
316
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.prototype.elevators', "Elevator type #{elevator_type} not recognized.")
317
+ return nil
318
+ end
326
319
 
327
- # identify occupancy schedule from largest space type of this building type
328
- space_type_size = {}
329
- space_type_hash.each do |space_type, hash|
330
- next unless building_type.include?(hash[:stds_bldg_type])
331
- space_type_size[space_type] = hash[:floor_area]
320
+ # determine elevator operation fraction for each timestep
321
+ people_per_ride = 5
322
+ rides_per_elevator = (change_num_people / people_per_ride) / number_of_elevators
323
+ operation_time = rides_per_elevator * time_per_ride
324
+ elevator_operation_fraction = operation_time / 3600
325
+ if elevator_operation_fraction > 1.00
326
+ elevator_operation_fraction = 1.00
327
+ end
328
+ elevator_hourly_fractions << elevator_operation_fraction
332
329
  end
333
330
 
334
- # Get the largest space type
335
- largest_space_type = space_type_size.key(space_type_size.values.max)
336
-
337
- # Get the occ sch, if one is specified
338
- occ_sch = nil
339
- if largest_space_type.defaultScheduleSet.is_initialized
340
- if largest_space_type.defaultScheduleSet.get.numberofPeopleSchedule.is_initialized
341
- occ_sch = largest_space_type.defaultScheduleSet.get.numberofPeopleSchedule.get
342
- end
343
- else
344
- occ_sch = model.alwaysOffDiscreteSchedule
345
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.prototype.elevators', "No occupancy schedule was specified for #{largest_space_type.name}, an always off schedule will be used for the elvevators and the elevators will never run.")
331
+ # replace hourly occupancy values with operating fractions
332
+ day_schedule.clearValues
333
+ (0..23).each do |hr|
334
+ t = OpenStudio::Time.new(0, hr, 0, 0)
335
+ value = elevator_hourly_fractions[hr]
336
+ value_plus = if hr <= 22
337
+ elevator_hourly_fractions[hr + 1]
338
+ else
339
+ elevator_hourly_fractions[0]
340
+ end
341
+ next if value == value_plus
342
+ day_schedule.addValue(t, elevator_hourly_fractions[hr])
346
343
  end
344
+ end
347
345
 
348
- # clone and assign to elevator
349
- elev_sch = occ_sch.clone(model)
350
- elevator_schedule = elev_sch.name.to_s
351
- elevator_fan_schedule = elev_sch.name.to_s
352
- elevator_lights_schedule = elev_sch.name.to_s
353
-
354
- # TODO: - scale down peak value based on building type lookup, or make parametric schedule based on hours of operation
355
- # includes RetailStandalone, RetailStripmall, QuickServiceRestaurant, FullServiceRestaurant, SuperMarket (made unique logic above for warehouse)
356
-
357
- if building_type == 'Warehouse'
358
- # alter default profile, summer, winter, and rules
359
- max_value = 0.2
360
- elev_sch = elev_sch.to_ScheduleRuleset.get
361
- day_schedules = []
362
- elev_sch.scheduleRules.each do |rule|
363
- day_schedules << rule.daySchedule
346
+ occ_schedule.setName('Elevator Schedule')
347
+
348
+ # clone new elevator schedule and assign to elevator
349
+ elev_sch = occ_schedule.clone(model)
350
+ elevator_schedule = elev_sch.name.to_s
351
+
352
+ # For elevator lights and fan, assume 100% operation during hours that elevator fraction > 0 (when elevator is in operation).
353
+ # elevator lights
354
+ lights_sch = occ_schedule.clone(model)
355
+ lights_sch = lights_sch.to_ScheduleRuleset.get
356
+ profiles = []
357
+ profiles << lights_sch.defaultDaySchedule
358
+ rules = lights_sch.scheduleRules
359
+ rules.each do |rule|
360
+ profiles << rule.daySchedule
361
+ end
362
+ profiles.each do |profile|
363
+ times = profile.times
364
+ values = profile.values
365
+ values.each_with_index do |val, i|
366
+ if val > 0
367
+ profile.addValue(times[i], 1.0)
364
368
  end
365
- day_schedules << elev_sch.defaultDaySchedule
366
- day_schedules << elev_sch.summerDesignDaySchedule
367
- day_schedules << elev_sch.winterDesignDaySchedule
368
-
369
- day_schedules.each do |day_schedule|
370
- values = day_schedule.values
371
- times = day_schedule.times
372
- values.each_with_index do |value, i|
373
- if value > max_value
374
- day_schedule.addValue(times[i], max_value)
375
- end
376
- end
369
+ end
370
+ end
371
+ elevator_lights_schedule = lights_sch.name.to_s
372
+
373
+ # elevator fan
374
+ fan_sch = occ_schedule.clone(model)
375
+ fan_sch = fan_sch.to_ScheduleRuleset.get
376
+ profiles = []
377
+ profiles << fan_sch.defaultDaySchedule
378
+ rules = fan_sch.scheduleRules
379
+ rules.each do |rule|
380
+ profiles << rule.daySchedule
381
+ end
382
+ profiles.each do |profile|
383
+ times = profile.times
384
+ values = profile.values
385
+ values.each_with_index do |val, i|
386
+ if val > 0
387
+ profile.addValue(times[i], 1.0)
377
388
  end
378
389
  end
379
-
380
390
  end
391
+ elevator_fan_schedule = fan_sch.name.to_s
381
392
 
382
393
  # TODO: - currently add elevator doesn't allow me to choose the size of the elevator?
383
394
  # ref bldg pdf has formula for motor hp based on weight, speed, counterweight fraction and mech eff (in 5.1.4)