openstudio-standards 0.6.3 → 0.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) 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 +1 -1
  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/parametric.rb +1 -1
  23. data/lib/openstudio-standards/service_water_heating/create_piping_losses.rb +152 -0
  24. data/lib/openstudio-standards/service_water_heating/create_water_heater.rb +544 -0
  25. data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +303 -0
  26. data/lib/openstudio-standards/service_water_heating/create_water_use.rb +95 -0
  27. data/lib/openstudio-standards/space/space.rb +1 -1
  28. data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +65 -70
  29. data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +12 -14
  30. data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +16 -5
  31. data/lib/openstudio-standards/standards/Standards.Model.rb +2 -2
  32. data/lib/openstudio-standards/standards/Standards.PlanarSurface.rb +10 -2
  33. data/lib/openstudio-standards/{prototypes/common/objects/Prototype.Model.swh.rb → standards/Standards.ServiceWaterHeating.rb} +209 -139
  34. data/lib/openstudio-standards/standards/Standards.Surface.rb +1 -1
  35. data/lib/openstudio-standards/standards/Standards.ZoneHVACComponent.rb +4 -8
  36. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.Model.rb +2 -2
  37. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +22251 -12963
  38. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_sets.json +91 -91
  39. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8981 -5228
  40. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8935 -5182
  41. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7281 -5391
  42. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_sets.json +91 -91
  43. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +9005 -15215
  44. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_sets.json +136 -136
  45. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +1 -1
  46. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +8717 -17168
  47. data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_sets.json +136 -136
  48. data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.constructions.json +1941 -651
  49. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.construction_properties.json +135 -135
  50. data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.construction_properties.json +135 -135
  51. data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/data/nrel_zne_ready_2017.construction_properties.json +36 -36
  52. data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/data/ze_aedg_multifamily.construction_properties.json +36 -36
  53. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlantLoop.rb +377 -99
  54. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.SpaceType.rb +2 -2
  55. data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.Model.rb +3 -3
  56. 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
  57. 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
  58. data/lib/openstudio-standards/standards/cbes/cbes_pre_1978/data/cbes_pre_1978.construction_properties.json +16 -16
  59. data/lib/openstudio-standards/standards/cbes/cbes_t24_1978/data/cbes_t24_1978.construction_properties.json +16 -16
  60. data/lib/openstudio-standards/standards/cbes/cbes_t24_1992/data/cbes_t24_1992.construction_properties.json +16 -16
  61. data/lib/openstudio-standards/standards/cbes/cbes_t24_2001/data/cbes_t24_2001.construction_properties.json +16 -16
  62. data/lib/openstudio-standards/standards/cbes/cbes_t24_2005/data/cbes_t24_2005.construction_properties.json +16 -16
  63. data/lib/openstudio-standards/standards/cbes/cbes_t24_2008/data/cbes_t24_2008.construction_properties.json +16 -16
  64. data/lib/openstudio-standards/standards/cbes/data/cbes.constructions.json +142 -142
  65. data/lib/openstudio-standards/standards/deer/data/deer.constructions.json +5 -1551
  66. data/lib/openstudio-standards/standards/deer/data/deer.materials.json +40 -0
  67. data/lib/openstudio-standards/standards/deer/deer_1985/data/deer_1985.motors.json +88 -8
  68. data/lib/openstudio-standards/standards/deer/deer_1996/data/deer_1996.motors.json +88 -8
  69. data/lib/openstudio-standards/standards/deer/deer_2003/data/deer_2003.motors.json +88 -8
  70. data/lib/openstudio-standards/standards/deer/deer_2007/data/deer_2007.motors.json +88 -8
  71. data/lib/openstudio-standards/standards/deer/deer_2011/data/deer_2011.motors.json +88 -8
  72. data/lib/openstudio-standards/standards/deer/deer_2014/data/deer_2014.motors.json +88 -8
  73. data/lib/openstudio-standards/standards/deer/deer_2015/data/deer_2015.motors.json +88 -8
  74. data/lib/openstudio-standards/standards/deer/deer_2017/data/deer_2017.motors.json +88 -8
  75. data/lib/openstudio-standards/standards/deer/deer_2020/data/deer_2020.motors.json +88 -8
  76. data/lib/openstudio-standards/standards/deer/deer_2025/data/deer_2025.motors.json +88 -8
  77. data/lib/openstudio-standards/standards/deer/deer_2030/data/deer_2030.motors.json +88 -8
  78. data/lib/openstudio-standards/standards/deer/deer_2035/data/deer_2035.motors.json +88 -8
  79. data/lib/openstudio-standards/standards/deer/deer_2040/data/deer_2040.motors.json +88 -8
  80. data/lib/openstudio-standards/standards/deer/deer_2045/data/deer_2045.motors.json +88 -8
  81. data/lib/openstudio-standards/standards/deer/deer_2050/data/deer_2050.motors.json +88 -8
  82. data/lib/openstudio-standards/standards/deer/deer_2055/data/deer_2055.motors.json +88 -8
  83. data/lib/openstudio-standards/standards/deer/deer_2060/data/deer_2060.motors.json +88 -8
  84. data/lib/openstudio-standards/standards/deer/deer_2065/data/deer_2065.motors.json +88 -8
  85. data/lib/openstudio-standards/standards/deer/deer_2070/data/deer_2070.motors.json +88 -8
  86. data/lib/openstudio-standards/standards/deer/deer_2075/data/deer_2075.motors.json +88 -8
  87. data/lib/openstudio-standards/standards/deer/deer_pre_1975/data/deer_pre_1975.motors.json +88 -8
  88. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/btap_pre1980.rb +17 -0
  89. data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_systems.rb +2 -1
  90. data/lib/openstudio-standards/standards/necb/ECMS/ecms.rb +4 -4
  91. data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +61 -88
  92. data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +3 -2
  93. data/lib/openstudio-standards/standards/necb/NECB2011/data/boiler_fuel_type_sets.json +54 -0
  94. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMidriseApartment.osm +1 -1
  95. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +1 -1
  96. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPPointTower.osm +1 -1
  97. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +1 -1
  98. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +4 -4
  99. data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +4 -4
  100. data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +32 -24
  101. data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +89 -15
  102. data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +5 -1
  103. data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +22 -65
  104. data/lib/openstudio-standards/standards/necb/NECB2011/system_fuels.rb +19 -0
  105. data/lib/openstudio-standards/standards/necb/common/btap_data.rb +56 -2
  106. data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +3 -1
  107. data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +2 -2
  108. data/lib/openstudio-standards/standards/necb/docs/air_system_names_method.md +127 -0
  109. data/lib/openstudio-standards/thermal_zone/thermal_zone.rb +1 -1
  110. data/lib/openstudio-standards/utilities/template_measure/resources/BTAPMeasureHelper.rb +1 -1
  111. data/lib/openstudio-standards/version.rb +1 -1
  112. data/lib/openstudio-standards/weather/information.rb +61 -5
  113. data/lib/openstudio-standards/weather/modify.rb +1 -1
  114. data/lib/openstudio-standards.rb +5 -3
  115. metadata +12 -63
  116. data/data/standards/OpenStudio_Standards-deer.xlsx +0 -0
  117. data/lib/openstudio-standards/prototypes/common/objects/Prototype.ServiceWaterHeating.rb +0 -1100
  118. data/lib/openstudio-standards/service_water_heating/component.rb +0 -189
