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
@@ -28,13 +28,12 @@ class Standard
28
28
  'Main Service Water Loop',
29
29
  water_heater_zone,
30
30
  OpenStudio.convert(prototype_input['main_service_water_temperature'], 'F', 'C').get,
31
- prototype_input['main_service_water_pump_head'],
31
+ prototype_input['main_service_water_pump_head'].to_f,
32
32
  prototype_input['main_service_water_pump_motor_efficiency'],
33
33
  OpenStudio.convert(prototype_input['main_water_heater_capacity'], 'Btu/hr', 'W').get,
34
34
  OpenStudio.convert(prototype_input['main_water_heater_volume'], 'gal', 'm^3').get,
35
35
  swh_fueltype,
36
- OpenStudio.convert(prototype_input['main_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get,
37
- building_type)
36
+ OpenStudio.convert(prototype_input['main_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get)
38
37
  end
39
38
 
40
39
  # Attach the end uses if specified in prototype inputs
@@ -52,8 +51,7 @@ class Standard
52
51
  OpenStudio.convert(prototype_input['main_service_water_peak_flowrate'], 'gal/min', 'm^3/s').get,
53
52
  prototype_input['main_service_water_flowrate_schedule'],
54
53
  OpenStudio.convert(prototype_input['main_water_use_temperature'], 'F', 'C').get,
55
- space_name,
56
- building_type)
54
+ space_name)
57
55
  end
58
56
  elsif building_type == 'LargeOfficeDetailed' && template != 'NECB2011'
59
57
 
@@ -65,8 +63,7 @@ class Standard
65
63
  OpenStudio.convert(prototype_input['main_service_water_peak_flowrate'], 'gal/min', 'm^3/s').get,
66
64
  prototype_input['main_service_water_flowrate_schedule'],
67
65
  OpenStudio.convert(prototype_input['main_water_use_temperature'], 'F', 'C').get,
68
- space_name,
69
- building_type)
66
+ space_name)
70
67
  end
71
68
  elsif building_type == 'RetailStripmall' && template != 'NECB2011'
72
69
 
@@ -87,13 +84,12 @@ class Standard
87
84
  "#{swh_thermal_zone.name} Service Water Loop",
88
85
  swh_thermal_zone,
89
86
  OpenStudio.convert(prototype_input['main_service_water_temperature'], 'F', 'C').get,
90
- prototype_input['main_service_water_pump_head'],
87
+ prototype_input['main_service_water_pump_head'].to_f,
91
88
  prototype_input['main_service_water_pump_motor_efficiency'],
92
89
  OpenStudio.convert(prototype_input['main_water_heater_capacity'], 'Btu/hr', 'W').get,
93
90
  OpenStudio.convert(prototype_input['main_water_heater_volume'], 'gal', 'm^3').get,
94
91
  prototype_input['main_water_heater_fuel'],
95
- OpenStudio.convert(prototype_input['main_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get,
96
- building_type)
92
+ OpenStudio.convert(prototype_input['main_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get)
97
93
 
98
94
  model_add_swh_end_uses(model,
99
95
  'Main',
@@ -101,8 +97,7 @@ class Standard
101
97
  rated_flow_rate_m3_per_s,
102
98
  swh_sch_name,
103
99
  OpenStudio.convert(prototype_input['main_water_use_temperature'], 'F', 'C').get,
104
- swh_space_name,
105
- building_type)
100
+ swh_space_name)
106
101
  end
107
102
 
108
103
  elsif prototype_input['main_service_water_peak_flowrate']
@@ -115,8 +110,7 @@ class Standard
115
110
  OpenStudio.convert(prototype_input['main_service_water_peak_flowrate'], 'gal/min', 'm^3/s').get,
116
111
  prototype_input['main_service_water_flowrate_schedule'],
117
112
  OpenStudio.convert(prototype_input['main_water_use_temperature'], 'F', 'C').get,
118
- nil,
119
- building_type)
113
+ nil)
120
114
 
121
115
  else
122
116
  OpenStudio.logFree(OpenStudio::Debug, 'openstudio.model.Model', 'Adding shw by space_type_map')
@@ -158,11 +152,9 @@ class Standard
158
152
  space.multiplier
159
153
  end
160
154
 
161
- water_fixture = model_add_swh_end_uses_by_space(model, model_get_lookup_name(building_type),
162
- climate_zone,
155
+ water_fixture = model_add_swh_end_uses_by_space(model,
163
156
  main_swh_loop,
164
- space_type_name,
165
- space_name,
157
+ space,
166
158
  space_multiplier)
167
159
  unless water_fixture.nil?
168
160
  water_fixtures << water_fixture
@@ -187,16 +179,14 @@ class Standard
187
179
  prototype_input['booster_water_heater_fuel'],
188
180
  OpenStudio.convert(prototype_input['booster_water_temperature'], 'F', 'C').get,
189
181
  0,
190
- nil,
191
- building_type)
182
+ nil)
192
183
 
193
184
  # Attach the end uses
194
185
  model_add_booster_swh_end_uses(model,
195
186
  swh_booster_loop,
196
187
  OpenStudio.convert(prototype_input['booster_service_water_peak_flowrate'], 'gal/min', 'm^3/s').get,
197
188
  prototype_input['booster_service_water_flowrate_schedule'],
198
- OpenStudio.convert(prototype_input['booster_water_use_temperature'], 'F', 'C').get,
199
- building_type)
189
+ OpenStudio.convert(prototype_input['booster_water_use_temperature'], 'F', 'C').get)
200
190
 
201
191
  end
202
192
 
@@ -208,13 +198,12 @@ class Standard
208
198
  'Laundry Service Water Loop',
209
199
  nil,
210
200
  OpenStudio.convert(prototype_input['laundry_service_water_temperature'], 'F', 'C').get,
211
- prototype_input['laundry_service_water_pump_head'],
201
+ prototype_input['laundry_service_water_pump_head'].to_f,
212
202
  prototype_input['laundry_service_water_pump_motor_efficiency'],
213
203
  OpenStudio.convert(prototype_input['laundry_water_heater_capacity'], 'Btu/hr', 'W').get,
214
204
  OpenStudio.convert(prototype_input['laundry_water_heater_volume'], 'gal', 'm^3').get,
215
205
  prototype_input['laundry_water_heater_fuel'],
