openstudio-standards 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/data/standards/OpenStudio_Standards-ashrae_90_1.xlsx +0 -0
  3. data/data/standards/manage_OpenStudio_Standards.rb +2 -49
  4. data/data/standards/openstudio_standards_duplicates_log.csv +1 -7962
  5. data/data/standards/test_performance_expected_dd_results.csv +2005 -97
  6. data/lib/openstudio-standards/create_typical/space_type_ratios.rb +47 -57
  7. data/lib/openstudio-standards/geometry/create.rb +8 -2
  8. data/lib/openstudio-standards/geometry/create_bar.rb +6 -3
  9. data/lib/openstudio-standards/geometry/create_shape.rb +1 -1
  10. data/lib/openstudio-standards/geometry/group.rb +1 -1
  11. data/lib/openstudio-standards/geometry/information.rb +1 -1
  12. data/lib/openstudio-standards/geometry/modify.rb +53 -1
  13. data/lib/openstudio-standards/infiltration/nist_infiltration.rb +1 -1
  14. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +11 -11
  15. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +11 -11
  16. data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.hvac_systems.rb +2 -2
  17. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SuperTallBuilding.rb +44 -47
  18. data/lib/openstudio-standards/prototypes/common/buildings/Prototype.TallBuilding.rb +43 -48
  19. data/lib/openstudio-standards/prototypes/common/objects/Prototype.CentralAirSourceHeatPump.rb +1 -1
  20. data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +44 -24
  21. data/lib/openstudio-standards/prototypes/common/objects/Prototype.refrigeration.rb +24 -24
  22. data/lib/openstudio-standards/schedules/information.rb +1 -1
  23. data/lib/openstudio-standards/schedules/parametric.rb +1 -1
  24. data/lib/openstudio-standards/service_water_heating/create_piping_losses.rb +152 -0
  25. data/lib/openstudio-standards/service_water_heating/create_water_heater.rb +544 -0
  26. data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +303 -0
  27. data/lib/openstudio-standards/service_water_heating/create_water_use.rb +95 -0
  28. data/lib/openstudio-standards/space/space.rb +1 -1
  29. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +65 -70
  30. data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +12 -14
  31. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +16 -5
  32. data/lib/openstudio-standards/standards/Standards.Model.rb +2 -2
  33. data/lib/openstudio-standards/standards/Standards.PlanarSurface.rb +10 -2
  34. data/lib/openstudio-standards/{prototypes/common/objects/Prototype.Model.swh.rb → standards/Standards.ServiceWaterHeating.rb} +209 -139
  35. data/lib/openstudio-standards/standards/Standards.Surface.rb +1 -1
  36. data/lib/openstudio-standards/standards/Standards.ZoneHVACComponent.rb +4 -8
  37. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.Model.rb +2 -2
  38. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +22251 -12963
  39. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_sets.json +91 -91
  40. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8981 -5228
  41. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8935 -5182
  42. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7281 -5391
  43. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_sets.json +91 -91
  44. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +9005 -15215
  45. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_sets.json +136 -136
  46. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +1 -1
  47. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +8717 -17168
  48. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_sets.json +136 -136
  49. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.constructions.json +1941 -651
  50. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.construction_properties.json +135 -135
  51. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.construction_properties.json +135 -135
  52. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/data/nrel_zne_ready_2017.construction_properties.json +36 -36
  53. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/data/ze_aedg_multifamily.construction_properties.json +36 -36
  54. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlantLoop.rb +377 -99
  55. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.SpaceType.rb +2 -2
  56. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.Model.rb +3 -3
  57. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.construction_properties.json +6889 -4044
  58. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/data/ashrae_90_1_prm_2019.prm_constructions.json +108 -108
  59. data/lib/openstudio-standards/standards/cbes/cbes_pre_1978/data/cbes_pre_1978.construction_properties.json +16 -16
  60. data/lib/openstudio-standards/standards/cbes/cbes_t24_1978/data/cbes_t24_1978.construction_properties.json +16 -16
  61. data/lib/openstudio-standards/standards/cbes/cbes_t24_1992/data/cbes_t24_1992.construction_properties.json +16 -16
  62. data/lib/openstudio-standards/standards/cbes/cbes_t24_2001/data/cbes_t24_2001.construction_properties.json +16 -16
  63. data/lib/openstudio-standards/standards/cbes/cbes_t24_2005/data/cbes_t24_2005.construction_properties.json +16 -16
  64. data/lib/openstudio-standards/standards/cbes/cbes_t24_2008/data/cbes_t24_2008.construction_properties.json +16 -16
  65. data/lib/openstudio-standards/standards/cbes/data/cbes.constructions.json +142 -142
  66. data/lib/openstudio-standards/standards/deer/data/deer.constructions.json +5 -1551
  67. data/lib/openstudio-standards/standards/deer/data/deer.materials.json +40 -0
  68. data/lib/openstudio-standards/standards/deer/deer_1985/data/deer_1985.motors.json +88 -8
  69. data/lib/openstudio-standards/standards/deer/deer_1996/data/deer_1996.motors.json +88 -8
  70. data/lib/openstudio-standards/standards/deer/deer_2003/data/deer_2003.motors.json +88 -8
  71. data/lib/openstudio-standards/standards/deer/deer_2007/data/deer_2007.motors.json +88 -8
  72. data/lib/openstudio-standards/standards/deer/deer_2011/data/deer_2011.motors.json +88 -8
  73. data/lib/openstudio-standards/standards/deer/deer_2014/data/deer_2014.motors.json +88 -8
  74. data/lib/openstudio-standards/standards/deer/deer_2015/data/deer_2015.motors.json +88 -8
  75. data/lib/openstudio-standards/standards/deer/deer_2017/data/deer_2017.motors.json +88 -8
  76. data/lib/openstudio-standards/standards/deer/deer_2020/data/deer_2020.motors.json +88 -8
  77. data/lib/openstudio-standards/standards/deer/deer_2025/data/deer_2025.motors.json +88 -8
  78. data/lib/openstudio-standards/standards/deer/deer_2030/data/deer_2030.motors.json +88 -8
  79. data/lib/openstudio-standards/standards/deer/deer_2035/data/deer_2035.motors.json +88 -8
  80. data/lib/openstudio-standards/standards/deer/deer_2040/data/deer_2040.motors.json +88 -8
  81. data/lib/openstudio-standards/standards/deer/deer_2045/data/deer_2045.motors.json +88 -8
  82. data/lib/openstudio-standards/standards/deer/deer_2050/data/deer_2050.motors.json +88 -8
  83. data/lib/openstudio-standards/standards/deer/deer_2055/data/deer_2055.motors.json +88 -8
  84. data/lib/openstudio-standards/standards/deer/deer_2060/data/deer_2060.motors.json +88 -8
  85. data/lib/openstudio-standards/standards/deer/deer_2065/data/deer_2065.motors.json +88 -8
  86. data/lib/openstudio-standards/standards/deer/deer_2070/data/deer_2070.motors.json +88 -8
  87. data/lib/openstudio-standards/standards/deer/deer_2075/data/deer_2075.motors.json +88 -8
  88. data/lib/openstudio-standards/standards/deer/deer_pre_1975/data/deer_pre_1975.motors.json +88 -8
  89. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/btap_pre1980.rb +17 -0
  90. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_systems.rb +2 -1
  91. data/lib/openstudio-standards/standards/necb/ECMS/ecms.rb +4 -4
  92. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +61 -88
  93. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +3 -2
  94. data/lib/openstudio-standards/standards/necb/NECB2011/data/boiler_fuel_type_sets.json +54 -0
  95. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMidriseApartment.osm +1 -1
  96. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +1 -1
  97. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPPointTower.osm +1 -1
  98. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +1 -1
  99. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +4 -4
  100. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +4 -4
  101. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +32 -24
  102. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +89 -15
  103. data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +5 -1
  104. data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +22 -65
  105. data/lib/openstudio-standards/standards/necb/NECB2011/system_fuels.rb +19 -0
  106. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +56 -2
  107. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +3 -1
  108. data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +2 -2
  109. data/lib/openstudio-standards/standards/necb/docs/air_system_names_method.md +127 -0
  110. data/lib/openstudio-standards/thermal_zone/thermal_zone.rb +1 -1
  111. data/lib/openstudio-standards/utilities/template_measure/resources/BTAPMeasureHelper.rb +1 -1
  112. data/lib/openstudio-standards/version.rb +1 -1
  113. data/lib/openstudio-standards/weather/information.rb +61 -5
  114. data/lib/openstudio-standards/weather/modify.rb +1 -1
  115. data/lib/openstudio-standards.rb +5 -3
  116. metadata +12 -63
  117. data/data/standards/OpenStudio_Standards-deer.xlsx +0 -0
  118. data/lib/openstudio-standards/prototypes/common/objects/Prototype.ServiceWaterHeating.rb +0 -1100
  119. data/lib/openstudio-standards/service_water_heating/component.rb +0 -189
