openstudio-standards 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
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)