216
- OpenStudio.convert(prototype_input['laundry_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get,
217
- building_type)
206
+ OpenStudio.convert(prototype_input['laundry_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get)
218
207
 
219
208
  # Attach the end uses if specified in prototype inputs
220
209
  model_add_swh_end_uses(model,
@@ -223,8 +212,7 @@ class Standard
223
212
  OpenStudio.convert(prototype_input['laundry_service_water_peak_flowrate'], 'gal/min', 'm^3/s').get,
224
213
  prototype_input['laundry_service_water_flowrate_schedule'],
225
214
  OpenStudio.convert(prototype_input['laundry_water_use_temperature'], 'F', 'C').get,
226
- nil,
227
- building_type)
215
+ nil)
228
216
 
229
217
  end
230
218
 
@@ -233,17 +221,18 @@ class Standard
233
221
  return true
234
222
  end
235
223
 
236
- # add swh
237
-
238
224
  # add typical swh demand and supply to model
239
225
  #
240
- # @param trust_effective_num_spaces [Bool]
241
- # @param fuel [String] (gas, electric, nil) nil is smart
242
- # @param pipe_insul_in [Double]
243
- # @param circulating [String] (circulating, noncirculating, nil) nil is smart
226
+ # @param water_heater_fuel [String] water heater fuel. Valid choices are NaturalGas, Electricity, and HeatPump.
227
+ # If not supplied, a smart default will be determined based on building type.
228
+ # @param pipe_insul_in [Double] thickness of the pipe insulation, in inches.
229
+ # @param circulating [String] whether the (circulating, noncirculating, nil) nil is smart
244
230
  # @return [Array] hot water loops
245
231
  # @todo - add in losses from tank and pipe insulation, etc.
246
- def model_add_typical_swh(model, trust_effective_num_spaces = false, fuel = nil, pipe_insul_in = nil, circulating = nil)
232
+ def model_add_typical_swh(model,
233
+ water_heater_fuel: nil,
234
+ pipe_insul_in: nil,
235
+ circulating: nil)
247
236
  # array of hot water loops
248
237
  swh_systems = []
249
238
 
@@ -253,30 +242,30 @@ class Standard
253
242
  # create space type hash (need num_units for MidriseApartment and RetailStripmall)
254
243
  space_type_hash = model_create_space_type_hash(model, trust_effective_num_spaces = false)
255
244
 
256
- # add temperate schedules to hash so they can be shared across water use equipment
257
- water_use_def_schedules = {} # key is temp C value is schedule
258
-
259
245
  # loop through space types adding demand side of swh
260
246
  model.getSpaceTypes.sort.each do |space_type|
261
247
  next unless space_type.standardsBuildingType.is_initialized
262
- next unless space_type.standardsSpaceType.is_initialized
263
248
  next unless space_type_hash.key?(space_type) # this is used for space types without any floor area
264
249
  stds_bldg_type = space_type.standardsBuildingType.get
265
- stds_space_type = space_type.standardsSpaceType.get
266
250
 
267
251
  # lookup space_type_properties
268
252
  space_type_properties = space_type_get_standards_data(space_type)
269
- gal_hr_per_area = space_type_properties['service_water_heating_peak_flow_per_area']
270
- gal_hr_peak_flow_rate = space_type_properties['service_water_heating_peak_flow_rate']
253
+ peak_flow_rate_gal_per_hr_per_ft2 = space_type_properties['service_water_heating_peak_flow_per_area'].to_f
254
+ peak_flow_rate_gal_per_hr = space_type_properties['service_water_heating_peak_flow_rate'].to_f
255
+ swh_system_type = space_type_properties['service_water_heating_system_type']
271
256
  flow_rate_fraction_schedule = model_add_schedule(model, space_type_properties['service_water_heating_schedule'])
272
- service_water_temperature_si = space_type_properties['service_water_heating_target_temperature']
257
+ service_water_temperature_f = space_type_properties['service_water_heating_target_temperature'].to_f
258
+ service_water_temperature_c = OpenStudio.convert(service_water_temperature_f, 'F', 'C').get
259
+ booster_water_temperature_f = space_type_properties['booster_water_heating_target_temperature'].to_f
260
+ booster_water_temperature_c = OpenStudio.convert(booster_water_temperature_f, 'F', 'C').get
261
+ booster_water_heater_fraction = space_type_properties['booster_water_heater_fraction'].to_f
273
262
  service_water_fraction_sensible = space_type_properties['service_water_heating_fraction_sensible']
274
263
  service_water_fraction_latent = space_type_properties['service_water_heating_fraction_latent']
275
- floor_area_si = space_type_hash[space_type][:floor_area]
276
- floor_area_ip = OpenStudio.convert(floor_area_si, 'm^2', 'ft^2').get
264
+ floor_area_m2 = space_type_hash[space_type][:floor_area]
265
+ floor_area_ft2 = OpenStudio.convert(floor_area_m2, 'm^2', 'ft^2').get
277
266
 
278
267
  # next if no service water heating demand
279
- next unless gal_hr_per_area.to_f > 0.0 || gal_hr_peak_flow_rate.to_f > 0.0
268
+ next unless peak_flow_rate_gal_per_hr_per_ft2 > 0.0 || peak_flow_rate_gal_per_hr > 0.0
280
269
 
281
270
  # If there is no SWH schedule specified, assume
282
271
  # that there should be no SWH consumption for this space type.
@@ -285,398 +274,233 @@ class Standard
285
274
  flow_rate_fraction_schedule = model.alwaysOffDiscreteSchedule
286
275
  end
287
276
 
288
- if (stds_bldg_type == 'MidriseApartment' && stds_space_type.include?('Apartment')) || stds_bldg_type == 'StripMall'
289
- num_units = space_type_hash[space_type][:num_units].round
290
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Adding dedicated water heating fpr #{num_units} #{space_type.name} units, each with max flow rate of #{gal_hr_peak_flow_rate} gal/hr per.")
291
-
292
- # add water use equipment definition
293
- water_use_equip_def = OpenStudio::Model::WaterUseEquipmentDefinition.new(model)
294
- water_use_equip_def.setName("#{space_type.name} SWH def")
295
- peak_flow_rate_si = OpenStudio.convert(gal_hr_peak_flow_rate, 'gal/hr', 'm^3/s').get
296
- water_use_equip_def.setPeakFlowRate(peak_flow_rate_si)
297
- target_temp = service_water_temperature_si # in spreadsheet in si, no conversion needed unless that changes
298
- name = "#{target_temp} C"
299
- if water_use_def_schedules.key?(name)
300
- target_temperature_sch = water_use_def_schedules[name]
301
- else
302
- target_temperature_sch = model_add_constant_schedule_ruleset(model, target_temp, name)
303
- water_use_def_schedules[name] = target_temperature_sch
304
- end
305
- water_use_equip_def.setTargetTemperatureSchedule(target_temperature_sch)
306
- name = "#{service_water_fraction_sensible} Fraction"
307
- if water_use_def_schedules.key?(name)
308
- service_water_fraction_sensible_sch = water_use_def_schedules[name]
309
- else
310
- service_water_fraction_sensible_sch = model_add_constant_schedule_ruleset(model, service_water_fraction_sensible, name)
311
- water_use_def_schedules[name] = service_water_fraction_sensible_sch
312
- end
313
- water_use_equip_def.setSensibleFractionSchedule(service_water_fraction_sensible_sch)
314
- name = "#{service_water_fraction_latent} Fraction"
315
- if water_use_def_schedules.key?(name)
316
- service_water_fraction_latent_sch = water_use_def_schedules[name]
317
- else
318
- service_water_fraction_latent_sch = model_add_constant_schedule_ruleset(model, service_water_fraction_sensible, name)
319
- water_use_def_schedules[name] = service_water_fraction_latent_sch
320
- end
321
- water_use_equip_def.setLatentFractionSchedule(service_water_fraction_latent_sch)
322
-
323
- # add water use equipment, connection, and loop for each unit
324
- num_units.times do |i|
325
- # add water use equipment
326
- water_use_equip = OpenStudio::Model::WaterUseEquipment.new(water_use_equip_def)
327
- water_use_equip.setFlowRateFractionSchedule(flow_rate_fraction_schedule)
328
- water_use_equip.setName("#{space_type.name} SWH #{i + 1}")
329
-
330
- # add water use connection
331
- water_use_connection = OpenStudio::Model::WaterUseConnections.new(model)
332
- water_use_connection.addWaterUseEquipment(water_use_equip)
333
- water_use_connection.setName("#{space_type.name} WUC #{i + 1}")
334
-
335
- # gather inputs for add_swh_loop
336
- # default fuel, capacity, and volume from Table A.1. Water Heating Equipment Enhancements to ASHRAE Standard 90.1 Prototype Building Models
337
- # temperature, pump head, motor efficiency, and parasitic load from Prototype Inputs
338
- system_name = "#{space_type.name} Service Water Loop #{i + 1}"
339
- water_heater_thermal_zone = nil
340
- service_water_temperature = service_water_temperature_si
341
- service_water_pump_head = 0.01
342
- service_water_pump_motor_efficiency = 1.0
343
- water_heater_fuel = if fuel.nil?
344
- 'Electric'
345
- else
346
- fuel
347
- end
348
- if stds_bldg_type == 'MidriseApartment'
349
- water_heater_capacity = OpenStudio.convert(15.0, 'kBtu/hr', 'W').get
350
- water_heater_volume = OpenStudio.convert(50.0, 'gal', 'm^3').get
351
- parasitic_fuel_consumption_rate = 0.0 # Prototype inputs has 87.75W but prototype IDF's use 0
352
- else # StripMall
353
- water_heater_capacity = OpenStudio.convert(12.0, 'kBtu/hr', 'W').get
354
- water_heater_volume = OpenStudio.convert(40.0, 'gal', 'm^3').get
355
- parasitic_fuel_consumption_rate = 173.0
356
- end
357
-
358
- # make loop for each unit and add on water use equipment
359
- unit_hot_water_loop = model_add_swh_loop(model,
360
- system_name,
361
- water_heater_thermal_zone,
362
- service_water_temperature,
363
- service_water_pump_head,
364
- service_water_pump_motor_efficiency,
365
- water_heater_capacity,
366
- water_heater_volume,
367
- water_heater_fuel,
368
- parasitic_fuel_consumption_rate,
369
- stds_bldg_type)
370
-
371
- # Connect the water use connection to the SWH loop
372
- unit_hot_water_loop.addDemandBranchForComponent(water_use_connection)
373
-
374
- # apply efficiency to hot water heater
375
- unit_hot_water_loop.supplyComponents.sort.each do |component|
376
- next if component.to_WaterHeaterMixed.empty?
377
- component = component.to_WaterHeaterMixed.get
378
- water_heater_mixed_apply_efficiency(component)
379
- end
380
-
381
- # add to list of systems
382
- swh_systems << unit_hot_water_loop
383
- end
384
-
385
- elsif stds_space_type.include?('Kitchen') || stds_space_type.include?('Laundry')
386
- gal_hr_peak_flow_rate = gal_hr_per_area * floor_area_ip
387
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Adding dedicated water heating for #{space_type.name} space type with max flow rate of #{gal_hr_peak_flow_rate.round} gal/hr.")
388
-
389
- # add water use equipment definition
390
- water_use_equip_def = OpenStudio::Model::WaterUseEquipmentDefinition.new(model)
391
- water_use_equip_def.setName("#{space_type.name} SWH def")
392
- peak_flow_rate_si = OpenStudio.convert(gal_hr_peak_flow_rate, 'gal/hr', 'm^3/s').get
393
- water_use_equip_def.setPeakFlowRate(peak_flow_rate_si)
394
- target_temp = service_water_temperature_si # in spreadsheet in si, no conversion needed unless that changes
395
- name = "#{target_temp} C"
396
- if water_use_def_schedules.key?(name)
397
- target_temperature_sch = water_use_def_schedules[name]
398
- else
399
- target_temperature_sch = model_add_constant_schedule_ruleset(model, target_temp, name)
400
- water_use_def_schedules[name] = target_temperature_sch
401
- end
402
- water_use_equip_def.setTargetTemperatureSchedule(target_temperature_sch)
403
- name = "#{service_water_fraction_sensible} Fraction"
404
- if water_use_def_schedules.key?(name)
405
- service_water_fraction_sensible_sch = water_use_def_schedules[name]
406
- else
407
- service_water_fraction_sensible_sch = model_add_constant_schedule_ruleset(model, service_water_fraction_sensible, name)
408
- water_use_def_schedules[name] = service_water_fraction_sensible_sch
409
- end
410
- water_use_equip_def.setSensibleFractionSchedule(service_water_fraction_sensible_sch)
411
- name = "#{service_water_fraction_latent} Fraction"
412
- if water_use_def_schedules.key?(name)
413
- service_water_fraction_latent_sch = water_use_def_schedules[name]
414
- else
415
- service_water_fraction_latent_sch = model_add_constant_schedule_ruleset(model, service_water_fraction_sensible, name)
416
- water_use_def_schedules[name] = service_water_fraction_latent_sch
417
- end
418
- water_use_equip_def.setLatentFractionSchedule(service_water_fraction_latent_sch)
419
-
420
- # add water use equipment
421
- water_use_equip = OpenStudio::Model::WaterUseEquipment.new(water_use_equip_def)
422
- water_use_equip.setFlowRateFractionSchedule(flow_rate_fraction_schedule)
423
- water_use_equip.setName("#{space_type.name} SWH")
424
-
425
- # add water use connection
426
- water_use_connection = OpenStudio::Model::WaterUseConnections.new(model)
427
- water_use_connection.addWaterUseEquipment(water_use_equip)
428
- water_use_connection.setName("#{space_type.name} WUC")
429
-
430
- # gather inputs for add_swh_loop
431
- system_name = "#{space_type.name} Service Water Loop"
432
- water_heater_thermal_zone = nil
433
- water_heater_temp_si = 60.0 # C
434
- service_water_pump_head = 0.01
435
- service_water_pump_motor_efficiency = 1.0
436
- water_heater_fuel = if fuel.nil?
437
- 'Gas'
438
- else
439
- fuel
440
- end
441
-
442
- # find_water_heater_capacity_volume_and_parasitic
443
- water_use_equipment_array = [water_use_equip]
444
- water_heater_sizing = model_find_water_heater_capacity_volume_and_parasitic(model, water_use_equipment_array)
445
- water_heater_capacity = water_heater_sizing[:water_heater_capacity]
446
- water_heater_volume = water_heater_sizing[:water_heater_volume]
447
- parasitic_fuel_consumption_rate = water_heater_sizing[:parasitic_fuel_consumption_rate]
448
-
449
- # make loop for each unit and add on water use equipment
450
- dedicated_hot_water_loop = model_add_swh_loop(model,
451
- system_name,
452
- water_heater_thermal_zone,
453
- water_heater_temp_si,
454
- service_water_pump_head,
455
- service_water_pump_motor_efficiency,
456
- water_heater_capacity,
457
- water_heater_volume,
458
- water_heater_fuel,
459
- parasitic_fuel_consumption_rate,
460
- stds_bldg_type)
461
-
462
- # Connect the water use connection to the SWH loop
463
- dedicated_hot_water_loop.addDemandBranchForComponent(water_use_connection)
464
-
465
- # find water heater
466
- dedicated_hot_water_loop.supplyComponents.sort.each do |component|
467
- next if component.to_WaterHeaterMixed.empty?
468
- water_heater = component.to_WaterHeaterMixed.get
469
-
470
- # apply efficiency to hot water heater
471
- water_heater_mixed_apply_efficiency(water_heater)
472
- end
473
-
474
- # add to list of systems
475
- swh_systems << dedicated_hot_water_loop
476
-
477
- # add booster to all kitchens except for QuickServiceRestaurant (QuickServiceRestaurant assumed to use chemicals instead of hotter water)
478
- # boosters are all 6 gal elec but heating capacity varies from 3 to 19 (kBtu/hr) for prototype buildings
479
- if stds_space_type.include?('Kitchen') && stds_bldg_type != 'QuickServiceRestaurant'
480
-
481
- # find_water_heater_capacity_volume_and_parasitic
482
- water_use_equipment_array = [water_use_equip]
483
- inlet_temp_ip = OpenStudio.convert(service_water_temperature_si, 'C', 'F').get # pre-booster temp
484
- outlet_temp_ip = inlet_temp_ip + 40.0
485
- peak_flow_fraction = 0.6 # assume 60% of peak for dish washing
486
- water_heater_sizing = model_find_water_heater_capacity_volume_and_parasitic(model, water_use_equipment_array, pipe_hash = {}, 1.0, 1.0, inlet_temp_ip, outlet_temp_ip, peak_flow_fraction)
487
- water_heater_capacity = water_heater_sizing[:water_heater_capacity]
488
-
489
- # gather additional inputs for add_swh_booster
490
- water_heater_volume = OpenStudio.convert(6, 'gal', 'm^3').get
491
- water_heater_fuel = 'Electric'
492
- booster_water_temperature = 82.22 # C
493
- parasitic_fuel_consumption_rate = 0.0
494
- booster_water_heater_thermal_zone = nil
495
-
496
- # add_swh_booster
497
- booster_service_water_loop = model_add_swh_booster(model,
498
- dedicated_hot_water_loop,
499
- water_heater_capacity,
500
- water_heater_volume,
501
- water_heater_fuel,
502
- booster_water_temperature,
503
- parasitic_fuel_consumption_rate,
504
- booster_water_heater_thermal_zone,
505
- stds_bldg_type)
506
-
507
- # find water heater
508
- booster_service_water_loop.supplyComponents.sort.each do |component|
509
- next if component.to_WaterHeaterMixed.empty?
510
- water_heater = component.to_WaterHeaterMixed.get
511
-
512
- # apply efficiency to hot water heater
513
- water_heater_mixed_apply_efficiency(water_heater)
514
- end
515
-
516
- # rename booster loop
517
- booster_service_water_loop.setName("#{space_type.name} Booster Service Water Loop")
518
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Adding Electric Booster water heater for #{space_type.name} on a loop named #{booster_service_water_loop.name}.")
519
-
520
- end
521
-
522
- else # store water use equip by building type in hash so can add general building type hot water loop
277
+ # Determine flow rate
278
+ case swh_system_type
279
+ when 'One Per Unit'
280
+ water_heater_fuel = 'Electricity' if water_heater_fuel.nil?
281
+ num_units = space_type_hash[space_type][:num_units].round # First try number of units
282
+ num_units = space_type_hash[space_type][:effective_num_spaces].round if num_units.zero? # Fall back on number of spaces
283
+ peak_flow_rate_gal_per_hr = num_units * peak_flow_rate_gal_per_hr
284
+ peak_flow_rate_m3_per_s = num_units * OpenStudio.convert(peak_flow_rate_gal_per_hr, 'gal/hr', 'm^3/s').get
285
+ use_name = "#{space_type.name} #{num_units} units"
286
+ else
287
+ # TODO: - add building type or sice specific logic or just assume Gas? (SmallOffice and Warehouse are only non unit prototypes with Electric heating)
288
+ water_heater_fuel = 'NaturalGas' if water_heater_fuel.nil?
289
+ num_units = 1
290
+ peak_flow_rate_gal_per_hr = peak_flow_rate_gal_per_hr_per_ft2 * floor_area_ft2
291
+ peak_flow_rate_m3_per_s = OpenStudio.convert(peak_flow_rate_gal_per_hr, 'gal/hr', 'm^3/s').get
292
+ use_name = "#{space_type.name}"
293
+ end
523
294
 
524
- gal_hr_peak_flow_rate = gal_hr_per_area * floor_area_ip
525
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Adding water heating for #{space_type.name} space type with max flow rate of #{gal_hr_peak_flow_rate.round} gal/hr on a shared loop.")
295
+ # Split flow rate between main and booster uses if specified
296
+ booster_water_use_equip = nil
297
+ if booster_water_heater_fraction > 0.0
298
+ booster_peak_flow_rate_m3_per_s = peak_flow_rate_m3_per_s * booster_water_heater_fraction
299
+ peak_flow_rate_m3_per_s -= booster_peak_flow_rate_m3_per_s
300
+
301
+ # Add booster water heater equipment and connections
302
+ booster_water_use_equip = model_add_swh_end_uses(model,
303
+ "Booster #{use_name}",
304
+ loop=nil,
305
+ booster_peak_flow_rate_m3_per_s,
306
+ flow_rate_fraction_schedule.name.get,
307
+ booster_water_temperature_c,
308
+ space_name=nil,
309
+ frac_sensible: service_water_fraction_sensible,
310
+ frac_latent: service_water_fraction_latent)
311
+ end
526
312
 
527
- # add water use equipment definition
528
- water_use_equip_def = OpenStudio::Model::WaterUseEquipmentDefinition.new(model)
529
- water_use_equip_def.setName("#{space_type.name} SWH def")
530
- peak_flow_rate_si = OpenStudio.convert(gal_hr_peak_flow_rate, 'gal/hr', 'm^3/s').get
531
- water_use_equip_def.setPeakFlowRate(peak_flow_rate_si)
532
- target_temp = service_water_temperature_si # in spreadsheet in si, no conversion needed unless that changes
533
- name = "#{target_temp} C"
534
- if water_use_def_schedules.key?(name)
535
- target_temperature_sch = water_use_def_schedules[name]
536
- else
537
- target_temperature_sch = model_add_constant_schedule_ruleset(model, target_temp, name)
538
- water_use_def_schedules[name] = target_temperature_sch
539
- end
540
- water_use_equip_def.setTargetTemperatureSchedule(target_temperature_sch)
541
- name = "#{service_water_fraction_sensible} Fraction"
542
- if water_use_def_schedules.key?(name)
543
- service_water_fraction_sensible_sch = water_use_def_schedules[name]
544
- else
545
- service_water_fraction_sensible_sch = model_add_constant_schedule_ruleset(model, service_water_fraction_sensible, name)
546
- water_use_def_schedules[name] = service_water_fraction_sensible_sch
547
- end
548
- water_use_equip_def.setSensibleFractionSchedule(service_water_fraction_sensible_sch)
549
- name = "#{service_water_fraction_latent} Fraction"
550
- if water_use_def_schedules.key?(name)
551
- service_water_fraction_latent_sch = water_use_def_schedules[name]
552
- else
553
- service_water_fraction_latent_sch = model_add_constant_schedule_ruleset(model, service_water_fraction_sensible, name)
554
- water_use_def_schedules[name] = service_water_fraction_latent_sch
555
- end
556
- water_use_equip_def.setLatentFractionSchedule(service_water_fraction_latent_sch)
313
+ # Add water use equipment and connections
314
+ water_use_equip = model_add_swh_end_uses(model,
315
+ use_name,
316
+ swh_loop=nil,
317
+ peak_flow_rate_m3_per_s,
318
+ flow_rate_fraction_schedule.name.get,
319
+ service_water_temperature_c,
320
+ space_name=nil,
321
+ frac_sensible: service_water_fraction_sensible,
322
+ frac_latent: service_water_fraction_latent)
323
+
324
+ # Water heater sizing
325
+ case swh_system_type
326
+ when 'One Per Unit'
327
+ water_heater_capacity_w = num_units * OpenStudio.convert(20.0, 'kBtu/hr', 'W').get
328
+ water_heater_volume_m3 = num_units * OpenStudio.convert(50.0, 'gal', 'm^3').get
329
+ num_water_heaters = num_units
330
+ else
331
+ water_use_equips = [water_use_equip]
332
+ water_use_equips << booster_water_use_equip unless booster_water_use_equip.nil? # Include booster in sizing since flows will be preheated by main water heater
333
+ water_heater_sizing = model_find_water_heater_capacity_volume_and_parasitic(model, water_use_equips)
334
+ water_heater_capacity_w = water_heater_sizing[:water_heater_capacity]
335
+ water_heater_volume_m3 = water_heater_sizing[:water_heater_volume]
336
+ num_water_heaters = 1
337
+ end
557
338
 
558
- # add water use equipment
559
- water_use_equip = OpenStudio::Model::WaterUseEquipment.new(water_use_equip_def)
560
- water_use_equip.setFlowRateFractionSchedule(flow_rate_fraction_schedule)
561
- water_use_equip.setName("#{space_type.name} SWH")
339
+ # Add either a dedicated SWH loop or save to add to shared SWH loop
340
+ case swh_system_type
341
+ when 'Shared'
562
342
 
343
+ # Store water use equip by building type to add to shared building hot water loop
563
344
  if water_use_equipment_hash.key?(stds_bldg_type)
564
345
  water_use_equipment_hash[stds_bldg_type] << water_use_equip
565
346
  else
566
347
  water_use_equipment_hash[stds_bldg_type] = [water_use_equip]
567
348
  end
568
349
 
350
+ when 'One Per Unit', 'Dedicated'
351
+ pipe_insul_in = 0.0 if pipe_insul_in.nil?
352
+
353
+ # Add service water loop with water heater
354
+ swh_loop = model_add_swh_loop(model,
355
+ system_name="#{space_type.name} Service Water Loop",
356
+ water_heater_thermal_zone=nil,
357
+ service_water_temperature_c,
358
+ service_water_pump_head=0.01,
359
+ service_water_pump_motor_efficiency=1.0,
360
+ water_heater_capacity_w,
361
+ water_heater_volume_m3,
362
+ water_heater_fuel,
363
+ parasitic_fuel_consumption_rate_w=0,
364
+ add_pipe_losses=true,
365
+ floor_area_served=OpenStudio.convert(950, 'ft^2', 'm^2').get,
366
+ number_of_stories=1,
367
+ pipe_insulation_thickness=OpenStudio.convert(pipe_insul_in, 'in', 'm').get,
368
+ num_water_heaters)
369
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.Model.Model', "In model_add_typical, num_water_heaters = #{num_water_heaters}")
370
+ # Add loop to list
371
+ swh_systems << swh_loop
372
+
373
+ # Attach water use equipment to the loop
374
+ swh_connection = water_use_equip.waterUseConnections
375
+ swh_loop.addDemandBranchForComponent(swh_connection.get) if swh_connection.is_initialized
376
+
377
+ # If a booster fraction is specified, some percentage of the water
378
+ # is assumed to be heated beyond the normal temperature by a separate
379
+ # booster water heater. This booster water heater is fed by the
380
+ # main water heater, so the booster is responsible for a smaller delta-T.
381
+ if booster_water_heater_fraction > 0
382
+ # find_water_heater_capacity_volume_and_parasitic
383
+ booster_water_heater_sizing = model_find_water_heater_capacity_volume_and_parasitic(model,
384
+ [booster_water_use_equip],
385
+ htg_eff: 1.0,
386
+ inlet_temp_f: service_water_temperature_f,
387
+ target_temp_f: booster_water_temperature_f)
388
+
389
+ # Add service water booster loop with water heater
390
+ # Note that booster water heaters are always assumed to be electric resistance
391
+ swh_booster_loop = model_add_swh_booster(model,
392
+ swh_loop,
393
+ booster_water_heater_sizing[:water_heater_capacity],
394
+ water_heater_volume_m3=OpenStudio.convert(6, 'gal', 'm^3').get,
395
+ water_heater_fuel='Electricity',
396
+ booster_water_temperature_c,
397
+ parasitic_fuel_consumption_rate_w=0.0,
398
+ booster_water_heater_thermal_zone=nil)
399
+
400
+ # Rename the service water booster loop
401
+ swh_booster_loop.setName("#{space_type.name} Service Water Booster Loop")
402
+
403
+ # Attach booster water use equipment to the booster loop
404
+ booster_swh_connection = booster_water_use_equip.waterUseConnections
405
+ swh_booster_loop.addDemandBranchForComponent(booster_swh_connection.get) if booster_swh_connection.is_initialized
406
+ end
407
+
408
+ else
409
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "'#{swh_system_type}' is not a valid Service Water Heating System Type, cannot add SWH to #{space_type.name}. Valid choices are One Per Unit, Dedicated, and Shared.")
569
410
  end
570
411
  end
571
412
 
572
413
  # get building floor area and effective number of stories
573
- bldg_floor_area = model.getBuilding.floorArea
414
+ bldg_floor_area_m2 = model.getBuilding.floorArea
574
415
  bldg_effective_num_stories_hash = model_effective_num_stories(model)
575
416
  bldg_effective_num_stories = bldg_effective_num_stories_hash[:below_grade] + bldg_effective_num_stories_hash[:above_grade]
576
417
 
577
418
  # add non-dedicated system(s) here. Separate systems for water use equipment from different building types
578
419
  water_use_equipment_hash.sort.each do |stds_bldg_type, water_use_equipment_array|
579
- # gather inputs for add_swh_loop
580
- system_name = "#{stds_bldg_type} Shared Service Water Loop"
581
- water_heater_thermal_zone = nil
582
- water_heater_temp_si = 60.0
420
+ # TODO: find the water use equipment with the highest temperature
421
+ water_heater_temp_f = 140.0
422
+ water_heater_temp_c = OpenStudio.convert(water_heater_temp_f, 'F', 'C').get
583
423
 
584
424
  # find pump values
585
425
  # Table A.2 in PrototypeModelEnhancements_2014_0.pdf shows 10ft on everything except SecondarySchool which has 11.4ft
586
- # todo - if SmallOffice then shouldn't have circulating pump
587
- if ['Office', 'PrimarySchool', 'Outpatient', 'Hospital', 'SmallHotel', 'LargeHotel', 'FullServiceRestaurant', 'HighriseApartment'].include?(stds_bldg_type)
588
- service_water_pump_head = OpenStudio.convert(10.0, 'ftH_{2}O', 'Pa').get
589
- service_water_pump_motor_efficiency = 0.3
590
- if circulating.nil? then
591
- irculating = true
592
- end
593
- if pipe_insul_in.nil? then
594
- pipe_insul_in = 0.5
595
- end
596
- elsif ['SecondarySchool'].include?(stds_bldg_type)
597
- service_water_pump_head = OpenStudio.convert(11.4, 'ftH_{2}O', 'Pa').get
426
+ # TODO: Remove hard-coded building-type-based lookups for circulating vs. non-circulating SWH systems
427
+ circulating_bldg_types = [
428
+ # DOE building types
429
+ 'Office',
430
+ 'PrimarySchool',
431
+ 'Outpatient',
432
+ 'Hospital',
433
+ 'SmallHotel',
434
+ 'LargeHotel',
435
+ 'FullServiceRestaurant',
436
+ 'HighriseApartment',
437
+ # DEER building types
438
+ 'Asm', # 'Assembly'
439
+ 'ECC', # 'Education - Community College'
440
+ 'EPr', # 'Education - Primary School'
441
+ 'ERC', # 'Education - Relocatable Classroom'
442
+ 'ESe', # 'Education - Secondary School'
443
+ 'EUn', # 'Education - University'
444
+ 'Gro', # 'Grocery'
445
+ 'Hsp', # 'Health/Medical - Hospital'
446
+ 'Htl', # 'Lodging - Hotel'
447
+ 'MBT', # 'Manufacturing Biotech'
448
+ 'MFm', # 'Residential Multi-family'
449
+ 'Mtl', # 'Lodging - Motel'
450
+ 'Nrs', # 'Health/Medical - Nursing Home'
451
+ 'OfL', # 'Office - Large'
452
+ # 'RFF', # 'Restaurant - Fast-Food'
453
+ 'RSD' # 'Restaurant - Sit-Down'
454
+ ]
455
+ if circulating_bldg_types.include?(stds_bldg_type)
456
+ service_water_pump_head_pa = OpenStudio.convert(10.0, 'ftH_{2}O', 'Pa').get
598
457
  service_water_pump_motor_efficiency = 0.3
599
- if circulating.nil? then
600
- irculating = true
601
- end
602
- if pipe_insul_in.nil? then
603
- pipe_insul_in = 0.5
604
- end
458
+ circulating = true if circulating.nil?
459
+ pipe_insul_in = 0.5 if pipe_insul_in.nil?
605
460
  else # values for non-circulating pump
606
- service_water_pump_head = 0.01
461
+ service_water_pump_head_pa = 0.01
607
462
  service_water_pump_motor_efficiency = 1.0
608
- if circulating.nil? then
609
- irculating = false
610
- end
611
- if pipe_insul_in.nil? then
612
- pipe_insul_in = 0.0
613
- end
614
- end
615
-
616
- # TODO: - add building type or sice specific logic or just assume Gas? (SmallOffice and Warehouse are only non unit prototypes with Electric heating)
617
- water_heater_fuel = if fuel.nil?
618
- 'Gas'
619
- else
620
- fuel
621
- end
622
-
623
- bldg_type_floor_area = 0.0
624
- space_type_hash.sort.each do |space_type, hash|
625
- next if hash[:stds_bldg_type] != stds_bldg_type
626
- bldg_type_floor_area += hash[:floor_area]
627
- end
628
-
629
- # inputs for find_water_heater_capacity_volume_and_parasitic
630
- pipe_hash = {}
631
- pipe_hash[:floor_area] = bldg_type_floor_area
632
- pipe_hash[:effective_num_stories] = bldg_effective_num_stories * (bldg_type_floor_area / bldg_floor_area)
633
- pipe_hash[:circulating] = circulating
634
- pipe_hash[:insulation_thickness] = pipe_insul_in
635
-
636
- # find_water_heater_capacity_volume_and_parasitic
637
- water_heater_sizing = model_find_water_heater_capacity_volume_and_parasitic(model, water_use_equipment_array, pipe_hash)
638
- water_heater_capacity = water_heater_sizing[:water_heater_capacity]
639
- water_heater_volume = water_heater_sizing[:water_heater_volume]
640
- parasitic_fuel_consumption_rate = water_heater_sizing[:parasitic_fuel_consumption_rate]
641
- if parasitic_fuel_consumption_rate > 0
642
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Adding parasitic loss for #{stds_bldg_type} loop of #{parasitic_fuel_consumption_rate.round} Btu/hr.")
463
+ circulating = false if circulating.nil?
464
+ pipe_insul_in = 0.0 if pipe_insul_in.nil?
643
465
  end
644
466
 
645
- # make loop for each unit and add on water use equipment
646
- shared_hot_water_loop = model_add_swh_loop(model,
647
- system_name,
648
- water_heater_thermal_zone,
649
- water_heater_temp_si,
650
- service_water_pump_head,
651
- service_water_pump_motor_efficiency,
652
- water_heater_capacity,
653
- water_heater_volume,
654
- water_heater_fuel,
655
- parasitic_fuel_consumption_rate,
656
- stds_bldg_type)
657
-
658
- # find water heater
659
- shared_hot_water_loop.supplyComponents.sort.each do |component|
660
- next if component.to_WaterHeaterMixed.empty?
661
- water_heater = component.to_WaterHeaterMixed.get
662
-
663
- # apply efficiency to hot water heater
664
- water_heater_mixed_apply_efficiency(water_heater)
467
+ bldg_type_floor_area_m2 = 0.0
468
+ space_type_hash.sort.each do |space_type, space_type_props|
469
+ bldg_type_floor_area_m2 += space_type_props[:floor_area] if space_type_props[:stds_bldg_type] == stds_bldg_type
665
470
  end
666
471
 
667
- # loop through water use equipment
472
+ # Calculate the number of stories covered by this building type
473
+ num_stories = bldg_effective_num_stories * (bldg_type_floor_area_m2 / bldg_floor_area_m2)
474
+
475
+ # Water heater sizing
476
+ water_heater_sizing = model_find_water_heater_capacity_volume_and_parasitic(model, water_use_equipment_array)
477
+ water_heater_capacity_w = water_heater_sizing[:water_heater_capacity]
478
+ water_heater_volume_m3 = water_heater_sizing[:water_heater_volume]
479
+
480
+ # Add a shared service water heating loop with water heater
481
+ shared_swh_loop = model_add_swh_loop(model,
482
+ "#{stds_bldg_type} Shared Service Water Loop",
483
+ water_heater_thermal_zone=nil,
484
+ water_heater_temp_c,
485
+ service_water_pump_head_pa,
486
+ service_water_pump_motor_efficiency,
487
+ water_heater_capacity_w,
488
+ water_heater_volume_m3,
489
+ water_heater_fuel,
490
+ parasitic_fuel_consumption_rate_w=0,
491
+ add_pipe_losses=true,
492
+ floor_area_served=bldg_type_floor_area_m2,
493
+ number_of_stories=num_stories,
494
+ pipe_insulation_thickness=OpenStudio.convert(pipe_insul_in, 'in', 'm').get)
495
+
496
+ # Attach all water use equipment to the shared loop
668
497
  water_use_equipment_array.sort.each do |water_use_equip|
669
- # add water use connection
670
- water_use_connection = OpenStudio::Model::WaterUseConnections.new(model)
671
- water_use_connection.addWaterUseEquipment(water_use_equip)
672
- water_use_connection.setName(water_use_equip.name.get.gsub('SWH', 'WUC'))
673
-
674
- # Connect the water use connection to the SWH loop
675
- shared_hot_water_loop.addDemandBranchForComponent(water_use_connection)
498
+ swh_connection = water_use_equip.waterUseConnections
499
+ shared_swh_loop.addDemandBranchForComponent(swh_connection.get) if swh_connection.is_initialized
676
500
  end
677
501
 
678
502
  # add to list of systems
679
- swh_systems << shared_hot_water_loop
503
+ swh_systems << shared_swh_loop
680
504
 
681
505
  OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Adding shared water heating loop for #{stds_bldg_type}.")
682
506
  end
@@ -684,21 +508,28 @@ class Standard
684
508
  return swh_systems
685
509
  end
686
510
 
687
- # set capacity, volume, and parasitic
511
+ # Use rules from DOE Prototype Building documentation to determine water heater capacity,
512
+ # volume, pipe dump losses, and pipe thermal losses.
688
513
  #
689
514
  # @param water_use_equipment_array [Array] array of water use equipment objects that will be using this water heater
690
- # @param storage_to_cap_ratio [Double] gal of storage to kBtu/hr of capacitiy
691
- # @param htg_eff [Double] fraction
692
- # @param inlet_temp_ip [Double] cold water temperature F
693
- # @param target_temp_ip [Double] F
515
+ # @param storage_to_cap_ratio_gal_to_kbtu_per_hr [Double] storage volume gal to kBtu/hr of capacity
516
+ # @param htg_eff [Double] water heater thermal efficiency, fraction
517
+ # @param inlet_temp_f [Double] inlet cold water temperature, degrees Fahrenheit
518
+ # @param target_temp_f [Double] target supply water temperatre from the tank, degrees Fahrenheit
694
519
  # @return [Hash] hash with values needed to size water heater made with downstream method
695
- def model_find_water_heater_capacity_volume_and_parasitic(model, water_use_equipment_array, pipe_hash = {}, storage_to_cap_ratio = 1.0, htg_eff = 0.8, inlet_temp_ip = 40.0, target_temp_ip = 140.0, peak_flow_fraction = 1.0)
520
+ def model_find_water_heater_capacity_volume_and_parasitic(model,
521
+ water_use_equipment_array,
522
+ storage_to_cap_ratio_gal_to_kbtu_per_hr: 1.0,
523
+ htg_eff: 0.8,
524
+ inlet_temp_f: 40.0,
525
+ target_temp_f: 140.0,
526
+ peak_flow_fraction: 1.0)
696
527
  # A.1.4 Total Storage Volume and Water Heater Capacity of PrototypeModelEnhancements_2014_0.pdf shows 1 gallon of storage to 1 kBtu/h of capacity
697
528
 
698
529
  water_heater_sizing = {}
699
530
 
700
- # get water use equipment
701
- max_flow_rate_array = [] # gallons per hour
531
+ # Get the maximum flow rates for all pieces of water use equipment
532
+ adjusted_max_flow_rates_gal_per_hr = [] # gallons per hour
702
533
  water_use_equipment_array.sort.each do |water_use_equip|
703
534
  water_use_equip_sch = water_use_equip.flowRateFractionSchedule
704
535
  next if water_use_equip_sch.empty?
@@ -712,78 +543,39 @@ class Standard
712
543
  elsif water_use_equip_sch.to_ScheduleCompact.is_initialized
713
544
  water_use_equip_sch = water_use_equip_sch.to_ScheduleCompact.get
714
545
  max_sch_value = schedule_compact_annual_min_max_value(water_use_equip_sch)['max']
546
+ else
547
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "The peak flow rate fraction for #{water_use_equip_sch.name} could not be determined, assuming 1 for water heater sizing purposes.")
548
+ max_sch_value = 1.0
715
549
  end