@@ -185,16 +185,16 @@ module TallBuilding
185
185
  OpenStudio.logFree(OpenStudio::Error, 'openstudio.model.Model', 'No story info in the SWH loop.')
186
186
  return false
187
187
  end
188
- main_swh_loop = model_add_swh_loop(model,
189
- swh_loop_name,
190
- nil,
191
- OpenStudio.convert(prototype_input['main_service_water_temperature'], 'F', 'C').get,
192
- prototype_input['main_service_water_pump_head'].to_f,
193
- prototype_input['main_service_water_pump_motor_efficiency'],
194
- OpenStudio.convert(prototype_input['main_water_heater_capacity'], 'Btu/hr', 'W').get,
195
- OpenStudio.convert(prototype_input['main_water_heater_volume'], 'gal', 'm^3').get,
196
- swh_fueltype,
197
- OpenStudio.convert(prototype_input['main_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get)
188
+ main_swh_loop = OpenstudioStandards::ServiceWaterHeating.create_service_water_heating_loop(model,
189
+ system_name: swh_loop_name,
190
+ service_water_temperature: OpenStudio.convert(prototype_input['main_service_water_temperature'], 'F', 'C').get,
191
+ service_water_pump_head: prototype_input['main_service_water_pump_head'].to_f,
192
+ service_water_pump_motor_efficiency: prototype_input['main_service_water_pump_motor_efficiency'],
193
+ water_heater_capacity: OpenStudio.convert(prototype_input['main_water_heater_capacity'], 'Btu/hr', 'W').get,
194
+ water_heater_volume: OpenStudio.convert(prototype_input['main_water_heater_volume'], 'gal', 'm^3').get,
195
+ water_heater_fuel: swh_fueltype,
196
+ on_cycle_parasitic_fuel_consumption_rate: OpenStudio.convert(prototype_input['main_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get,
197
+ off_cycle_parasitic_fuel_consumption_rate: OpenStudio.convert(prototype_input['main_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get)
198
198
 
199
199
  # Attach the end uses based on floor function type
200
200
  # Office and retail: add to mechanical room only
@@ -225,11 +225,9 @@ module TallBuilding
225
225
  next if data['service_water_heating_peak_flow_rate'].to_f < 0.00001 && data['service_water_heating_peak_flow_per_area'].to_f < 0.00001
226
226
 
227
227
  # Add a service water use for each space
228
- space_multiplier = space.multiplier
229
228
  water_fixture = model_add_swh_end_uses_by_space(model,
230
229
  main_swh_loop,
231
- space,
232
- space_multiplier)
230
+ space)
233
231
  unless water_fixture.nil?
234
232
  water_fixtures << water_fixture
235
233
  end
@@ -246,47 +244,44 @@ module TallBuilding
246
244
 
247
245
  # Add the booster water loop if there is any hotel floor
248
246
  if additional_params[:num_of_floor_hotel].to_i > 0
249
- swh_booster_loop = model_add_swh_booster(model,
250
- hotel_swh_loop,
251
- OpenStudio.convert(prototype_input['booster_water_heater_capacity'], 'Btu/hr', 'W').get,
252
- OpenStudio.convert(prototype_input['booster_water_heater_volume'], 'gal', 'm^3').get,
253
- prototype_input['booster_water_heater_fuel'],
254
- OpenStudio.convert(prototype_input['booster_water_temperature'], 'F', 'C').get,
255
- 0,
256
- nil)
257
-
258
- # Attach the end uses
259
- model_add_booster_swh_end_uses(model,
260
- swh_booster_loop,
261
- OpenStudio.convert(prototype_input['booster_service_water_peak_flowrate'], 'gal/min', 'm^3/s').get,
262
- prototype_input['booster_service_water_flowrate_schedule'],
263
- OpenStudio.convert(prototype_input['booster_water_use_temperature'], 'F', 'C').get)
264
-
247
+ swh_booster_loop = OpenstudioStandards::ServiceWaterHeating.create_booster_water_heating_loop(model,
248
+ water_heater_capacity: OpenStudio.convert(prototype_input['booster_water_heater_capacity'], 'Btu/hr', 'W').get,
249
+ water_heater_volume: OpenStudio.convert(prototype_input['booster_water_heater_volume'], 'gal', 'm^3').get,
250
+ water_heater_fuel: prototype_input['booster_water_heater_fuel'],
251
+ service_water_temperature: OpenStudio.convert(prototype_input['booster_water_temperature'], 'F', 'C').get,
252
+ service_water_loop: hotel_swh_loop)
253
+
254
+ # add booster water use
255
+ OpenstudioStandards::ServiceWaterHeating.create_water_use(model,
256
+ name: 'Booster',
257
+ flow_rate: OpenStudio.convert(prototype_input['booster_service_water_peak_flowrate'], 'gal/min', 'm^3/s').get,
258
+ flow_rate_fraction_schedule: model_add_schedule(model, prototype_input['booster_service_water_flowrate_schedule']),
259
+ water_use_temperature: OpenStudio.convert(prototype_input['booster_water_use_temperature'], 'F', 'C').get,
260
+ service_water_loop: swh_booster_loop)
265
261
  end
266
262
 
267
263
  # for tall and super tall buildings, there is laundry only if hotel has more than 1 floors
268
264
  # hotel_bot has laundry, if only one floor, doesn't have hotel_bot
269
265
  if additional_params[:num_of_floor_hotel].to_i > 1
270
266
  # Add the laundry service water heating loop
271
- laundry_swh_loop = model_add_swh_loop(model,
272
- 'Laundry Service Water Loop',
273
- nil,
274
- OpenStudio.convert(prototype_input['laundry_service_water_temperature'], 'F', 'C').get,
275
- prototype_input['laundry_service_water_pump_head'].to_f,
276
- prototype_input['laundry_service_water_pump_motor_efficiency'],
277
- OpenStudio.convert(prototype_input['laundry_water_heater_capacity'], 'Btu/hr', 'W').get,
278
- OpenStudio.convert(prototype_input['laundry_water_heater_volume'], 'gal', 'm^3').get,
279
- prototype_input['laundry_water_heater_fuel'],
280
- OpenStudio.convert(prototype_input['laundry_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get)
281
-
282
- # Attach the end uses if specified in prototype inputs
283
- model_add_swh_end_uses(model,
284
- 'Laundry',
285
- laundry_swh_loop,
286
- OpenStudio.convert(prototype_input['laundry_service_water_peak_flowrate'], 'gal/min', 'm^3/s').get,
287
- prototype_input['laundry_service_water_flowrate_schedule'],
288
- OpenStudio.convert(prototype_input['laundry_water_use_temperature'], 'F', 'C').get,
289
- nil)
267
+ laundry_swh_loop = OpenstudioStandards::ServiceWaterHeating.create_service_water_heating_loop(model,
268
+ system_name: 'Laundry Service Water Loop',
269
+ service_water_temperature: OpenStudio.convert(prototype_input['laundry_service_water_temperature'], 'F', 'C').get,
270
+ service_water_pump_head: prototype_input['laundry_service_water_pump_head'].to_f,
271
+ service_water_pump_motor_efficiency: prototype_input['laundry_service_water_pump_motor_efficiency'],
272
+ water_heater_capacity: OpenStudio.convert(prototype_input['laundry_water_heater_capacity'], 'Btu/hr', 'W').get,
273
+ water_heater_volume: OpenStudio.convert(prototype_input['laundry_water_heater_volume'], 'gal', 'm^3').get,
274
+ water_heater_fuel: prototype_input['laundry_water_heater_fuel'],
275
+ on_cycle_parasitic_fuel_consumption_rate: OpenStudio.convert(prototype_input['laundry_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get,
276
+ off_cycle_parasitic_fuel_consumption_rate: OpenStudio.convert(prototype_input['laundry_service_water_parasitic_fuel_consumption_rate'], 'Btu/hr', 'W').get)
277
+
278
+ # add water use
279
+ OpenstudioStandards::ServiceWaterHeating.create_water_use(model,
280
+ name: 'Laundry',
281
+ flow_rate: OpenStudio.convert(prototype_input['laundry_service_water_peak_flowrate'], 'gal/min', 'm^3/s').get,
282
+ flow_rate_fraction_schedule: model_add_schedule(model, prototype_input['laundry_service_water_flowrate_schedule']),
283
+ water_use_temperature: OpenStudio.convert(prototype_input['laundry_water_use_temperature'], 'F', 'C').get,
284
+ service_water_loop: laundry_swh_loop)
290
285
  end
291
286
  return true
292
287
  end
@@ -27,7 +27,7 @@ class Standard
27
27
  end
28
28
 
29
29
  # change equipment name for EMS validity
30
- plant_comp.setName(name.gsub(/[ +-.]/, '_'))
30
+ plant_comp.setName(ems_friendly_name(name))
31
31
 
32
32
  # set plant component properties
33
33
  plant_comp.setPlantLoadingMode('MeetsLoadWithNominalCapacityHiOutLimit')
@@ -296,6 +296,10 @@ class Standard
296
296
  # Change the chilled water loop to have a two-way common pipes
297
297
  chilled_water_loop.setCommonPipeSimulation('CommonPipe')
298
298
  elsif pri_sec_config == 'heat_exchanger'
299
+ # Check number of chillers
300
+ if num_chillers > 3
301
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.PlantLoop', "EMS Code for multiple chiller pump has not been written for greater than 3 chillers. This has #{num_chillers} chillers")
302
+ end
299
303
  # NOTE: PRECONDITIONING for `const_pri_var_sec` pump type is only applicable for PRM routine and only applies to System Type 7 and System Type 8
300
304
  # See: model_add_prm_baseline_system under Model object.
301
305
  # In this scenario, we will need to create a primary and secondary configuration:
@@ -309,10 +313,15 @@ class Standard
309
313
  secondary_chilled_water_loop.setName(secondary_loop_name)
310
314
  chw_sizing_control(model, secondary_chilled_water_loop, dsgn_sup_wtr_temp, dsgn_sup_wtr_temp_delt)
311
315
  chilled_water_loop.additionalProperties.setFeature('is_primary_loop', true)
316
+ chilled_water_loop.additionalProperties.setFeature('secondary_loop_name', secondary_chilled_water_loop.name.to_s)
312
317
  secondary_chilled_water_loop.additionalProperties.setFeature('is_secondary_loop', true)
313
- # primary chilled water pump
318
+ # primary chilled water pumps are added when adding chillers
314
319
  # Add Constant pump, in plant loop, the number of chiller adjustment will assign pump to each chiller
315
- pri_chw_pump = OpenStudio::Model::PumpConstantSpeed.new(model)
320
+ # pri_chw_pump = OpenStudio::Model::PumpConstantSpeed.new(model)
321
+ pri_chw_pump = OpenStudio::Model::PumpVariableSpeed.new(model)
322
+ pump_variable_speed_set_control_type(pri_chw_pump, control_type = 'Riding Curve')
323
+ # This pump name is important for function add_ems_for_multiple_chiller_pumps_w_secondary_plant. If you update
324
+ # it here, you must update the logic there to account for this
316
325
  pri_chw_pump.setName("#{chilled_water_loop.name} Primary Pump")
317
326
  # Will need to adjust the pump power after a sizing run
318
327
  pri_chw_pump.setRatedPumpHead(OpenStudio.convert(15.0, 'ftH_{2}O', 'Pa').get / num_chillers)
@@ -375,8 +384,23 @@ class Standard
375
384
  dist_clg.autosizeNominalCapacity
376
385
  chilled_water_loop.addSupplyBranchForComponent(dist_clg)
377
386
  else
387
+
388
+ # use default efficiency from 90.1-2019
389
+ # 1.188 kw/ton for a 150 ton AirCooled chiller
390
+ # 0.66 kw/ton for a 150 ton Water Cooled positive displacement chiller
391
+ case chiller_cooling_type
392
+ when 'AirCooled'
393
+ default_cop = kw_per_ton_to_cop(1.188)
394
+ when 'WaterCooled'
395
+ default_cop = kw_per_ton_to_cop(0.66)
396
+ else
397
+ default_cop = kw_per_ton_to_cop(0.66)
398
+ end
399
+
378
400
  # make the correct type of chiller based these properties
379
401
  chiller_sizing_factor = (1.0 / num_chillers).round(2)
402
+
403
+ # Create chillers and set plant operation scheme
380
404
  num_chillers.times do |i|
381
405
  chiller = OpenStudio::Model::ChillerElectricEIR.new(model)
382
406
  chiller.setName("#{template} #{chiller_cooling_type} #{chiller_condenser_type} #{chiller_compressor_type} Chiller #{i}")
@@ -391,18 +415,6 @@ class Standard
391
415
  chiller.setMinimumUnloadingRatio(0.25)
392
416
  chiller.setChillerFlowMode('ConstantFlow')
393
417
  chiller.setSizingFactor(chiller_sizing_factor)
394
-
395
- # use default efficiency from 90.1-2019
396
- # 1.188 kw/ton for a 150 ton AirCooled chiller
397
- # 0.66 kw/ton for a 150 ton Water Cooled positive displacement chiller
398
- case chiller_cooling_type
399
- when 'AirCooled'
400
- default_cop = kw_per_ton_to_cop(1.188)
401
- when 'WaterCooled'
402
- default_cop = kw_per_ton_to_cop(0.66)
403
- else
404
- default_cop = kw_per_ton_to_cop(0.66)
405
- end
406
418
  chiller.setReferenceCOP(default_cop)
407
419
 
408
420
  # connect the chiller to the condenser loop if one was supplied
@@ -668,11 +680,10 @@ class Standard
668
680
  summer_oat_wbs_f << OpenStudio.convert(summer_oat_wb_c, 'C', 'F').get
669
681
  else
670
682
  if dd.wetBulbOrDewPointAtMaximumDryBulb.is_initialized
671
- summer_oat_wb_c = dd.wetBulbOrDewPointAtMaximumDryBulb
683
+ summer_oat_wb_c = dd.wetBulbOrDewPointAtMaximumDryBulb.get
672
684
  summer_oat_wbs_f << OpenStudio.convert(summer_oat_wb_c, 'C', 'F').get
673
685
  end
674
686
  end
675
-
676
687
  end
677
688
  end
678
689
  end
@@ -1020,22 +1031,25 @@ class Standard
1020
1031
  loop_stpt_manager.setName("#{ground_hx_loop.name} Supply Outlet Setpoint")
1021
1032
  loop_stpt_manager.addToNode(ground_hx_loop.supplyOutletNode)
1022
1033
 
1034
+ # edit name to be EMS friendly
1035
+ ground_hx_ems_name = ems_friendly_name(ground_hx.name)
1036
+
1023
1037
  # sensor to read supply inlet temperature
1024
1038
  inlet_temp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model,
1025
1039
  'System Node Temperature')
1026
- inlet_temp_sensor.setName("#{ground_hx.name.to_s.gsub(/[ +-.]/, '_')} Inlet Temp Sensor")
1040
+ inlet_temp_sensor.setName("#{ground_hx_ems_name} Inlet Temp Sensor")
1027
1041
  inlet_temp_sensor.setKeyName(ground_hx_loop.supplyInletNode.handle.to_s)
1028
1042
 
1029
1043
  # actuator to set supply outlet temperature
1030
1044
  outlet_temp_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(hx_temp_sch,
1031
1045
  'Schedule:Constant',
1032
1046
  'Schedule Value')
1033
- outlet_temp_actuator.setName("#{ground_hx.name} Outlet Temp Actuator")
1047
+ outlet_temp_actuator.setName("#{ground_hx_ems_name} Outlet Temp Actuator")
1034
1048
 
1035
1049
  # program to control outlet temperature
1036
1050
  # adjusts delta-t based on calculation of slope and intercept from control temperatures
1037
1051
  program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
1038
- program.setName("#{ground_hx.name.to_s.gsub(/[ +-.]/, '_')} Temperature Control")
1052
+ program.setName("#{ground_hx_ems_name} Temperature Control")
1039
1053
  program_body = <<-EMS
1040
1054
  SET Tin = #{inlet_temp_sensor.handle}
1041
1055
  SET Tout = #{slope_c_per_c.round(2)} * Tin + #{intercept_c.round(1)}
@@ -1045,7 +1059,7 @@ class Standard
1045
1059
 
1046
1060
  # program calling manager
1047
1061
  pcm = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
1048
- pcm.setName("#{program.name.to_s.gsub(/[ +-.]/, '_')} Calling Manager")
1062
+ pcm.setName("#{program.name} Calling Manager")
1049
1063
  pcm.setCallingPoint('InsideHVACSystemIterationLoop')
1050
1064
  pcm.addProgram(program)
1051
1065
 
@@ -4451,20 +4465,20 @@ class Standard
4451
4465
  # Create a sensor to read the zone load
4452
4466
  zn_load_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model,
4453
4467
  'Zone Predicted Sensible Load to Cooling Setpoint Heat Transfer Rate')
4454
- zn_load_sensor.setName("#{zone_name_clean.to_s.gsub(/[ +-.]/, '_')} Clg Load Sensor")
4468
+ zn_load_sensor.setName("#{ems_friendly_name(zone_name_clean)} Clg Load Sensor")
4455
4469
  zn_load_sensor.setKeyName(zone.handle.to_s)
4456
4470
 
4457
4471
  # Create an actuator to set the airloop availability
4458
4472
  air_loop_avail_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(air_loop_avail_sch,
4459
4473
  'Schedule:Constant',
4460
4474
  'Schedule Value')
4461
- air_loop_avail_actuator.setName("#{air_loop.name.to_s.gsub(/[ +-.]/, '_')} Availability Actuator")
4475
+ air_loop_avail_actuator.setName("#{ems_friendly_name(air_loop.name)} Availability Actuator")
4462
4476
 
4463
4477
  # Create a program to turn on Evap Cooler if
4464
4478
  # there is a cooling load in the target zone.
4465
4479
  # Load < 0.0 is a cooling load.
4466
4480
  avail_program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
4467
- avail_program.setName("#{air_loop.name.to_s.gsub(/[ +-.]/, '_')} Availability Control")
4481
+ avail_program.setName("#{ems_friendly_name(air_loop.name)} Availability Control")
4468
4482
  avail_program_body = <<-EMS
4469
4483
  IF #{zn_load_sensor.handle} < 0.0
4470
4484
  SET #{air_loop_avail_actuator.handle} = 1
@@ -4477,10 +4491,16 @@ class Standard
4477
4491
  programs << avail_program
4478
4492
 
4479
4493
  # Direct Evap Cooler
4480
- # @todo better assumptions for evap cooler performance and fan pressure rise
4494
+ # @todo better assumptions for fan pressure rise
4481
4495
  evap = OpenStudio::Model::EvaporativeCoolerDirectResearchSpecial.new(model, model.alwaysOnDiscreteSchedule)
4482
4496
  evap.setName("#{zone.name} Evap Media")
4497
+ # assume 90% design effectiveness from https://basc.pnnl.gov/resource-guides/evaporative-cooling-systems#edit-group-description
4498
+ evap.setCoolerDesignEffectiveness(0.90)
4483
4499
  evap.autosizePrimaryAirDesignFlowRate
4500
+ evap.autosizeRecirculatingWaterPumpPowerConsumption
4501
+ # use suggested E+ default values of 90.0 W-s/m^3 for pump sizing factor and 3.0 for blowdown concentration
4502
+ evap.setWaterPumpPowerSizingFactor(90.0)
4503
+ evap.setBlowdownConcentrationRatio(3.0)
4484
4504
  evap.addToNode(air_loop.supplyInletNode)
4485
4505
 
4486
4506
  # Fan (cycling), must be inside unitary system to cycle on airloop
@@ -612,11 +612,11 @@ class Standard
612
612
 
613
613
  # Add defrost and dripdown schedules
614
614
  defrost_sch = OpenStudio::Model::ScheduleRuleset.new(model)
615
- defrost_sch.setName('Refrigeration Defrost Schedule')
616
- defrost_sch.defaultDaySchedule.setName("Refrigeration Defrost Schedule Default - #{case_type}")
615
+ defrost_sch.setName("#{ref_case.name} Defrost")
616
+ defrost_sch.defaultDaySchedule.setName("#{ref_case.name} Defrost Default")
617
617
  dripdown_sch = OpenStudio::Model::ScheduleRuleset.new(model)
618
- dripdown_sch.setName('Refrigeration Dripdown Schedule')
619
- dripdown_sch.defaultDaySchedule.setName("Refrigeration Dripdown Schedule Default - #{case_type}")
618
+ dripdown_sch.setName("#{ref_case.name} Dripdown")
619
+ dripdown_sch.defaultDaySchedule.setName("#{ref_case.name} Dripdown Default")
620
620
 
621
621
  # Stagger the defrosts for cases by 1 hr
622
622
  interval_defrost = (24 / numb_defrosts_per_day).floor # Hour interval between each defrost period
@@ -630,9 +630,9 @@ class Standard
630
630
  (1..numb_defrosts_per_day).each do |defrost_of_day|
631
631
  def_start_hr = first_def_start_hr + ((1 - defrost_of_day) * interval_defrost)
632
632
  defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, 0, 0), 0)
633
- defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, minutes_defrost.to_int, 0), 0)
633
+ defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, minutes_defrost.to_int, 0), 1)
634
634
  dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, 0, 0), 0) # Dripdown is synced with defrost
635
- dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, minutes_dripdown.to_int, 0), 0)
635
+ dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, minutes_dripdown.to_int, 0), 1)
636
636
  end
