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.
- checksums.yaml +4 -4
- data/data/standards/OpenStudio_Standards-ashrae_90_1.xlsx +0 -0
- data/data/standards/manage_OpenStudio_Standards.rb +2 -49
- data/data/standards/openstudio_standards_duplicates_log.csv +1 -7962
- data/data/standards/test_performance_expected_dd_results.csv +2005 -97
- data/lib/openstudio-standards/create_typical/space_type_ratios.rb +47 -57
- data/lib/openstudio-standards/geometry/create.rb +1 -1
- data/lib/openstudio-standards/geometry/create_bar.rb +6 -3
- data/lib/openstudio-standards/geometry/create_shape.rb +1 -1
- data/lib/openstudio-standards/geometry/group.rb +1 -1
- data/lib/openstudio-standards/geometry/information.rb +1 -1
- data/lib/openstudio-standards/geometry/modify.rb +53 -1
- data/lib/openstudio-standards/infiltration/nist_infiltration.rb +1 -1
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2016/ashrae_90_1_2016.Model.rb +11 -11
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.Model.rb +11 -11
- data/lib/openstudio-standards/prototypes/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.hvac_systems.rb +2 -2
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.SuperTallBuilding.rb +44 -47
- data/lib/openstudio-standards/prototypes/common/buildings/Prototype.TallBuilding.rb +43 -48
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.CentralAirSourceHeatPump.rb +1 -1
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +44 -24
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.refrigeration.rb +24 -24
- data/lib/openstudio-standards/schedules/parametric.rb +1 -1
- data/lib/openstudio-standards/service_water_heating/create_piping_losses.rb +152 -0
- data/lib/openstudio-standards/service_water_heating/create_water_heater.rb +544 -0
- data/lib/openstudio-standards/service_water_heating/create_water_heating_loop.rb +303 -0
- data/lib/openstudio-standards/service_water_heating/create_water_use.rb +95 -0
- data/lib/openstudio-standards/space/space.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +65 -70
- data/lib/openstudio-standards/standards/Standards.CoilCoolingWaterToAirHeatPumpEquationFit.rb +12 -14
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXSingleSpeed.rb +16 -5
- data/lib/openstudio-standards/standards/Standards.Model.rb +2 -2
- data/lib/openstudio-standards/standards/Standards.PlanarSurface.rb +10 -2
- data/lib/openstudio-standards/{prototypes/common/objects/Prototype.Model.swh.rb → standards/Standards.ServiceWaterHeating.rb} +209 -139
- data/lib/openstudio-standards/standards/Standards.Surface.rb +1 -1
- data/lib/openstudio-standards/standards/Standards.ZoneHVACComponent.rb +4 -8
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/ashrae_90_1_2004.Model.rb +2 -2
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_properties.json +22251 -12963
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2004/data/ashrae_90_1_2004.construction_sets.json +91 -91
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2007/data/ashrae_90_1_2007.construction_properties.json +8981 -5228
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2010/data/ashrae_90_1_2010.construction_properties.json +8935 -5182
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_properties.json +7281 -5391
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2013/data/ashrae_90_1_2013.construction_sets.json +91 -91
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_properties.json +9005 -15215
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2016/data/ashrae_90_1_2016.construction_sets.json +136 -136
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/ashrae_90_1_2019.AirLoopHVAC.rb +1 -1
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_properties.json +8717 -17168
- data/lib/openstudio-standards/standards/ashrae_90_1/ashrae_90_1_2019/data/ashrae_90_1_2019.construction_sets.json +136 -136
- data/lib/openstudio-standards/standards/ashrae_90_1/data/ashrae_90_1.constructions.json +1941 -651
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_1980_2004/data/doe_ref_1980_2004.construction_properties.json +135 -135
- data/lib/openstudio-standards/standards/ashrae_90_1/doe_ref_pre_1980/data/doe_ref_pre_1980.construction_properties.json +135 -135
- data/lib/openstudio-standards/standards/ashrae_90_1/nrel_zne_ready_2017/data/nrel_zne_ready_2017.construction_properties.json +36 -36
- data/lib/openstudio-standards/standards/ashrae_90_1/ze_aedg_multifamily/data/ze_aedg_multifamily.construction_properties.json +36 -36
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.PlantLoop.rb +377 -99
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm.SpaceType.rb +2 -2
- data/lib/openstudio-standards/standards/ashrae_90_1_prm/ashrae_90_1_prm_2019/ashrae_90_1_prm_2019.Model.rb +3 -3
- 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
- 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
- data/lib/openstudio-standards/standards/cbes/cbes_pre_1978/data/cbes_pre_1978.construction_properties.json +16 -16
- data/lib/openstudio-standards/standards/cbes/cbes_t24_1978/data/cbes_t24_1978.construction_properties.json +16 -16
- data/lib/openstudio-standards/standards/cbes/cbes_t24_1992/data/cbes_t24_1992.construction_properties.json +16 -16
- data/lib/openstudio-standards/standards/cbes/cbes_t24_2001/data/cbes_t24_2001.construction_properties.json +16 -16
- data/lib/openstudio-standards/standards/cbes/cbes_t24_2005/data/cbes_t24_2005.construction_properties.json +16 -16
- data/lib/openstudio-standards/standards/cbes/cbes_t24_2008/data/cbes_t24_2008.construction_properties.json +16 -16
- data/lib/openstudio-standards/standards/cbes/data/cbes.constructions.json +142 -142
- data/lib/openstudio-standards/standards/deer/data/deer.constructions.json +5 -1551
- data/lib/openstudio-standards/standards/deer/data/deer.materials.json +40 -0
- data/lib/openstudio-standards/standards/deer/deer_1985/data/deer_1985.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_1996/data/deer_1996.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2003/data/deer_2003.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2007/data/deer_2007.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2011/data/deer_2011.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2014/data/deer_2014.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2015/data/deer_2015.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2017/data/deer_2017.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2020/data/deer_2020.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2025/data/deer_2025.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2030/data/deer_2030.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2035/data/deer_2035.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2040/data/deer_2040.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2045/data/deer_2045.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2050/data/deer_2050.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2055/data/deer_2055.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2060/data/deer_2060.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2065/data/deer_2065.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2070/data/deer_2070.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_2075/data/deer_2075.motors.json +88 -8
- data/lib/openstudio-standards/standards/deer/deer_pre_1975/data/deer_pre_1975.motors.json +88 -8
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/btap_pre1980.rb +17 -0
- data/lib/openstudio-standards/standards/necb/BTAPPRE1980/hvac_systems.rb +2 -1
- data/lib/openstudio-standards/standards/necb/ECMS/ecms.rb +4 -4
- data/lib/openstudio-standards/standards/necb/ECMS/hvac_systems.rb +61 -88
- data/lib/openstudio-standards/standards/necb/NECB2011/autozone.rb +3 -2
- data/lib/openstudio-standards/standards/necb/NECB2011/data/boiler_fuel_type_sets.json +54 -0
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMidriseApartment.osm +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPMultiTower.osm +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPPointTower.osm +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/LEEPTownHouse.osm +1 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernEducation.osm +4 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/data/geometry/NorthernHealthCare.osm +4 -4
- data/lib/openstudio-standards/standards/necb/NECB2011/hvac_systems.rb +32 -24
- data/lib/openstudio-standards/standards/necb/NECB2011/necb_2011.rb +89 -15
- data/lib/openstudio-standards/standards/necb/NECB2011/qaqc/necb_qaqc.rb +5 -1
- data/lib/openstudio-standards/standards/necb/NECB2011/service_water_heating.rb +22 -65
- data/lib/openstudio-standards/standards/necb/NECB2011/system_fuels.rb +19 -0
- data/lib/openstudio-standards/standards/necb/common/btap_data.rb +56 -2
- data/lib/openstudio-standards/standards/necb/common/btap_datapoint.rb +3 -1
- data/lib/openstudio-standards/standards/necb/common/construction_defaults.osm +2 -2
- data/lib/openstudio-standards/standards/necb/docs/air_system_names_method.md +127 -0
- data/lib/openstudio-standards/thermal_zone/thermal_zone.rb +1 -1
- data/lib/openstudio-standards/utilities/template_measure/resources/BTAPMeasureHelper.rb +1 -1
- data/lib/openstudio-standards/version.rb +1 -1
- data/lib/openstudio-standards/weather/information.rb +61 -5
- data/lib/openstudio-standards/weather/modify.rb +1 -1
- data/lib/openstudio-standards.rb +5 -3
- metadata +12 -63
- data/data/standards/OpenStudio_Standards-deer.xlsx +0 -0
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.ServiceWaterHeating.rb +0 -1100
- 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
|