openstudio-standards 0.2.8 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/data/geometry/ASHRAEPrimarySchool.osm +36 -2
- data/data/geometry/ASHRAESecondarySchool.osm +19 -2
- data/data/standards/OpenStudio_Standards_elevators.json +10756 -0
- data/lib/openstudio-standards.rb +0 -2
- data/lib/openstudio-standards/hvac_sizing/Siz.HVACComponent.rb +36 -0
- data/lib/openstudio-standards/hvac_sizing/Siz.Model.rb +3 -0
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.elevators.rb +175 -164
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.Model.swh.rb +268 -476
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.ServiceWaterHeating.rb +625 -116
- data/lib/openstudio-standards/prototypes/common/objects/Prototype.hvac_systems.rb +4 -0
- data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +2 -19
- data/lib/openstudio-standards/standards/Standards.ThermalZone.rb +112 -68
- data/lib/openstudio-standards/standards/Standards.WaterHeaterMixed.rb +10 -2
- data/lib/openstudio-standards/standards/necb/necb_2011/data/space_types.json +224 -224
- data/lib/openstudio-standards/standards/necb/necb_2011/service_water_heating.rb +8 -16
- data/lib/openstudio-standards/standards/necb/necb_2015/data/space_types.json +318 -318
- data/lib/openstudio-standards/standards/standard.rb +1 -0
- data/lib/openstudio-standards/version.rb +1 -1
- metadata +4 -4
- data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_1980_2004/doe_ref_1980_2004.hvac_systems.rb +0 -15
- data/lib/openstudio-standards/prototypes/ashrae_90_1/doe_ref_pre_1980/doe_ref_pre_1980.hvac_systems.rb +0 -15
@@ -5,18 +5,21 @@ class Standard
|
|
5
5
|
#
|
6
6
|
# @param system_name [String] the name of the system, or nil in which case it will be defaulted
|
7
7
|
# @param water_heater_thermal_zone [OpenStudio::Model::ThermalZone]
|
8
|
-
#
|
8
|
+
# zones to place water heater in. If nil, will be assumed in 70F air for heat loss.
|
9
9
|
# @param service_water_temperature [Double] service water temperature, in C
|
10
10
|
# @param service_water_pump_head [Double] service water pump head, in Pa
|
11
|
-
# @param service_water_pump_motor_efficiency [Double]
|
12
|
-
# service water pump motor efficiency, as decimal.
|
11
|
+
# @param service_water_pump_motor_efficiency [Double] service water pump motor efficiency, as decimal.
|
13
12
|
# @param water_heater_capacity [Double] water heater heating capacity, in W
|
14
13
|
# @param water_heater_volume [Double] water heater volume, in m^3
|
15
|
-
# @param water_heater_fuel [String] water heater fuel.
|
16
|
-
#
|
17
|
-
# @param
|
18
|
-
#
|
19
|
-
# @param
|
14
|
+
# @param water_heater_fuel [String] water heater fuel. Valid choices are NaturalGas, Electricity
|
15
|
+
# @param parasitic_fuel_consumption_rate [Double] the parasitic fuel consumption rate of the water heater, in W
|
16
|
+
# @param add_pipe_losses [Bool] if true, add piping and associated heat losses to system. If false, add no pipe heat losses
|
17
|
+
# @param floor_area_served [Double] area served by the SWH loop, in m^2. Used for pipe loss piping length estimation
|
18
|
+
# @param number_of_stories [Integer] number of stories served by the SWH loop. Used for pipe loss piping length estimation
|
19
|
+
# @param pipe_insulation_thickness [Double] thickness of the fiberglass batt pipe insulation, in m. Use 0 for uninsulated pipes
|
20
|
+
# @param number_water_heaters [Double] the number of water heaters represented by the capacity and volume inputs.
|
21
|
+
# Used to modify efficiencies for water heaters based on individual component size while avoiding having to model
|
22
|
+
# lots of individual water heaters (for runtime sake).
|
20
23
|
# @return [OpenStudio::Model::PlantLoop]
|
21
24
|
# the resulting service water loop.
|
22
25
|
def model_add_swh_loop(model,
|
@@ -29,14 +32,17 @@ class Standard
|
|
29
32
|
water_heater_volume,
|
30
33
|
water_heater_fuel,
|
31
34
|
parasitic_fuel_consumption_rate,
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
+
add_pipe_losses = false,
|
36
|
+
floor_area_served = 465,
|
37
|
+
number_of_stories = 1,
|
38
|
+
pipe_insulation_thickness = 0.0127, # 1/2in
|
39
|
+
number_water_heaters = 1)
|
40
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.Model.Model', "In model_add_swh_loop, number_water_heaters = #{number_water_heaters}")
|
35
41
|
|
36
42
|
# Service water heating loop
|
37
43
|
service_water_loop = OpenStudio::Model::PlantLoop.new(model)
|
38
|
-
service_water_loop.setMinimumLoopTemperature(10)
|
39
|
-
service_water_loop.setMaximumLoopTemperature(60)
|
44
|
+
service_water_loop.setMinimumLoopTemperature(10.0)
|
45
|
+
service_water_loop.setMaximumLoopTemperature(60.0)
|
40
46
|
|
41
47
|
if system_name.nil?
|
42
48
|
service_water_loop.setName('Service Water Loop')
|
@@ -45,17 +51,17 @@ class Standard
|
|
45
51
|
end
|
46
52
|
|
47
53
|
# Temperature schedule type limits
|
48
|
-
temp_sch_type_limits =
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
+
temp_sch_type_limits = model_add_schedule_type_limits(model,
|
55
|
+
name: 'Temperature Schedule Type Limits',
|
56
|
+
lower_limit_value: 0.0,
|
57
|
+
upper_limit_value: 100.0,
|
58
|
+
numeric_type: 'Continuous',
|
59
|
+
unit_type: 'Temperature')
|
54
60
|
|
55
61
|
# Service water heating loop controls
|
56
62
|
swh_temp_c = service_water_temperature
|
57
63
|
swh_temp_f = OpenStudio.convert(swh_temp_c, 'C', 'F').get
|
58
|
-
swh_delta_t_r = 9 # 9F delta-T
|
64
|
+
swh_delta_t_r = 9.0 # 9F delta-T
|
59
65
|
swh_delta_t_k = OpenStudio.convert(swh_delta_t_r, 'R', 'K').get
|
60
66
|
swh_temp_sch = model_add_constant_schedule_ruleset(model,
|
61
67
|
swh_temp_c,
|
@@ -69,25 +75,28 @@ class Standard
|
|
69
75
|
sizing_plant.setDesignLoopExitTemperature(swh_temp_c)
|
70
76
|
sizing_plant.setLoopDesignTemperatureDifference(swh_delta_t_k)
|
71
77
|
|
72
|
-
#
|
78
|
+
# Determine if circulating or non-circulating based on supplied head pressure
|
73
79
|
swh_pump_head_press_pa = service_water_pump_head
|
74
|
-
|
75
|
-
if swh_pump_head_press_pa.nil?
|
80
|
+
circulating = true
|
81
|
+
if swh_pump_head_press_pa.nil? || swh_pump_head_press_pa <= 1
|
76
82
|
# As if there is no circulation pump
|
77
83
|
swh_pump_head_press_pa = 0.001
|
78
|
-
|
84
|
+
service_water_pump_motor_efficiency = 1
|
85
|
+
circulating = false
|
79
86
|
end
|
80
87
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
+
# Service water heating pump
|
89
|
+
if circulating
|
90
|
+
swh_pump = OpenStudio::Model::PumpConstantSpeed.new(model)
|
91
|
+
swh_pump.setName("#{service_water_loop.name} Circulator Pump")
|
92
|
+
swh_pump.setPumpControlType('Intermittent')
|
93
|
+
else
|
94
|
+
swh_pump = OpenStudio::Model::PumpVariableSpeed.new(model)
|
95
|
+
swh_pump.setName("#{service_water_loop.name} Water Mains Pressure Driven")
|
96
|
+
swh_pump.setPumpControlType('Continuous')
|
97
|
+
end
|
88
98
|
swh_pump.setRatedPumpHead(swh_pump_head_press_pa.to_f)
|
89
|
-
swh_pump.setMotorEfficiency(
|
90
|
-
swh_pump.setPumpControlType('Intermittent')
|
99
|
+
swh_pump.setMotorEfficiency(service_water_pump_motor_efficiency)
|
91
100
|
swh_pump.addToNode(service_water_loop.supplyInletNode)
|
92
101
|
|
93
102
|
water_heater = model_add_water_heater(model,
|
@@ -100,10 +109,21 @@ class Standard
|
|
100
109
|
false,
|
101
110
|
0.0,
|
102
111
|
nil,
|
103
|
-
water_heater_thermal_zone
|
112
|
+
water_heater_thermal_zone,
|
113
|
+
number_water_heaters)
|
104
114
|
|
105
115
|
service_water_loop.addSupplyBranchForComponent(water_heater)
|
106
116
|
|
117
|
+
# Pipe losses
|
118
|
+
if add_pipe_losses
|
119
|
+
model_add_piping_losses_to_swh_system(model,
|
120
|
+
service_water_loop,
|
121
|
+
circulating,
|
122
|
+
pipe_insulation_thickness: pipe_insulation_thickness,
|
123
|
+
floor_area_served: floor_area_served,
|
124
|
+
number_of_stories: number_of_stories)
|
125
|
+
end
|
126
|
+
|
107
127
|
# Service water heating loop bypass pipes
|
108
128
|
water_heater_bypass_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
|
109
129
|
service_water_loop.addSupplyBranchForComponent(water_heater_bypass_pipe)
|
@@ -111,40 +131,35 @@ class Standard
|
|
111
131
|
service_water_loop.addDemandBranchForComponent(coil_bypass_pipe)
|
112
132
|
supply_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
|
113
133
|
supply_outlet_pipe.addToNode(service_water_loop.supplyOutletNode)
|
114
|
-
demand_inlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
|
115
|
-
demand_inlet_pipe.addToNode(service_water_loop.demandInletNode)
|
116
134
|
demand_outlet_pipe = OpenStudio::Model::PipeAdiabatic.new(model)
|
117
135
|
demand_outlet_pipe.addToNode(service_water_loop.demandOutletNode)
|
118
136
|
|
119
|
-
|
120
|
-
|
137
|
+
if circulating
|
138
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Added circulating SWH loop called #{service_water_loop.name}")
|
139
|
+
else
|
140
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Added non-circulating SWH loop called #{service_water_loop.name}")
|
141
|
+
end
|
121
142
|
|
122
|
-
|
123
|
-
# @return [String] the SWH pump type: ConstantSpeed, VariableSpeed
|
124
|
-
def model_swh_pump_type(model, building_type)
|
125
|
-
swh_pump_type = 'ConstantSpeed'
|
126
|
-
return swh_pump_type
|
143
|
+
return service_water_loop
|
127
144
|
end
|
128
145
|
|
129
146
|
# Creates a water heater and attaches it to the supplied service water heating loop.
|
130
147
|
#
|
131
148
|
# @param water_heater_capacity [Double] water heater capacity, in W
|
132
149
|
# @param water_heater_volume [Double] water heater volume, in m^3
|
133
|
-
# @param water_heater_fuel [Double] valid choices are
|
134
|
-
# Natural Gas, Electricity
|
150
|
+
# @param water_heater_fuel [Double] valid choices are NaturalGas, Electricity
|
135
151
|
# @param service_water_temperature [Double] water heater temperature, in C
|
136
|
-
# @param parasitic_fuel_consumption_rate [Double] water heater parasitic
|
137
|
-
#
|
138
|
-
# @param
|
139
|
-
# schedule. If nil, will be defaulted.
|
140
|
-
# @param set_peak_use_flowrate [Bool] if true, the peak flow rate
|
141
|
-
# and flow rate schedule will be set.
|
152
|
+
# @param parasitic_fuel_consumption_rate [Double] water heater parasitic fuel consumption rate, in W
|
153
|
+
# @param swh_temp_sch [OpenStudio::Model::Schedule] the service water heating schedule. If nil, will be defaulted.
|
154
|
+
# @param set_peak_use_flowrate [Bool] if true, the peak flow rate and flow rate schedule will be set.
|
142
155
|
# @param peak_flowrate [Double] in m^3/s
|
143
156
|
# @param flowrate_schedule [String] name of the flow rate schedule
|
144
|
-
# @param water_heater_thermal_zone [OpenStudio::Model::ThermalZone]
|
145
|
-
#
|
146
|
-
# @
|
147
|
-
#
|
157
|
+
# @param water_heater_thermal_zone [OpenStudio::Model::ThermalZone] zone to place water heater in.
|
158
|
+
# If nil, will be assumed in 70F air for heat loss.
|
159
|
+
# @param number_water_heaters [Double] the number of water heaters represented by the capacity and volume inputs.
|
160
|
+
# Used to modify efficiencies for water heaters based on individual component size while avoiding having to model
|
161
|
+
# lots of individual water heaters (for runtime sake).
|
162
|
+
# @return [OpenStudio::Model::WaterHeaterMixed] the resulting water heater
|
148
163
|
def model_add_water_heater(model,
|
149
164
|
water_heater_capacity,
|
150
165
|
water_heater_volume,
|
@@ -155,10 +170,9 @@ class Standard
|
|
155
170
|
set_peak_use_flowrate,
|
156
171
|
peak_flowrate,
|
157
172
|
flowrate_schedule,
|
158
|
-
water_heater_thermal_zone
|
159
|
-
|
160
|
-
OpenStudio.logFree(OpenStudio::
|
161
|
-
|
173
|
+
water_heater_thermal_zone,
|
174
|
+
number_water_heaters)
|
175
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.Model.Model', "In model_add_water_heater, number_water_heaters = #{number_water_heaters}")
|
162
176
|
# Water heater
|
163
177
|
# TODO Standards - Change water heater methodology to follow
|
164
178
|
# 'Model Enhancements Appendix A.'
|
@@ -168,11 +182,11 @@ class Standard
|
|
168
182
|
|
169
183
|
# Temperature schedule type limits
|
170
184
|
temp_sch_type_limits = model_add_schedule_type_limits(model,
|
171
|
-
name:
|
185
|
+
name: 'Temperature Schedule Type Limits',
|
172
186
|
lower_limit_value: 0.0,
|
173
187
|
upper_limit_value: 100.0,
|
174
|
-
numeric_type:
|
175
|
-
unit_type:
|
188
|
+
numeric_type: 'Continuous',
|
189
|
+
unit_type: 'Temperature')
|
176
190
|
|
177
191
|
if swh_temp_sch.nil?
|
178
192
|
# Service water heating loop controls
|
@@ -189,9 +203,18 @@ class Standard
|
|
189
203
|
|
190
204
|
# Water heater depends on the fuel type
|
191
205
|
water_heater = OpenStudio::Model::WaterHeaterMixed.new(model)
|
192
|
-
|
206
|
+
|
207
|
+
# Assign a quantity to the water heater if it represents multiple water heaters
|
208
|
+
if number_water_heaters > 1
|
209
|
+
water_heater.setName("#{number_water_heaters}X #{(water_heater_vol_gal/number_water_heaters).round}gal #{water_heater_fuel} Water Heater - #{(water_heater_capacity_kbtu_per_hr/number_water_heaters).round}kBtu/hr")
|
210
|
+
water_heater.set_component_quantity(number_water_heaters)
|
211
|
+
else
|
212
|
+
water_heater.setName("#{water_heater_vol_gal.round}gal #{water_heater_fuel} Water Heater - #{water_heater_capacity_kbtu_per_hr.round}kBtu/hr")
|
213
|
+
end
|
214
|
+
|
193
215
|
water_heater.setTankVolume(OpenStudio.convert(water_heater_vol_gal, 'gal', 'm^3').get)
|
194
216
|
water_heater.setSetpointTemperatureSchedule(swh_temp_sch)
|
217
|
+
water_heater.setDeadbandTemperatureDifference(2.0)
|
195
218
|
|
196
219
|
if water_heater_thermal_zone.nil?
|
197
220
|
# Assume the water heater is indoors at 70F or 72F
|
@@ -214,7 +237,7 @@ class Standard
|
|
214
237
|
water_heater.resetAmbientTemperatureSchedule
|
215
238
|
end
|
216
239
|
|
217
|
-
water_heater.setMaximumTemperatureLimit(
|
240
|
+
water_heater.setMaximumTemperatureLimit(service_water_temperature)
|
218
241
|
water_heater.setDeadbandTemperatureDifference(OpenStudio.convert(3.6, 'R', 'K').get)
|
219
242
|
water_heater.setHeaterControlType('Cycle')
|
220
243
|
water_heater.setHeaterMaximumCapacity(OpenStudio.convert(water_heater_capacity_btu_per_hr, 'Btu/hr', 'W').get)
|
@@ -229,7 +252,7 @@ class Standard
|
|
229
252
|
water_heater.setOnCycleParasiticFuelType('Electricity')
|
230
253
|
water_heater.setOffCycleLossCoefficienttoAmbientTemperature(1.053)
|
231
254
|
water_heater.setOnCycleLossCoefficienttoAmbientTemperature(1.053)
|
232
|
-
elsif water_heater_fuel == 'Natural Gas'
|
255
|
+
elsif water_heater_fuel == 'Natural Gas' || water_heater_fuel == 'NaturalGas'
|
233
256
|
water_heater.setHeaterFuelType('Gas')
|
234
257
|
water_heater.setHeaterThermalEfficiency(0.78)
|
235
258
|
water_heater.setOffCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
|
@@ -238,6 +261,34 @@ class Standard
|
|
238
261
|
water_heater.setOnCycleParasiticFuelType('Gas')
|
239
262
|
water_heater.setOffCycleLossCoefficienttoAmbientTemperature(6.0)
|
240
263
|
water_heater.setOnCycleLossCoefficienttoAmbientTemperature(6.0)
|
264
|
+
elsif water_heater_fuel == 'HeatPump'
|
265
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.Model.Model', 'Simple but crappy workaround to represent heat pump water heaters without incurring significant runtime penalty associated with using correct objects.')
|
266
|
+
# Make a part-load efficiency modifier curve with a value above 1, which
|
267
|
+
# is multiplied by the nominal efficiency of 100% to represent
|
268
|
+
# the COP of a HPWH.
|
269
|
+
# TODO could make this workaround better by using EMS
|
270
|
+
# to modify this curve output in realtime based on
|
271
|
+
# the OA temperature.
|
272
|
+
hpwh_cop = 2.8
|
273
|
+
eff_f_of_plr = OpenStudio::Model::CurveCubic.new(model)
|
274
|
+
eff_f_of_plr.setName("HPWH_COP_#{hpwh_cop}")
|
275
|
+
eff_f_of_plr.setCoefficient1Constant(hpwh_cop)
|
276
|
+
eff_f_of_plr.setCoefficient2x(0.0)
|
277
|
+
eff_f_of_plr.setCoefficient3xPOW2(0.0)
|
278
|
+
eff_f_of_plr.setCoefficient4xPOW3(0.0)
|
279
|
+
eff_f_of_plr.setMinimumValueofx(0.0)
|
280
|
+
eff_f_of_plr.setMaximumValueofx(1.0)
|
281
|
+
water_heater.setHeaterFuelType('Electricity')
|
282
|
+
water_heater.setHeaterThermalEfficiency(1.0)
|
283
|
+
water_heater.setPartLoadFactorCurve(eff_f_of_plr)
|
284
|
+
water_heater.setOffCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
|
285
|
+
water_heater.setOnCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
|
286
|
+
water_heater.setOffCycleParasiticFuelType('Electricity')
|
287
|
+
water_heater.setOnCycleParasiticFuelType('Electricity')
|
288
|
+
water_heater.setOffCycleLossCoefficienttoAmbientTemperature(1.053)
|
289
|
+
water_heater.setOnCycleLossCoefficienttoAmbientTemperature(1.053)
|
290
|
+
else
|
291
|
+
OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', "#{water_heater_fuel} is not a valid water heater fuel. Valid choices are Electricity, NaturalGas, and HeatPump.")
|
241
292
|
end
|
242
293
|
|
243
294
|
if set_peak_use_flowrate
|
@@ -249,9 +300,284 @@ class Standard
|
|
249
300
|
water_heater.setUseFlowRateFractionSchedule(schedule)
|
250
301
|
end
|
251
302
|
|
303
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Added water heater called #{water_heater.name}")
|
304
|
+
|
252
305
|
return water_heater
|
253
306
|
end
|
254
307
|
|
308
|
+
# Creates a heatpump water heater and attaches it to the supplied service water heating loop.
|
309
|
+
#
|
310
|
+
# @param water_heater_capacity [Double] water heater capacity, in W
|
311
|
+
# @param water_heater_volume [Double] water heater volume, in m^3
|
312
|
+
# @param service_water_temperature [Double] water heater temperature, in C
|
313
|
+
# @param parasitic_fuel_consumption_rate [Double] water heater parasitic fuel consumption rate, in W
|
314
|
+
# @param swh_temp_sch [OpenStudio::Model::Schedule] the service water heating schedule. If nil, will be defaulted.
|
315
|
+
# @param set_peak_use_flowrate [Bool] if true, the peak flow rate and flow rate schedule will be set.
|
316
|
+
# @param peak_flowrate [Double] in m^3/s
|
317
|
+
# @param flowrate_schedule [String] name of the flow rate schedule
|
318
|
+
# @param water_heater_thermal_zone [OpenStudio::Model::ThermalZone] zone to place water heater in.
|
319
|
+
# If nil, will be assumed in 70F air for heat loss.
|
320
|
+
# @return [OpenStudio::Model::WaterHeaterMixed] the resulting water heater
|
321
|
+
def model_add_heatpump_water_heater(model,
|
322
|
+
type: 'PumpedCondenser',
|
323
|
+
water_heater_capacity: 500,
|
324
|
+
electric_backup_capacity: 4500,
|
325
|
+
water_heater_volume: OpenStudio.convert(80.0, 'gal', 'm^3').get,
|
326
|
+
service_water_temperature: OpenStudio.convert(125.0, 'F', 'C').get,
|
327
|
+
parasitic_fuel_consumption_rate: 3.0,
|
328
|
+
swh_temp_sch: nil,
|
329
|
+
cop: 2.8,
|
330
|
+
shr: 0.88,
|
331
|
+
tank_ua: 3.9,
|
332
|
+
set_peak_use_flowrate: false,
|
333
|
+
peak_flowrate: 0.0,
|
334
|
+
flowrate_schedule: nil,
|
335
|
+
water_heater_thermal_zone: nil)
|
336
|
+
|
337
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', 'Adding heat pump water heater')
|
338
|
+
|
339
|
+
# create heat pump water heater
|
340
|
+
if type == 'WrappedCondenser'
|
341
|
+
hpwh = OpenStudio::Model::WaterHeaterHeatPumpWrappedCondenser.new(model)
|
342
|
+
elsif type == 'PumpedCondenser'
|
343
|
+
hpwh = OpenStudio::Model::WaterHeaterHeatPump.new(model)
|
344
|
+
end
|
345
|
+
|
346
|
+
# calculate tank height and radius
|
347
|
+
water_heater_capacity_kbtu_per_hr = OpenStudio.convert(water_heater_capacity, 'W', 'kBtu/hr').get
|
348
|
+
hpwh_vol_gal = OpenStudio.convert(water_heater_volume, 'm^3', 'gal').get
|
349
|
+
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
|
350
|
+
tank_radius = (0.9 * water_heater_volume / (Math::PI * tank_height))**0.5
|
351
|
+
tank_surface_area = 2.0 * Math::PI * tank_radius * (tank_radius + tank_height)
|
352
|
+
u_tank = (5.678 * tank_ua) / OpenStudio.convert(tank_surface_area, 'm^2', 'ft^2').get
|
353
|
+
hpwh.setName("#{hpwh_vol_gal.round}gal Heat Pump Water Heater - #{water_heater_capacity_kbtu_per_hr.round(0)}kBtu/hr")
|
354
|
+
|
355
|
+
if type == 'WrappedCondenser'
|
356
|
+
hpwh.setMinimumInletAirTemperatureforCompressorOperation(OpenStudio.convert(45.0, 'F', 'C').get)
|
357
|
+
hpwh.setMaximumInletAirTemperatureforCompressorOperation(OpenStudio.convert(120.0, 'F', 'C').get)
|
358
|
+
# set sensor heights
|
359
|
+
if hpwh_vol_gal <= 50.0
|
360
|
+
hpwh.setDeadBandTemperatureDifference(0.5)
|
361
|
+
h_UE = (1 - (3.5 / 12.0)) * tank_height # in the 4th node of the tank (counting from top)
|
362
|
+
h_LE = (1 - (10.5 / 12.0)) * tank_height # in the 11th node of the tank (counting from top)
|
363
|
+
h_condtop = (1 - (5.5 / 12.0)) * tank_height # in the 6th node of the tank (counting from top)
|
364
|
+
h_condbot = (1 - (10.99 / 12.0)) * tank_height # in the 11th node of the tank
|
365
|
+
h_hpctrl = (1 - (2.5 / 12.0)) * tank_height # in the 3rd node of the tank
|
366
|
+
hpwh.setControlSensor1HeightInStratifiedTank(h_hpctrl)
|
367
|
+
hpwh.setControlSensor1Weight(1.0)
|
368
|
+
hpwh.setControlSensor2HeightInStratifiedTank(h_hpctrl)
|
369
|
+
else
|
370
|
+
hpwh.setDeadBandTemperatureDifference(3.89)
|
371
|
+
h_UE = (1 - (3.5 / 12.0)) * tank_height # in the 3rd node of the tank (counting from top)
|
372
|
+
h_LE = (1 - (9.5 / 12.0)) * tank_height # in the 10th node of the tank (counting from top)
|
373
|
+
h_condtop = (1 - (5.5 / 12.0)) * tank_height # in the 6th node of the tank (counting from top)
|
374
|
+
h_condbot = 0.01 # bottom node
|
375
|
+
h_hpctrl_up = (1 - (2.5 / 12.0)) * tank_height # in the 3rd node of the tank
|
376
|
+
h_hpctrl_low = (1 - (8.5 / 12.0)) * tank_height # in the 9th node of the tank
|
377
|
+
hpwh.setControlSensor1HeightInStratifiedTank(h_hpctrl_up)
|
378
|
+
hpwh.setControlSensor1Weight(0.75)
|
379
|
+
hpwh.setControlSensor2HeightInStratifiedTank(h_hpctrl_low)
|
380
|
+
end
|
381
|
+
hpwh.setCondenserBottomLocation(h_condbot)
|
382
|
+
hpwh.setCondenserTopLocation(h_condtop)
|
383
|
+
hpwh.setTankElementControlLogic('MutuallyExclusive')
|
384
|
+
elsif type == 'PumpedCondenser'
|
385
|
+
hpwh.setDeadBandTemperatureDifference(3.89)
|
386
|
+
end
|
387
|
+
|
388
|
+
# set heat pump water heater properties
|
389
|
+
hpwh.setEvaporatorAirFlowRate(OpenStudio.convert(181.0, 'ft^3/min', 'm^3/s').get)
|
390
|
+
hpwh.setFanPlacement('DrawThrough')
|
391
|
+
hpwh.setOnCycleParasiticElectricLoad(0.0)
|
392
|
+
hpwh.setOffCycleParasiticElectricLoad(0.0)
|
393
|
+
hpwh.setParasiticHeatRejectionLocation('Outdoors')
|
394
|
+
|
395
|
+
# set temperature setpoint schedule
|
396
|
+
if swh_temp_sch.nil?
|
397
|
+
# temperature schedule type limits
|
398
|
+
temp_sch_type_limits = model_add_schedule_type_limits(model,
|
399
|
+
name: 'Temperature Schedule Type Limits',
|
400
|
+
lower_limit_value: 0.0,
|
401
|
+
upper_limit_value: 100.0,
|
402
|
+
numeric_type: 'Continuous',
|
403
|
+
unit_type: 'Temperature')
|
404
|
+
# service water heating loop controls
|
405
|
+
swh_temp_c = service_water_temperature
|
406
|
+
swh_temp_f = OpenStudio.convert(swh_temp_c, 'C', 'F').get
|
407
|
+
swh_delta_t_r = 9.0 # 9F delta-T
|
408
|
+
swh_temp_c = OpenStudio.convert(swh_temp_f, 'F', 'C').get
|
409
|
+
swh_delta_t_k = OpenStudio.convert(swh_delta_t_r, 'R', 'K').get
|
410
|
+
swh_temp_sch = model_add_constant_schedule_ruleset(model,
|
411
|
+
swh_temp_c,
|
412
|
+
name = "Heat Pump Water Heater Temp - #{swh_temp_f.round}F")
|
413
|
+
swh_temp_sch.setScheduleTypeLimits(temp_sch_type_limits)
|
414
|
+
end
|
415
|
+
hpwh.setCompressorSetpointTemperatureSchedule(swh_temp_sch)
|
416
|
+
|
417
|
+
# coil curves
|
418
|
+
hpwh_cap = OpenStudio::Model::CurveBiquadratic.new(model)
|
419
|
+
hpwh_cap.setName('HPWH-Cap-fT')
|
420
|
+
hpwh_cap.setCoefficient1Constant(0.563)
|
421
|
+
hpwh_cap.setCoefficient2x(0.0437)
|
422
|
+
hpwh_cap.setCoefficient3xPOW2(0.000039)
|
423
|
+
hpwh_cap.setCoefficient4y(0.0055)
|
424
|
+
hpwh_cap.setCoefficient5yPOW2(-0.000148)
|
425
|
+
hpwh_cap.setCoefficient6xTIMESY(-0.000145)
|
426
|
+
hpwh_cap.setMinimumValueofx(0.0)
|
427
|
+
hpwh_cap.setMaximumValueofx(100.0)
|
428
|
+
hpwh_cap.setMinimumValueofy(0.0)
|
429
|
+
hpwh_cap.setMaximumValueofy(100.0)
|
430
|
+
|
431
|
+
hpwh_cop = OpenStudio::Model::CurveBiquadratic.new(model)
|
432
|
+
hpwh_cop.setName('HPWH-COP-fT')
|
433
|
+
hpwh_cop.setCoefficient1Constant(1.1332)
|
434
|
+
hpwh_cop.setCoefficient2x(0.063)
|
435
|
+
hpwh_cop.setCoefficient3xPOW2(-0.0000979)
|
436
|
+
hpwh_cop.setCoefficient4y(-0.00972)
|
437
|
+
hpwh_cop.setCoefficient5yPOW2(-0.0000214)
|
438
|
+
hpwh_cop.setCoefficient6xTIMESY(-0.000686)
|
439
|
+
hpwh_cop.setMinimumValueofx(0.0)
|
440
|
+
hpwh_cop.setMaximumValueofx(100.0)
|
441
|
+
hpwh_cop.setMinimumValueofy(0.0)
|
442
|
+
hpwh_cop.setMaximumValueofy(100.0)
|
443
|
+
|
444
|
+
# create DX coil object
|
445
|
+
if type == 'WrappedCondenser'
|
446
|
+
coil = hpwh.dXCoil.to_CoilWaterHeatingAirToWaterHeatPumpWrapped.get
|
447
|
+
coil.setRatedCondenserWaterTemperature(48.89)
|
448
|
+
elsif type == 'PumpedCondenser'
|
449
|
+
coil = OpenStudio::Model::CoilWaterHeatingAirToWaterHeatPump.new(model)
|
450
|
+
hpwh.setDXCoil(coil)
|
451
|
+
end
|
452
|
+
|
453
|
+
# set coil properties
|
454
|
+
coil.setName("#{hpwh.name} Coil")
|
455
|
+
coil.setRatedHeatingCapacity(water_heater_capacity * cop)
|
456
|
+
coil.setRatedCOP(cop)
|
457
|
+
coil.setRatedSensibleHeatRatio(shr)
|
458
|
+
coil.setRatedEvaporatorInletAirDryBulbTemperature(OpenStudio.convert(67.5, 'F', 'C').get)
|
459
|
+
coil.setRatedEvaporatorInletAirWetBulbTemperature(OpenStudio.convert(56.426, 'F', 'C').get)
|
460
|
+
coil.setRatedEvaporatorAirFlowRate(OpenStudio.convert(181.0, 'ft^3/min', 'm^3/s').get)
|
461
|
+
coil.setEvaporatorFanPowerIncludedinRatedCOP(true)
|
462
|
+
coil.setEvaporatorAirTemperatureTypeforCurveObjects('WetBulbTemperature')
|
463
|
+
coil.setHeatingCapacityFunctionofTemperatureCurve(hpwh_cap)
|
464
|
+
coil.setHeatingCOPFunctionofTemperatureCurve(hpwh_cop)
|
465
|
+
coil.setMaximumAmbientTemperatureforCrankcaseHeaterOperation(0.0)
|
466
|
+
|
467
|
+
# set tank properties
|
468
|
+
if type == 'WrappedCondenser'
|
469
|
+
tank = hpwh.tank.to_WaterHeaterStratified.get
|
470
|
+
tank.setTankHeight(tank_height)
|
471
|
+
tank.setHeaterPriorityControl('MasterSlave')
|
472
|
+
if hpwh_vol_gal <= 50.0
|
473
|
+
tank.setHeater1DeadbandTemperatureDifference(25.0)
|
474
|
+
tank.setHeater2DeadbandTemperatureDifference(30.0)
|
475
|
+
else
|
476
|
+
tank.setHeater1DeadbandTemperatureDifference(18.5)
|
477
|
+
tank.setHeater2DeadbandTemperatureDifference(3.89)
|
478
|
+
end
|
479
|
+
hpwh_bottom_element_sp = OpenStudio::Model::ScheduleConstant.new(model)
|
480
|
+
hpwh_bottom_element_sp.setName("#{hpwh.name} BottomElementSetpoint")
|
481
|
+
hpwh_top_element_sp = OpenStudio::Model::ScheduleConstant.new(model)
|
482
|
+
hpwh_top_element_sp.setName("#{hpwh.name} TopElementSetpoint")
|
483
|
+
tank.setHeater1Capacity(electric_backup_capacity)
|
484
|
+
tank.setHeater1Height(h_UE)
|
485
|
+
tank.setHeater1SetpointTemperatureSchedule(hpwh_top_element_sp) # Overwritten later by EMS
|
486
|
+
tank.setHeater2Capacity(electric_backup_capacity)
|
487
|
+
tank.setHeater2Height(h_LE)
|
488
|
+
tank.setHeater2SetpointTemperatureSchedule(hpwh_bottom_element_sp)
|
489
|
+
tank.setUniformSkinLossCoefficientperUnitAreatoAmbientTemperature(u_tank)
|
490
|
+
tank.setNumberofNodes(12)
|
491
|
+
tank.setAdditionalDestratificationConductivity(0)
|
492
|
+
tank.setNode1AdditionalLossCoefficient(0)
|
493
|
+
tank.setNode2AdditionalLossCoefficient(0)
|
494
|
+
tank.setNode3AdditionalLossCoefficient(0)
|
495
|
+
tank.setNode4AdditionalLossCoefficient(0)
|
496
|
+
tank.setNode5AdditionalLossCoefficient(0)
|
497
|
+
tank.setNode6AdditionalLossCoefficient(0)
|
498
|
+
tank.setNode7AdditionalLossCoefficient(0)
|
499
|
+
tank.setNode8AdditionalLossCoefficient(0)
|
500
|
+
tank.setNode9AdditionalLossCoefficient(0)
|
501
|
+
tank.setNode10AdditionalLossCoefficient(0)
|
502
|
+
tank.setNode11AdditionalLossCoefficient(0)
|
503
|
+
tank.setNode12AdditionalLossCoefficient(0)
|
504
|
+
tank.setUseSideDesignFlowRate(0.9 * water_heater_volume / 60.1)
|
505
|
+
tank.setSourceSideDesignFlowRate(0)
|
506
|
+
tank.setSourceSideFlowControlMode('')
|
507
|
+
tank.setSourceSideInletHeight(0)
|
508
|
+
tank.setSourceSideOutletHeight(0)
|
509
|
+
elsif type == 'PumpedCondenser'
|
510
|
+
tank = OpenStudio::Model::WaterHeaterMixed.new(model)
|
511
|
+
hpwh.setTank(tank)
|
512
|
+
tank.setDeadbandTemperatureDifference(3.89)
|
513
|
+
tank.setHeaterControlType('Cycle')
|
514
|
+
tank.setHeaterMaximumCapacity(electric_backup_capacity)
|
515
|
+
end
|
516
|
+
tank.setName("#{hpwh.name} Tank")
|
517
|
+
tank.setEndUseSubcategory('Service Hot Water')
|
518
|
+
tank.setTankVolume(0.9 * water_heater_volume)
|
519
|
+
tank.setMaximumTemperatureLimit(90.0)
|
520
|
+
tank.setHeaterFuelType('Electricity')
|
521
|
+
tank.setHeaterThermalEfficiency(1.0)
|
522
|
+
tank.setOffCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
|
523
|
+
tank.setOffCycleParasiticFuelType('Electricity')
|
524
|
+
tank.setOnCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
|
525
|
+
tank.setOnCycleParasiticFuelType('Electricity')
|
526
|
+
|
527
|
+
# set fan properties
|
528
|
+
fan = hpwh.fan.to_FanOnOff.get
|
529
|
+
fan.setName("#{hpwh.name} Fan")
|
530
|
+
fan_power = 0.0462 # watts per cfm
|
531
|
+
if hpwh_vol_gal <= 50.0
|
532
|
+
fan.setFanEfficiency(23.0 / fan_power * OpenStudio.convert(1.0, 'ft^3/min', 'm^3/s').get)
|
533
|
+
fan.setPressureRise(23.0)
|
534
|
+
else
|
535
|
+
fan.setFanEfficiency(65.0 / fan_power * OpenStudio.convert(1.0, 'ft^3/min', 'm^3/s').get)
|
536
|
+
fan.setPressureRise(65.0)
|
537
|
+
end
|
538
|
+
fan.setMaximumFlowRate(OpenStudio.convert(181.0, 'ft^3/min', 'm^3/s').get)
|
539
|
+
fan.setMotorEfficiency(1.0)
|
540
|
+
fan.setMotorInAirstreamFraction(1.0)
|
541
|
+
fan.setEndUseSubcategory('Service Hot Water')
|
542
|
+
|
543
|
+
if water_heater_thermal_zone.nil?
|
544
|
+
# add in schedules for Tamb, RHamb, and the compressor
|
545
|
+
# assume the water heater is indoors at 70F for now
|
546
|
+
default_water_heater_ambient_temp_sch = model_add_constant_schedule_ruleset(model,
|
547
|
+
OpenStudio.convert(70.0, 'F', 'C').get,
|
548
|
+
name = 'Water Heater Ambient Temp Schedule - 70F')
|
549
|
+
default_water_heater_ambient_temp_sch.setScheduleTypeLimits(temp_sch_type_limits)
|
550
|
+
tank.setAmbientTemperatureIndicator('Schedule')
|
551
|
+
tank.setAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch)
|
552
|
+
tank.resetAmbientTemperatureThermalZone
|
553
|
+
hpwh_rhamb = OpenStudio::Model::ScheduleConstant.new(model)
|
554
|
+
hpwh_rhamb.setName("#{hpwh.name} Ambient Humidity Schedule")
|
555
|
+
hpwh_rhamb.setValue(0.5)
|
556
|
+
hpwh.setInletAirConfiguration('Schedule')
|
557
|
+
hpwh.setInletAirTemperatureSchedule(default_water_heater_ambient_temp_sch)
|
558
|
+
hpwh.setInletAirHumiditySchedule(hpwh_rhamb)
|
559
|
+
hpwh.setCompressorLocation('Schedule')
|
560
|
+
hpwh.setCompressorAmbientTemperatureSchedule(default_water_heater_ambient_temp_sch)
|
561
|
+
else
|
562
|
+
hpwh.addToThermalZone(water_heater_thermal_zone)
|
563
|
+
hpwh.setInletAirConfiguration('ZoneAirOnly')
|
564
|
+
hpwh.setCompressorLocation('Zone')
|
565
|
+
tank.setAmbientTemperatureIndicator('ThermalZone')
|
566
|
+
tank.setAmbientTemperatureThermalZone(water_heater_thermal_zone)
|
567
|
+
tank.resetAmbientTemperatureSchedule
|
568
|
+
end
|
569
|
+
|
570
|
+
if set_peak_use_flowrate
|
571
|
+
rated_flow_rate_m3_per_s = peak_flowrate
|
572
|
+
rated_flow_rate_gal_per_min = OpenStudio.convert(rated_flow_rate_m3_per_s, 'm^3/s', 'gal/min').get
|
573
|
+
tank.setPeakUseFlowRate(rated_flow_rate_m3_per_s)
|
574
|
+
schedule = model_add_schedule(model, flowrate_schedule)
|
575
|
+
tank.setUseFlowRateFractionSchedule(schedule)
|
576
|
+
end
|
577
|
+
|
578
|
+
return hpwh
|
579
|
+
end
|
580
|
+
|
255
581
|
# Creates a booster water heater and attaches it
|
256
582
|
# to the supplied service water heating loop.
|
257
583
|
#
|
@@ -266,7 +592,6 @@ class Standard
|
|
266
592
|
# fuel consumption rate, in W
|
267
593
|
# @param booster_water_heater_thermal_zone [OpenStudio::Model::ThermalZone]
|
268
594
|
# zones to place water heater in. If nil, will be assumed in 70F air for heat loss.
|
269
|
-
# @param building_type [String] the building type
|
270
595
|
# @return [OpenStudio::Model::PlantLoop]
|
271
596
|
# the resulting booster water loop.
|
272
597
|
def model_add_swh_booster(model,
|
@@ -276,8 +601,7 @@ class Standard
|
|
276
601
|
water_heater_fuel,
|
277
602
|
booster_water_temperature,
|
278
603
|
parasitic_fuel_consumption_rate,
|
279
|
-
booster_water_heater_thermal_zone
|
280
|
-
building_type = nil)
|
604
|
+
booster_water_heater_thermal_zone)
|
281
605
|
|
282
606
|
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding booster water heater to #{main_service_water_loop.name}")
|
283
607
|
|
@@ -311,12 +635,13 @@ class Standard
|
|
311
635
|
sizing_plant.setLoopDesignTemperatureDifference(swh_delta_t_k)
|
312
636
|
|
313
637
|
# Booster water heating pump
|
314
|
-
swh_pump = OpenStudio::Model::
|
638
|
+
swh_pump = OpenStudio::Model::PumpVariableSpeed.new(model)
|
315
639
|
swh_pump.setName('Booster Water Loop Pump')
|
316
|
-
|
317
|
-
swh_pump.
|
640
|
+
swh_pump.setRatedPumpHead(0.0) # As if there is no circulation pump
|
641
|
+
swh_pump.setRatedPowerConsumption(0.0) # As if there is no circulation pump
|
318
642
|
swh_pump.setMotorEfficiency(1)
|
319
|
-
swh_pump.setPumpControlType('
|
643
|
+
swh_pump.setPumpControlType('Continuous')
|
644
|
+
swh_pump.setMinimumFlowRate(0.0)
|
320
645
|
swh_pump.addToNode(booster_service_water_loop.supplyInletNode)
|
321
646
|
|
322
647
|
# Water heater
|
@@ -331,6 +656,8 @@ class Standard
|
|
331
656
|
water_heater.setName("#{water_heater_vol_gal}gal #{water_heater_fuel} Booster Water Heater - #{water_heater_capacity_kbtu_per_hr.round}kBtu/hr")
|
332
657
|
water_heater.setTankVolume(OpenStudio.convert(water_heater_vol_gal, 'gal', 'm^3').get)
|
333
658
|
water_heater.setSetpointTemperatureSchedule(swh_temp_sch)
|
659
|
+
water_heater.setDeadbandTemperatureDifference(2.0)
|
660
|
+
water_heater.setEndUseSubcategory('Booster')
|
334
661
|
|
335
662
|
if booster_water_heater_thermal_zone.nil?
|
336
663
|
# Assume the water heater is indoors at 70F or 72F
|
@@ -353,7 +680,7 @@ class Standard
|
|
353
680
|
water_heater.resetAmbientTemperatureSchedule
|
354
681
|
end
|
355
682
|
|
356
|
-
water_heater.setMaximumTemperatureLimit(
|
683
|
+
water_heater.setMaximumTemperatureLimit(swh_temp_c)
|
357
684
|
water_heater.setDeadbandTemperatureDifference(OpenStudio.convert(3.6, 'R', 'K').get)
|
358
685
|
water_heater.setHeaterControlType('Cycle')
|
359
686
|
water_heater.setHeaterMaximumCapacity(OpenStudio.convert(water_heater_capacity_btu_per_hr, 'Btu/hr', 'W').get)
|
@@ -368,7 +695,7 @@ class Standard
|
|
368
695
|
water_heater.setOnCycleParasiticFuelType('Electricity')
|
369
696
|
water_heater.setOffCycleLossCoefficienttoAmbientTemperature(1.053)
|
370
697
|
water_heater.setOnCycleLossCoefficienttoAmbientTemperature(1.053)
|
371
|
-
elsif water_heater_fuel == 'Natural Gas'
|
698
|
+
elsif water_heater_fuel == 'Natural Gas' || water_heater_fuel == 'NaturalGas'
|
372
699
|
water_heater.setHeaterFuelType('Gas')
|
373
700
|
water_heater.setHeaterThermalEfficiency(0.8)
|
374
701
|
water_heater.setOffCycleParasiticFuelConsumptionRate(parasitic_fuel_consumption_rate)
|
@@ -379,15 +706,6 @@ class Standard
|
|
379
706
|
water_heater.setOnCycleLossCoefficienttoAmbientTemperature(6.0)
|
380
707
|
end
|
381
708
|
|
382
|
-
if water_heater_fuel == 'Electricity'
|
383
|
-
water_heater.setHeaterFuelType('Electricity')
|
384
|
-
water_heater.setOffCycleParasiticFuelType('Electricity')
|
385
|
-
water_heater.setOnCycleParasiticFuelType('Electricity')
|
386
|
-
elsif water_heater_fuel == 'Natural Gas'
|
387
|
-
water_heater.setHeaterFuelType('Gas')
|
388
|
-
water_heater.setOffCycleParasiticFuelType('Gas')
|
389
|
-
water_heater.setOnCycleParasiticFuelType('Gas')
|
390
|
-
end
|
391
709
|
booster_service_water_loop.addSupplyBranchForComponent(water_heater)
|
392
710
|
|
393
711
|
# Service water heating loop bypass pipes
|
@@ -417,6 +735,43 @@ class Standard
|
|
417
735
|
# the main service water loop.
|
418
736
|
main_service_water_loop.addDemandBranchForComponent(hx)
|
419
737
|
|
738
|
+
# Add a plant component temperature source to the demand outlet
|
739
|
+
# of the HX to represent the fact that the water used by the booster
|
740
|
+
# would in reality be at the mains temperature.
|
741
|
+
mains_src = OpenStudio::Model::PlantComponentTemperatureSource.new(model)
|
742
|
+
mains_src.setName('Mains Water Makeup for SWH Booster')
|
743
|
+
mains_src.addToNode(hx.demandOutletModelObject.get.to_Node.get)
|
744
|
+
|
745
|
+
# Mains water temperature sensor
|
746
|
+
mains_water_temp_sen = OpenStudio::Model::EnergyManagementSystemSensor.new(model, 'Site Mains Water Temperature')
|
747
|
+
mains_water_temp_sen.setName('Mains_Water_Temp_Sen')
|
748
|
+
mains_water_temp_sen.setKeyName('Environment')
|
749
|
+
|
750
|
+
# Schedule to actuate
|
751
|
+
water_mains_temp_sch = OpenStudio::Model::ScheduleConstant.new(model)
|
752
|
+
water_mains_temp_sch.setName('Mains Water Temperature')
|
753
|
+
water_mains_temp_sch.setValue(OpenStudio.convert(50, 'F', 'C').get)
|
754
|
+
|
755
|
+
# Actuator for mains water temperature schedule
|
756
|
+
mains_water_temp_sch_act = OpenStudio::Model::EnergyManagementSystemActuator.new(water_mains_temp_sch, 'Schedule:Constant', 'Schedule Value')
|
757
|
+
mains_water_temp_sch_act.setName('Mains_Water_Temp_Act')
|
758
|
+
|
759
|
+
# Program
|
760
|
+
mains_prg = OpenStudio::Model::EnergyManagementSystemProgram.new(model)
|
761
|
+
mains_prg.setName('Mains_Water_Prg')
|
762
|
+
mains_prg_body = "SET #{mains_water_temp_sch_act.handle} = #{mains_water_temp_sen.handle}"
|
763
|
+
mains_prg.setBody(mains_prg_body)
|
764
|
+
|
765
|
+
# Program Calling Manager
|
766
|
+
mains_mgr = OpenStudio::Model::EnergyManagementSystemProgramCallingManager.new(model)
|
767
|
+
mains_mgr.setName("Mains_Water_Prg_Mgr")
|
768
|
+
mains_mgr.setCallingPoint('BeginTimestepBeforePredictor')
|
769
|
+
mains_mgr.addProgram(mains_prg)
|
770
|
+
|
771
|
+
# Make the plant component use the actuated schedule
|
772
|
+
mains_src.setTemperatureSpecificationType('Scheduled')
|
773
|
+
mains_src.setSourceTemperatureSchedule(water_mains_temp_sch)
|
774
|
+
|
420
775
|
return booster_service_water_loop
|
421
776
|
end
|
422
777
|
|
@@ -432,7 +787,6 @@ class Standard
|
|
432
787
|
# @param water_use_temperature [Double] mixed water use temperature, in C
|
433
788
|
# @param space_name [String] the name of the space to add the water fixture to,
|
434
789
|
# or nil, in which case it will not be assigned to any particular space.
|
435
|
-
# @param building_type [String] the building type
|
436
790
|
# @return [OpenStudio::Model::WaterUseEquipment]
|
437
791
|
# the resulting water fixture.
|
438
792
|
def model_add_swh_end_uses(model,
|
@@ -442,10 +796,8 @@ class Standard
|
|
442
796
|
flowrate_schedule,
|
443
797
|
water_use_temperature,
|
444
798
|
space_name,
|
445
|
-
|
446
|
-
|
447
|
-
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding water fixture to #{swh_loop.name}.")
|
448
|
-
|
799
|
+
frac_sensible: 0.2,
|
800
|
+
frac_latent: 0.05)
|
449
801
|
# Water use connection
|
450
802
|
swh_connection = OpenStudio::Model::WaterUseConnections.new(model)
|
451
803
|
|
@@ -453,8 +805,7 @@ class Standard
|
|
453
805
|
water_fixture_def = OpenStudio::Model::WaterUseEquipmentDefinition.new(model)
|
454
806
|
rated_flow_rate_m3_per_s = peak_flowrate
|
455
807
|
rated_flow_rate_gal_per_min = OpenStudio.convert(rated_flow_rate_m3_per_s, 'm^3/s', 'gal/min').get
|
456
|
-
|
457
|
-
frac_latent = 0.05
|
808
|
+
|
458
809
|
water_use_sensible_frac_sch = model_add_constant_schedule_ruleset(model,
|
459
810
|
frac_sensible,
|
460
811
|
name = "Fraction Sensible - #{frac_sensible}",
|
@@ -466,7 +817,7 @@ class Standard
|
|
466
817
|
water_fixture_def.setSensibleFractionSchedule(water_use_sensible_frac_sch)
|
467
818
|
water_fixture_def.setLatentFractionSchedule(water_use_latent_frac_sch)
|
468
819
|
water_fixture_def.setPeakFlowRate(rated_flow_rate_m3_per_s)
|
469
|
-
water_fixture_def.setName("#{use_name
|
820
|
+
water_fixture_def.setName("#{use_name} Service Water Use Def #{rated_flow_rate_gal_per_min.round(2)}gpm")
|
470
821
|
# Target mixed water temperature
|
471
822
|
mixed_water_temp_f = OpenStudio.convert(water_use_temperature, 'C', 'F').get
|
472
823
|
mixed_water_temp_sch = model_add_constant_schedule_ruleset(model,
|
@@ -480,9 +831,11 @@ class Standard
|
|
480
831
|
water_fixture.setFlowRateFractionSchedule(schedule)
|
481
832
|
|
482
833
|
if space_name.nil?
|
483
|
-
water_fixture.setName("#{use_name
|
834
|
+
water_fixture.setName("#{use_name} Service Water Use #{rated_flow_rate_gal_per_min.round(2)}gpm at #{mixed_water_temp_f.round}F")
|
835
|
+
swh_connection.setName("#{use_name} WUC #{rated_flow_rate_gal_per_min.round(2)}gpm at #{mixed_water_temp_f.round}F")
|
484
836
|
else
|
485
|
-
water_fixture.setName("#{space_name
|
837
|
+
water_fixture.setName("#{space_name} Service Water Use #{rated_flow_rate_gal_per_min.round(2)}gpm at #{mixed_water_temp_f.round}F")
|
838
|
+
swh_connection.setName("#{space_name} WUC #{rated_flow_rate_gal_per_min.round(2)}gpm at #{mixed_water_temp_f.round}F")
|
486
839
|
end
|
487
840
|
|
488
841
|
unless space_name.nil?
|
@@ -494,31 +847,69 @@ class Standard
|
|
494
847
|
swh_connection.addWaterUseEquipment(water_fixture)
|
495
848
|
|
496
849
|
# Connect the water use connection to the SWH loop
|
497
|
-
swh_loop.
|
850
|
+
unless swh_loop.nil?
|
851
|
+
swh_loop.addDemandBranchForComponent(swh_connection)
|
852
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding water fixture to #{swh_loop.name}.")
|
853
|
+
end
|
854
|
+
|
855
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Added #{water_fixture.name}.")
|
498
856
|
|
499
857
|
return water_fixture
|
500
858
|
end
|
501
859
|
|
502
|
-
# This method will add
|
503
|
-
#
|
504
|
-
|
860
|
+
# This method will add a swh water fixture to the model for the space.
|
861
|
+
# It will return a water fixture object, or NIL if there is no water load at all.
|
862
|
+
#
|
863
|
+
# Adds a WaterUseEquipment object representing the SWH loads of the supplied Space.
|
864
|
+
# Attaches this WaterUseEquipment to the supplied PlantLoop via a new WaterUseConnections object.
|
865
|
+
#
|
866
|
+
# @param model [OpenStudio::Model::Model] the model
|
867
|
+
# @param swh_loop [OpenStudio::Model::PlantLoop] the SWH loop to connect the WaterUseEquipment to
|
868
|
+
# @space [OpenStudio::Model::Space] the Space to add a WaterUseEquipment for
|
869
|
+
# @space_multiplier [Double] the multiplier to use if the supplied Space actually represents
|
870
|
+
# more area than is shown in the model.
|
871
|
+
# @param is_flow_per_area [Bool] if true, use the value in the 'service_water_heating_peak_flow_per_area'
|
872
|
+
# field of the space_types JSON. If false, use the value in the 'service_water_heating_peak_flow_rate' field.
|
873
|
+
# @return [OpenStudio::Model::WaterUseEquipment] the WaterUseEquipment for the
|
874
|
+
def model_add_swh_end_uses_by_space(model,
|
875
|
+
swh_loop,
|
876
|
+
space,
|
877
|
+
space_multiplier = 1.0,
|
878
|
+
is_flow_per_area = true)
|
879
|
+
# SpaceType
|
880
|
+
if space.spaceType.empty?
|
881
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "Space #{space.name} does not have a Space Type assigned, cannot add SWH end uses.")
|
882
|
+
return nil
|
883
|
+
end
|
884
|
+
space_type = space.spaceType.get
|
885
|
+
|
886
|
+
# Standards Building Type
|
887
|
+
if space_type.standardsBuildingType.empty?
|
888
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "Space #{space.name}'s Space Type does not have a Standards Building Type assigned, cannot add SWH end uses.")
|
889
|
+
return nil
|
890
|
+
end
|
891
|
+
stds_bldg_type = space_type.standardsBuildingType.get
|
892
|
+
building_type = model_get_lookup_name(stds_bldg_type)
|
893
|
+
|
894
|
+
# Standards Space Type
|
895
|
+
if space_type.standardsSpaceType.empty?
|
896
|
+
OpenStudio.logFree(OpenStudio::Warn, 'openstudio.model.Model', "Space #{space.name}'s Space Type does not have a Standards Space Type assigned, cannot add SWH end uses.")
|
897
|
+
return nil
|
898
|
+
end
|
899
|
+
stds_spc_type = space_type.standardsSpaceType.get
|
900
|
+
|
505
901
|
# find the specific space_type properties from standard.json
|
506
902
|
search_criteria = {
|
507
903
|
'template' => template,
|
508
904
|
'building_type' => building_type,
|
509
|
-
'space_type' =>
|
905
|
+
'space_type' => stds_spc_type
|
510
906
|
}
|
511
907
|
data = model_find_object(standards_data['space_types'], search_criteria)
|
512
908
|
if data.nil?
|
513
909
|
OpenStudio.logFree(OpenStudio::Error, 'openstudio.Model.Model', "Could not find space type for: #{search_criteria}.")
|
514
910
|
return nil
|
515
911
|
end
|
516
|
-
space = model.getSpaceByName(space_name)
|
517
|
-
space = space.get
|
518
912
|
space_area = OpenStudio.convert(space.floorArea, 'm^2', 'ft^2').get # ft2
|
519
|
-
if space_multiplier.nil?
|
520
|
-
space_multiplier = 1
|
521
|
-
end
|
522
913
|
|
523
914
|
# If there is no service hot water load.. Don't bother adding anything.
|
524
915
|
if data['service_water_heating_peak_flow_per_area'].to_f == 0.0 &&
|
@@ -550,10 +941,10 @@ class Standard
|
|
550
941
|
water_fixture_def.setSensibleFractionSchedule(water_use_sensible_frac_sch)
|
551
942
|
water_fixture_def.setLatentFractionSchedule(water_use_latent_frac_sch)
|
552
943
|
water_fixture_def.setPeakFlowRate(rated_flow_rate_m3_per_s)
|
553
|
-
water_fixture_def.setName("#{
|
944
|
+
water_fixture_def.setName("#{space.name.get} Service Water Use Def #{rated_flow_rate_gal_per_min.round(2)}gpm")
|
554
945
|
# Target mixed water temperature
|
555
|
-
|
556
|
-
|
946
|
+
mixed_water_temp_f = data['service_water_heating_target_temperature']
|
947
|
+
mixed_water_temp_c = OpenStudio.convert(mixed_water_temp_f, 'F', 'C').get
|
557
948
|
mixed_water_temp_sch = model_add_constant_schedule_ruleset(model,
|
558
949
|
mixed_water_temp_c,
|
559
950
|
name = "Mixed Water At Faucet Temp - #{mixed_water_temp_f.round}F")
|
@@ -563,7 +954,7 @@ class Standard
|
|
563
954
|
water_fixture = OpenStudio::Model::WaterUseEquipment.new(water_fixture_def)
|
564
955
|
schedule = model_add_schedule(model, data['service_water_heating_schedule'])
|
565
956
|
water_fixture.setFlowRateFractionSchedule(schedule)
|
566
|
-
water_fixture.setName("#{
|
957
|
+
water_fixture.setName("#{space.name.get} Service Water Use #{rated_flow_rate_gal_per_min.round(2)}gpm")
|
567
958
|
swh_connection.addWaterUseEquipment(water_fixture)
|
568
959
|
# Assign water fixture to a space
|
569
960
|
water_fixture.setSpace(space) if model_attach_water_fixtures_to_spaces?(model)
|
@@ -586,17 +977,13 @@ class Standard
|
|
586
977
|
# @param peak_flowrate [Double] in m^3/s
|
587
978
|
# @param flowrate_schedule [String] name of the flow rate schedule
|
588
979
|
# @param water_use_temperature [Double] mixed water use temperature, in C
|
589
|
-
# @param building_type [String] the building type
|
590
980
|
# @return [OpenStudio::Model::WaterUseEquipment]
|
591
981
|
# the resulting water fixture.
|
592
982
|
def model_add_booster_swh_end_uses(model,
|
593
983
|
swh_booster_loop,
|
594
984
|
peak_flowrate,
|
595
985
|
flowrate_schedule,
|
596
|
-
water_use_temperature
|
597
|
-
building_type = nil)
|
598
|
-
|
599
|
-
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding water fixture to #{swh_booster_loop.name}.")
|
986
|
+
water_use_temperature)
|
600
987
|
|
601
988
|
# Water use connection
|
602
989
|
swh_connection = OpenStudio::Model::WaterUseConnections.new(model)
|
@@ -605,10 +992,10 @@ class Standard
|
|
605
992
|
water_fixture_def = OpenStudio::Model::WaterUseEquipmentDefinition.new(model)
|
606
993
|
rated_flow_rate_m3_per_s = peak_flowrate
|
607
994
|
rated_flow_rate_gal_per_min = OpenStudio.convert(rated_flow_rate_m3_per_s, 'm^3/s', 'gal/min').get
|
608
|
-
water_fixture_def.setName("Water Fixture Def - #{rated_flow_rate_gal_per_min}
|
995
|
+
water_fixture_def.setName("Booster Water Fixture Def - #{rated_flow_rate_gal_per_min.round(2)} gpm")
|
609
996
|
water_fixture_def.setPeakFlowRate(rated_flow_rate_m3_per_s)
|
610
997
|
# Target mixed water temperature
|
611
|
-
mixed_water_temp_f = OpenStudio.convert(water_use_temperature, '
|
998
|
+
mixed_water_temp_f = OpenStudio.convert(water_use_temperature, 'C', 'F').get
|
612
999
|
mixed_water_temp_sch = model_add_constant_schedule_ruleset(model,
|
613
1000
|
OpenStudio.convert(mixed_water_temp_f, 'F', 'C').get,
|
614
1001
|
name = "Mixed Water At Faucet Temp - #{mixed_water_temp_f.round}F")
|
@@ -616,15 +1003,137 @@ class Standard
|
|
616
1003
|
|
617
1004
|
# Water use equipment
|
618
1005
|
water_fixture = OpenStudio::Model::WaterUseEquipment.new(water_fixture_def)
|
619
|
-
water_fixture.setName("Booster Water Fixture - #{rated_flow_rate_gal_per_min}
|
1006
|
+
water_fixture.setName("Booster Water Fixture - #{rated_flow_rate_gal_per_min.round(2)} gpm at #{mixed_water_temp_f.round}F")
|
620
1007
|
schedule = model_add_schedule(model, flowrate_schedule)
|
621
1008
|
water_fixture.setFlowRateFractionSchedule(schedule)
|
622
1009
|
swh_connection.addWaterUseEquipment(water_fixture)
|
623
1010
|
|
624
1011
|
# Connect the water use connection to the SWH loop
|
625
|
-
swh_booster_loop.
|
1012
|
+
unless swh_booster_loop.nil?
|
1013
|
+
swh_booster_loop.addDemandBranchForComponent(swh_connection)
|
1014
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.Model.Model', "Adding water fixture to #{swh_booster_loop.name}.")
|
1015
|
+
end
|
626
1016
|
|
627
1017
|
return water_fixture
|
628
1018
|
end
|
629
1019
|
|
1020
|
+
# Adds insulated 0.75in copper piping to the model.
|
1021
|
+
# For circulating systems, assume length of piping is proportional
|
1022
|
+
# to the area and number of stories in the building.
|
1023
|
+
# For non-circulating systems, assume that the water heaters
|
1024
|
+
# are close to the point of use.
|
1025
|
+
# Assume that piping is located in a zone
|
1026
|
+
#
|
1027
|
+
# @param model [OpenStudio::Model::Model] the model
|
1028
|
+
# @param swh_loop [OpenStudio::Model::PlantLoop] the service water heating loop
|
1029
|
+
# @param floor_area_served [Double] the area of building served by the service water heating loop, in m^2
|
1030
|
+
# @param number_of_stories [Integer] the number of stories served by the service water heating loop
|
1031
|
+
# @param pipe_insulation_thickness [Double] the thickness of the pipe insulation, in m. Use 0 for no insulation
|
1032
|
+
# @param circulating [Bool] use true for circulating systems, false for non-circulating systems
|
1033
|
+
# @param air_temp_surrounding_piping [Double] the temperature of the air surrounding the piping, in C.
|
1034
|
+
def model_add_piping_losses_to_swh_system(model,
|
1035
|
+
swh_loop,
|
1036
|
+
circulating,
|
1037
|
+
pipe_insulation_thickness: 0,
|
1038
|
+
floor_area_served: 465,
|
1039
|
+
number_of_stories: 1,
|
1040
|
+
air_temp_surrounding_piping: 21.1111)
|
1041
|
+
|
1042
|
+
# Estimate pipe length
|
1043
|
+
if circulating
|
1044
|
+
# For circulating systems, get pipe length based on the size of the building.
|
1045
|
+
# Formula from A.3.1 PrototypeModelEnhancements_2014_0.pdf
|
1046
|
+
floor_area_ft2 = OpenStudio.convert(floor_area_served, 'm^2', 'ft^2').get
|
1047
|
+
pipe_length_ft = 2.0 * (Math.sqrt(floor_area_ft2 / number_of_stories) + (10.0 * (number_of_stories - 1.0)))
|
1048
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "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) ) )")
|
1049
|
+
else
|
1050
|
+
# For non-circulating systems, assume water heater is close to point of use
|
1051
|
+
pipe_length_ft = 20.0
|
1052
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Pipe length #{pipe_length_ft.round}ft. For non-circulating systems, assume water heater is close to point of use.")
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
# For systems whose water heater object represents multiple pieces
|
1056
|
+
# of equipment, multiply the piping length by the number of pieces of equipment.
|
1057
|
+
swh_loop.supplyComponents('OS_WaterHeater_Mixed'.to_IddObjectType).each do |sc|
|
1058
|
+
next unless sc.to_WaterHeaterMixed.is_initialized
|
1059
|
+
water_heater = sc.to_WaterHeaterMixed.get
|
1060
|
+
comp_qty = water_heater.component_quantity
|
1061
|
+
if comp_qty > 1
|
1062
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Piping length has been multiplied by #{comp_qty}X because #{water_heater.name} represents #{comp_qty} pieces of equipment.")
|
1063
|
+
pipe_length_ft *= comp_qty
|
1064
|
+
break
|
1065
|
+
end
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
# Service water heating piping heat loss scheduled air temperature
|
1069
|
+
swh_piping_air_temp_c = air_temp_surrounding_piping
|
1070
|
+
swh_piping_air_temp_f = OpenStudio.convert(swh_piping_air_temp_c, 'C', 'F').get
|
1071
|
+
swh_piping_air_temp_sch = model_add_constant_schedule_ruleset(model,
|
1072
|
+
swh_piping_air_temp_c,
|
1073
|
+
name = "#{swh_loop.name} Piping Air Temp - #{swh_piping_air_temp_f.round}F")
|
1074
|
+
|
1075
|
+
# Service water heating piping heat loss scheduled air velocity
|
1076
|
+
swh_piping_air_velocity_m_per_s = 0.3
|
1077
|
+
swh_piping_air_velocity_mph = OpenStudio.convert(swh_piping_air_velocity_m_per_s, 'm/s', 'mile/hr').get
|
1078
|
+
swh_piping_air_velocity_sch = model_add_constant_schedule_ruleset(model,
|
1079
|
+
swh_piping_air_velocity_m_per_s,
|
1080
|
+
name = "#{swh_loop.name} Piping Air Velocity - #{swh_piping_air_velocity_mph.round(2)}mph")
|
1081
|
+
|
1082
|
+
# Material for 3/4in type L (heavy duty) copper pipe
|
1083
|
+
copper_pipe = OpenStudio::Model::StandardOpaqueMaterial.new(model)
|
1084
|
+
copper_pipe.setName("Copper pipe 0.75in type L")
|
1085
|
+
copper_pipe.setRoughness('Smooth')
|
1086
|
+
copper_pipe.setThickness(OpenStudio.convert(0.045, 'in', 'm').get)
|
1087
|
+
copper_pipe.setConductivity(386.0)
|
1088
|
+
copper_pipe.setDensity(OpenStudio.convert(556, 'lb/ft^3', 'kg/m^3').get)
|
1089
|
+
copper_pipe.setSpecificHeat(OpenStudio.convert(0.092, 'Btu/lb*R', 'J/kg*K').get)
|
1090
|
+
copper_pipe.setThermalAbsorptance(0.9) # TODO: find reference for property
|
1091
|
+
copper_pipe.setSolarAbsorptance(0.7) # TODO: find reference for property
|
1092
|
+
copper_pipe.setVisibleAbsorptance(0.7) # TODO: find reference for property
|
1093
|
+
|
1094
|
+
# Construction for pipe
|
1095
|
+
pipe_construction = OpenStudio::Model::Construction.new(model)
|
1096
|
+
|
1097
|
+
# Add insulation material to insulated pipe
|
1098
|
+
if pipe_insulation_thickness > 0
|
1099
|
+
# Material for fiberglass insulation
|
1100
|
+
# R-value from Owens-Corning 1/2in fiberglass pipe insulation
|
1101
|
+
# https://www.grainger.com/product/OWENS-CORNING-1-2-Thick-40PP22
|
1102
|
+
# but modified until simulated heat loss = 17.7 Btu/hr/ft of pipe with 140F water and 70F air
|
1103
|
+
pipe_insulation_thickness_in = OpenStudio.convert(pipe_insulation_thickness, 'm', 'in').get
|
1104
|
+
insulation = OpenStudio::Model::StandardOpaqueMaterial.new(model)
|
1105
|
+
insulation.setName("Fiberglass batt #{pipe_insulation_thickness_in.round(2)}in")
|
1106
|
+
insulation.setRoughness('Smooth')
|
1107
|
+
insulation.setThickness(OpenStudio.convert(pipe_insulation_thickness_in, 'in', 'm').get)
|
1108
|
+
insulation.setConductivity(OpenStudio.convert(0.46, 'Btu*in/hr*ft^2*R', 'W/m*K').get)
|
1109
|
+
insulation.setDensity(OpenStudio.convert(0.7, 'lb/ft^3', 'kg/m^3').get)
|
1110
|
+
insulation.setSpecificHeat(OpenStudio.convert(0.2, 'Btu/lb*R', 'J/kg*K').get)
|
1111
|
+
insulation.setThermalAbsorptance(0.9) # Irrelevant for Pipe:Indoor; no radiation model is used
|
1112
|
+
insulation.setSolarAbsorptance(0.7) # Irrelevant for Pipe:Indoor; no radiation model is used
|
1113
|
+
insulation.setVisibleAbsorptance(0.7) # Irrelevant for Pipe:Indoor; no radiation model is used
|
1114
|
+
|
1115
|
+
pipe_construction.setName("Copper pipe 0.75in type L with #{pipe_insulation_thickness_in.round(2)}in fiberglass batt")
|
1116
|
+
pipe_construction.setLayers([insulation, copper_pipe])
|
1117
|
+
else
|
1118
|
+
pipe_construction.setName("Uninsulated copper pipe 0.75in type L")
|
1119
|
+
pipe_construction.setLayers([copper_pipe])
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
heat_loss_pipe = OpenStudio::Model::PipeIndoor.new(model)
|
1123
|
+
heat_loss_pipe.setName("#{swh_loop.name} Pipe #{pipe_length_ft}ft")
|
1124
|
+
heat_loss_pipe.setEnvironmentType('Schedule')
|
1125
|
+
# heat_loss_pipe.setAmbientTemperatureSchedule(swh_piping_air_temp_sch) # TODO: schedule type registry error for this setter
|
1126
|
+
heat_loss_pipe.setPointer(7, swh_piping_air_temp_sch.handle)
|
1127
|
+
# heat_loss_pipe.setAmbientAirVelocitySchedule(model.alwaysOffDiscreteSchedule) # TODO: schedule type registry error for this setter
|
1128
|
+
heat_loss_pipe.setPointer(8, swh_piping_air_velocity_sch.handle)
|
1129
|
+
heat_loss_pipe.setConstruction(pipe_construction)
|
1130
|
+
heat_loss_pipe.setPipeInsideDiameter(OpenStudio.convert(0.785, 'in', 'm').get)
|
1131
|
+
heat_loss_pipe.setPipeLength(OpenStudio.convert(pipe_length_ft, 'ft', 'm').get)
|
1132
|
+
|
1133
|
+
heat_loss_pipe.addToNode(swh_loop.demandInletNode)
|
1134
|
+
|
1135
|
+
OpenStudio.logFree(OpenStudio::Info, 'openstudio.model.Model', "Added #{pipe_length_ft.round}ft of #{pipe_construction.name} losing heat to #{swh_piping_air_temp_f.round}F air to #{swh_loop.name}.")
|
1136
|
+
return true
|
1137
|
+
end
|
1138
|
+
|
630
1139
|
end
|