716
550
 
717
- # get water_use_equip_def to get max flow rate
718
- water_use_equip_def = water_use_equip.waterUseEquipmentDefinition
719
- peak_flow_rate = water_use_equip_def.peakFlowRate
720
-
721
- # calculate adjusted flow rate
722
- adjusted_peak_flow_rate_si = max_sch_value * peak_flow_rate
723
- adjusted_peak_flow_rate_ip = OpenStudio.convert(adjusted_peak_flow_rate_si, 'm^3/s', 'gal/min').get
724
- max_flow_rate_array << adjusted_peak_flow_rate_ip * 60.0 # min per hour
725
- end
551
+ # Get peak flow rate from water use equipment definition
552
+ peak_flow_rate_m3_per_s = water_use_equip.waterUseEquipmentDefinition.peakFlowRate
726
553
 
727
- # warn if max_flow_rate_array size doesn't match equipment size (one or more didn't have ruleset schedule)
728
- if max_flow_rate_array.size != water_use_equipment_array.size
729
- OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', 'One or more Water Use Equipment Fraction Flow Rate Scheules were not Schedule Rulestes and were excluding from Water Heating Sizing.')
554
+ # Calculate adjusted flow rate based on the peak fraction found in the flow rate fraction schedule
555
+ adjusted_peak_flow_rate_m3_per_s = max_sch_value * peak_flow_rate_m3_per_s
556
+ adjusted_max_flow_rates_gal_per_hr << OpenStudio.convert(adjusted_peak_flow_rate_m3_per_s, 'm^3/s', 'gal/hr').get
730
557
  end