@@ -0,0 +1,544 @@
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 Water Heater
5
+ # Methods to add service water heaters
6
+
7
+ # Creates a water heater and attaches it to the supplied service water heating loop.
8
+ #
9
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
10
+ # @param water_heater_capacity [Double] water heater capacity, in W. Defaults to 58.6 kW / 200 kBtu/hr
11
+ # @param water_heater_volume [Double] water heater volume, in m^3. Defaults to 0.378 m^3 / 100 gal
12
+ # @param water_heater_fuel [String] water heating fuel. Valid choices are 'NaturalGas', 'Electricity', or 'HeatPump'
13
+ # @param on_cycle_parasitic_fuel_consumption_rate [Double] water heater on cycle parasitic fuel consumption rate, in W
14
+ # @param off_cycle_parasitic_fuel_consumption_rate [Double] water heater off cycle parasitic fuel consumption rate, in W
15
+ # @param service_water_temperature [Double] water heater temperature, in degrees C. Default is 60 C / 140 F.
16
+ # @param service_water_temperature_schedule [OpenStudio::Model::Schedule] the service water heating schedule.
17
+ # If nil, will be defaulted to a constant temperature schedule based on the service_water_temperature
18
+ # @param set_peak_use_flowrate [Boolean] if true, the peak flow rate and flow rate schedule will be set.
19
+ # @param peak_flowrate [Double] peak flow rate in m^3/s
20
+ # @param flowrate_schedule [OpenStudio::Model::Schedule] the flow rate fraction schedule
21
+ # @param water_heater_thermal_zone [OpenStudio::Model::ThermalZone] Thermal zone for ambient heat loss.
22
+ # If nil, will assume 71.6 F / 22 C ambient air temperature.
23
+ # @param number_of_water_heaters [Integer] the number of water heaters represented by the capacity and volume inputs.
24
+ # Used to modify efficiencies for water heaters based on individual component size while avoiding having to model
25
+ # lots of individual water heaters (for runtime sake).
26
+ # @param service_water_loop [OpenStudio::Model::PlantLoop] if provided, add the water heater to this loop
27
+ # @return [OpenStudio::Model::WaterHeaterMixed] OpenStudio WaterHeaterMixed object
28
+ def self.create_water_heater(model,
29
+ water_heater_capacity: nil,
30
+ water_heater_volume: nil,
31
+ water_heater_fuel: 'Electricity',
32
+ on_cycle_parasitic_fuel_consumption_rate: 0.0,
33
+ off_cycle_parasitic_fuel_consumption_rate: 0.0,
34
+ service_water_temperature: 60.0,
35
+ service_water_temperature_schedule: nil,
36
+ set_peak_use_flowrate: false,
37
+ peak_flowrate: nil,
38
+ flowrate_schedule: nil,
39
+ water_heater_thermal_zone: nil,
40
+ number_of_water_heaters: 1,
41
+ service_water_loop: nil)
42
+ # create water heater object
43
+ # @todo Standards - Change water heater methodology to follow 'Model Enhancements Appendix A.'
44
+ water_heater = OpenStudio::Model::WaterHeaterMixed.new(model)
45
+
46
+ # default water heater capacity if nil
47
+ if water_heater_capacity.nil?
48
+ water_heater_capacity = OpenStudio.convert(200.0, 'kBtu/hr', 'W').get
49
+ end
50
+ water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity, 'W', 'kBtu/hr').get
51
+ water_heater.setHeaterMaximumCapacity(water_heater_capacity)
52
+
53
+ # default water heater volume if nil
54
+ if water_heater_volume.nil?
55
+ water_heater_volume = OpenStudio.convert(100.0, 'gal', 'm^3').get
56
+ end
57
+ water_heater_volume_gal = OpenStudio.convert(water_heater_volume, 'm^3', 'gal').get
58
+ water_heater.setTankVolume(water_heater_volume)
59
+
60
+ # set the water heater fuel
61
+ case water_heater_fuel
62
+ when 'Natural Gas', 'NaturalGas', 'Gas'
63
+ water_heater.setHeaterFuelType('Gas')
64
+ water_heater.setHeaterThermalEfficiency(0.78)
65
+ water_heater.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate)
66
+ water_heater.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate)
67
+ water_heater.setOnCycleParasiticFuelType('Gas')
68
+ water_heater.setOffCycleParasiticFuelType('Gas')
69
+ water_heater.setOffCycleLossCoefficienttoAmbientTemperature(6.0)
70
+ water_heater.setOnCycleLossCoefficienttoAmbientTemperature(6.0)
71
+ when 'Electricity', 'Electric', 'Elec'
72
+ water_heater.setHeaterFuelType('Electricity')
73
+ water_heater.setHeaterThermalEfficiency(1.0)
74
+ water_heater.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate)
75
+ water_heater.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate)
76
+ water_heater.setOnCycleParasiticFuelType('Electricity')
77
+ water_heater.setOffCycleParasiticFuelType('Electricity')
78
+ water_heater.setOffCycleLossCoefficienttoAmbientTemperature(1.053)
79
+ water_heater.setOnCycleLossCoefficienttoAmbientTemperature(1.053)
80
+ when 'FuelOilNo2'
81
+ water_heater.setHeaterFuelType('FuelOilNo2')
82
+ water_heater.setHeaterThermalEfficiency(0.78)
83
+ water_heater.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate)
84
+ water_heater.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate)
85
+ water_heater.setOnCycleParasiticFuelType('FuelOilNo2')
86
+ water_heater.setOffCycleParasiticFuelType('FuelOilNo2')
87
+ water_heater.setOffCycleLossCoefficienttoAmbientTemperature(6.0)
88
+ water_heater.setOnCycleLossCoefficienttoAmbientTemperature(6.0)
89
+ when 'HeatPump', 'SimpleHeatPump'
90
+ OpenStudio.logFree(OpenStudio::Warn, 'openstudio.standards.ServiceWaterHeating', 'Simple workaround to represent heat pump water heaters without incurring significant runtime penalty associated with using correct objects.')
91
+ # Make a part-load efficiency modifier curve with a value above 1, which is multiplied by the nominal efficiency of 100% to represent the COP of a HPWH.
92
+ # @todo could make this workaround better by using EMS to modify this curve output in realtime based on the OA temperature.
93
+ hpwh_cop = 2.8
94
+ water_heater.setHeaterFuelType('Electricity')
95
+ water_heater.setHeaterThermalEfficiency(1.0)
96
+ eff_f_of_plr = OpenStudio::Model::CurveCubic.new(model)
97
+ eff_f_of_plr.setName("HPWH_COP_#{hpwh_cop}")
98
+ eff_f_of_plr.setCoefficient1Constant(hpwh_cop)
99
+ eff_f_of_plr.setCoefficient2x(0.0)
100
+ eff_f_of_plr.setCoefficient3xPOW2(0.0)
101
+ eff_f_of_plr.setCoefficient4xPOW3(0.0)
102
+ eff_f_of_plr.setMinimumValueofx(0.0)
103
+ eff_f_of_plr.setMaximumValueofx(1.0)
104
+ water_heater.setPartLoadFactorCurve(eff_f_of_plr)
105
+ water_heater.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate)
106
+ water_heater.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate)
107
+ water_heater.setOnCycleParasiticFuelType('Electricity')
108
+ water_heater.setOffCycleParasiticFuelType('Electricity')
109
+ water_heater.setOffCycleLossCoefficienttoAmbientTemperature(1.053)
110
+ water_heater.setOnCycleLossCoefficienttoAmbientTemperature(1.053)
111
+ else
112
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.ServiceWaterHeating', "#{water_heater_fuel} is not a valid water heater fuel. Valid choices are NaturalGas, Electricity, and HeatPump.")
113
+ end
114
+
115
+ # set water temperature properties
116
+ water_heater.setDeadbandTemperatureDifference(2.0)
117
+ water_heater.setDeadbandTemperatureDifference(OpenStudio.convert(3.6, 'R', 'K').get)
118
+ water_heater.setHeaterControlType('Cycle')
119
+ water_heater.setOffCycleParasiticHeatFractiontoTank(0.8)
120
+ water_heater.setIndirectWaterHeatingRecoveryTime(1.5) # 1.5hrs
121
+
122
+ # create service water temperature schedule based on the service_water_temperature if none provided
123
+ if service_water_temperature_schedule.nil?
124
+ swh_temp_c = service_water_temperature
125
+ swh_temp_f = OpenStudio.convert(swh_temp_c, 'C', 'F').get
126
+ service_water_temperature_schedule = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
127
+ swh_temp_c,
128
+ name: "Service Water Loop Temp - #{swh_temp_f.round}F",
129
+ schedule_type_limit: 'Temperature')
130
+ end
131
+ water_heater.setMaximumTemperatureLimit(service_water_temperature)
132
+ water_heater.setSetpointTemperatureSchedule(service_water_temperature_schedule)
133
+
134
+ # set peak flow rate characteristics
135
+ if set_peak_use_flowrate
136
+ water_heater.setPeakUseFlowRate(peak_flowrate) unless peak_flowrate.nil?
137
+ water_heater.setUseFlowRateFractionSchedule(flowrate_schedule) unless flowrate_schedule.nil?
138
+ end
139
+
140
+ # set the water heater ambient conditions
141
+ if water_heater_thermal_zone.nil?
142
+ # assume the water heater is indoors at 71.6F / 22C
143
+ indoor_temp_f = 71.6
144
+ indoor_temp_c = OpenStudio.convert(indoor_temp_f, 'F', 'C').get
145
+ default_water_heater_ambient_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
146
+ indoor_temp_c,
147
+ name: "Water Heater Ambient Temp Schedule #{indoor_temp_f}F",
148
+ schedule_type_limit: 'Temperature')
149
+ water_heater.setAmbientTemperatureIndicator('Schedule')
150
+ water_heater.setAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch)
151
+ water_heater.resetAmbientTemperatureThermalZone
152
+ else
153
+ water_heater.setAmbientTemperatureIndicator('ThermalZone')
154
+ water_heater.setAmbientTemperatureThermalZone(water_heater_thermal_zone)
155
+ water_heater.resetAmbientTemperatureSchedule
156
+ end
157
+
158
+ # assign a quantity to the water heater if it represents multiple water heaters
159
+ if number_of_water_heaters > 1
160
+ water_heater.setName("#{number_of_water_heaters}X #{(water_heater_volume_gal / number_of_water_heaters).round}gal #{water_heater_fuel} Water Heater - #{(water_heater_capacity_kbtu_per_hr / number_of_water_heaters).round}kBtu/hr")
161
+ water_heater.additionalProperties.setFeature('component_quantity', number_of_water_heaters)
162
+ else
163
+ water_heater.setName("#{water_heater_volume_gal.round}gal #{water_heater_fuel} Water Heater - #{water_heater_capacity_kbtu_per_hr.round}kBtu/hr")
164
+ end
165
+
166
+ # add the water heater to the service water loop if provided
167
+ unless service_water_loop.nil?
168
+ service_water_loop.addSupplyBranchForComponent(water_heater)
169
+ end
170
+
171
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added water heater called #{water_heater.name}")
172
+
173
+ return water_heater
174
+ end
175
+
176
+ # Creates a heatpump water heater and attaches it to the supplied service water heating loop.
177
+ #
178
+ # @param model [OpenStudio::Model::Model] OpenStudio model object
179
+ # @param heat_pump_type [String] valid option are 'WrappedCondenser' or 'PumpedCondenser' (default).
180
+ # The 'WrappedCondenser' uses a WaterHeaterStratified tank, 'PumpedCondenser' uses a WaterHeaterMixed tank.
181
+ # @param water_heater_capacity [Double] water heater capacity, in W. Defaults to 500 W / 3.41 kBtu/hr
182
+ # @param water_heater_volume [Double] water heater volume, in m^3. Defaults to 0.303 m^3 / 80 gal
183
+ # @param coefficient_of_performance [Double] rated coefficient_of_performance
184
+ # @param electric_backup_capacity [Double] electric heating backup capacity, in W. Default is 4500 W.
185
+ # @param on_cycle_parasitic_fuel_consumption_rate [Double] water heater on cycle parasitic fuel consumption rate, in W
186
+ # @param off_cycle_parasitic_fuel_consumption_rate [Double] water heater off cycle parasitic fuel consumption rate, in W
187
+ # @param service_water_temperature [Double] water heater temperature, in degrees C. Default is 51.67 C / 125 F.
188
+ # @param service_water_temperature_schedule [OpenStudio::Model::Schedule] the service water heating schedule.
189
+ # If nil, will be defaulted to a constant temperature schedule based on the service_water_temperature
190
+ # @param set_peak_use_flowrate [Boolean] if true, the peak flow rate and flow rate schedule will be set.
191
+ # @param peak_flowrate [Double] peak flow rate in m^3/s
192
+ # @param flowrate_schedule [OpenStudio::Model::Schedule] the flow rate fraction schedule
193
+ # @param water_heater_thermal_zone [OpenStudio::Model::ThermalZone] Thermal zone for ambient heat loss.
194
+ # If nil, will assume 71.6 F / 22 C ambient air temperature.
195
+ # @param service_water_loop [OpenStudio::Model::PlantLoop] if provided, add the water heater to this loop
196
+ # @param use_ems_control [Boolean] if true, use ems control logic if using a 'WrappedCondenser' style HPWH.
197
+ # @return [OpenStudio::Model::WaterHeaterMixed] OpenStudio WaterHeaterMixed object
198
+ def self.create_heatpump_water_heater(model,
199
+ heat_pump_type: 'PumpedCondenser',
200
+ water_heater_capacity: 500.0,
201
+ water_heater_volume: OpenStudio.convert(80.0, 'gal', 'm^3').get,
202
+ coefficient_of_performance: 2.8,
203
+ electric_backup_capacity: 4500.0,
204
+ on_cycle_parasitic_fuel_consumption_rate: 0.0,
205
+ off_cycle_parasitic_fuel_consumption_rate: 0.0,
206
+ service_water_temperature: OpenStudio.convert(125.0, 'F', 'C').get,
207
+ service_water_temperature_schedule: nil,
208
+ set_peak_use_flowrate: false,
209
+ peak_flowrate: nil,
210
+ flowrate_schedule: nil,
211
+ water_heater_thermal_zone: nil,
212
+ service_water_loop: nil,
213
+ use_ems_control: false)
214
+ # create heat pump water heater
215
+ if heat_pump_type == 'WrappedCondenser'
216
+ hpwh = OpenStudio::Model::WaterHeaterHeatPumpWrappedCondenser.new(model)
217
+ elsif heat_pump_type == 'PumpedCondenser'
218
+ hpwh = OpenStudio::Model::WaterHeaterHeatPump.new(model)
219
+ end
220
+
221
+ # calculate tank height and radius
222
+ water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity, 'W', 'kBtu/hr').get
223
+ hpwh_vol_gal = OpenStudio.convert(water_heater_volume, 'm^3', 'gal').get
224
+ tank_height = (0.0188 * hpwh_vol_gal) + 0.0935 # linear relationship that gets GE height at 50 gal and AO Smith height at 80 gal
225
+ tank_radius = (0.9 * water_heater_volume / (Math::PI * tank_height))**0.5
226
+ tank_surface_area = 2.0 * Math::PI * tank_radius * (tank_radius + tank_height)
227
+ tank_ua = 3.9 # default ua assumption
228
+ u_tank = (5.678 * tank_ua) / OpenStudio.convert(tank_surface_area, 'm^2', 'ft^2').get
229
+ hpwh.setName("#{hpwh_vol_gal.round}gal Heat Pump Water Heater - #{water_heater_capacity_kbtu_per_hr.round(0)}kBtu/hr")
230
+
231
+ # set min/max HPWH operating temperature limit
232
+ hpwh_op_min_temp_c = OpenStudio.convert(45.0, 'F', 'C').get
233
+ hpwh_op_max_temp_c = OpenStudio.convert(120.0, 'F', 'C').get
234
+
235
+ if heat_pump_type == 'WrappedCondenser'
236
+ hpwh.setMinimumInletAirTemperatureforCompressorOperation(hpwh_op_min_temp_c)
237
+ hpwh.setMaximumInletAirTemperatureforCompressorOperation(hpwh_op_max_temp_c)
238
+ # set sensor heights
239
+ if hpwh_vol_gal <= 50.0
240
+ hpwh.setDeadBandTemperatureDifference(0.5)
241
+ h_ue = (1 - (3.5 / 12.0)) * tank_height # in the 4th node of the tank (counting from top)
242
+ h_le = (1 - (10.5 / 12.0)) * tank_height # in the 11th node of the tank (counting from top)
243
+ h_condtop = (1 - (5.5 / 12.0)) * tank_height # in the 6th node of the tank (counting from top)
244
+ h_condbot = (1 - (10.99 / 12.0)) * tank_height # in the 11th node of the tank
245
+ h_hpctrl = (1 - (2.5 / 12.0)) * tank_height # in the 3rd node of the tank
246
+ hpwh.setControlSensor1HeightInStratifiedTank(h_hpctrl)
247
+ hpwh.setControlSensor1Weight(1.0)
248
+ hpwh.setControlSensor2HeightInStratifiedTank(h_hpctrl)
249
+ else
250
+ hpwh.setDeadBandTemperatureDifference(3.89)
251
+ h_ue = (1 - (3.5 / 12.0)) * tank_height # in the 3rd node of the tank (counting from top)
252
+ h_le = (1 - (9.5 / 12.0)) * tank_height # in the 10th node of the tank (counting from top)
253
+ h_condtop = (1 - (5.5 / 12.0)) * tank_height # in the 6th node of the tank (counting from top)
254
+ h_condbot = 0.01 # bottom node
255
+ h_hpctrl_up = (1 - (2.5 / 12.0)) * tank_height # in the 3rd node of the tank
256
+ h_hpctrl_low = (1 - (8.5 / 12.0)) * tank_height # in the 9th node of the tank
257
+ hpwh.setControlSensor1HeightInStratifiedTank(h_hpctrl_up)
258
+ hpwh.setControlSensor1Weight(0.75)
259
+ hpwh.setControlSensor2HeightInStratifiedTank(h_hpctrl_low)
260
+ end
261
+ hpwh.setCondenserBottomLocation(h_condbot)
262
+ hpwh.setCondenserTopLocation(h_condtop)
263
+ hpwh.setTankElementControlLogic('MutuallyExclusive')
264
+ hpwh.autocalculateEvaporatorAirFlowRate
265
+ elsif heat_pump_type == 'PumpedCondenser'
266
+ hpwh.setDeadBandTemperatureDifference(3.89)
267
+ hpwh.autosizeEvaporatorAirFlowRate
268
+ end
269
+
270
+ # set heat pump water heater properties
271
+ hpwh.setFanPlacement('DrawThrough')
272
+ hpwh.setOnCycleParasiticElectricLoad(0.0)
273
+ hpwh.setOffCycleParasiticElectricLoad(0.0)
274
+ hpwh.setParasiticHeatRejectionLocation('Outdoors')
275
+
276
+ # set temperature setpoint schedule
277
+ if service_water_temperature_schedule.nil?
278
+ # service water heating loop controls
279
+ swh_temp_c = service_water_temperature
280
+ swh_temp_f = OpenStudio.convert(swh_temp_c, 'C', 'F').get
281
+ swh_delta_t_r = 9.0 # 9F delta-T
282
+ swh_temp_c = OpenStudio.convert(swh_temp_f, 'F', 'C').get
283
+ swh_delta_t_k = OpenStudio.convert(swh_delta_t_r, 'R', 'K').get
284
+ service_water_temperature_schedule = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
285
+ swh_temp_c,
286
+ name: "Heat Pump Water Heater Temp - #{swh_temp_f.round}F",
287
+ schedule_type_limit: 'Temperature')
288
+ end
289
+ hpwh.setCompressorSetpointTemperatureSchedule(service_water_temperature_schedule)
290
+
291
+ # coil curves
292
+ hpwh_cap = OpenStudio::Model::CurveBiquadratic.new(model)
293
+ hpwh_cap.setName('HPWH-Cap-fT')
294
+ hpwh_cap.setCoefficient1Constant(0.563)
295
+ hpwh_cap.setCoefficient2x(0.0437)
296
+ hpwh_cap.setCoefficient3xPOW2(0.000039)
297
+ hpwh_cap.setCoefficient4y(0.0055)
298
+ hpwh_cap.setCoefficient5yPOW2(-0.000148)
299
+ hpwh_cap.setCoefficient6xTIMESY(-0.000145)
300
+ hpwh_cap.setMinimumValueofx(0.0)
301
+ hpwh_cap.setMaximumValueofx(100.0)
302
+ hpwh_cap.setMinimumValueofy(0.0)
303
+ hpwh_cap.setMaximumValueofy(100.0)
304
+
305
+ hpwh_cop = OpenStudio::Model::CurveBiquadratic.new(model)
306
+ hpwh_cop.setName('HPWH-COP-fT')
307
+ hpwh_cop.setCoefficient1Constant(1.1332)
308
+ hpwh_cop.setCoefficient2x(0.063)
309
+ hpwh_cop.setCoefficient3xPOW2(-0.0000979)
310
+ hpwh_cop.setCoefficient4y(-0.00972)
311
+ hpwh_cop.setCoefficient5yPOW2(-0.0000214)
312
+ hpwh_cop.setCoefficient6xTIMESY(-0.000686)
313
+ hpwh_cop.setMinimumValueofx(0.0)
314
+ hpwh_cop.setMaximumValueofx(100.0)
315
+ hpwh_cop.setMinimumValueofy(0.0)
316
+ hpwh_cop.setMaximumValueofy(100.0)
317
+
318
+ # create DX coil object
319
+ if heat_pump_type == 'WrappedCondenser'
320
+ coil = hpwh.dXCoil.to_CoilWaterHeatingAirToWaterHeatPumpWrapped.get
321
+ coil.setRatedCondenserWaterTemperature(48.89)
322
+ coil.autocalculateRatedEvaporatorAirFlowRate
323
+ elsif heat_pump_type == 'PumpedCondenser'
324
+ coil = hpwh.dXCoil.to_CoilWaterHeatingAirToWaterHeatPump.get
325
+ coil.autosizeRatedEvaporatorAirFlowRate
326
+ end
327
+
328
+ # set coil properties
329
+ coil.setName("#{hpwh.name} Coil")
330
+ coil.setRatedHeatingCapacity(water_heater_capacity)
331
+ coil.setRatedCOP(coefficient_of_performance)
332
+ coil.setRatedSensibleHeatRatio(0.88) # default sensible_heat_ratio assumption
333
+ coil.setRatedEvaporatorInletAirDryBulbTemperature(OpenStudio.convert(67.5, 'F', 'C').get)
334
+ coil.setRatedEvaporatorInletAirWetBulbTemperature(OpenStudio.convert(56.426, 'F', 'C').get)
335
+ coil.setEvaporatorFanPowerIncludedinRatedCOP(true)
336
+ coil.setEvaporatorAirTemperatureTypeforCurveObjects('WetBulbTemperature')
337
+ coil.setHeatingCapacityFunctionofTemperatureCurve(hpwh_cap)
338
+ coil.setHeatingCOPFunctionofTemperatureCurve(hpwh_cop)
339
+ coil.setMaximumAmbientTemperatureforCrankcaseHeaterOperation(0.0)
340
+
341
+ # set tank properties
342
+ if heat_pump_type == 'WrappedCondenser'
343
+ tank = hpwh.tank.to_WaterHeaterStratified.get
344
+ tank.setTankHeight(tank_height)
345
+ tank.setHeaterPriorityControl('MasterSlave')
346
+ if hpwh_vol_gal <= 50.0
347
+ tank.setHeater1DeadbandTemperatureDifference(25.0)
348
+ tank.setHeater2DeadbandTemperatureDifference(30.0)
349
+ else
350
+ tank.setHeater1DeadbandTemperatureDifference(18.5)
351
+ tank.setHeater2DeadbandTemperatureDifference(3.89)
352
+ end
353
+ hpwh_bottom_element_sp = OpenStudio::Model::ScheduleConstant.new(model)
354
+ hpwh_bottom_element_sp.setName("#{hpwh.name} BottomElementSetpoint")
355
+ hpwh_top_element_sp = OpenStudio::Model::ScheduleConstant.new(model)
356
+ hpwh_top_element_sp.setName("#{hpwh.name} TopElementSetpoint")
357
+ tank.setHeater1Capacity(electric_backup_capacity)
358
+ tank.setHeater1Height(h_ue)
359
+ tank.setHeater1SetpointTemperatureSchedule(hpwh_top_element_sp) # Overwritten later by EMS
360
+ tank.setHeater2Capacity(electric_backup_capacity)
361
+ tank.setHeater2Height(h_le)
362
+ tank.setHeater2SetpointTemperatureSchedule(hpwh_bottom_element_sp)
363
+ tank.setUniformSkinLossCoefficientperUnitAreatoAmbientTemperature(u_tank)
364
+ tank.setNumberofNodes(12)
365
+ tank.setAdditionalDestratificationConductivity(0)
366
+ tank.setNode1AdditionalLossCoefficient(0)
367
+ tank.setNode2AdditionalLossCoefficient(0)
368
+ tank.setNode3AdditionalLossCoefficient(0)
369
+ tank.setNode4AdditionalLossCoefficient(0)
370
+ tank.setNode5AdditionalLossCoefficient(0)
371
+ tank.setNode6AdditionalLossCoefficient(0)
372
+ tank.setNode7AdditionalLossCoefficient(0)
373
+ tank.setNode8AdditionalLossCoefficient(0)
374
+ tank.setNode9AdditionalLossCoefficient(0)
375
+ tank.setNode10AdditionalLossCoefficient(0)
376
+ tank.setNode11AdditionalLossCoefficient(0)
377
+ tank.setNode12AdditionalLossCoefficient(0)
378
+ tank.setUseSideDesignFlowRate(0.9 * water_heater_volume / 60.1)
379
+ tank.setSourceSideDesignFlowRate(0)
380
+ tank.setSourceSideFlowControlMode('')
381
+ tank.setSourceSideInletHeight(0)
382
+ tank.setSourceSideOutletHeight(0)
383
+ elsif heat_pump_type == 'PumpedCondenser'
384
+ tank = hpwh.tank.to_WaterHeaterMixed.get
385
+ tank.setDeadbandTemperatureDifference(3.89)
386
+ tank.setHeaterControlType('Cycle')
387
+ tank.setHeaterMaximumCapacity(electric_backup_capacity)
388
+ end
389
+ tank.setName("#{hpwh.name} Tank")
390
+ tank.setEndUseSubcategory('Service Hot Water')
391
+ tank.setTankVolume(0.9 * water_heater_volume)
392
+ tank.setMaximumTemperatureLimit(90.0)
393
+ tank.setHeaterFuelType('Electricity')
394
+ tank.setHeaterThermalEfficiency(1.0)
395
+ tank.setOffCycleParasiticFuelConsumptionRate(off_cycle_parasitic_fuel_consumption_rate)
396
+ tank.setOffCycleParasiticFuelType('Electricity')
397
+ tank.setOnCycleParasiticFuelConsumptionRate(on_cycle_parasitic_fuel_consumption_rate)
398
+ tank.setOnCycleParasiticFuelType('Electricity')
399
+
400
+ # set fan properties
401
+ fan = hpwh.fan.to_FanOnOff.get
402
+ fan.setName("#{hpwh.name} Fan")
403
+ fan_power = 0.0462 # watts per cfm
404
+ if hpwh_vol_gal <= 50.0
405
+ fan.setFanEfficiency(23.0 / fan_power * OpenStudio.convert(1.0, 'ft^3/min', 'm^3/s').get)
406
+ fan.setPressureRise(23.0)
407
+ else
408
+ fan.setFanEfficiency(65.0 / fan_power * OpenStudio.convert(1.0, 'ft^3/min', 'm^3/s').get)
409
+ fan.setPressureRise(65.0)
410
+ end
411
+ # determine maximum flow rate from water heater capacity
412
+ # use 5.035E-5 m^3/s/W from EnergyPlus used to autocalculate the evaporator air flow rate in WaterHeater:HeatPump:PumpedCondenser and Coil:WaterHeating:AirToWaterHeatPump:Pumped
413
+ fan_flow_rate_m3_per_s = water_heater_capacity * 5.035e-5
414
+ fan.setMaximumFlowRate(fan_flow_rate_m3_per_s)
415
+ fan.setMotorEfficiency(1.0)
416
+ fan.setMotorInAirstreamFraction(1.0)
417
+ fan.setEndUseSubcategory('Service Hot Water')
418
+
419
+ if water_heater_thermal_zone.nil?
420
+ # add in schedules for Tamb, RHamb, and the compressor
421
+ # assume the water heater is indoors at 71.6F / 22C
422
+ default_water_heater_ambient_temp_sch = OpenstudioStandards::Schedules.create_constant_schedule_ruleset(model,
423
+ OpenStudio.convert(71.6, 'F', 'C').get,
424
+ name: 'Water Heater Ambient Temp Schedule 70F',
425
+ schedule_type_limit: 'Temperature')
426
+ tank.setAmbientTemperatureIndicator('Schedule')
427
+ tank.setAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch)
428
+ tank.resetAmbientTemperatureThermalZone
429
+ hpwh_rhamb = OpenStudio::Model::ScheduleConstant.new(model)
430
+ hpwh_rhamb.setName("#{hpwh.name} Ambient Humidity Schedule")
431
+ hpwh_rhamb.setValue(0.5)
432
+ hpwh.setInletAirConfiguration('Schedule')
433
+ hpwh.setInletAirTemperatureSchedule(default_water_heater_ambient_temp_sch)
434
+ hpwh.setInletAirHumiditySchedule(hpwh_rhamb)
435
+ hpwh.setCompressorLocation('Schedule')
436
+ hpwh.setCompressorAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch)
437
+ else
438
+ hpwh.addToThermalZone(water_heater_thermal_zone)
439
+ hpwh.setInletAirConfiguration('ZoneAirOnly')
440
+ hpwh.setCompressorLocation('Zone')
441
+ tank.setAmbientTemperatureIndicator('ThermalZone')
442
+ tank.setAmbientTemperatureThermalZone(water_heater_thermal_zone)
443
+ tank.resetAmbientTemperatureSchedule
444
+ end
445
+
446
+ if set_peak_use_flowrate
447
+ rated_flow_rate_m3_per_s = peak_flowrate
448
+ rated_flow_rate_gal_per_min = OpenStudio.convert(rated_flow_rate_m3_per_s, 'm^3/s', 'gal/min').get
449
+ tank.setPeakUseFlowRate(rated_flow_rate_m3_per_s)
450
+ tank.setUseFlowRateFractionSchedule(flowrate_schedule) unless flowrate_schedule.nil?
451
+ end
452
+
453
+ # add EMS for overriding HPWH setpoints schedules (for upper/lower heating element in water tank and compressor in heat pump)
454
+ if heat_pump_type == 'WrappedCondenser' && use_ems_control
455
+ std = Standard.build('90.1-2013')
456
+ hpwh_name_ems_friendly = std.ems_friendly_name(hpwh.name)
457
+
458
+ # create an ambient temperature sensor for the air that blows through the HPWH evaporator
459
+ if water_heater_thermal_zone.nil?
460
+ # assume the condenser is outside
461
+ amb_temp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Outdoor Air Drybulb Temperature')
462
+ amb_temp_sensor.setName("#{hpwh_name_ems_friendly}_amb_temp")
463
+ amb_temp_sensor.setKeyName('Environment')
464
+ else
465
+ amb_temp_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Zone Mean Air Temperature')
466
+ amb_temp_sensor.setName("#{hpwh_name_ems_friendly}_amb_temp")
467
+ amb_temp_sensor.setKeyName(water_heater_thermal_zone.name.to_s)
468
+ end
469
+
470
+ # create actuator for heat pump compressor
471
+ if service_water_temperature_schedule.to_ScheduleConstant.is_initialized
472
+ service_water_temperature_schedule = service_water_temperature_schedule.to_ScheduleConstant.get
473
+ schedule_type = 'Schedule:Constant'
474
+ elsif service_water_temperature_schedule.to_ScheduleCompact.is_initialized
475
+ service_water_temperature_schedule = service_water_temperature_schedule.to_ScheduleCompact.get
476
+ schedule_type = 'Schedule:Compact'
477
+ elsif service_water_temperature_schedule.to_ScheduleRuleset.is_initialized
478
+ service_water_temperature_schedule = service_water_temperature_schedule.to_ScheduleRuleset.get
479
+ schedule_type = 'Schedule:Year'
480
+ else
481
+ OpenStudio.logFree(OpenStudio::Error, 'openstudio.standards.ServiceWaterHeating', "Unsupported schedule type for HPWH setpoint schedule #{service_water_temperature_schedule.name}.")
482
+ return false
483
+ end
484
+ hpwhschedoverride_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(service_water_temperature_schedule, schedule_type, 'Schedule Value')
485
+ hpwhschedoverride_actuator.setName("#{hpwh_name_ems_friendly}_HPWHSchedOverride")
486
+
487
+ # create actuator for lower heating element in water tank
488
+ leschedoverride_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(hpwh_bottom_element_sp, 'Schedule:Constant', 'Schedule Value')
489
+ leschedoverride_actuator.setName("#{hpwh_name_ems_friendly}_LESchedOverride")
490
+
491
+ # create actuator for upper heating element in water tank
492
+ ueschedoverride_actuator = OpenStudio::Model::EnergyManagementSystemActuator.new(hpwh_top_element_sp, 'Schedule:Constant', 'Schedule Value')
493
+ ueschedoverride_actuator.setName("#{hpwh_name_ems_friendly}_UESchedOverride")
494
+
495
+ # create sensor for heat pump compressor
496
+ t_set_sensor = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Schedule Value')
497
+ t_set_sensor.setName("#{hpwh_name_ems_friendly}_T_set")
498
+ t_set_sensor.setKeyName(service_water_temperature_schedule.name.to_s)
499
+
500
+ # define control configuration
501
+ t_offset = 9.0 # deg-C
502
+
503
+ # get tank specifications
504
+ upper_element_db = tank.heater1DeadbandTemperatureDifference
505
+
506
+ # define control logic
507
+ hpwh_ctrl_program = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
508
+ hpwh_ctrl_program.setName("#{hpwh_name_ems_friendly}_Control")
509
+ hpwh_ctrl_program.addLine("SET #{hpwhschedoverride_actuator.name} = #{t_set_sensor.name}")
510
+ # lockout hp when ambient temperature is either too high or too low
511
+ hpwh_ctrl_program.addLine("IF (#{amb_temp_sensor.name}<#{hpwh_op_min_temp_c}) || (#{amb_temp_sensor.name}>#{hpwh_op_max_temp_c})")
512
+ hpwh_ctrl_program.addLine("SET #{ueschedoverride_actuator.name} = #{t_set_sensor.name}")
513
+ hpwh_ctrl_program.addLine("SET #{leschedoverride_actuator.name} = #{t_set_sensor.name}")
514
+ hpwh_ctrl_program.addLine('ELSE')
515
+ # upper element setpoint temperature
516
+ hpwh_ctrl_program.addLine("SET #{ueschedoverride_actuator.name} = #{t_set_sensor.name} - #{t_offset}")
517
+ # upper element cut-in temperature
518
+ hpwh_ctrl_program.addLine("SET #{ueschedoverride_actuator.name}_cut_in = #{ueschedoverride_actuator.name} - #{upper_element_db}")
519
+ # lower element disabled
520
+ hpwh_ctrl_program.addLine("SET #{leschedoverride_actuator.name} = 0")
521
+ # lower element disabled
522
+ hpwh_ctrl_program.addLine("SET #{leschedoverride_actuator.name}_cut_in = 0")
523
+ hpwh_ctrl_program.addLine('ENDIF')
524
+
525
+ # create a program calling manager
526
+ program_calling_manager = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
527
+ program_calling_manager.setName("#{hpwh_name_ems_friendly}_ProgramManager")
528
+ program_calling_manager.setCallingPoint('InsideHVACSystemIterationLoop')
529
+ program_calling_manager.addProgram(hpwh_ctrl_program)
530
+ end
531
+
532
+ # add the water heater to the service water loop if provided
533
+ unless service_water_loop.nil?
534
+ service_water_loop.addSupplyBranchForComponent(tank)
535
+ end
536
+
537
+ OpenStudio.logFree(OpenStudio::Info, 'openstudio.standards.ServiceWaterHeating', "Added heat pump water heater called #{tank.name}")
538
+
539
+ return hpwh
540
+ end
541
+
542
+ # @!endgroup Create Water Heater
543
+ end
544
+ end