637
637
  defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0)
638
638
  dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0)
@@ -698,11 +698,11 @@ class Standard
698
698
 
699
699
  # Add defrost and dripdown schedules
700
700
  defrost_sch_walkin = OpenStudio::Model::ScheduleRuleset.new(model)
701
- defrost_sch_walkin.setName('Refrigeration Defrost Schedule')
702
- defrost_sch_walkin.defaultDaySchedule.setName("Refrigeration Defrost Schedule Default - #{walkin_type}")
701
+ defrost_sch_walkin.setName("#{ref_walkin.name} Defrost")
702
+ defrost_sch_walkin.defaultDaySchedule.setName("#{ref_walkin.name} Defrost Default")
703
703
  dripdown_sch_walkin = OpenStudio::Model::ScheduleRuleset.new(model)
704
- dripdown_sch_walkin.setName('Refrigeration Dripdown Schedule')
705
- dripdown_sch_walkin.defaultDaySchedule.setName("Refrigeration Dripdown Schedule Default - #{walkin_type}")
704
+ dripdown_sch_walkin.setName("#{ref_walkin.name} Dripdown")
705
+ dripdown_sch_walkin.defaultDaySchedule.setName("#{ref_walkin.name} Dripdown Default")
706
706
 
707
707
  # Stagger the defrosts for cases by 1 hr