731
558
 
732
- # sum gpm values from water use equipment to use in formula
733
- adjusted_flow_rate_sum = max_flow_rate_array.inject(:+)
559
+ # Sum gph values from water use equipment to use in formula
560
+ total_adjusted_flow_rate_gal_per_hr = adjusted_max_flow_rates_gal_per_hr.inject(:+)
734
561
 
735
- # use formula to calculate volume and capacity based on analysis of combined water use equipment maximum flow rates and schedules
562
+ # Calculate capacity based on analysis of combined water use equipment maximum flow rates and schedules
736
563
  # Max gal/hr * 8.4 lb/gal * 1 Btu/lb F * (120F - 40F)/0.8 = Btu/hr
737
- water_heater_capacity_ip = peak_flow_fraction * adjusted_flow_rate_sum * 8.4 * 1.0 * (target_temp_ip - inlet_temp_ip) / htg_eff
738
- OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Capacity of #{water_heater_capacity_ip} Btu/hr = #{peak_flow_fraction} peak fraction * #{adjusted_flow_rate_sum.round} gal/hr * 8.4 lb/gal * 1.0 Btu/lb F * (#{target_temp_ip.round} - #{inlet_temp_ip.round} deltaF / #{htg_eff} htg eff).")
739
- water_heater_capacity_si = OpenStudio.convert(water_heater_capacity_ip, 'Btu/hr', 'W').get
740
- # Assume 1 gal of volume per 1 kBtu/hr of heating capacity
741
- water_heater_volume_ip = OpenStudio.convert(water_heater_capacity_ip, 'Btu/hr', 'kBtu/hr').get
564
+ water_heater_capacity_btu_per_hr = peak_flow_fraction * total_adjusted_flow_rate_gal_per_hr * 8.4 * 1.0 * (target_temp_f - inlet_temp_f) / htg_eff
565
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Capacity of #{water_heater_capacity_btu_per_hr.round} Btu/hr = #{peak_flow_fraction} peak fraction * #{total_adjusted_flow_rate_gal_per_hr.round} gal/hr * 8.4 lb/gal * 1.0 Btu/lb F * (#{target_temp_f.round} - #{inlet_temp_f.round} deltaF / #{htg_eff} htg eff).")
566
+ water_heater_capacity_m3_per_s = OpenStudio.convert(water_heater_capacity_btu_per_hr, 'Btu/hr', 'W').get
567
+
568
+ # Calculate volume based on capacity
569
+ # Default assumption is 1 gal of volume per 1 kBtu/hr of heating capacity
570
+ water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity_btu_per_hr, 'Btu/hr', 'kBtu/hr').get
571
+ water_heater_volume_gal = water_heater_capacity_kbtu_per_hr * storage_to_cap_ratio_gal_to_kbtu_per_hr
742
572
  # increase tank size to 40 galons if calculated value is smaller
743
- if water_heater_volume_ip < 40.0 # gal
744
- water_heater_volume_ip = 40.0
745
- end
746
- water_heater_volume_si = OpenStudio.convert(water_heater_volume_ip, 'gal', 'm^3').get
747
-
748
- # populate return hash
749
- water_heater_sizing[:water_heater_capacity] = water_heater_capacity_si
750
- water_heater_sizing[:water_heater_volume] = water_heater_volume_si
751
-
752
- # get pipe length (formula from A.3.1 PrototypeModelEnhancements_2014_0.pdf)
753
- if !pipe_hash.empty?
754
-
755
- pipe_length = 2.0 * (Math.sqrt(pipe_hash[:floor_area] / pipe_hash[:effective_num_stories]) + (10.0 * (pipe_hash[:effective_num_stories] - 1.0)))
756
- pipe_length_ip = OpenStudio.convert(pipe_length, 'm', 'ft').get
757
-
758
- # calculate pipe dump (from A.4.1)
759
- pipe_dump = pipe_length_ip * 0.689 # Btu/hr
760
-
761
- pipe_loss_per_foot = if pipe_hash[:circulating]
762
- if pipe_hash[:insulation_thickness] >= 1.0
763
- 16.10
764
- elsif pipe_hash[:insulation_thickness] >= 0.5
765
- 17.5
766
- else
767
- 30.8
768
- end
769
- else
770
- if pipe_hash[:insulation_thickness] >= 1.0
771
- 11.27
772
- elsif pipe_hash[:insulation_thickness] >= 0.5
773
- 12.25
774
- else
775
- 28.07
776
- end
777
- end
778
-
779
- # calculate pipe loss (from Table A.3 in section A.4.2)
780
- pipe_loss = pipe_length * pipe_loss_per_foot # Btu/hr
781
-
782
- # calculate parasitic loss
783
- water_heater_sizing[:parasitic_fuel_consumption_rate] = pipe_dump + pipe_loss
784
- else
785
- water_heater_sizing[:parasitic_fuel_consumption_rate] = 0.0
786
- end
573
+ water_heater_volume_gal = 40.0 if water_heater_volume_gal < 40.0 # gal
574
+ water_heater_volume_m3 = OpenStudio.convert(water_heater_volume_gal, 'gal', 'm^3').get
575
+
576
+ # Populate return hash
577
+ water_heater_sizing[:water_heater_capacity] = water_heater_capacity_m3_per_s
578
+ water_heater_sizing[:water_heater_volume] = water_heater_volume_m3
787
579
 
788
580
  return water_heater_sizing
789
581
  end