708
708
  interval_defrost = (24 / numb_defrosts_per_day).floor # Hour interval between each defrost period
@@ -716,9 +716,9 @@ class Standard
716
716
  (1..numb_defrosts_per_day).each do |defrost_of_day|
717
717
  def_start_hr = first_def_start_hr + ((1 - defrost_of_day) * interval_defrost)
718
718
  defrost_sch_walkin.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, 0, 0), 0)
719
- defrost_sch_walkin.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, minutes_defrost.to_int, 0), 0)
719
+ defrost_sch_walkin.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, minutes_defrost.to_int, 0), 1)
720
720
  dripdown_sch_walkin.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, 0, 0), 0) # Dripdown is synced with defrost
721
- dripdown_sch_walkin.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, minutes_dripdown.to_int, 0), 0)
721
+ dripdown_sch_walkin.defaultDaySchedule.addValue(OpenStudio::Time.new(0, def_start_hr, minutes_dripdown.to_int, 0), 1)
722
722
  end
723
723
  defrost_sch_walkin.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0)
724
724
  dripdown_sch_walkin.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0)
@@ -899,22 +899,22 @@ class Standard
899
899
  ########################################
900
900
  # Defrost schedule
901
901
  defrost_sch = OpenStudio::Model::ScheduleRuleset.new(model)
902
- defrost_sch.setName('Refrigeration Defrost Schedule')
903
- defrost_sch.defaultDaySchedule.setName('Refrigeration Defrost Schedule Default')
902
+ defrost_sch.setName("#{ref_case.name} Defrost")
903
+ defrost_sch.defaultDaySchedule.setName("#{ref_case.name} Defrost Default")
904
904
  defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 0, 0), 0)
905
- defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 59, 0), 0)
905
+ defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 59, 0), 1)
906
906
  defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0)
907
907
  # Dripdown schedule
908
908
  dripdown_sch = OpenStudio::Model::ScheduleRuleset.new(model)
909
- dripdown_sch.setName('Refrigeration Defrost Schedule')
910
- dripdown_sch.defaultDaySchedule.setName('Refrigeration Defrost Schedule Default')
909
+ dripdown_sch.setName("#{ref_case.name} Defrost")
910
+ dripdown_sch.defaultDaySchedule.setName("#{ref_case.name} Defrost Default")
911
911
  dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 0, 0), 0)
912
- dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 59, 0), 0)
912
+ dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 59, 0), 1)
913
913
  dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0)
914
914
  # Case Credit Schedule
915
915
  case_credit_sch = OpenStudio::Model::ScheduleRuleset.new(model)
916
- case_credit_sch.setName('Refrigeration Case Credit Schedule')
917
- case_credit_sch.defaultDaySchedule.setName('Refrigeration Case Credit Schedule Default')
916
+ case_credit_sch.setName("#{ref_case.name} Case Credit")
917
+ case_credit_sch.defaultDaySchedule.setName("#{ref_case.name} Case Credit Default")
918
918
  case_credit_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 7, 0, 0), 0.2)
919
919
  case_credit_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 21, 0, 0), 0.4)
920
920
  case_credit_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0.2)
@@ -937,8 +937,8 @@ class Standard
937
937
  ########################################
938
938
  # Defrost schedule
939
939
  defrost_sch = OpenStudio::Model::ScheduleRuleset.new(model)
940
- defrost_sch.setName('Refrigeration Defrost Schedule')
941
- defrost_sch.defaultDaySchedule.setName('Refrigeration Defrost Schedule Default')
940
+ defrost_sch.setName("#{ref_walkin.name} Defrost")
941
+ defrost_sch.defaultDaySchedule.setName("#{ref_walkin.name} Defrost Default")
942
942
  defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 0, 0), 0)
943
943
  defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 59, 0), 1)
944
944
  defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i + 10, 0, 0), 0)
@@ -946,8 +946,8 @@ class Standard
946
946
  defrost_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, 24, 0, 0), 0)
947
947
  # Dripdown schedule
948
948
  dripdown_sch = OpenStudio::Model::ScheduleRuleset.new(model)
949
- dripdown_sch.setName('Refrigeration Defrost Schedule')
950
- dripdown_sch.defaultDaySchedule.setName('Refrigeration Defrost Schedule Default')
949
+ dripdown_sch.setName("#{ref_walkin.name} Defrost")
950
+ dripdown_sch.defaultDaySchedule.setName("#{ref_walkin.name} Defrost Default")
951
951
  dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 0, 0), 0)
952
952
  dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i, 59, 0), 1)
953
953
  dripdown_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0, i + 10, 0, 0), 0)
@@ -365,7 +365,7 @@ module OpenstudioStandards
365
365
  else
366
366
  num_timesteps = model.getTimestep.numberOfTimestepsPerHour
367
367
  day_timeseries = schedule_day.timeSeries.values.to_a
368
- schedule_values = day_timeseries.each_slice(num_timesteps).map { |slice| slice.sum / slice.size.to_f }
368
+ schedule_values = day_timeseries.each_slice(num_timesteps).map { |slice| (slice.sum / slice.size.to_f).round(10) }
369
369
  end
370
370
 
371
371
  unless schedule_values.size == 24
@@ -1056,7 +1056,7 @@ module OpenstudioStandards
1056
1056
  remainder = days_to_fill - value[:days_used]
1057
1057
  day_for_rule = days_to_fill - remainder
1058
1058
  if remainder.size < days_to_fill.size
1059
- autogen_rules[profile_index] = { days_to_fill: day_for_rule, hoo_start: hoo_start, hoo_end: hoo_end}
1059
+ autogen_rules[profile_index] = { days_to_fill: day_for_rule, hoo_start: hoo_start, hoo_end: hoo_end }
1060
1060
  end
1061
1061
  days_to_fill = remainder
1062
1062
  end
@@ -0,0 +1,152 @@
1
+ module OpenstudioStandards
2
+ # The ServiceWaterHeating module provides methods to create, modify, and get information about service water heating
3
+ module ServiceWaterHeating
4
+ # @!group Create Piping Losses
5
+ # Methods to add service water heating piping losses
6
+
7
+ # Adds piping losses to a service water heating Loop.
8
+ # Assumes the piping system use insulated 0.75 inch copper piping.
9
+ # For circulating systems, assume length of piping is proportional to the building floor area and number of stories.
10
+ # For non-circulating systems, assume that the water heaters are close to the point of use.
11
+ #
12
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
13
+ # @param service_water_loop [OpenStudio::Model::PlantLoop] the service water heating loop
14
+ # @param circulating [Boolean] use true for circulating systems, false for non-circulating systems
15
+ # @param pipe_insulation_thickness [Double] the thickness of the pipe insulation, in m. Use 0 for no insulation
16
+ # @param floor_area [Double] the area of building served by the service water heating loop, in m^2
17
+ # If nil, will use the total building floor area. Only used if circulating is true.
18
+ # @param number_of_stories [Integer] the number of stories served by the service water heating loop
19
+ # If nil, will use the total building number of stories. Only used if circulating is true.
20
+ # @param pipe_length [Double] the length of the pipe in meters. Default is 6.1 m / 20 ft.
21
+ # Only used if circulating is false.
22
+ # @param air_temperature [Double] the temperature of the air surrounding the piping, in C. Default is 21.1 C / 70 F.
23
+ # @return [Boolean] returns true if successful, false if not
24
+ def self.create_service_water_heating_piping_losses(model,
25
+ service_water_loop,
26
+ circulating: true,
27
+ pipe_insulation_thickness: 0.0,
28
+ floor_area: nil,
29
+ number_of_stories: nil,
30
+ pipe_length: 6.1,
31
+ air_temperature: 21.1)
32
+
33
+ # Estimate pipe length
34
+ if circulating
35
+ # For circulating systems, get pipe length based on the size of the building.
36
+ # Formula from A.3.1 PrototypeModelEnhancements_2014_0.pdf
37
+
38
+ # get the floor area
39
+ floor_area = model.getBuilding.floorArea if floor_area.nil?
40
+ floor_area_ft2 = OpenStudio.convert(floor_area, 'm^2', 'ft^2').get
41
+
42
+ # get the number of stories
43
+ number_of_stories = model.getBuilding.buildingStories.size if number_of_stories.nil?
44
+
45
+ # calculate the piping length
46
+ pipe_length_ft = 2.0 * (Math.sqrt(floor_area_ft2 / number_of_stories) + (10.0 * (number_of_stories - 1.0)))
47
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Pipe length #{pipe_length_ft.round}ft = 2.0 * ( (#{floor_area_ft2.round}ft2 / #{number_of_stories} stories)^0.5 + (10.0ft * (#{number_of_stories} stories - 1.0) ) )")
48
+ else
49
+ # For non-circulating systems, assume water heater is close to point of use
50
+
51
+ # get pipe length
52
+ pipe_length_m = pipe_length.nil? ? 6.1 : pipe_length
53
+
54
+ pipe_length_ft = OpenStudio.convert(pipe_length_m, 'm', 'ft').get
55
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Pipe length #{pipe_length_ft.round}ft. For non-circulating systems, assume water heater is close to point of use.")
56
+ end
57
+
58
+ # For systems whose water heater object represents multiple pieces
59
+ # of equipment, multiply the piping length by the number of pieces of equipment.
60
+ service_water_loop.supplyComponents('OS_WaterHeater_Mixed'.to_IddObjectType).each do |sc|
61
+ next unless sc.to_WaterHeaterMixed.is_initialized
62
+
63
+ water_heater = sc.to_WaterHeaterMixed.get
64
+
65
+ # get number of water heaters
66
+ if water_heater.additionalProperties.getFeatureAsInteger('component_quantity').is_initialized
67
+ comp_qty = water_heater.additionalProperties.getFeatureAsInteger('component_quantity').get
68
+ else
69
+ comp_qty = 1
70
+ end
71
+
72
+ # if more than 1 water heater, multiply the pipe length by the number of water heaters,
73
+ # unless the user has specified a pipe length
74
+ if comp_qty > 1 && pipe_length.nil?
75
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Piping length has been multiplied by #{comp_qty}X because #{water_heater.name} represents #{comp_qty} pieces of equipment.")
76
+ pipe_length_ft *= comp_qty
77
+ break
78
+ end
79
+ end
80
+
81
+ # Service water heating piping heat loss scheduled air temperature
82
+ air_temperature_f = OpenStudio.convert(air_temperature, 'C', 'F').get
83
+ swh_piping_air_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
84
+ air_temperature,
85
+ name: "#{service_water_loop.name} Piping Air Temp - #{air_temperature_f.round}F",
86
+ schedule_type_limit: 'Temperature')
87
+
88
+ # Service water heating piping heat loss scheduled air velocity
89
+ swh_piping_air_velocity_m_per_s = 0.3
90
+ swh_piping_air_velocity_mph = OpenStudio.convert(swh_piping_air_velocity_m_per_s, 'm/s', 'mile/hr').get
91
+ swh_piping_air_velocity_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
92
+ swh_piping_air_velocity_m_per_s,
93
+ name: "#{service_water_loop.name} Piping Air Velocity - #{swh_piping_air_velocity_mph.round(2)}mph")
94
+
95
+ # Material for 3/4in type L (heavy duty) copper pipe
96
+ copper_pipe = OpenStudio::Model::StandardOpaqueMaterial.new(model)
97
+ copper_pipe.setName('Copper pipe 0.75in type L')
98
+ copper_pipe.setRoughness('Smooth')
99
+ copper_pipe.setThickness(OpenStudio.convert(0.045, 'in', 'm').get)
100
+ copper_pipe.setThermalConductivity(386.0)
101
+ copper_pipe.setDensity(OpenStudio.convert(556, 'lb/ft^3', 'kg/m^3').get)
102
+ copper_pipe.setSpecificHeat(OpenStudio.convert(0.092, 'Btu/lb*R', 'J/kg*K').get)
103
+ copper_pipe.setThermalAbsorptance(0.9) # @todo find reference for property
104
+ copper_pipe.setSolarAbsorptance(0.7) # @todo find reference for property
105
+ copper_pipe.setVisibleAbsorptance(0.7) # @todo find reference for property
106
+
107
+ # Construction for pipe
108
+ pipe_construction = OpenStudio::Model::Construction.new(model)
109
+
110
+ # Add insulation material to insulated pipe
111
+ if pipe_insulation_thickness > 0
112
+ # Material for fiberglass insulation
113
+ # R-value from Owens-Corning 1/2in fiberglass pipe insulation
114
+ # https://www.grainger.com/product/OWENS-CORNING-1-2-Thick-40PP22
115
+ # but modified until simulated heat loss = 17.7 Btu/hr/ft of pipe with 140F water and 70F air
116
+ pipe_insulation_thickness_in = OpenStudio.convert(pipe_insulation_thickness, 'm', 'in').get
117
+ insulation = OpenStudio::Model::StandardOpaqueMaterial.new(model)
118
+ insulation.setName("Fiberglass batt #{pipe_insulation_thickness_in.round(2)}in")
119
+ insulation.setRoughness('Smooth')
120
+ insulation.setThickness(OpenStudio.convert(pipe_insulation_thickness_in, 'in', 'm').get)
121
+ insulation.setThermalConductivity(OpenStudio.convert(0.46, 'Btu*in/hr*ft^2*R', 'W/m*K').get)
122
+ insulation.setDensity(OpenStudio.convert(0.7, 'lb/ft^3', 'kg/m^3').get)
123
+ insulation.setSpecificHeat(OpenStudio.convert(0.2, 'Btu/lb*R', 'J/kg*K').get)
124
+ insulation.setThermalAbsorptance(0.9) # Irrelevant for Pipe:Indoor; no radiation model is used
125
+ insulation.setSolarAbsorptance(0.7) # Irrelevant for Pipe:Indoor; no radiation model is used
126
+ insulation.setVisibleAbsorptance(0.7) # Irrelevant for Pipe:Indoor; no radiation model is used
127
+
128
+ pipe_construction.setName("Copper pipe 0.75in type L with #{pipe_insulation_thickness_in.round(2)}in fiberglass batt")
129
+ pipe_construction.setLayers([insulation, copper_pipe])
130
+ else
131
+ pipe_construction.setName('Uninsulated copper pipe 0.75in type L')
132
+ pipe_construction.setLayers([copper_pipe])
133
+ end
134
+
135
+ heat_loss_pipe = OpenStudio::Model::PipeIndoor.new(model)
136
+ heat_loss_pipe.setName("#{service_water_loop.name} Pipe #{pipe_length_ft.round}ft")
137
+ heat_loss_pipe.setEnvironmentType('Schedule')
138
+ heat_loss_pipe.setAmbientTemperatureSchedule(swh_piping_air_temp_sch)
139
+ heat_loss_pipe.setAmbientAirVelocitySchedule(swh_piping_air_velocity_sch)
140
+ heat_loss_pipe.setConstruction(pipe_construction)
141
+ heat_loss_pipe.setPipeInsideDiameter(OpenStudio.convert(0.785, 'in', 'm').get)
142
+ heat_loss_pipe.setPipeLength(OpenStudio.convert(pipe_length_ft, 'ft', 'm').get)
143
+
144
+ heat_loss_pipe.addToNode(service_water_loop.demandInletNode)
145
+
146
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added #{pipe_length_ft.round}ft of #{pipe_construction.name} losing heat to #{air_temperature_f.round}F air to #{service_water_loop.name}.")
147
+ return true
148
+ end
149
+
150
+ # @!endgroup Create Piping Losses
151
+ end
152
+ end