openstudio-standards 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/data/standards/OpenStudio_Standards.xlsx +0 -0
- data/data/standards/OpenStudio_Standards_chillers.json +5 -5
- data/data/standards/OpenStudio_Standards_construction_sets.json +2 -2
- data/data/standards/OpenStudio_Standards_curve_cubics.json +12 -0
- data/data/standards/OpenStudio_Standards_heat_pumps.json +2126 -72
- data/data/standards/OpenStudio_Standards_heat_pumps_heating.json +766 -14
- data/data/standards/OpenStudio_Standards_heat_rejection.json +172 -0
- data/data/standards/OpenStudio_Standards_prototype_inputs.json +355 -267
- data/data/standards/OpenStudio_Standards_schedules.json +262 -10
- data/data/standards/OpenStudio_Standards_space_types.json +1466 -794
- data/data/standards/manage_OpenStudio_Standards.rb +19 -21
- data/data/weather/weather_info.csv +96 -0
- data/lib/openstudio-standards/btap/btap.rb +1 -1
- data/lib/openstudio-standards/btap/compliance.rb +135 -40
- data/lib/openstudio-standards/btap/envelope.rb +26 -5
- data/lib/openstudio-standards/btap/geometry.rb +11 -1
- data/lib/openstudio-standards/btap/hvac.rb +489 -56
- data/lib/openstudio-standards/btap/simmanager.rb +1 -1
- data/lib/openstudio-standards/hvac_sizing/HVACSizing.CoilHeatingDXMultiSpeed.rb +120 -0
- data/lib/openstudio-standards/hvac_sizing/Siz.CoilCoolingDXMultiSpeed.rb +151 -7
- data/lib/openstudio-standards/hvac_sizing/Siz.CoilHeatingGasMultiStage.rb +48 -7
- data/lib/openstudio-standards/hvac_sizing/Siz.Model.rb +14 -12
- data/lib/openstudio-standards/prototypes/Prototype.AirTerminalSingleDuctVAVReheat.rb +33 -7
- data/lib/openstudio-standards/prototypes/Prototype.Model.hvac.rb +123 -91
- data/lib/openstudio-standards/prototypes/Prototype.Model.rb +130 -45
- data/lib/openstudio-standards/prototypes/Prototype.Model.swh.rb +13 -7
- data/lib/openstudio-standards/prototypes/Prototype.full_service_restaurant.rb +332 -324
- data/lib/openstudio-standards/prototypes/Prototype.hospital.rb +401 -99
- data/lib/openstudio-standards/prototypes/Prototype.hvac_systems.rb +309 -222
- data/lib/openstudio-standards/prototypes/Prototype.large_hotel.rb +100 -80
- data/lib/openstudio-standards/prototypes/Prototype.large_office.rb +37 -22
- data/lib/openstudio-standards/prototypes/Prototype.medium_office.rb +68 -53
- data/lib/openstudio-standards/prototypes/Prototype.mid_rise_apartment.rb +109 -88
- data/lib/openstudio-standards/prototypes/Prototype.outpatient.rb +52 -4
- data/lib/openstudio-standards/prototypes/Prototype.primary_school.rb +230 -213
- data/lib/openstudio-standards/prototypes/Prototype.quick_service_restaurant.rb +225 -218
- data/lib/openstudio-standards/prototypes/Prototype.retail_standalone.rb +29 -17
- data/lib/openstudio-standards/prototypes/Prototype.retail_stripmall.rb +42 -32
- data/lib/openstudio-standards/prototypes/Prototype.secondary_school.rb +331 -314
- data/lib/openstudio-standards/prototypes/Prototype.small_hotel.rb +233 -219
- data/lib/openstudio-standards/prototypes/Prototype.small_office.rb +40 -32
- data/lib/openstudio-standards/prototypes/Prototype.warehouse.rb +19 -6
- data/lib/openstudio-standards/standards/Standards.AirLoopHVAC.rb +576 -555
- data/lib/openstudio-standards/standards/Standards.AirTerminalSingleDuctVAVReheat.rb +3 -1
- data/lib/openstudio-standards/standards/Standards.BoilerHotWater.rb +35 -17
- data/lib/openstudio-standards/standards/Standards.ChillerElectricEIR.rb +51 -23
- data/lib/openstudio-standards/standards/Standards.CoilCoolingDXMultiSpeed.rb +255 -0
- data/lib/openstudio-standards/standards/Standards.CoilHeatingDXMultiSpeed.rb +192 -0
- data/lib/openstudio-standards/standards/Standards.CoilHeatingGasMultiStage.rb +65 -0
- data/lib/openstudio-standards/standards/Standards.Fan.rb +37 -6
- data/lib/openstudio-standards/standards/Standards.Model.rb +28 -3
- data/lib/openstudio-standards/standards/Standards.SpaceType.rb +3 -1
- data/lib/openstudio-standards/standards/Standards.WaterHeaterMixed.rb +41 -15
- data/lib/openstudio-standards/version.rb +1 -1
- data/lib/openstudio-standards/weather/Weather.Model.rb +509 -5
- data/lib/openstudio-standards/weather/Weather.stat_file.rb +145 -5
- metadata +8 -3
- data/lib/openstudio-standards/btap/environment.rb +0 -718
@@ -3,12 +3,20 @@
|
|
3
3
|
class OpenStudio::Model::Model
|
4
4
|
|
5
5
|
def define_space_type_map(building_type, building_vintage, climate_zone)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
space_type_map = nil
|
7
|
+
|
8
|
+
case building_vintage
|
9
|
+
when 'NECB 2011'
|
10
|
+
space_type_map ={
|
11
|
+
"- undefined -" => ["Attic"],
|
12
|
+
"Office - enclosed" => ["Core_ZN", "Perimeter_ZN_1", "Perimeter_ZN_2", "Perimeter_ZN_3", "Perimeter_ZN_4"]
|
13
|
+
}
|
14
|
+
else
|
15
|
+
space_type_map = {
|
16
|
+
'WholeBuilding - Sm Office' => ['Perimeter_ZN_1', 'Perimeter_ZN_2', 'Perimeter_ZN_3', 'Perimeter_ZN_4', 'Core_ZN'],
|
17
|
+
'Attic' => ['Attic']
|
18
|
+
}
|
19
|
+
end
|
12
20
|
return space_type_map
|
13
21
|
|
14
22
|
end
|
@@ -17,46 +25,46 @@ class OpenStudio::Model::Model
|
|
17
25
|
|
18
26
|
system_to_space_map = [
|
19
27
|
{
|
20
|
-
|
21
|
-
|
22
|
-
|
28
|
+
'type' => 'PSZ-AC',
|
29
|
+
'name' => 'PSZ-AC-2',
|
30
|
+
'space_names' =>
|
23
31
|
[
|
24
|
-
|
25
|
-
|
32
|
+
'Perimeter_ZN_1'
|
33
|
+
]
|
26
34
|
},
|
27
35
|
{
|
28
|
-
|
29
|
-
|
30
|
-
|
36
|
+
'type' => 'PSZ-AC',
|
37
|
+
'name' => 'PSZ-AC-3',
|
38
|
+
'space_names' =>
|
31
39
|
[
|
32
|
-
|
33
|
-
|
40
|
+
'Perimeter_ZN_2'
|
41
|
+
]
|
34
42
|
},
|
35
43
|
{
|
36
|
-
|
37
|
-
|
38
|
-
|
44
|
+
'type' => 'PSZ-AC',
|
45
|
+
'name' => 'PSZ-AC-4',
|
46
|
+
'space_names' =>
|
39
47
|
[
|
40
|
-
|
41
|
-
|
48
|
+
'Perimeter_ZN_3'
|
49
|
+
]
|
42
50
|
},
|
43
51
|
{
|
44
|
-
|
45
|
-
|
46
|
-
|
52
|
+
'type' => 'PSZ-AC',
|
53
|
+
'name' => 'PSZ-AC-5',
|
54
|
+
'space_names' =>
|
47
55
|
[
|
48
|
-
|
49
|
-
|
56
|
+
'Perimeter_ZN_4'
|
57
|
+
]
|
50
58
|
},
|
51
59
|
{
|
52
|
-
|
53
|
-
|
54
|
-
|
60
|
+
'type' => 'PSZ-AC',
|
61
|
+
'name' => 'PSZ-AC-1',
|
62
|
+
'space_names' =>
|
55
63
|
[
|
56
|
-
|
57
|
-
|
64
|
+
'Core_ZN'
|
65
|
+
]
|
58
66
|
}
|
59
|
-
|
67
|
+
]
|
60
68
|
|
61
69
|
return system_to_space_map
|
62
70
|
|
@@ -3,11 +3,23 @@
|
|
3
3
|
class OpenStudio::Model::Model
|
4
4
|
|
5
5
|
def define_space_type_map(building_type, building_vintage, climate_zone)
|
6
|
-
space_type_map =
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
space_type_map = nil
|
7
|
+
|
8
|
+
case building_vintage
|
9
|
+
|
10
|
+
when 'NECB 2011'
|
11
|
+
space_type_map ={
|
12
|
+
"Warehouse - med/blk" => ["Zone3 Bulk Storage"],
|
13
|
+
"Warehouse - fine" => ["Zone2 Fine Storage"],
|
14
|
+
"Office - enclosed" => ["Zone1 Office"]
|
15
|
+
}
|
16
|
+
else
|
17
|
+
space_type_map = {
|
18
|
+
'Bulk' => ['Zone3 Bulk Storage'],
|
19
|
+
'Fine' => ['Zone2 Fine Storage'],
|
20
|
+
'Office' => ['Zone1 Office']
|
21
|
+
}
|
22
|
+
end
|
11
23
|
return space_type_map
|
12
24
|
end
|
13
25
|
|
@@ -87,7 +99,7 @@ class OpenStudio::Model::Model
|
|
87
99
|
|
88
100
|
def update_waterheater_loss_coefficient(building_vintage)
|
89
101
|
case building_vintage
|
90
|
-
when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
|
102
|
+
when '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013', 'NECB 2011'
|
91
103
|
self.getWaterHeaterMixeds.sort.each do |water_heater|
|
92
104
|
water_heater.setOffCycleLossCoefficienttoAmbientTemperature(0.798542707)
|
93
105
|
water_heater.setOnCycleLossCoefficienttoAmbientTemperature(0.798542707)
|
@@ -103,4 +115,5 @@ class OpenStudio::Model::Model
|
|
103
115
|
|
104
116
|
end
|
105
117
|
|
118
|
+
|
106
119
|
end
|
@@ -16,25 +16,25 @@ class OpenStudio::Model::AirLoopHVAC
|
|
16
16
|
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "Damper positions not modified for DOE Ref Pre-1980 or DOE Ref 1980-2004 vintages.")
|
17
17
|
return true
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
# First time adjustment:
|
21
21
|
# Only applies to multi-zone vav systems
|
22
|
-
# exclusion: for Outpatient: (1) both AHU1 and AHU2 in 'DOE Ref Pre-1980' and 'DOE Ref 1980-2004'
|
22
|
+
# exclusion: for Outpatient: (1) both AHU1 and AHU2 in 'DOE Ref Pre-1980' and 'DOE Ref 1980-2004'
|
23
23
|
# (2) AHU1 in 2004-2013
|
24
24
|
if self.is_multizone_vav_system && !(self.name.to_s.include? "Outpatient F1")
|
25
25
|
self.adjust_minimum_vav_damper_positions
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
# Second time adjustment:
|
29
29
|
# Only apply to 2010 and 2013 Outpatient (both AHU1 and AHU2)
|
30
30
|
# TODO maybe apply to hospital as well?
|
31
31
|
if (self.name.to_s.include? "Outpatient") && (template == '90.1-2010' || template == '90.1-2013')
|
32
32
|
self.adjust_minimum_vav_damper_positions_outpatient
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
return true
|
36
|
-
|
37
|
-
end
|
36
|
+
|
37
|
+
end
|
38
38
|
|
39
39
|
# Apply all standard required controls to the airloop
|
40
40
|
#
|
@@ -45,22 +45,22 @@ class OpenStudio::Model::AirLoopHVAC
|
|
45
45
|
# @todo nightcycle control
|
46
46
|
# @todo night fan shutoff
|
47
47
|
def apply_standard_controls(template, climate_zone)
|
48
|
-
|
48
|
+
|
49
49
|
# Energy Recovery Ventilation
|
50
50
|
if self.is_energy_recovery_ventilator_required(template, climate_zone)
|
51
51
|
self.apply_energy_recovery_ventilator
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
# Economizers
|
55
55
|
self.set_economizer_limits(template, climate_zone)
|
56
|
-
self.set_economizer_integration(template, climate_zone)
|
57
|
-
|
56
|
+
self.set_economizer_integration(template, climate_zone)
|
57
|
+
|
58
58
|
# Multizone VAV Systems
|
59
59
|
if self.is_multizone_vav_system
|
60
|
-
|
60
|
+
|
61
61
|
# VAV Reheat Control
|
62
62
|
self.set_vav_damper_action(template)
|
63
|
-
|
63
|
+
|
64
64
|
# Multizone VAV Optimization
|
65
65
|
# This rule does not apply to two hospital and one outpatient systems (TODO add hospital two systems as exception)
|
66
66
|
if !(self.name.to_s.include? "Outpatient F1")
|
@@ -70,7 +70,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
70
70
|
self.disable_multizone_vav_optimization
|
71
71
|
end
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
# VAV Static Pressure Reset
|
75
75
|
# assume all systems have DDC control of VAV terminals
|
76
76
|
has_ddc = true
|
@@ -83,9 +83,9 @@ class OpenStudio::Model::AirLoopHVAC
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
# Single zone systems
|
90
90
|
# if self.thermalZones.size == 1
|
91
91
|
# self.apply_single_zone_controls(template, climate_zone)
|
@@ -108,15 +108,15 @@ class OpenStudio::Model::AirLoopHVAC
|
|
108
108
|
if self.is_supply_air_temperature_reset_required(template, climate_zone)
|
109
109
|
self.enable_supply_air_temperature_reset_outdoor_temperature
|
110
110
|
# self.enable_supply_air_temperature_reset_warmest_zone(template)
|
111
|
-
end
|
112
|
-
|
111
|
+
end
|
112
|
+
|
113
113
|
# Unoccupied shutdown
|
114
114
|
if self.is_unoccupied_fan_shutoff_required(template)
|
115
115
|
self.enable_unoccupied_fan_shutoff
|
116
116
|
else
|
117
117
|
self.setAvailabilitySchedule(self.model.alwaysOnDiscreteSchedule)
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
# Motorized OA damper
|
121
121
|
if self.is_motorized_oa_damper_required(template, climate_zone)
|
122
122
|
# Assume that the availability schedule has already been
|
@@ -125,16 +125,16 @@ class OpenStudio::Model::AirLoopHVAC
|
|
125
125
|
else
|
126
126
|
self.remove_motorized_oa_damper
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
# TODO Optimum Start
|
130
130
|
# for systems exceeding 10,000 cfm
|
131
131
|
# Don't think that OS will be able to do this.
|
132
132
|
# OS currently only allows 1 availability manager
|
133
|
-
# at a time on an AirLoopHVAC. If we add an
|
134
|
-
# AvailabilityManager:OptimumStart, it
|
133
|
+
# at a time on an AirLoopHVAC. If we add an
|
134
|
+
# AvailabilityManager:OptimumStart, it
|
135
135
|
# will replace the AvailabilityManager:NightCycle.
|
136
|
-
|
137
|
-
end
|
136
|
+
|
137
|
+
end
|
138
138
|
|
139
139
|
# Apply all PRM baseline required controls to the airloop.
|
140
140
|
# Only applies those controls that differ from the normal
|
@@ -144,7 +144,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
144
144
|
# @param (see #is_economizer_required)
|
145
145
|
# @return [Bool] returns true if successful, false if not
|
146
146
|
def apply_performance_rating_method_baseline_controls(template, climate_zone)
|
147
|
-
|
147
|
+
|
148
148
|
# Economizers
|
149
149
|
if self.is_performance_rating_method_baseline_economizer_required(template, climate_zone)
|
150
150
|
self.apply_performance_rating_method_baseline_economizer(template, climate_zone)
|
@@ -153,7 +153,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
153
153
|
# Multizone VAV Systems
|
154
154
|
if self.is_multizone_vav_system
|
155
155
|
|
156
|
-
# SAT Reset
|
156
|
+
# SAT Reset
|
157
157
|
# G3.1.3.12 SAT reset required for all Multizone VAV systems,
|
158
158
|
# even if not required by prescriptive section.
|
159
159
|
case template
|
@@ -162,13 +162,13 @@ class OpenStudio::Model::AirLoopHVAC
|
|
162
162
|
end
|
163
163
|
|
164
164
|
end
|
165
|
-
|
165
|
+
|
166
166
|
# Unoccupied shutdown
|
167
167
|
self.enable_unoccupied_fan_shutoff
|
168
|
-
|
168
|
+
|
169
169
|
return true
|
170
|
-
|
171
|
-
end
|
170
|
+
|
171
|
+
end
|
172
172
|
|
173
173
|
# Calculate and apply the performance rating method
|
174
174
|
# baseline fan power to this air loop.
|
@@ -182,18 +182,18 @@ class OpenStudio::Model::AirLoopHVAC
|
|
182
182
|
# if the proposed model had multiple fans (supply, return, exhaust, etc.)
|
183
183
|
# return [Bool] true if successful, false if not.
|
184
184
|
def set_performance_rating_method_baseline_fan_power(template)
|
185
|
-
|
185
|
+
|
186
186
|
# Main AHU fans
|
187
|
-
|
187
|
+
|
188
188
|
# Calculate the allowable fan motor bhp
|
189
189
|
# for the entire airloop.
|
190
190
|
allowable_fan_bhp = self.allowable_system_brake_horsepower(template)
|
191
191
|
|
192
192
|
# Divide the allowable power evenly between the fans
|
193
193
|
# on this airloop.
|
194
|
-
all_fans = self.supply_return_exhaust_relief_fans
|
195
|
-
allowable_fan_bhp = allowable_fan_bhp / all_fans.size
|
196
|
-
|
194
|
+
all_fans = self.supply_return_exhaust_relief_fans
|
195
|
+
allowable_fan_bhp = allowable_fan_bhp / all_fans.size
|
196
|
+
|
197
197
|
# Set the motor efficiencies
|
198
198
|
# for all fans based on the calculated
|
199
199
|
# allowed brake hp. Then calculate the allowable
|
@@ -206,18 +206,18 @@ class OpenStudio::Model::AirLoopHVAC
|
|
206
206
|
end
|
207
207
|
|
208
208
|
# Fan powered terminal fans
|
209
|
-
|
209
|
+
|
210
210
|
# Adjust each terminal fan
|
211
211
|
self.demandComponents.each do |dc|
|
212
212
|
next if dc.to_AirTerminalSingleDuctParallelPIUReheat.empty?
|
213
213
|
pfp_term = dc.to_AirTerminalSingleDuctParallelPIUReheat.get
|
214
214
|
pfp_term.set_performance_rating_method_baseline_fan_power(template)
|
215
215
|
end
|
216
|
-
|
216
|
+
|
217
217
|
return true
|
218
|
-
|
218
|
+
|
219
219
|
end
|
220
|
-
|
220
|
+
|
221
221
|
# Determine the fan power limitation pressure drop adjustment
|
222
222
|
# Per Table 6.5.3.1.1B
|
223
223
|
#
|
@@ -226,7 +226,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
226
226
|
# units = horsepower
|
227
227
|
# @todo Determine the presence of MERV filters and other stuff in Table 6.5.3.1.1B. May need to extend AirLoopHVAC data model
|
228
228
|
def fan_power_limitation_pressure_drop_adjustment_brake_horsepower(template = "ASHRAE 90.1-2007")
|
229
|
-
|
229
|
+
|
230
230
|
# Get design supply air flow rate (whether autosized or hard-sized)
|
231
231
|
dsn_air_flow_m3_per_s = 0
|
232
232
|
dsn_air_flow_cfm = 0
|
@@ -238,30 +238,30 @@ class OpenStudio::Model::AirLoopHVAC
|
|
238
238
|
dsn_air_flow_m3_per_s = self.designSupplyAirFlowRate.get
|
239
239
|
dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
|
240
240
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = Hard sized Design Supply Air Flow Rate.")
|
241
|
-
end
|
242
|
-
|
241
|
+
end
|
242
|
+
|
243
243
|
# TODO determine the presence of MERV filters and other stuff
|
244
244
|
# in Table 6.5.3.1.1B
|
245
245
|
# perhaps need to extend AirLoopHVAC data model
|
246
246
|
has_fully_ducted_return_and_or_exhaust_air_systems = false
|
247
|
-
|
247
|
+
|
248
248
|
# Calculate Fan Power Limitation Pressure Drop Adjustment (in wc)
|
249
249
|
fan_pwr_adjustment_in_wc = 0
|
250
|
-
|
250
|
+
|
251
251
|
# Fully ducted return and/or exhaust air systems
|
252
252
|
if has_fully_ducted_return_and_or_exhaust_air_systems
|
253
253
|
adj_in_wc = 0.5
|
254
254
|
fan_pwr_adjustment_in_wc += adj_in_wc
|
255
255
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","--Added #{adj_in_wc} in wc for Fully ducted return and/or exhaust air systems")
|
256
256
|
end
|
257
|
-
|
257
|
+
|
258
258
|
# Convert the pressure drop adjustment to brake horsepower (bhp)
|
259
259
|
# assuming that all supply air passes through all devices
|
260
260
|
fan_pwr_adjustment_bhp = fan_pwr_adjustment_in_wc*dsn_air_flow_cfm / 4131
|
261
261
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: Fan Power Limitation Pressure Drop Adjustment = #{(fan_pwr_adjustment_bhp.round(2))} bhp")
|
262
|
-
|
262
|
+
|
263
263
|
return fan_pwr_adjustment_bhp
|
264
|
-
|
264
|
+
|
265
265
|
end
|
266
266
|
|
267
267
|
# Determine the allowable fan system brake horsepower
|
@@ -271,7 +271,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
271
271
|
# @return [Double] allowable fan system brake horsepower
|
272
272
|
# units = horsepower
|
273
273
|
def allowable_system_brake_horsepower(template = "ASHRAE 90.1-2007")
|
274
|
-
|
274
|
+
|
275
275
|
# Get design supply air flow rate (whether autosized or hard-sized)
|
276
276
|
dsn_air_flow_m3_per_s = 0
|
277
277
|
dsn_air_flow_cfm = 0
|
@@ -287,10 +287,10 @@ class OpenStudio::Model::AirLoopHVAC
|
|
287
287
|
|
288
288
|
# Get the fan limitation pressure drop adjustment bhp
|
289
289
|
fan_pwr_adjustment_bhp = self.fan_power_limitation_pressure_drop_adjustment_brake_horsepower
|
290
|
-
|
290
|
+
|
291
291
|
# Determine the number of zones the system serves
|
292
292
|
num_zones_served = self.thermalZones.size
|
293
|
-
|
293
|
+
|
294
294
|
# Get the supply air fan and determine whether VAV or CAV system.
|
295
295
|
# Assume that supply air fan is fan closest to the demand outlet node.
|
296
296
|
# The fan may be inside of a piece of unitary equipment.
|
@@ -314,16 +314,16 @@ class OpenStudio::Model::AirLoopHVAC
|
|
314
314
|
elsif fan.to_FanVariableVolume.is_initialized
|
315
315
|
fan_pwr_limit_type = "variable volume"
|
316
316
|
end
|
317
|
-
end
|
317
|
+
end
|
318
318
|
end
|
319
|
-
|
320
|
-
# For 90.1-2010, single-zone VAV systems use the
|
319
|
+
|
320
|
+
# For 90.1-2010, single-zone VAV systems use the
|
321
321
|
# constant volume limitation per 6.5.3.1.1
|
322
322
|
if template == "ASHRAE 90.1-2010" && fan_pwr_limit_type = "variable volume" && num_zones_served == 1
|
323
323
|
fan_pwr_limit_type = "constant volume"
|
324
324
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","For #{self.name}: Using the constant volume limitation because single-zone VAV system.")
|
325
325
|
end
|
326
|
-
|
326
|
+
|
327
327
|
# Calculate the Allowable Fan System brake horsepower per Table G3.1.2.9
|
328
328
|
allowable_fan_bhp = 0
|
329
329
|
if fan_pwr_limit_type == "constant volume"
|
@@ -332,7 +332,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
332
332
|
allowable_fan_bhp = dsn_air_flow_cfm*0.0013+fan_pwr_adjustment_bhp
|
333
333
|
end
|
334
334
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","For #{self.name}: Allowable brake horsepower = #{(allowable_fan_bhp).round(2)}HP based on #{dsn_air_flow_cfm.round} cfm and #{fan_pwr_adjustment_bhp.round(2)} bhp of adjustment.")
|
335
|
-
|
335
|
+
|
336
336
|
# Calculate and report the total area for debugging/testing
|
337
337
|
floor_area_served_m2 = self.floor_area_served
|
338
338
|
floor_area_served_ft2 = OpenStudio.convert(floor_area_served_m2, 'm^2', 'ft^2').get
|
@@ -341,7 +341,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
341
341
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: area served = #{floor_area_served_ft2.round} ft^2.")
|
342
342
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: flow per area = #{cfm_per_ft2.round} cfm/ft^2.")
|
343
343
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC","For #{self.name}: flow per hp = #{cfm_per_hp.round} cfm/hp.")
|
344
|
-
|
344
|
+
|
345
345
|
return allowable_fan_bhp
|
346
346
|
|
347
347
|
end
|
@@ -349,14 +349,14 @@ class OpenStudio::Model::AirLoopHVAC
|
|
349
349
|
# Get all of the supply, return, exhaust, and relief fans on this system
|
350
350
|
#
|
351
351
|
# @return [Array] an array of FanConstantVolume, FanVariableVolume, and FanOnOff objects
|
352
|
-
def supply_return_exhaust_relief_fans()
|
353
|
-
|
352
|
+
def supply_return_exhaust_relief_fans()
|
353
|
+
|
354
354
|
# Fans on the supply side of the airloop directly, or inside of unitary equipment.
|
355
355
|
fans = []
|
356
356
|
sup_and_oa_comps = self.supplyComponents
|
357
357
|
sup_and_oa_comps += self.oaComponents
|
358
358
|
sup_and_oa_comps.each do |comp|
|
359
|
-
if comp.to_FanConstantVolume.is_initialized
|
359
|
+
if comp.to_FanConstantVolume.is_initialized
|
360
360
|
fans << comp.to_FanConstantVolume.get
|
361
361
|
elsif comp.to_FanVariableVolume.is_initialized
|
362
362
|
fans << comp.to_FanVariableVolume.get
|
@@ -376,33 +376,33 @@ class OpenStudio::Model::AirLoopHVAC
|
|
376
376
|
elsif sup_fan.to_FanOnOff.is_initialized
|
377
377
|
fans << sup_fan.to_FanOnOff.get
|
378
378
|
elsif sup_fan.to_FanVariableVolume.is_initialized
|
379
|
-
fans << sup_fan.to_FanVariableVolume.get
|
380
|
-
end
|
379
|
+
fans << sup_fan.to_FanVariableVolume.get
|
380
|
+
end
|
381
381
|
end
|
382
|
-
end
|
383
|
-
|
382
|
+
end
|
383
|
+
|
384
384
|
return fans
|
385
|
-
|
385
|
+
|
386
386
|
end
|
387
|
-
|
387
|
+
|
388
388
|
# Determine the total brake horsepower of the fans on the system
|
389
389
|
# with or without the fans inside of fan powered terminals.
|
390
390
|
#
|
391
391
|
# @param include_terminal_fans [Bool] if true, power from fan powered terminals will be included
|
392
392
|
# @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
|
393
393
|
# @return [Double] total brake horsepower of the fans on the system
|
394
|
-
# units = horsepower
|
394
|
+
# units = horsepower
|
395
395
|
def system_fan_brake_horsepower(include_terminal_fans = true, template = "ASHRAE 90.1-2007")
|
396
396
|
|
397
397
|
# TODO get the template from the parent model itself?
|
398
398
|
# Or not because maybe you want to see the difference between two standards?
|
399
399
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC","#{self.name}-Determining #{template} allowable system fan power.")
|
400
|
-
|
400
|
+
|
401
401
|
# Get all fans
|
402
402
|
fans = []
|
403
403
|
# Supply, exhaust, relief, and return fans
|
404
404
|
fans += self.supply_return_exhaust_relief_fans
|
405
|
-
|
405
|
+
|
406
406
|
# Fans inside of fan-powered terminals
|
407
407
|
if include_terminal_fans
|
408
408
|
self.demandComponents.each do |comp|
|
@@ -415,45 +415,45 @@ class OpenStudio::Model::AirLoopHVAC
|
|
415
415
|
term_fan = comp.to_AirTerminalSingleDuctParallelPIUReheat.get.fan
|
416
416
|
if term_fan.to_FanConstantVolume.is_initialized
|
417
417
|
fans << term_fan.to_FanConstantVolume.get
|
418
|
-
end
|
418
|
+
end
|
419
419
|
end
|
420
420
|
end
|
421
421
|
end
|
422
|
-
|
422
|
+
|
423
423
|
# Loop through all fans on the system and
|
424
424
|
# sum up their brake horsepower values.
|
425
425
|
sys_fan_bhp = 0
|
426
426
|
fans.sort.each do |fan|
|
427
427
|
sys_fan_bhp += fan.brakeHorsepower
|
428
428
|
end
|
429
|
-
|
429
|
+
|
430
430
|
return sys_fan_bhp
|
431
|
-
|
432
|
-
end
|
433
|
-
|
431
|
+
|
432
|
+
end
|
433
|
+
|
434
434
|
# Set the fan pressure rises that will result in
|
435
435
|
# the system hitting the baseline allowable fan power
|
436
436
|
#
|
437
|
-
# @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
|
437
|
+
# @param template [String] valid choices: 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
|
438
438
|
def set_baseline_fan_pressure_rise(template = "ASHRAE 90.1-2007")
|
439
439
|
|
440
440
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "#{self.name}-Setting #{template} baseline fan power.")
|
441
|
-
|
441
|
+
|
442
442
|
# Get the total system bhp from the proposed system, including terminal fans
|
443
443
|
proposed_sys_bhp = self.system_fan_brake_horsepower(true)
|
444
|
-
|
444
|
+
|
445
445
|
# Get the allowable fan brake horsepower
|
446
446
|
allowable_fan_bhp = self.allowable_system_brake_horsepower(template)
|
447
447
|
|
448
448
|
# Get the fan power limitation from proposed system
|
449
449
|
fan_pwr_adjustment_bhp = self.fan_power_limitation_pressure_drop_adjustment_brake_horsepower
|
450
|
-
|
450
|
+
|
451
451
|
# Subtract the fan power adjustment
|
452
452
|
allowable_fan_bhp = allowable_fan_bhp-fan_pwr_adjustment_bhp
|
453
|
-
|
453
|
+
|
454
454
|
# Get all fans
|
455
|
-
fans = self.supply_return_exhaust_relief_fans
|
456
|
-
|
455
|
+
fans = self.supply_return_exhaust_relief_fans
|
456
|
+
|
457
457
|
# TODO improve description
|
458
458
|
# Loop through the fans, changing the pressure rise
|
459
459
|
# until the fan bhp is the same percentage of the baseline allowable bhp
|
@@ -464,27 +464,27 @@ class OpenStudio::Model::AirLoopHVAC
|
|
464
464
|
next if fan.name.to_s.include?("UnitHeater Fan")
|
465
465
|
|
466
466
|
OpenStudio::logFree(OpenStudio::Info, "#{fan.name}")
|
467
|
-
|
467
|
+
|
468
468
|
# Get the bhp of the fan on the proposed system
|
469
469
|
proposed_fan_bhp = fan.brakeHorsepower
|
470
|
-
|
470
|
+
|
471
471
|
# Get the bhp of the fan on the proposed system
|
472
472
|
proposed_fan_bhp_frac = proposed_fan_bhp / proposed_sys_bhp
|
473
|
-
|
473
|
+
|
474
474
|
# Determine the target bhp of the fan on the baseline system
|
475
475
|
baseline_fan_bhp = proposed_fan_bhp_frac*allowable_fan_bhp
|
476
476
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "* #{(baseline_fan_bhp).round(1)} bhp = Baseline fan brake horsepower.")
|
477
|
-
|
478
|
-
# Set the baseline impeller eff of the fan,
|
477
|
+
|
478
|
+
# Set the baseline impeller eff of the fan,
|
479
479
|
# preserving the proposed motor eff.
|
480
480
|
baseline_impeller_eff = fan.baselineImpellerEfficiency(template)
|
481
481
|
fan.changeImpellerEfficiency(baseline_impeller_eff)
|
482
482
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "* #{(baseline_impeller_eff*100).round(1)}% = Baseline fan impeller efficiency.")
|
483
|
-
|
483
|
+
|
484
484
|
# Set the baseline motor efficiency for the specified bhp
|
485
485
|
baseline_motor_eff = fan.standardMinimumMotorEfficiency(template, standards, allowable_fan_bhp)
|
486
486
|
fan.changeMotorEfficiency(baseline_motor_eff)
|
487
|
-
|
487
|
+
|
488
488
|
# Get design supply air flow rate (whether autosized or hard-sized)
|
489
489
|
dsn_air_flow_m3_per_s = 0
|
490
490
|
if fan.autosizedDesignSupplyAirFlowRate.is_initialized
|
@@ -496,7 +496,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
496
496
|
dsn_air_flow_cfm = OpenStudio.convert(dsn_air_flow_m3_per_s, "m^3/s", "cfm").get
|
497
497
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "* #{dsn_air_flow_cfm.round} cfm = User entered Design Supply Air Flow Rate.")
|
498
498
|
end
|
499
|
-
|
499
|
+
|
500
500
|
# Determine the fan pressure rise that will result in the target bhp
|
501
501
|
# pressure_rise_pa = fan_bhp*746 / fan_motor_eff*fan_total_eff / dsn_air_flow_m3_per_s
|
502
502
|
baseline_pressure_rise_pa = baseline_fan_bhp*746 / fan.motorEfficiency*fan.fanEfficiency / dsn_air_flow_m3_per_s
|
@@ -510,7 +510,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
510
510
|
end
|
511
511
|
|
512
512
|
end
|
513
|
-
|
513
|
+
|
514
514
|
# Calculate the total bhp of the system to make sure it matches the goal
|
515
515
|
calc_sys_bhp = self.system_fan_brake_horsepower(false)
|
516
516
|
if ((calc_sys_bhp-allowable_fan_bhp) / allowable_fan_bhp).abs > 0.02
|
@@ -526,7 +526,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
526
526
|
# @todo Change to pull water coil nominal capacity instead of design load; not a huge difference, but water coil nominal capacity not available in sizing table.
|
527
527
|
# @todo Handle all additional cooling coil types. Currently only handles CoilCoolingDXSingleSpeed, CoilCoolingDXTwoSpeed, and CoilCoolingWater
|
528
528
|
def total_cooling_capacity
|
529
|
-
|
529
|
+
|
530
530
|
# Sum the cooling capacity for all cooling components
|
531
531
|
# on the airloop, which may be inside of unitary systems.
|
532
532
|
total_cooling_capacity_w = 0
|
@@ -542,7 +542,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
542
542
|
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
|
543
543
|
end
|
544
544
|
# CoilCoolingDXTwoSpeed
|
545
|
-
elsif sc.to_CoilCoolingDXTwoSpeed.is_initialized
|
545
|
+
elsif sc.to_CoilCoolingDXTwoSpeed.is_initialized
|
546
546
|
coil = sc.to_CoilCoolingDXTwoSpeed.get
|
547
547
|
if coil.ratedHighSpeedTotalCoolingCapacity.is_initialized
|
548
548
|
total_cooling_capacity_w += coil.ratedHighSpeedTotalCoolingCapacity.get
|
@@ -560,7 +560,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
560
560
|
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
|
561
561
|
end
|
562
562
|
# CoilCoolingWaterToAirHeatPumpEquationFit
|
563
|
-
elsif sc.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
|
563
|
+
elsif sc.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
|
564
564
|
coil = sc.to_CoilCoolingWaterToAirHeatPumpEquationFit.get
|
565
565
|
if coil.ratedTotalCoolingCapacity.is_initialized
|
566
566
|
total_cooling_capacity_w += coil.ratedTotalCoolingCapacity.get
|
@@ -584,7 +584,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
584
584
|
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
|
585
585
|
end
|
586
586
|
# CoilCoolingDXTwoSpeed
|
587
|
-
elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
|
587
|
+
elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
|
588
588
|
coil = clg_coil.to_CoilCoolingDXTwoSpeed.get
|
589
589
|
if coil.ratedHighSpeedTotalCoolingCapacity.is_initialized
|
590
590
|
total_cooling_capacity_w += coil.ratedHighSpeedTotalCoolingCapacity.get
|
@@ -602,7 +602,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
602
602
|
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
|
603
603
|
end
|
604
604
|
# CoilCoolingWaterToAirHeatPumpEquationFit
|
605
|
-
elsif clg_coil.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
|
605
|
+
elsif clg_coil.to_CoilCoolingWaterToAirHeatPumpEquationFit.is_initialized
|
606
606
|
coil = clg_coil.to_CoilCoolingWaterToAirHeatPumpEquationFit.get
|
607
607
|
if coil.ratedTotalCoolingCapacity.is_initialized
|
608
608
|
total_cooling_capacity_w += coil.ratedTotalCoolingCapacity.get
|
@@ -627,7 +627,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
627
627
|
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name} capacity of #{coil.name} is not available, total cooling capacity of air loop will be incorrect when applying standard.")
|
628
628
|
end
|
629
629
|
# CoilCoolingDXTwoSpeed
|
630
|
-
elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
|
630
|
+
elsif clg_coil.to_CoilCoolingDXTwoSpeed.is_initialized
|
631
631
|
coil = clg_coil.to_CoilCoolingDXTwoSpeed.get
|
632
632
|
if coil.ratedHighSpeedTotalCoolingCapacity.is_initialized
|
633
633
|
total_cooling_capacity_w += coil.ratedHighSpeedTotalCoolingCapacity.get
|
@@ -655,16 +655,16 @@ class OpenStudio::Model::AirLoopHVAC
|
|
655
655
|
# CoilCoolingCooledBeam
|
656
656
|
# CoilCoolingWaterToAirHeatPumpEquationFit
|
657
657
|
# AirLoopHVACUnitaryHeatCoolVAVChangeoverBypass
|
658
|
-
# AirLoopHVACUnitaryHeatPumpAirToAir
|
659
|
-
# AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed
|
658
|
+
# AirLoopHVACUnitaryHeatPumpAirToAir
|
659
|
+
# AirLoopHVACUnitaryHeatPumpAirToAirMultiSpeed
|
660
660
|
# AirLoopHVACUnitarySystem
|
661
661
|
end
|
662
662
|
end
|
663
663
|
|
664
664
|
return total_cooling_capacity_w
|
665
|
-
|
665
|
+
|
666
666
|
end
|
667
|
-
|
667
|
+
|
668
668
|
# Determine whether or not this system
|
669
669
|
# is required to have an economizer.
|
670
670
|
#
|
@@ -672,25 +672,25 @@ class OpenStudio::Model::AirLoopHVAC
|
|
672
672
|
# @param climate_zone [String] valid choices: 'ASHRAE 169-2006-1A', 'ASHRAE 169-2006-1B', 'ASHRAE 169-2006-2A', 'ASHRAE 169-2006-2B',
|
673
673
|
# 'ASHRAE 169-2006-3A', 'ASHRAE 169-2006-3B', 'ASHRAE 169-2006-3C', 'ASHRAE 169-2006-4A', 'ASHRAE 169-2006-4B', 'ASHRAE 169-2006-4C',
|
674
674
|
# 'ASHRAE 169-2006-5A', 'ASHRAE 169-2006-5B', 'ASHRAE 169-2006-5C', 'ASHRAE 169-2006-6A', 'ASHRAE 169-2006-6B', 'ASHRAE 169-2006-7A',
|
675
|
-
# 'ASHRAE 169-2006-7B', 'ASHRAE 169-2006-8A', 'ASHRAE 169-2006-8B'
|
675
|
+
# 'ASHRAE 169-2006-7B', 'ASHRAE 169-2006-8A', 'ASHRAE 169-2006-8B'
|
676
676
|
# @return [Bool] returns true if an economizer is required, false if not
|
677
677
|
def is_economizer_required(template, climate_zone)
|
678
|
-
|
678
|
+
|
679
679
|
economizer_required = false
|
680
|
-
|
680
|
+
|
681
681
|
return economizer_required if self.name.to_s.include? "Outpatient F1"
|
682
|
-
|
682
|
+
|
683
683
|
# A big number of btu per hr as the minimum requirement
|
684
684
|
infinity_btu_per_hr = 999999999999
|
685
685
|
minimum_capacity_btu_per_hr = infinity_btu_per_hr
|
686
|
-
|
686
|
+
|
687
687
|
# Determine if the airloop serves any computer rooms
|
688
688
|
# / data centers, which changes the economizer.
|
689
689
|
is_dc = false
|
690
690
|
if self.data_center_area_served > 0
|
691
691
|
is_dc = true
|
692
692
|
end
|
693
|
-
|
693
|
+
|
694
694
|
# Determine the minimum capacity that requires an economizer
|
695
695
|
case template
|
696
696
|
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
|
@@ -734,7 +734,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
734
734
|
'ASHRAE 169-2006-7B',
|
735
735
|
'ASHRAE 169-2006-8A',
|
736
736
|
'ASHRAE 169-2006-8B'
|
737
|
-
minimum_capacity_btu_per_hr = 135000
|
737
|
+
minimum_capacity_btu_per_hr = 135000
|
738
738
|
when 'ASHRAE 169-2006-3B',
|
739
739
|
'ASHRAE 169-2006-3C',
|
740
740
|
'ASHRAE 169-2006-4B',
|
@@ -744,7 +744,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
744
744
|
'ASHRAE 169-2006-6B'
|
745
745
|
minimum_capacity_btu_per_hr = 65000
|
746
746
|
end
|
747
|
-
else
|
747
|
+
else
|
748
748
|
case climate_zone
|
749
749
|
when 'ASHRAE 169-2006-1A',
|
750
750
|
'ASHRAE 169-2006-1B'
|
@@ -772,7 +772,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
772
772
|
when 'NECB 2011'
|
773
773
|
minimum_capacity_btu_per_hr = 68243 # NECB requires economizer for cooling cap > 20 kW
|
774
774
|
end
|
775
|
-
|
775
|
+
|
776
776
|
# Check whether the system requires an economizer by comparing
|
777
777
|
# the system capacity to the minimum capacity.
|
778
778
|
total_cooling_capacity_w = self.total_cooling_capacity
|
@@ -789,20 +789,20 @@ class OpenStudio::Model::AirLoopHVAC
|
|
789
789
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{self.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr for data centers.")
|
790
790
|
else
|
791
791
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "#{self.name} does not require an economizer because the total cooling capacity of #{total_cooling_capacity_btu_per_hr.round} Btu/hr is less than the minimum capacity of #{minimum_capacity_btu_per_hr.round} Btu/hr.")
|
792
|
-
end
|
792
|
+
end
|
793
793
|
end
|
794
|
-
|
794
|
+
|
795
795
|
return economizer_required
|
796
|
-
|
796
|
+
|
797
797
|
end
|
798
|
-
|
798
|
+
|
799
799
|
# Set the economizer limits per the standard. Limits are based on the economizer
|
800
800
|
# type currently specified in the ControllerOutdoorAir object on this air loop.
|
801
801
|
#
|
802
802
|
# @param (see #is_economizer_required)
|
803
803
|
# @return [Bool] returns true if successful, false if not
|
804
804
|
def set_economizer_limits(template, climate_zone)
|
805
|
-
|
805
|
+
|
806
806
|
# EnergyPlus economizer types
|
807
807
|
# 'NoEconomizer'
|
808
808
|
# 'FixedDryBulb'
|
@@ -811,8 +811,8 @@ class OpenStudio::Model::AirLoopHVAC
|
|
811
811
|
# 'DifferentialEnthalpy'
|
812
812
|
# 'FixedDewPointAndDryBulb'
|
813
813
|
# 'ElectronicEnthalpy'
|
814
|
-
# 'DifferentialDryBulbAndEnthalpy'
|
815
|
-
|
814
|
+
# 'DifferentialDryBulbAndEnthalpy'
|
815
|
+
|
816
816
|
# Get the OA system and OA controller
|
817
817
|
oa_sys = self.airLoopHVACOutdoorAirSystem
|
818
818
|
if oa_sys.is_initialized
|
@@ -822,12 +822,12 @@ class OpenStudio::Model::AirLoopHVAC
|
|
822
822
|
end
|
823
823
|
oa_control = oa_sys.getControllerOutdoorAir
|
824
824
|
economizer_type = oa_control.getEconomizerControlType
|
825
|
-
|
825
|
+
|
826
826
|
# Return false if no economizer is present
|
827
827
|
if economizer_type == 'NoEconomizer'
|
828
828
|
return false
|
829
829
|
end
|
830
|
-
|
830
|
+
|
831
831
|
# Determine the limits according to the type
|
832
832
|
drybulb_limit_f = nil
|
833
833
|
enthalpy_limit_btu_per_lb = nil
|
@@ -894,8 +894,8 @@ class OpenStudio::Model::AirLoopHVAC
|
|
894
894
|
drybulb_limit_f = 75
|
895
895
|
dewpoint_limit_f = 55
|
896
896
|
end
|
897
|
-
end
|
898
|
-
|
897
|
+
end
|
898
|
+
|
899
899
|
# Set the limits
|
900
900
|
case economizer_type
|
901
901
|
when 'FixedDryBulb'
|
@@ -918,10 +918,10 @@ class OpenStudio::Model::AirLoopHVAC
|
|
918
918
|
oa_control.setEconomizerMaximumLimitDewpointTemperature(dewpoint_limit_c)
|
919
919
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F, dew-point limit = #{dewpoint_limit_f}F")
|
920
920
|
end
|
921
|
-
end
|
921
|
+
end
|
922
922
|
|
923
923
|
return true
|
924
|
-
|
924
|
+
|
925
925
|
end
|
926
926
|
|
927
927
|
# For systems required to have an economizer, set the economizer
|
@@ -932,7 +932,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
932
932
|
# @param (see #is_economizer_required)
|
933
933
|
# @return [Bool] returns true if successful, false if not
|
934
934
|
def set_economizer_integration(template, climate_zone)
|
935
|
-
|
935
|
+
|
936
936
|
# Determine if the system is a VAV system based on the fan
|
937
937
|
# which may be inside of a unitary system.
|
938
938
|
is_vav = false
|
@@ -951,20 +951,20 @@ class OpenStudio::Model::AirLoopHVAC
|
|
951
951
|
is_vav = true
|
952
952
|
end
|
953
953
|
end
|
954
|
-
end
|
954
|
+
end
|
955
955
|
end
|
956
956
|
|
957
957
|
# Determine the number of zones the system serves
|
958
958
|
num_zones_served = self.thermalZones.size
|
959
|
-
|
959
|
+
|
960
960
|
# A big number of btu per hr as the minimum requirement
|
961
961
|
infinity_btu_per_hr = 999999999999
|
962
962
|
minimum_capacity_btu_per_hr = infinity_btu_per_hr
|
963
|
-
|
963
|
+
|
964
964
|
# Determine if an integrated economizer is required
|
965
965
|
integrated_economizer_required = true
|
966
966
|
case template
|
967
|
-
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
|
967
|
+
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
|
968
968
|
minimum_capacity_btu_per_hr = 65000
|
969
969
|
minimum_capacity_w = OpenStudio.convert(minimum_capacity_btu_per_hr, "Btu/hr", "W").get
|
970
970
|
# 6.5.1.3 Integrated Economizer Control
|
@@ -1007,10 +1007,10 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1007
1007
|
integrated_economizer_required = true
|
1008
1008
|
when 'NECB 2011'
|
1009
1009
|
# this means that compressor allowed to turn on when economizer is open
|
1010
|
-
# (NoLockout); as per 5.2.2.8(3)
|
1010
|
+
# (NoLockout); as per 5.2.2.8(3)
|
1011
1011
|
integrated_economizer_required = true
|
1012
1012
|
end
|
1013
|
-
|
1013
|
+
|
1014
1014
|
# Get the OA system and OA controller
|
1015
1015
|
oa_sys = self.airLoopHVACOutdoorAirSystem
|
1016
1016
|
if oa_sys.is_initialized
|
@@ -1018,8 +1018,8 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1018
1018
|
else
|
1019
1019
|
return false # No OA system
|
1020
1020
|
end
|
1021
|
-
oa_control = oa_sys.getControllerOutdoorAir
|
1022
|
-
|
1021
|
+
oa_control = oa_sys.getControllerOutdoorAir
|
1022
|
+
|
1023
1023
|
# Apply integrated or non-integrated economizer
|
1024
1024
|
if integrated_economizer_required
|
1025
1025
|
oa_control.setLockoutType('NoLockout')
|
@@ -1028,22 +1028,22 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1028
1028
|
end
|
1029
1029
|
|
1030
1030
|
return true
|
1031
|
-
|
1031
|
+
|
1032
1032
|
end
|
1033
|
-
|
1033
|
+
|
1034
1034
|
# Determine if an economizer is required per the PRM.
|
1035
1035
|
#
|
1036
1036
|
# @param (see #is_economizer_required)
|
1037
1037
|
# @return [Bool] returns true if required, false if not
|
1038
1038
|
def is_performance_rating_method_baseline_economizer_required(template, climate_zone)
|
1039
|
-
|
1039
|
+
|
1040
1040
|
economizer_required = false
|
1041
|
-
|
1041
|
+
|
1042
1042
|
# A big number of ft2 as the minimum requirement
|
1043
1043
|
infinity_ft2 = 999999999999
|
1044
1044
|
min_int_area_served_ft2 = infinity_ft2
|
1045
1045
|
min_ext_area_served_ft2 = infinity_ft2
|
1046
|
-
|
1046
|
+
|
1047
1047
|
# Determine the minimum capacity that requires an economizer
|
1048
1048
|
case template
|
1049
1049
|
when '90.1-2004'
|
@@ -1083,21 +1083,21 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1083
1083
|
'ASHRAE 169-2006-4A'
|
1084
1084
|
min_int_area_served_ft2 = infinity_ft2 # No requirement
|
1085
1085
|
min_ext_area_served_ft2 = infinity_ft2 # No requirement
|
1086
|
-
else
|
1086
|
+
else
|
1087
1087
|
min_int_area_served_ft2 = 0 # Always required
|
1088
1088
|
min_ext_area_served_ft2 = 0 # Always required
|
1089
1089
|
end
|
1090
1090
|
end
|
1091
|
-
|
1091
|
+
|
1092
1092
|
# Check whether the system requires an economizer by comparing
|
1093
1093
|
# the system capacity to the minimum capacity.
|
1094
1094
|
min_int_area_served_m2 = OpenStudio.convert(min_int_area_served_ft2, "ft^2", "m^2").get
|
1095
1095
|
min_ext_area_served_m2 = OpenStudio.convert(min_ext_area_served_ft2, "ft^2", "m^2").get
|
1096
|
-
|
1096
|
+
|
1097
1097
|
# Get the interior and exterior area served
|
1098
1098
|
int_area_served_m2 = self.floor_area_served_interior_zones
|
1099
1099
|
ext_area_served_m2 = self.floor_area_served_exterior_zones
|
1100
|
-
|
1100
|
+
|
1101
1101
|
# Check the floor area exception
|
1102
1102
|
if int_area_served_m2 < min_int_area_served_m2 && ext_area_served_m2 < min_ext_area_served_m2
|
1103
1103
|
if min_int_area_served_ft2 == infinity_ft2 && min_ext_area_served_ft2 == infinity_ft2
|
@@ -1107,21 +1107,21 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1107
1107
|
end
|
1108
1108
|
return economizer_required
|
1109
1109
|
end
|
1110
|
-
|
1110
|
+
|
1111
1111
|
# If here, economizer required
|
1112
1112
|
economizer_required = true
|
1113
1113
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer required for the performance rating method baseline.")
|
1114
|
-
|
1115
|
-
return economizer_required
|
1114
|
+
|
1115
|
+
return economizer_required
|
1116
1116
|
|
1117
1117
|
end
|
1118
|
-
|
1118
|
+
|
1119
1119
|
# Apply the PRM economizer type and set temperature limits
|
1120
1120
|
#
|
1121
1121
|
# @param (see #is_economizer_required)
|
1122
1122
|
# @return [Bool] returns true if successful, false if not
|
1123
1123
|
def apply_performance_rating_method_baseline_economizer(template, climate_zone)
|
1124
|
-
|
1124
|
+
|
1125
1125
|
# EnergyPlus economizer types
|
1126
1126
|
# 'NoEconomizer'
|
1127
1127
|
# 'FixedDryBulb'
|
@@ -1130,7 +1130,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1130
1130
|
# 'DifferentialEnthalpy'
|
1131
1131
|
# 'FixedDewPointAndDryBulb'
|
1132
1132
|
# 'ElectronicEnthalpy'
|
1133
|
-
# 'DifferentialDryBulbAndEnthalpy'
|
1133
|
+
# 'DifferentialDryBulbAndEnthalpy'
|
1134
1134
|
|
1135
1135
|
# Determine the type and limits
|
1136
1136
|
economizer_type = nil
|
@@ -1178,7 +1178,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1178
1178
|
'ASHRAE 169-2006-7B',
|
1179
1179
|
'ASHRAE 169-2006-8A',
|
1180
1180
|
'ASHRAE 169-2006-8B'
|
1181
|
-
economizer_type = 'FixedDryBulb'
|
1181
|
+
economizer_type = 'FixedDryBulb'
|
1182
1182
|
drybulb_limit_f = 75
|
1183
1183
|
when 'ASHRAE 169-2006-2A',
|
1184
1184
|
'ASHRAE 169-2006-3A',
|
@@ -1188,14 +1188,14 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1188
1188
|
when 'ASHRAE 169-2006-5A',
|
1189
1189
|
'ASHRAE 169-2006-6A',
|
1190
1190
|
'ASHRAE 169-2006-7A'
|
1191
|
-
economizer_type = 'FixedDryBulb'
|
1191
|
+
economizer_type = 'FixedDryBulb'
|
1192
1192
|
drybulb_limit_f = 70
|
1193
1193
|
else
|
1194
|
-
economizer_type = 'FixedDryBulb'
|
1194
|
+
economizer_type = 'FixedDryBulb'
|
1195
1195
|
drybulb_limit_f = 65
|
1196
1196
|
end
|
1197
1197
|
end
|
1198
|
-
|
1198
|
+
|
1199
1199
|
# Get the OA system and OA controller
|
1200
1200
|
oa_sys = self.airLoopHVACOutdoorAirSystem
|
1201
1201
|
if oa_sys.is_initialized
|
@@ -1204,7 +1204,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1204
1204
|
return false # No OA system
|
1205
1205
|
end
|
1206
1206
|
oa_control = oa_sys.getControllerOutdoorAir
|
1207
|
-
|
1207
|
+
|
1208
1208
|
# Set the limits
|
1209
1209
|
case economizer_type
|
1210
1210
|
when 'FixedDryBulb'
|
@@ -1227,12 +1227,12 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1227
1227
|
oa_control.setEconomizerMaximumLimitDewpointTemperature(dewpoint_limit_c)
|
1228
1228
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Economizer type = #{economizer_type}, dry bulb limit = #{drybulb_limit_f}F, dew-point limit = #{dewpoint_limit_f}F")
|
1229
1229
|
end
|
1230
|
-
end
|
1230
|
+
end
|
1231
1231
|
|
1232
1232
|
return true
|
1233
1233
|
|
1234
1234
|
end
|
1235
|
-
|
1235
|
+
|
1236
1236
|
# Check the economizer type currently specified in the ControllerOutdoorAir object on this air loop
|
1237
1237
|
# is acceptable per the standard.
|
1238
1238
|
#
|
@@ -1240,7 +1240,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1240
1240
|
# @return [Bool] Returns true if allowable, if the system has no economizer or no OA system.
|
1241
1241
|
# Returns false if the economizer type is not allowable.
|
1242
1242
|
def is_economizer_type_allowable(template, climate_zone)
|
1243
|
-
|
1243
|
+
|
1244
1244
|
# EnergyPlus economizer types
|
1245
1245
|
# 'NoEconomizer'
|
1246
1246
|
# 'FixedDryBulb'
|
@@ -1250,7 +1250,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1250
1250
|
# 'FixedDewPointAndDryBulb'
|
1251
1251
|
# 'ElectronicEnthalpy'
|
1252
1252
|
# 'DifferentialDryBulbAndEnthalpy'
|
1253
|
-
|
1253
|
+
|
1254
1254
|
# Get the OA system and OA controller
|
1255
1255
|
oa_sys = self.airLoopHVACOutdoorAirSystem
|
1256
1256
|
if oa_sys.is_initialized
|
@@ -1260,12 +1260,12 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1260
1260
|
end
|
1261
1261
|
oa_control = oa_sys.getControllerOutdoorAir
|
1262
1262
|
economizer_type = oa_control.getEconomizerControlType
|
1263
|
-
|
1263
|
+
|
1264
1264
|
# Return true if no economizer is present
|
1265
1265
|
if economizer_type == 'NoEconomizer'
|
1266
1266
|
return true
|
1267
1267
|
end
|
1268
|
-
|
1268
|
+
|
1269
1269
|
# Determine the minimum capacity that requires an economizer
|
1270
1270
|
prohibited_types = []
|
1271
1271
|
case template
|
@@ -1290,7 +1290,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1290
1290
|
'ASHRAE 169-2006-3A',
|
1291
1291
|
'ASHRAE 169-2006-4A'
|
1292
1292
|
prohibited_types = ['DifferentialDryBulb']
|
1293
|
-
when
|
1293
|
+
when
|
1294
1294
|
'ASHRAE 169-2006-5A',
|
1295
1295
|
'ASHRAE 169-2006-6A',
|
1296
1296
|
prohibited_types = []
|
@@ -1316,31 +1316,30 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1316
1316
|
'ASHRAE 169-2006-3A',
|
1317
1317
|
'ASHRAE 169-2006-4A'
|
1318
1318
|
prohibited_types = ['FixedDryBulb', 'DifferentialDryBulb']
|
1319
|
-
when
|
1319
|
+
when
|
1320
1320
|
'ASHRAE 169-2006-5A',
|
1321
1321
|
'ASHRAE 169-2006-6A',
|
1322
1322
|
prohibited_types = []
|
1323
1323
|
end
|
1324
1324
|
end
|
1325
|
-
|
1325
|
+
|
1326
1326
|
# Check if the specified type is allowed
|
1327
1327
|
economizer_type_allowed = true
|
1328
1328
|
if prohibited_types.include?(economizer_type)
|
1329
1329
|
economizer_type_allowed = false
|
1330
1330
|
end
|
1331
|
-
|
1331
|
+
|
1332
1332
|
return economizer_type_allowed
|
1333
|
-
|
1333
|
+
|
1334
1334
|
end
|
1335
|
-
|
1335
|
+
|
1336
1336
|
# Check if ERV is required on this airloop.
|
1337
1337
|
#
|
1338
1338
|
# @param (see #is_economizer_required)
|
1339
|
-
# @return [Bool] Returns true if required, false if not.
|
1339
|
+
# @return [Bool] Returns true if required, false if not.
|
1340
1340
|
# @todo Add exception logic for systems serving parking garage, warehouse, or multifamily
|
1341
1341
|
def is_energy_recovery_ventilator_required(template, climate_zone)
|
1342
|
-
|
1343
|
-
# ERV Not Applicable for AHUs that serve
|
1342
|
+
# ERV Not Applicable for AHUs that serve
|
1344
1343
|
# parking garage, warehouse, or multifamily
|
1345
1344
|
# if space_types_served_names.include?('PNNL_Asset_Rating_Apartment_Space_Type') ||
|
1346
1345
|
# space_types_served_names.include?('PNNL_Asset_Rating_LowRiseApartment_Space_Type') ||
|
@@ -1349,22 +1348,41 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1349
1348
|
# OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not applicable because it because it serves parking garage, warehouse, or multifamily.")
|
1350
1349
|
# return false
|
1351
1350
|
# end
|
1352
|
-
|
1351
|
+
|
1353
1352
|
erv_required = nil
|
1354
1353
|
# ERV not applicable for medical AHUs (AHU1 in Outpatient), per AIA 2001 - 7.31.D2.
|
1355
1354
|
if self.name.to_s.include? "Outpatient F1"
|
1356
1355
|
erv_required = false
|
1357
1356
|
return erv_required
|
1358
1357
|
end
|
1359
|
-
|
1358
|
+
|
1359
|
+
# ERV not applicable for medical AHUs, per AIA 2001 - 7.31.D2.
|
1360
|
+
if self.name.to_s.include? "VAV_ER"
|
1361
|
+
erv_required = false
|
1362
|
+
return erv_required
|
1363
|
+
elsif self.name.to_s.include? "VAV_OR"
|
1364
|
+
erv_required = false
|
1365
|
+
return erv_required
|
1366
|
+
end
|
1367
|
+
case template
|
1368
|
+
when '90.1-2004', '90.1-2007'
|
1369
|
+
if self.name.to_s.include? "VAV_ICU"
|
1370
|
+
erv_required = false
|
1371
|
+
return erv_required
|
1372
|
+
elsif self.name.to_s.include? "VAV_PATRMS"
|
1373
|
+
erv_required = false
|
1374
|
+
return erv_required
|
1375
|
+
end
|
1376
|
+
end
|
1377
|
+
|
1360
1378
|
# ERV Not Applicable for AHUs that have DCV
|
1361
|
-
# or that have no OA intake.
|
1379
|
+
# or that have no OA intake.
|
1362
1380
|
controller_oa = nil
|
1363
1381
|
controller_mv = nil
|
1364
1382
|
oa_system = nil
|
1365
1383
|
if self.airLoopHVACOutdoorAirSystem.is_initialized
|
1366
1384
|
oa_system = self.airLoopHVACOutdoorAirSystem.get
|
1367
|
-
controller_oa = oa_system.getControllerOutdoorAir
|
1385
|
+
controller_oa = oa_system.getControllerOutdoorAir
|
1368
1386
|
controller_mv = controller_oa.controllerMechanicalVentilation
|
1369
1387
|
if controller_mv.demandControlledVentilation == true
|
1370
1388
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not applicable because DCV enabled.")
|
@@ -1375,6 +1393,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1375
1393
|
return false
|
1376
1394
|
end
|
1377
1395
|
|
1396
|
+
|
1378
1397
|
# Get the AHU design supply air flow rate
|
1379
1398
|
dsn_flow_m3_per_s = nil
|
1380
1399
|
if self.designSupplyAirFlowRate.is_initialized
|
@@ -1386,7 +1405,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1386
1405
|
return false
|
1387
1406
|
end
|
1388
1407
|
dsn_flow_cfm = OpenStudio.convert(dsn_flow_m3_per_s, 'm^3/s', 'cfm').get
|
1389
|
-
|
1408
|
+
|
1390
1409
|
# Get the minimum OA flow rate
|
1391
1410
|
min_oa_flow_m3_per_s = nil
|
1392
1411
|
if controller_oa.minimumOutdoorAirFlowRate.is_initialized
|
@@ -1398,10 +1417,10 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1398
1417
|
return false
|
1399
1418
|
end
|
1400
1419
|
min_oa_flow_cfm = OpenStudio.convert(min_oa_flow_m3_per_s, 'm^3/s', 'cfm').get
|
1401
|
-
|
1420
|
+
|
1402
1421
|
# Calculate the percent OA at design airflow
|
1403
1422
|
pct_oa = min_oa_flow_m3_per_s/dsn_flow_m3_per_s
|
1404
|
-
|
1423
|
+
|
1405
1424
|
case template
|
1406
1425
|
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
|
1407
1426
|
erv_cfm = nil # Not required
|
@@ -1427,7 +1446,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1427
1446
|
erv_cfm = nil
|
1428
1447
|
elsif pct_oa >= 0.7 && pct_oa < 0.8
|
1429
1448
|
erv_cfm = 5000
|
1430
|
-
elsif pct_oa >= 0.8
|
1449
|
+
elsif pct_oa >= 0.8
|
1431
1450
|
erv_cfm = 5000
|
1432
1451
|
end
|
1433
1452
|
when 'ASHRAE 169-2006-1B', 'ASHRAE 169-2006-2B', 'ASHRAE 169-2006-5C'
|
@@ -1443,7 +1462,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1443
1462
|
erv_cfm = 12000
|
1444
1463
|
elsif pct_oa >= 0.7 && pct_oa < 0.8
|
1445
1464
|
erv_cfm = 5000
|
1446
|
-
elsif pct_oa >= 0.8
|
1465
|
+
elsif pct_oa >= 0.8
|
1447
1466
|
erv_cfm = 4000
|
1448
1467
|
end
|
1449
1468
|
when 'ASHRAE 169-2006-6B'
|
@@ -1459,9 +1478,9 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1459
1478
|
erv_cfm = 3500
|
1460
1479
|
elsif pct_oa >= 0.7 && pct_oa < 0.8
|
1461
1480
|
erv_cfm = 2500
|
1462
|
-
elsif pct_oa >= 0.8
|
1481
|
+
elsif pct_oa >= 0.8
|
1463
1482
|
erv_cfm = 1500
|
1464
|
-
end
|
1483
|
+
end
|
1465
1484
|
when 'ASHRAE 169-2006-1A', 'ASHRAE 169-2006-2A', 'ASHRAE 169-2006-3A', 'ASHRAE 169-2006-4A', 'ASHRAE 169-2006-5A', 'ASHRAE 169-2006-6A'
|
1466
1485
|
if pct_oa < 0.3
|
1467
1486
|
erv_cfm = nil
|
@@ -1475,9 +1494,9 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1475
1494
|
erv_cfm = 2000
|
1476
1495
|
elsif pct_oa >= 0.7 && pct_oa < 0.8
|
1477
1496
|
erv_cfm = 1000
|
1478
|
-
elsif pct_oa >= 0.8
|
1497
|
+
elsif pct_oa >= 0.8
|
1479
1498
|
erv_cfm = 0
|
1480
|
-
end
|
1499
|
+
end
|
1481
1500
|
when 'ASHRAE 169-2006-7A', 'ASHRAE 169-2006-7B', 'ASHRAE 169-2006-8A', 'ASHRAE 169-2006-8B'
|
1482
1501
|
if pct_oa < 0.3
|
1483
1502
|
erv_cfm = nil
|
@@ -1491,9 +1510,9 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1491
1510
|
erv_cfm = 0
|
1492
1511
|
elsif pct_oa >= 0.7 && pct_oa < 0.8
|
1493
1512
|
erv_cfm = 0
|
1494
|
-
elsif pct_oa >= 0.8
|
1513
|
+
elsif pct_oa >= 0.8
|
1495
1514
|
erv_cfm = 0
|
1496
|
-
end
|
1515
|
+
end
|
1497
1516
|
end
|
1498
1517
|
when '90.1-2013'
|
1499
1518
|
# Table 6.5.6.1-2
|
@@ -1517,7 +1536,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1517
1536
|
erv_cfm = 3000
|
1518
1537
|
elsif pct_oa >= 0.7 && pct_oa < 0.8
|
1519
1538
|
erv_cfm = 1500
|
1520
|
-
elsif pct_oa >= 0.8
|
1539
|
+
elsif pct_oa >= 0.8
|
1521
1540
|
erv_cfm = 0
|
1522
1541
|
end
|
1523
1542
|
when 'ASHRAE 169-2006-1A', 'ASHRAE 169-2006-2A', 'ASHRAE 169-2006-3A', 'ASHRAE 169-2006-4B', 'ASHRAE 169-2006-5B'
|
@@ -1543,39 +1562,39 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1543
1562
|
end
|
1544
1563
|
when 'NECB 2011'
|
1545
1564
|
# The NECB 2011 requirement is that systems with an exhaust heat content > 150 kW require an HRV
|
1546
|
-
# The calculation for this is done below, to modify erv_required
|
1565
|
+
# The calculation for this is done below, to modify erv_required
|
1547
1566
|
# erv_cfm set to nil here as placeholder, will lead to erv_required = false
|
1548
1567
|
erv_cfm = nil
|
1549
1568
|
end
|
1550
|
-
|
1569
|
+
|
1551
1570
|
# Determine if an ERV is required
|
1552
1571
|
# erv_required = nil
|
1553
1572
|
if erv_cfm.nil?
|
1554
1573
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on #{(pct_oa*100).round}% OA flow, design flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}.")
|
1555
|
-
erv_required = false
|
1574
|
+
erv_required = false
|
1556
1575
|
elsif dsn_flow_cfm < erv_cfm
|
1557
1576
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on #{(pct_oa*100).round}% OA flow, design flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Does not exceed minimum flow requirement of #{erv_cfm}cfm.")
|
1558
|
-
erv_required = false
|
1577
|
+
erv_required = false
|
1559
1578
|
else
|
1560
1579
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV required based on #{(pct_oa*100).round}% OA flow, design flow of #{dsn_flow_cfm.round}cfm, and climate zone #{climate_zone}. Exceeds minimum flow requirement of #{erv_cfm}cfm.")
|
1561
|
-
erv_required = true
|
1580
|
+
erv_required = true
|
1562
1581
|
end
|
1563
|
-
|
1582
|
+
|
1564
1583
|
# This code modifies erv_required for NECB 2011
|
1565
1584
|
# Calculation of exhaust heat content and check whether it is > 150 kW
|
1566
|
-
|
1567
|
-
if template == 'NECB 2011'
|
1568
|
-
|
1585
|
+
|
1586
|
+
if template == 'NECB 2011'
|
1587
|
+
|
1569
1588
|
# get all zones in the model
|
1570
1589
|
zones = self.thermalZones
|
1571
|
-
|
1590
|
+
|
1572
1591
|
# initialize counters
|
1573
1592
|
sum_zone_oa = 0.0
|
1574
1593
|
sum_zoneoaTimesheatDesignT = 0.0
|
1575
|
-
|
1594
|
+
|
1576
1595
|
# zone loop
|
1577
1596
|
zones.each do |zone|
|
1578
|
-
|
1597
|
+
|
1579
1598
|
# get design heat temperature for each zone; this is equivalent to design exhaust temperature
|
1580
1599
|
zone_sizing = zone.sizingZone
|
1581
1600
|
heatDesignTemp = zone_sizing.zoneHeatingDesignSupplyAirTemperature
|
@@ -1584,77 +1603,77 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1584
1603
|
zone_oa = 0.0
|
1585
1604
|
# outdoor defined at space level; get OA flow for all spaces within zone
|
1586
1605
|
spaces = zone.spaces
|
1587
|
-
|
1606
|
+
|
1588
1607
|
# space loop
|
1589
1608
|
spaces.each do |space|
|
1590
1609
|
if not space.designSpecificationOutdoorAir.empty? # if empty, don't do anything
|
1591
|
-
outdoor_air = space.designSpecificationOutdoorAir.get
|
1592
|
-
|
1610
|
+
outdoor_air = space.designSpecificationOutdoorAir.get
|
1611
|
+
|
1593
1612
|
# in bTAP, outdoor air specified as outdoor air per person (m3/s/person)
|
1594
1613
|
oa_flow_per_person = outdoor_air.outdoorAirFlowperPerson
|
1595
1614
|
num_people = space.peoplePerFloorArea * space.floorArea
|
1596
1615
|
oa_flow = oa_flow_per_person * num_people # oa flow for the space
|
1597
1616
|
zone_oa = zone_oa + oa_flow # add up oa flow for all spaces to get zone air flow
|
1598
|
-
end
|
1599
|
-
|
1617
|
+
end
|
1618
|
+
|
1600
1619
|
end # space loop
|
1601
|
-
|
1620
|
+
|
1602
1621
|
sum_zone_oa = sum_zone_oa + zone_oa # sum of all zone oa flows to get system oa flow
|
1603
1622
|
sum_zoneoaTimesheatDesignT = sum_zoneoaTimesheatDesignT + (zone_oa * heatDesignTemp) # calculated to get oa flow weighted average of design exhaust temperature
|
1604
|
-
|
1623
|
+
|
1605
1624
|
end # zone loop
|
1606
|
-
|
1625
|
+
|
1607
1626
|
# Calculate average exhaust temperature (oa flow weighted average)
|
1608
|
-
avg_exhaust_temp = sum_zoneoaTimesheatDesignT / sum_zone_oa
|
1609
|
-
|
1610
|
-
# for debugging/testing
|
1627
|
+
avg_exhaust_temp = sum_zoneoaTimesheatDesignT / sum_zone_oa
|
1628
|
+
|
1629
|
+
# for debugging/testing
|
1611
1630
|
# puts "average exhaust temp = #{avg_exhaust_temp}"
|
1612
1631
|
# puts "sum_zone_oa = #{sum_zone_oa}"
|
1613
|
-
|
1632
|
+
|
1614
1633
|
# Get January winter design temperature
|
1615
1634
|
# get model weather file name
|
1616
1635
|
weather_file = BTAP::Environment::WeatherFile.new(self.model.weatherFile.get.path.get)
|
1617
|
-
|
1636
|
+
|
1618
1637
|
# get winter(heating) design temp stored in array
|
1619
1638
|
# Note that the NECB 2011 specifies using the 2.5% january design temperature
|
1620
1639
|
# The outdoor temperature used here is the 0.4% heating design temperature of the coldest month, available in stat file
|
1621
1640
|
outdoor_temp = weather_file.heating_design_info[1]
|
1622
|
-
|
1641
|
+
|
1623
1642
|
# for debugging/testing
|
1624
|
-
# puts "outdoor design temp = #{outdoor_temp}"
|
1625
|
-
|
1643
|
+
# puts "outdoor design temp = #{outdoor_temp}"
|
1644
|
+
|
1626
1645
|
# Calculate exhaust heat content
|
1627
1646
|
exhaust_heat_content = 0.00123 * sum_zone_oa * 1000.0 * (avg_exhaust_temp - outdoor_temp)
|
1628
|
-
|
1647
|
+
|
1629
1648
|
# for debugging/testing
|
1630
1649
|
# puts "exhaust heat content = #{exhaust_heat_content}"
|
1631
|
-
|
1632
|
-
|
1650
|
+
|
1651
|
+
|
1633
1652
|
# Modify erv_required based on exhaust heat content
|
1634
1653
|
if ( exhaust_heat_content > 150.0 ) then
|
1635
1654
|
erv_required = true
|
1636
|
-
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV required based on exhaust heat content.")
|
1655
|
+
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV required based on exhaust heat content.")
|
1637
1656
|
else
|
1638
1657
|
erv_required = false
|
1639
|
-
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on exhaust heat content.")
|
1658
|
+
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV not required based on exhaust heat content.")
|
1640
1659
|
end
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1660
|
+
|
1661
|
+
|
1662
|
+
|
1644
1663
|
end # of NECB 2011 condition
|
1645
|
-
|
1664
|
+
|
1646
1665
|
# for debugging/testing
|
1647
|
-
# puts "erv_required = #{erv_required}"
|
1648
|
-
|
1666
|
+
# puts "erv_required = #{erv_required}"
|
1667
|
+
|
1649
1668
|
return erv_required
|
1650
|
-
|
1651
|
-
end
|
1652
|
-
|
1669
|
+
|
1670
|
+
end
|
1671
|
+
|
1653
1672
|
# Add an ERV to this airloop.
|
1654
1673
|
# Will be a rotary-type HX
|
1655
1674
|
#
|
1656
1675
|
# @param (see #is_economizer_required)
|
1657
|
-
# @return [Bool] Returns true if required, false if not.
|
1676
|
+
# @return [Bool] Returns true if required, false if not.
|
1658
1677
|
# @todo Add exception logic for systems serving parking garage, warehouse, or multifamily
|
1659
1678
|
def apply_energy_recovery_ventilator()
|
1660
1679
|
|
@@ -1666,7 +1685,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1666
1685
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, ERV cannot be added because the system has no OA intake.")
|
1667
1686
|
return false
|
1668
1687
|
end
|
1669
|
-
|
1688
|
+
|
1670
1689
|
# Create an ERV
|
1671
1690
|
erv = OpenStudio::Model::HeatExchangerAirToAirSensibleAndLatent.new(self.model)
|
1672
1691
|
erv.setName("#{self.name} ERV")
|
@@ -1678,16 +1697,16 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1678
1697
|
erv.setLatentEffectivenessat100CoolingAirFlow(0.6)
|
1679
1698
|
erv.setSensibleEffectivenessat75CoolingAirFlow(0.75)
|
1680
1699
|
erv.setLatentEffectivenessat75CoolingAirFlow(0.6)
|
1681
|
-
erv.setSupplyAirOutletTemperatureControl(true)
|
1700
|
+
erv.setSupplyAirOutletTemperatureControl(true)
|
1682
1701
|
erv.setHeatExchangerType('Rotary')
|
1683
1702
|
erv.setFrostControlType('ExhaustOnly')
|
1684
1703
|
erv.setEconomizerLockout(true)
|
1685
1704
|
erv.setThresholdTemperature(-23.3) # -10F
|
1686
1705
|
erv.setInitialDefrostTimeFraction(0.167)
|
1687
1706
|
erv.setRateofDefrostTimeFractionIncrease(1.44)
|
1688
|
-
|
1707
|
+
|
1689
1708
|
# Add the ERV to the OA system
|
1690
|
-
erv.addToNode(oa_system.outboardOANode.get)
|
1709
|
+
erv.addToNode(oa_system.outboardOANode.get)
|
1691
1710
|
|
1692
1711
|
# Add a setpoint manager OA pretreat
|
1693
1712
|
# to control the ERV
|
@@ -1696,8 +1715,8 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1696
1715
|
spm_oa_pretreat.setMaximumSetpointTemperature(99.0)
|
1697
1716
|
spm_oa_pretreat.setMinimumSetpointHumidityRatio(0.00001)
|
1698
1717
|
spm_oa_pretreat.setMaximumSetpointHumidityRatio(1.0)
|
1699
|
-
# Reference setpoint node and
|
1700
|
-
# Mixed air stream node are outlet
|
1718
|
+
# Reference setpoint node and
|
1719
|
+
# Mixed air stream node are outlet
|
1701
1720
|
# node of the OA system
|
1702
1721
|
mixed_air_node = oa_system.mixedAirModelObject.get.to_Node.get
|
1703
1722
|
spm_oa_pretreat.setReferenceSetpointNode(mixed_air_node)
|
@@ -1715,34 +1734,34 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1715
1734
|
|
1716
1735
|
# Apply the prototype Heat Exchanger power assumptions.
|
1717
1736
|
erv.setPrototypeNominalElectricPower
|
1718
|
-
|
1737
|
+
|
1719
1738
|
return true
|
1720
|
-
|
1721
|
-
end
|
1722
|
-
|
1739
|
+
|
1740
|
+
end
|
1741
|
+
|
1723
1742
|
# Determine if multizone vav optimization is required.
|
1724
1743
|
#
|
1725
1744
|
# @param (see #is_economizer_required)
|
1726
|
-
# @return [Bool] Returns true if required, false if not.
|
1727
|
-
# @todo Add exception logic for
|
1745
|
+
# @return [Bool] Returns true if required, false if not.
|
1746
|
+
# @todo Add exception logic for
|
1728
1747
|
# systems with AIA healthcare ventilation requirements
|
1729
1748
|
# dual duct systems
|
1730
1749
|
def is_multizone_vav_optimization_required(template, climate_zone)
|
1731
1750
|
|
1732
1751
|
multizone_opt_required = false
|
1733
|
-
|
1752
|
+
|
1734
1753
|
case template
|
1735
1754
|
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
|
1736
|
-
|
1755
|
+
|
1737
1756
|
# Not required before 90.1-2010
|
1738
1757
|
return multizone_opt_required
|
1739
|
-
|
1758
|
+
|
1740
1759
|
when '90.1-2010', '90.1-2013'
|
1741
|
-
|
1760
|
+
|
1742
1761
|
# Not required for systems with fan-powered terminals
|
1743
1762
|
num_fan_powered_terminals = 0
|
1744
1763
|
self.demandComponents.each do |comp|
|
1745
|
-
if comp.to_AirTerminalSingleDuctParallelPIUReheat.is_initialized || comp.to_AirTerminalSingleDuctSeriesPIUReheat.is_initialized
|
1764
|
+
if comp.to_AirTerminalSingleDuctParallelPIUReheat.is_initialized || comp.to_AirTerminalSingleDuctSeriesPIUReheat.is_initialized
|
1746
1765
|
num_fan_powered_terminals += 1
|
1747
1766
|
end
|
1748
1767
|
end
|
@@ -1750,26 +1769,26 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1750
1769
|
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name}, multizone vav optimization is not required because the system has #{num_fan_powered_terminals} fan-powered terminals.")
|
1751
1770
|
return multizone_opt_required
|
1752
1771
|
end
|
1753
|
-
|
1772
|
+
|
1754
1773
|
# Not required for systems that require an ERV
|
1755
1774
|
if self.has_energy_recovery
|
1756
1775
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: multizone vav optimization is not required because the system has Energy Recovery.")
|
1757
1776
|
return multizone_opt_required
|
1758
1777
|
end
|
1759
|
-
|
1778
|
+
|
1760
1779
|
# Get the OA intake
|
1761
1780
|
controller_oa = nil
|
1762
1781
|
controller_mv = nil
|
1763
1782
|
oa_system = nil
|
1764
1783
|
if self.airLoopHVACOutdoorAirSystem.is_initialized
|
1765
1784
|
oa_system = self.airLoopHVACOutdoorAirSystem.get
|
1766
|
-
controller_oa = oa_system.getControllerOutdoorAir
|
1785
|
+
controller_oa = oa_system.getControllerOutdoorAir
|
1767
1786
|
controller_mv = controller_oa.controllerMechanicalVentilation
|
1768
1787
|
else
|
1769
1788
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, multizone optimization is not applicable because system has no OA intake.")
|
1770
1789
|
return multizone_opt_required
|
1771
1790
|
end
|
1772
|
-
|
1791
|
+
|
1773
1792
|
# Get the AHU design supply air flow rate
|
1774
1793
|
dsn_flow_m3_per_s = nil
|
1775
1794
|
if self.designSupplyAirFlowRate.is_initialized
|
@@ -1781,7 +1800,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1781
1800
|
return multizone_opt_required
|
1782
1801
|
end
|
1783
1802
|
dsn_flow_cfm = OpenStudio.convert(dsn_flow_m3_per_s, 'm^3/s', 'cfm').get
|
1784
|
-
|
1803
|
+
|
1785
1804
|
# Get the minimum OA flow rate
|
1786
1805
|
min_oa_flow_m3_per_s = nil
|
1787
1806
|
if controller_oa.minimumOutdoorAirFlowRate.is_initialized
|
@@ -1793,10 +1812,10 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1793
1812
|
return multizone_opt_required
|
1794
1813
|
end
|
1795
1814
|
min_oa_flow_cfm = OpenStudio.convert(min_oa_flow_m3_per_s, 'm^3/s', 'cfm').get
|
1796
|
-
|
1815
|
+
|
1797
1816
|
# Calculate the percent OA at design airflow
|
1798
1817
|
pct_oa = min_oa_flow_m3_per_s/dsn_flow_m3_per_s
|
1799
|
-
|
1818
|
+
|
1800
1819
|
# Not required for systems where
|
1801
1820
|
# exhaust is more than 70% of the total OA intake.
|
1802
1821
|
if pct_oa > 0.7
|
@@ -1809,61 +1828,61 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1809
1828
|
# OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{controller_oa.name}: multizone optimization is not applicable because it is a dual duct system")
|
1810
1829
|
# return multizone_opt_required
|
1811
1830
|
# end
|
1812
|
-
|
1831
|
+
|
1813
1832
|
# If here, multizone vav optimization is required
|
1814
1833
|
multizone_opt_required = true
|
1815
|
-
|
1834
|
+
|
1816
1835
|
return multizone_opt_required
|
1817
|
-
|
1836
|
+
|
1818
1837
|
end
|
1819
|
-
|
1820
|
-
end
|
1821
|
-
|
1838
|
+
|
1839
|
+
end
|
1840
|
+
|
1822
1841
|
# Enable multizone vav optimization by changing the Outdoor Air Method
|
1823
1842
|
# in the Controller:MechanicalVentilation object to 'VentilationRateProcedure'
|
1824
1843
|
#
|
1825
|
-
# @return [Bool] Returns true if required, false if not.
|
1844
|
+
# @return [Bool] Returns true if required, false if not.
|
1826
1845
|
def enable_multizone_vav_optimization
|
1827
|
-
|
1846
|
+
|
1828
1847
|
# Enable multizone vav optimization
|
1829
1848
|
# at each timestep.
|
1830
1849
|
if self.airLoopHVACOutdoorAirSystem.is_initialized
|
1831
1850
|
oa_system = self.airLoopHVACOutdoorAirSystem.get
|
1832
|
-
controller_oa = oa_system.getControllerOutdoorAir
|
1851
|
+
controller_oa = oa_system.getControllerOutdoorAir
|
1833
1852
|
controller_mv = controller_oa.controllerMechanicalVentilation
|
1834
1853
|
controller_mv.setSystemOutdoorAirMethod('VentilationRateProcedure')
|
1835
1854
|
else
|
1836
1855
|
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name}, cannot enable multizone vav optimization because the system has no OA intake.")
|
1837
1856
|
return false
|
1838
1857
|
end
|
1839
|
-
|
1840
|
-
end
|
1841
|
-
|
1858
|
+
|
1859
|
+
end
|
1860
|
+
|
1842
1861
|
# Disable multizone vav optimization by changing the Outdoor Air Method
|
1843
1862
|
# in the Controller:MechanicalVentilation object to 'ZoneSum'
|
1844
1863
|
#
|
1845
1864
|
# @return [Bool] Returns true if required, false if not.
|
1846
1865
|
def disable_multizone_vav_optimization
|
1847
|
-
|
1866
|
+
|
1848
1867
|
# Disable multizone vav optimization
|
1849
1868
|
# at each timestep.
|
1850
1869
|
if self.airLoopHVACOutdoorAirSystem.is_initialized
|
1851
1870
|
oa_system = self.airLoopHVACOutdoorAirSystem.get
|
1852
|
-
controller_oa = oa_system.getControllerOutdoorAir
|
1871
|
+
controller_oa = oa_system.getControllerOutdoorAir
|
1853
1872
|
controller_mv = controller_oa.controllerMechanicalVentilation
|
1854
1873
|
controller_mv.setSystemOutdoorAirMethod('ZoneSum')
|
1855
1874
|
else
|
1856
1875
|
OpenStudio::logFree(OpenStudio::Warn, "openstudio.standards.AirLoopHVAC", "For #{self.name}, cannot disable multizone vav optimization because the system has no OA intake.")
|
1857
1876
|
return false
|
1858
1877
|
end
|
1859
|
-
|
1860
|
-
end
|
1878
|
+
|
1879
|
+
end
|
1861
1880
|
|
1862
1881
|
# Set the minimum VAV damper positions
|
1863
1882
|
#
|
1864
|
-
#
|
1883
|
+
#
|
1865
1884
|
def set_minimum_vav_damper_positions(template)
|
1866
|
-
|
1885
|
+
|
1867
1886
|
self.thermalZones.each do |zone|
|
1868
1887
|
zone.equipment.each do |equip|
|
1869
1888
|
if equip.to_AirTerminalSingleDuctVAVReheat.is_initialized
|
@@ -1873,39 +1892,41 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1873
1892
|
end
|
1874
1893
|
end
|
1875
1894
|
end
|
1876
|
-
|
1895
|
+
|
1877
1896
|
return true
|
1878
|
-
|
1897
|
+
|
1879
1898
|
end
|
1880
|
-
|
1899
|
+
|
1881
1900
|
# Adjust minimum VAV damper positions to the values
|
1882
1901
|
#
|
1883
1902
|
# @param (see #is_economizer_required)
|
1884
|
-
# @return [Bool] Returns true if required, false if not.
|
1903
|
+
# @return [Bool] Returns true if required, false if not.
|
1885
1904
|
# @todo Add exception logic for systems serving parking garage, warehouse, or multifamily
|
1886
1905
|
def adjust_minimum_vav_damper_positions
|
1887
|
-
|
1906
|
+
|
1888
1907
|
# Total uncorrected outdoor airflow rate
|
1889
1908
|
v_ou = 0.0
|
1890
1909
|
self.thermalZones.each do |zone|
|
1891
1910
|
v_ou += zone.outdoor_airflow_rate
|
1892
1911
|
end
|
1912
|
+
|
1893
1913
|
v_ou_cfm = OpenStudio.convert(v_ou, 'm^3/s', 'cfm').get
|
1894
|
-
|
1914
|
+
|
1895
1915
|
# System primary airflow rate (whether autosized or hard-sized)
|
1896
1916
|
v_ps = 0.0
|
1917
|
+
|
1897
1918
|
if self.autosizedDesignSupplyAirFlowRate.is_initialized
|
1898
1919
|
v_ps = self.autosizedDesignSupplyAirFlowRate.get
|
1899
1920
|
else
|
1900
1921
|
v_ps = self.designSupplyAirFlowRate.get
|
1901
1922
|
end
|
1902
1923
|
v_ps_cfm = OpenStudio.convert(v_ps, 'm^3/s', 'cfm').get
|
1903
|
-
|
1924
|
+
|
1904
1925
|
# Average outdoor air fraction
|
1905
1926
|
x_s = v_ou / v_ps
|
1906
|
-
|
1907
|
-
OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: v_ou = #{v_ou_cfm.round} cfm, v_ps = #{v_ps_cfm.round} cfm, x_s = #{x_s.round(2)}.")
|
1908
|
-
|
1927
|
+
|
1928
|
+
OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: v_ou = #{v_ou_cfm.round} cfm, v_ps = #{v_ps_cfm.round} cfm, x_s = #{x_s.round(2)}.")
|
1929
|
+
|
1909
1930
|
# Determine the zone ventilation effectiveness
|
1910
1931
|
# for every zone on the system.
|
1911
1932
|
# When ventilation effectiveness is too low,
|
@@ -1914,18 +1935,18 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1914
1935
|
e_vzs_adj = []
|
1915
1936
|
num_zones_adj = 0
|
1916
1937
|
self.thermalZones.sort.each do |zone|
|
1917
|
-
|
1938
|
+
|
1918
1939
|
# Breathing zone airflow rate
|
1919
|
-
v_bz = zone.outdoor_airflow_rate
|
1920
|
-
|
1940
|
+
v_bz = zone.outdoor_airflow_rate
|
1941
|
+
|
1921
1942
|
# Zone air distribution, assumed 1 per PNNL
|
1922
|
-
e_z = 1.0
|
1923
|
-
|
1943
|
+
e_z = 1.0
|
1944
|
+
|
1924
1945
|
# Zone airflow rate
|
1925
|
-
v_oz = v_bz / e_z
|
1926
|
-
|
1946
|
+
v_oz = v_bz / e_z
|
1947
|
+
|
1927
1948
|
# Primary design airflow rate
|
1928
|
-
# max of heating and cooling
|
1949
|
+
# max of heating and cooling
|
1929
1950
|
# design air flow rates
|
1930
1951
|
v_pz = 0.0
|
1931
1952
|
clg_dsn_flow = zone.autosizedCoolingDesignAirFlowRate
|
@@ -1946,7 +1967,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1946
1967
|
else
|
1947
1968
|
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: #{zone.name} htg_dsn_flow could not be found.")
|
1948
1969
|
end
|
1949
|
-
|
1970
|
+
|
1950
1971
|
# Get the minimum damper position
|
1951
1972
|
mdp = 1.0
|
1952
1973
|
zone.equipment.each do |equip|
|
@@ -1966,46 +1987,46 @@ class OpenStudio::Model::AirLoopHVAC
|
|
1966
1987
|
mdp = term.constantMinimumAirFlowFraction
|
1967
1988
|
end
|
1968
1989
|
end
|
1969
|
-
|
1990
|
+
|
1970
1991
|
# Zone minimum discharge airflow rate
|
1971
1992
|
v_dz = v_pz*mdp
|
1972
|
-
|
1993
|
+
|
1973
1994
|
# Zone discharge air fraction
|
1974
1995
|
z_d = v_oz / v_dz
|
1975
|
-
|
1996
|
+
|
1976
1997
|
# Zone ventilation effectiveness
|
1977
1998
|
e_vz = 1+x_s-z_d
|
1978
|
-
|
1999
|
+
|
1979
2000
|
# Store the ventilation effectiveness
|
1980
2001
|
e_vzs << e_vz
|
1981
|
-
|
2002
|
+
|
1982
2003
|
OpenStudio::logFree(OpenStudio::Debug, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Zone #{zone.name} v_oz = #{v_oz.round(2)} m^3/s, v_pz = #{v_pz.round(2)} m^3/s, v_dz = #{v_dz.round(2)}, z_d = #{z_d.round(2)}.")
|
1983
|
-
|
2004
|
+
|
1984
2005
|
# Check the ventilation effectiveness against
|
1985
2006
|
# the minimum limit per PNNL and increase
|
1986
2007
|
# as necessary.
|
1987
2008
|
if e_vz < 0.6
|
1988
|
-
|
2009
|
+
|
1989
2010
|
# Adjusted discharge air fraction
|
1990
2011
|
z_d_adj = 1+x_s-0.6
|
1991
|
-
|
2012
|
+
|
1992
2013
|
# Adjusted min discharge airflow rate
|
1993
2014
|
v_dz_adj = v_oz / z_d_adj
|
1994
|
-
|
2015
|
+
|
1995
2016
|
# Adjusted minimum damper position
|
1996
2017
|
mdp_adj = v_dz_adj / v_pz
|
1997
|
-
|
2018
|
+
|
1998
2019
|
# Don't allow values > 1
|
1999
2020
|
if mdp_adj > 1.0
|
2000
2021
|
mdp_adj = 1.0
|
2001
2022
|
end
|
2002
|
-
|
2023
|
+
|
2003
2024
|
# Zone ventilation effectiveness
|
2004
2025
|
e_vz_adj = 1+x_s-z_d_adj
|
2005
|
-
|
2026
|
+
|
2006
2027
|
# Store the ventilation effectiveness
|
2007
2028
|
e_vzs_adj << e_vz_adj
|
2008
|
-
|
2029
|
+
|
2009
2030
|
# Set the adjusted minimum damper position
|
2010
2031
|
zone.equipment.each do |equip|
|
2011
2032
|
if equip.to_AirTerminalSingleDuctVAVHeatAndCoolNoReheat.is_initialized
|
@@ -2022,46 +2043,46 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2022
2043
|
term.setConstantMinimumAirFlowFraction(mdp_adj)
|
2023
2044
|
end
|
2024
2045
|
end
|
2025
|
-
|
2046
|
+
|
2026
2047
|
num_zones_adj += 1
|
2027
|
-
|
2048
|
+
|
2028
2049
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Zone #{zone.name} has a ventilation effectiveness of #{e_vz.round(2)}. Increasing to #{e_vz_adj.round(2)} by increasing minimum damper position from #{mdp.round(2)} to #{mdp_adj.round(2)}.")
|
2029
2050
|
|
2030
2051
|
else
|
2031
2052
|
# Store the unadjusted value
|
2032
2053
|
e_vzs_adj << e_vz
|
2033
2054
|
end
|
2034
|
-
|
2055
|
+
|
2035
2056
|
end
|
2036
|
-
|
2057
|
+
|
2037
2058
|
# Min system zone ventilation effectiveness
|
2038
2059
|
e_v = e_vzs.min
|
2039
|
-
|
2040
|
-
# Total system outdoor intake flow rate
|
2060
|
+
|
2061
|
+
# Total system outdoor intake flow rate
|
2041
2062
|
v_ot = v_ou / e_v
|
2042
2063
|
v_ot_cfm = OpenStudio.convert(v_ot, 'm^3/s', 'cfm').get
|
2043
|
-
|
2064
|
+
|
2044
2065
|
# Min system zone ventilation effectiveness
|
2045
2066
|
e_v_adj = e_vzs_adj.min
|
2046
|
-
|
2047
|
-
# Total system outdoor intake flow rate
|
2067
|
+
|
2068
|
+
# Total system outdoor intake flow rate
|
2048
2069
|
v_ot_adj = v_ou / e_v_adj
|
2049
2070
|
v_ot_adj_cfm = OpenStudio.convert(v_ot_adj, 'm^3/s', 'cfm').get
|
2050
|
-
|
2071
|
+
|
2051
2072
|
# Report out the results of the multizone calculations
|
2052
2073
|
if num_zones_adj > 0
|
2053
2074
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: the multizone outdoor air calculation method was applied. A simple summation of the zone outdoor air requirements gives a value of #{v_ou_cfm.round} cfm. Applying the multizone method gives a value of #{v_ot_cfm.round} cfm, with an original system ventilation effectiveness of #{e_v.round(2)}. After increasing the minimum damper position in #{num_zones_adj} critical zones, the resulting requirement is #{v_ot_adj_cfm.round} cfm with a system ventilation effectiveness of #{e_v_adj.round(2)}.")
|
2054
2075
|
else
|
2055
2076
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: the multizone outdoor air calculation method was applied. A simple summation of the zone requirements gives a value of #{v_ou_cfm.round} cfm. However, applying the multizone method requires #{v_ot_adj_cfm.round} cfm based on the ventilation effectiveness of the system.")
|
2056
2077
|
end
|
2057
|
-
|
2078
|
+
|
2058
2079
|
# Hard-size the sizing:system
|
2059
2080
|
# object with the calculated min OA flow rate
|
2060
2081
|
sizing_system = self.sizingSystem
|
2061
2082
|
sizing_system.setDesignOutdoorAirFlowRate(v_ot_adj)
|
2062
|
-
|
2083
|
+
|
2063
2084
|
return true
|
2064
|
-
|
2085
|
+
|
2065
2086
|
end
|
2066
2087
|
|
2067
2088
|
# For critical zones of Outpatient, if the minimum airflow rate required by the accreditation standard (AIA 2001) is significantly
|
@@ -2098,30 +2119,30 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2098
2119
|
end
|
2099
2120
|
return true
|
2100
2121
|
end
|
2101
|
-
|
2122
|
+
|
2102
2123
|
# Determine if demand control ventilation (DCV) is
|
2103
2124
|
# required for this air loop.
|
2104
2125
|
#
|
2105
2126
|
# @param (see #is_economizer_required)
|
2106
|
-
# @return [Bool] Returns true if required, false if not.
|
2107
|
-
# @todo Add exception logic for
|
2127
|
+
# @return [Bool] Returns true if required, false if not.
|
2128
|
+
# @todo Add exception logic for
|
2108
2129
|
# systems that serve multifamily, parking garage, warehouse
|
2109
2130
|
def is_demand_control_ventilation_required(template, climate_zone)
|
2110
|
-
|
2131
|
+
|
2111
2132
|
dcv_required = false
|
2112
|
-
|
2133
|
+
|
2113
2134
|
# Not required by the old vintages
|
2114
|
-
if template == 'DOE Ref Pre-1980' || template == 'DOE Ref 1980-2004'
|
2115
|
-
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required for any system.")
|
2135
|
+
if template == 'DOE Ref Pre-1980' || template == 'DOE Ref 1980-2004' || template == 'NECB 2011'
|
2136
|
+
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{template} #{climate_zone}: #{self.name}: DCV is not required for any system.")
|
2116
2137
|
return dcv_required
|
2117
2138
|
end
|
2118
|
-
|
2139
|
+
|
2119
2140
|
# Not required for systems that require an ERV
|
2120
2141
|
if self.has_energy_recovery
|
2121
2142
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system has Energy Recovery.")
|
2122
2143
|
return dcv_required
|
2123
2144
|
end
|
2124
|
-
|
2145
|
+
|
2125
2146
|
# Area, occupant density, and OA flow limits
|
2126
2147
|
min_area_ft2 = 0
|
2127
2148
|
min_occ_per_1000_ft2 = 0
|
@@ -2144,7 +2165,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2144
2165
|
min_oa_without_economizer_cfm = 3000
|
2145
2166
|
min_oa_with_economizer_cfm = 750
|
2146
2167
|
end
|
2147
|
-
|
2168
|
+
|
2148
2169
|
# Get the area served and the number of occupants
|
2149
2170
|
area_served_m2 = 0
|
2150
2171
|
num_people = 0
|
@@ -2154,28 +2175,28 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2154
2175
|
num_people += space.numberOfPeople
|
2155
2176
|
end
|
2156
2177
|
end
|
2157
|
-
|
2178
|
+
|
2158
2179
|
# Check the minimum area
|
2159
2180
|
area_served_ft2 = OpenStudio.convert(area_served_m2, 'm^2', 'ft^2').get
|
2160
2181
|
if area_served_ft2 < min_area_ft2
|
2161
2182
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system serves #{area_served_ft2.round} ft2, but the minimum size is #{min_area_ft2.round} ft2.")
|
2162
2183
|
return dcv_required
|
2163
2184
|
end
|
2164
|
-
|
2185
|
+
|
2165
2186
|
# Check the minimum occupancy density
|
2166
2187
|
occ_per_ft2 = num_people / area_served_ft2
|
2167
|
-
occ_per_1000_ft2 = occ_per_ft2*1000
|
2168
|
-
|
2188
|
+
occ_per_1000_ft2 = occ_per_ft2*1000
|
2189
|
+
|
2169
2190
|
if occ_per_1000_ft2 < min_occ_per_1000_ft2
|
2170
2191
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system occupant density is #{occ_per_1000_ft2.round} people/1000 ft2, but the minimum occupant density is #{min_occ_per_1000_ft2.round} people/1000 ft2.")
|
2171
2192
|
return dcv_required
|
2172
2193
|
end
|
2173
|
-
|
2174
|
-
# Get the min OA flow rate
|
2194
|
+
|
2195
|
+
# Get the min OA flow rate
|
2175
2196
|
oa_flow_m3_per_s = 0
|
2176
2197
|
if self.airLoopHVACOutdoorAirSystem.is_initialized
|
2177
2198
|
oa_system = self.airLoopHVACOutdoorAirSystem.get
|
2178
|
-
controller_oa = oa_system.getControllerOutdoorAir
|
2199
|
+
controller_oa = oa_system.getControllerOutdoorAir
|
2179
2200
|
if controller_oa.minimumOutdoorAirFlowRate.is_initialized
|
2180
2201
|
oa_flow_m3_per_s = controller_oa.minimumOutdoorAirFlowRate.get
|
2181
2202
|
elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized
|
@@ -2186,8 +2207,8 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2186
2207
|
return dcv_required
|
2187
2208
|
end
|
2188
2209
|
oa_flow_cfm = OpenStudio.convert(oa_flow_m3_per_s, 'm^3/s', 'cfm').get
|
2189
|
-
|
2190
|
-
|
2210
|
+
|
2211
|
+
|
2191
2212
|
# Check for min OA without an economizer OR has economizer
|
2192
2213
|
if oa_flow_cfm < min_oa_without_economizer_cfm && self.has_economizer == false
|
2193
2214
|
# Message if doesn't pass OA limit
|
@@ -2206,25 +2227,25 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2206
2227
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV is not required since the system has an economizer, but the min oa flow is #{oa_flow_cfm.round} cfm, less than the minimum of #{min_oa_with_economizer_cfm.round} cfm for systems with an economizer.")
|
2207
2228
|
return dcv_required
|
2208
2229
|
end
|
2209
|
-
|
2230
|
+
|
2210
2231
|
# If here, DCV is required
|
2211
2232
|
dcv_required = true
|
2212
|
-
|
2233
|
+
|
2213
2234
|
return dcv_required
|
2214
|
-
|
2215
|
-
end
|
2235
|
+
|
2236
|
+
end
|
2216
2237
|
|
2217
2238
|
# Enable demand control ventilation (DCV) for this air loop.
|
2218
2239
|
#
|
2219
2240
|
# @return [Bool] Returns true if required, false if not.
|
2220
2241
|
def enable_demand_control_ventilation()
|
2221
|
-
|
2242
|
+
|
2222
2243
|
# Get the OA intake
|
2223
2244
|
controller_oa = nil
|
2224
2245
|
controller_mv = nil
|
2225
2246
|
if self.airLoopHVACOutdoorAirSystem.is_initialized
|
2226
2247
|
oa_system = self.airLoopHVACOutdoorAirSystem.get
|
2227
|
-
controller_oa = oa_system.getControllerOutdoorAir
|
2248
|
+
controller_oa = oa_system.getControllerOutdoorAir
|
2228
2249
|
controller_mv = controller_oa.controllerMechanicalVentilation
|
2229
2250
|
if controller_mv.demandControlledVentilation == true
|
2230
2251
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: DCV was already enabled.")
|
@@ -2234,29 +2255,29 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2234
2255
|
OpenStudio::logFree(OpenStudio::Warn, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Could not enable DCV since the system has no OA intake.")
|
2235
2256
|
return false
|
2236
2257
|
end
|
2237
|
-
|
2258
|
+
|
2238
2259
|
# Change the min flow rate in the controller outdoor air
|
2239
2260
|
controller_oa.setMinimumOutdoorAirFlowRate(0.0)
|
2240
|
-
|
2261
|
+
|
2241
2262
|
# Enable DCV in the controller mechanical ventilation
|
2242
2263
|
controller_mv.setDemandControlledVentilation(true)
|
2243
2264
|
|
2244
2265
|
return true
|
2245
2266
|
|
2246
2267
|
end
|
2247
|
-
|
2268
|
+
|
2248
2269
|
# Determine if the system required supply air temperature
|
2249
2270
|
# (SAT) reset.
|
2250
2271
|
#
|
2251
2272
|
# @param (see #is_economizer_required)
|
2252
|
-
# @return [Bool] Returns true if required, false if not.
|
2273
|
+
# @return [Bool] Returns true if required, false if not.
|
2253
2274
|
def is_supply_air_temperature_reset_required(template, climate_zone)
|
2254
|
-
|
2275
|
+
|
2255
2276
|
is_sat_reset_required = false
|
2256
|
-
|
2277
|
+
|
2257
2278
|
# Only required for multizone VAV systems
|
2258
2279
|
return is_sat_reset_required unless self.is_multizone_vav_system
|
2259
|
-
|
2280
|
+
|
2260
2281
|
# Not required until 90.1-2010
|
2261
2282
|
case template
|
2262
2283
|
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007'
|
@@ -2284,11 +2305,11 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2284
2305
|
'ASHRAE 169-2006-8A',
|
2285
2306
|
'ASHRAE 169-2006-8B'
|
2286
2307
|
is_sat_reset_required = true
|
2287
|
-
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset is required.")
|
2308
|
+
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset is required.")
|
2288
2309
|
return is_sat_reset_required
|
2289
2310
|
end
|
2290
2311
|
end
|
2291
|
-
|
2312
|
+
|
2292
2313
|
end
|
2293
2314
|
|
2294
2315
|
# Enable supply air temperature (SAT) reset based
|
@@ -2341,14 +2362,14 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2341
2362
|
# increased by 5F when outdoor air is below 50F, and reset
|
2342
2363
|
# linearly when outdoor air is between 50F and 70F.
|
2343
2364
|
#
|
2344
|
-
# @return [Bool] Returns true if successful, false if not.
|
2365
|
+
# @return [Bool] Returns true if successful, false if not.
|
2345
2366
|
|
2346
2367
|
def enable_supply_air_temperature_reset_outdoor_temperature()
|
2347
|
-
|
2368
|
+
|
2348
2369
|
# for AHU1 in Outpatient, SAT is 52F constant, no reset
|
2349
2370
|
return true if self.name.get == 'PVAV Outpatient F1'
|
2350
|
-
|
2351
|
-
# Get the current setpoint and calculate
|
2371
|
+
|
2372
|
+
# Get the current setpoint and calculate
|
2352
2373
|
# the new setpoint.
|
2353
2374
|
sizing_system = self.sizingSystem
|
2354
2375
|
sat_at_hi_oat_c = sizing_system.centralCoolingDesignSupplyAirTemperature
|
@@ -2358,13 +2379,13 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2358
2379
|
increase_f = 5.0
|
2359
2380
|
sat_at_lo_oat_f = sat_at_hi_oat_f+increase_f
|
2360
2381
|
sat_at_lo_oat_c = OpenStudio.convert(sat_at_lo_oat_f, 'F', 'C').get
|
2361
|
-
|
2382
|
+
|
2362
2383
|
# Define the high and low outdoor air temperatures
|
2363
2384
|
lo_oat_f = 50
|
2364
2385
|
lo_oat_c = OpenStudio.convert(lo_oat_f, 'F', 'C').get
|
2365
2386
|
hi_oat_f = 70
|
2366
2387
|
hi_oat_c = OpenStudio.convert(hi_oat_f, 'F', 'C').get
|
2367
|
-
|
2388
|
+
|
2368
2389
|
# Create a setpoint manager
|
2369
2390
|
sat_oa_reset = OpenStudio::Model::SetpointManagerOutdoorAirReset.new(model)
|
2370
2391
|
sat_oa_reset.setName("#{self.name} SAT Reset")
|
@@ -2373,22 +2394,22 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2373
2394
|
sat_oa_reset.setOutdoorLowTemperature(lo_oat_c)
|
2374
2395
|
sat_oa_reset.setSetpointatOutdoorHighTemperature(sat_at_hi_oat_c)
|
2375
2396
|
sat_oa_reset.setOutdoorHighTemperature(hi_oat_c)
|
2376
|
-
|
2397
|
+
|
2377
2398
|
# Attach the setpoint manager to the
|
2378
2399
|
# supply outlet node of the system.
|
2379
2400
|
sat_oa_reset.addToNode(self.supplyOutletNode)
|
2380
|
-
|
2401
|
+
|
2381
2402
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Supply air temperature reset was enabled. When OAT > #{hi_oat_f.round}F, SAT is #{sat_at_hi_oat_f.round}F. When OAT < #{lo_oat_f.round}F, SAT is #{sat_at_lo_oat_f.round}F. It varies linearly in between these points.")
|
2382
|
-
|
2403
|
+
|
2383
2404
|
return true
|
2384
|
-
|
2405
|
+
|
2385
2406
|
end
|
2386
|
-
|
2407
|
+
|
2387
2408
|
# Determine if the system has an economizer
|
2388
2409
|
#
|
2389
|
-
# @return [Bool] Returns true if required, false if not.
|
2410
|
+
# @return [Bool] Returns true if required, false if not.
|
2390
2411
|
def has_economizer()
|
2391
|
-
|
2412
|
+
|
2392
2413
|
# Get the OA system and OA controller
|
2393
2414
|
oa_sys = self.airLoopHVACOutdoorAirSystem
|
2394
2415
|
if oa_sys.is_initialized
|
@@ -2398,28 +2419,28 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2398
2419
|
end
|
2399
2420
|
oa_control = oa_sys.getControllerOutdoorAir
|
2400
2421
|
economizer_type = oa_control.getEconomizerControlType
|
2401
|
-
|
2422
|
+
|
2402
2423
|
# Return false if no economizer is present
|
2403
2424
|
if economizer_type == 'NoEconomizer'
|
2404
2425
|
return false
|
2405
2426
|
else
|
2406
2427
|
return true
|
2407
2428
|
end
|
2408
|
-
|
2429
|
+
|
2409
2430
|
end
|
2410
|
-
|
2431
|
+
|
2411
2432
|
# Determine if the system is a multizone VAV system
|
2412
2433
|
#
|
2413
|
-
# @return [Bool] Returns true if required, false if not.
|
2434
|
+
# @return [Bool] Returns true if required, false if not.
|
2414
2435
|
def is_multizone_vav_system()
|
2415
|
-
|
2436
|
+
|
2416
2437
|
is_multizone_vav_system = false
|
2417
|
-
|
2438
|
+
|
2418
2439
|
# Must serve more than 1 zone
|
2419
2440
|
if self.thermalZones.size < 2
|
2420
2441
|
return is_multizone_vav_system
|
2421
2442
|
end
|
2422
|
-
|
2443
|
+
|
2423
2444
|
# Must be a variable volume system
|
2424
2445
|
has_vav_fan = false
|
2425
2446
|
self.supplyComponents.each do |comp|
|
@@ -2430,36 +2451,36 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2430
2451
|
if has_vav_fan == false
|
2431
2452
|
return is_multizone_vav_system
|
2432
2453
|
end
|
2433
|
-
|
2454
|
+
|
2434
2455
|
# If here, it's a multizone VAV system
|
2435
2456
|
is_multizone_vav_system = true
|
2436
|
-
|
2457
|
+
|
2437
2458
|
return is_multizone_vav_system
|
2438
2459
|
|
2439
2460
|
end
|
2440
|
-
|
2461
|
+
|
2441
2462
|
# Determine if the system has energy recovery already
|
2442
2463
|
#
|
2443
|
-
# @return [Bool] Returns true if an ERV is present, false if not.
|
2464
|
+
# @return [Bool] Returns true if an ERV is present, false if not.
|
2444
2465
|
def has_energy_recovery()
|
2445
|
-
|
2466
|
+
|
2446
2467
|
has_erv = false
|
2447
|
-
|
2468
|
+
|
2448
2469
|
# Get the OA system
|
2449
2470
|
oa_sys = self.airLoopHVACOutdoorAirSystem
|
2450
2471
|
if oa_sys.is_initialized
|
2451
2472
|
oa_sys = oa_sys.get
|
2452
2473
|
else
|
2453
2474
|
return has_erv # No OA system
|
2454
|
-
end
|
2455
|
-
|
2475
|
+
end
|
2476
|
+
|
2456
2477
|
# Find any ERV on the OA system
|
2457
2478
|
oa_sys.oaComponents.each do |oa_comp|
|
2458
2479
|
if oa_comp.to_HeatExchangerAirToAirSensibleAndLatent.is_initialized
|
2459
2480
|
has_erv = true
|
2460
2481
|
end
|
2461
2482
|
end
|
2462
|
-
|
2483
|
+
|
2463
2484
|
return has_erv
|
2464
2485
|
|
2465
2486
|
end
|
@@ -2471,13 +2492,13 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2471
2492
|
# @todo see if this impacts the sizing run.
|
2472
2493
|
def set_vav_damper_action(template)
|
2473
2494
|
damper_action = nil
|
2474
|
-
case template
|
2495
|
+
case template
|
2475
2496
|
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', 'NECB 2011'
|
2476
2497
|
damper_action = 'Single Maximum'
|
2477
2498
|
when '90.1-2007', '90.1-2010', '90.1-2013'
|
2478
2499
|
damper_action = 'Dual Maximum'
|
2479
2500
|
end
|
2480
|
-
|
2501
|
+
|
2481
2502
|
# Interpret this as an EnergyPlus input
|
2482
2503
|
damper_action_eplus = nil
|
2483
2504
|
if damper_action == 'Single Maximum'
|
@@ -2485,7 +2506,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2485
2506
|
elsif damper_action == 'Dual Maximum'
|
2486
2507
|
damper_action_eplus = 'Reverse'
|
2487
2508
|
end
|
2488
|
-
|
2509
|
+
|
2489
2510
|
# Set the control for any VAV reheat terminals
|
2490
2511
|
# on this airloop.
|
2491
2512
|
self.demandComponents.each do |equip|
|
@@ -2493,17 +2514,17 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2493
2514
|
term = equip.to_AirTerminalSingleDuctVAVReheat.get
|
2494
2515
|
term.setDamperHeatingAction(damper_action_eplus)
|
2495
2516
|
end
|
2496
|
-
end
|
2497
|
-
|
2517
|
+
end
|
2518
|
+
|
2498
2519
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: VAV damper action was set to #{damper_action} control.")
|
2499
|
-
|
2520
|
+
|
2500
2521
|
return true
|
2501
|
-
|
2522
|
+
|
2502
2523
|
end
|
2503
2524
|
|
2504
2525
|
# Determine if a motorized OA damper is required
|
2505
2526
|
def is_motorized_oa_damper_required(template, climate_zone)
|
2506
|
-
|
2527
|
+
|
2507
2528
|
motorized_oa_damper_required = false
|
2508
2529
|
|
2509
2530
|
if self.name.to_s.include? "Outpatient F1"
|
@@ -2511,7 +2532,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2511
2532
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: always has a damper, the minimum OA schedule is the same as airloop availability schedule.")
|
2512
2533
|
return motorized_oa_damper_required
|
2513
2534
|
end
|
2514
|
-
|
2535
|
+
|
2515
2536
|
# If the system has an economizer, it must have
|
2516
2537
|
# a motorized damper.
|
2517
2538
|
if self.has_economizer
|
@@ -2521,11 +2542,11 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2521
2542
|
end
|
2522
2543
|
|
2523
2544
|
# Determine the exceptions based on
|
2524
|
-
# number of stories, climate zone, and
|
2545
|
+
# number of stories, climate zone, and
|
2525
2546
|
# outdoor air intake rates.
|
2526
2547
|
minimum_oa_flow_cfm = 0
|
2527
2548
|
maximum_stories = 0
|
2528
|
-
case template
|
2549
|
+
case template
|
2529
2550
|
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
|
2530
2551
|
# Assuming that older buildings always
|
2531
2552
|
# used backdraft gravity dampers
|
@@ -2561,22 +2582,22 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2561
2582
|
maximum_stories = 0
|
2562
2583
|
end
|
2563
2584
|
end
|
2564
|
-
|
2585
|
+
|
2565
2586
|
# Get the number of stories
|
2566
2587
|
num_stories = self.model.getBuildingStorys.size
|
2567
|
-
|
2588
|
+
|
2568
2589
|
# Check the number of stories exception,
|
2569
2590
|
# which is climate-zone dependent.
|
2570
2591
|
if num_stories < maximum_stories
|
2571
2592
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Motorized OA damper not required because the building has #{num_stories} stories, less than the maximum of #{maximum_stories} stories for climate zone #{climate_zone}.")
|
2572
2593
|
return motorized_oa_damper_required
|
2573
|
-
end
|
2574
|
-
|
2575
|
-
# Get the min OA flow rate
|
2594
|
+
end
|
2595
|
+
|
2596
|
+
# Get the min OA flow rate
|
2576
2597
|
oa_flow_m3_per_s = 0
|
2577
2598
|
if self.airLoopHVACOutdoorAirSystem.is_initialized
|
2578
2599
|
oa_system = self.airLoopHVACOutdoorAirSystem.get
|
2579
|
-
controller_oa = oa_system.getControllerOutdoorAir
|
2600
|
+
controller_oa = oa_system.getControllerOutdoorAir
|
2580
2601
|
if controller_oa.minimumOutdoorAirFlowRate.is_initialized
|
2581
2602
|
oa_flow_m3_per_s = controller_oa.minimumOutdoorAirFlowRate.get
|
2582
2603
|
elsif controller_oa.autosizedMinimumOutdoorAirFlowRate.is_initialized
|
@@ -2586,8 +2607,8 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2586
2607
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}, Motorized OA damper not applicable because it has no OA intake.")
|
2587
2608
|
return motorized_oa_damper_required
|
2588
2609
|
end
|
2589
|
-
oa_flow_cfm = OpenStudio.convert(oa_flow_m3_per_s, 'm^3/s', 'cfm').get
|
2590
|
-
|
2610
|
+
oa_flow_cfm = OpenStudio.convert(oa_flow_m3_per_s, 'm^3/s', 'cfm').get
|
2611
|
+
|
2591
2612
|
# Check the OA flow rate exception
|
2592
2613
|
if oa_flow_cfm < minimum_oa_flow_cfm
|
2593
2614
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: Motorized OA damper not required because the system OA intake of #{oa_flow_cfm.round} cfm is less than the minimum threshold of #{minimum_oa_flow_cfm} cfm.")
|
@@ -2596,11 +2617,11 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2596
2617
|
|
2597
2618
|
# If here, motorized damper is required
|
2598
2619
|
motorized_oa_damper_required = true
|
2599
|
-
|
2620
|
+
|
2600
2621
|
return motorized_oa_damper_required
|
2601
2622
|
|
2602
2623
|
end
|
2603
|
-
|
2624
|
+
|
2604
2625
|
# Add a motorized damper by modifying the OA schedule
|
2605
2626
|
# to require zero OA during unoccupied hours. This means
|
2606
2627
|
# that even during morning warmup or nightcyling, no OA will
|
@@ -2608,15 +2629,15 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2608
2629
|
# If no occupancy schedule is supplied, one will be created.
|
2609
2630
|
# In this case, occupied is defined as the total percent
|
2610
2631
|
# occupancy for the loop for all zones served.
|
2611
|
-
#
|
2632
|
+
#
|
2612
2633
|
# @param min_occ_pct [Double] the fractional value below which
|
2613
2634
|
# the system will be considered unoccupied.
|
2614
2635
|
# @param occ_sch [OpenStudio::Model::Schedule] the occupancy schedule.
|
2615
2636
|
# If not supplied, one will be created based on the supplied
|
2616
2637
|
# occupancy threshold.
|
2617
|
-
# @return [Bool] true if successful, false if not
|
2638
|
+
# @return [Bool] true if successful, false if not
|
2618
2639
|
def add_motorized_oa_damper(min_occ_pct = 0.15, occ_sch = nil)
|
2619
|
-
|
2640
|
+
|
2620
2641
|
# Get the airloop occupancy schedule if none supplied
|
2621
2642
|
if occ_sch.nil?
|
2622
2643
|
occ_sch = self.get_occupancy_schedule(min_occ_pct)
|
@@ -2625,7 +2646,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2625
2646
|
else
|
2626
2647
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Setting motorized OA damper schedule to #{occ_sch.name}.")
|
2627
2648
|
end
|
2628
|
-
|
2649
|
+
|
2629
2650
|
# Get the OA system and OA controller
|
2630
2651
|
oa_sys = self.airLoopHVACOutdoorAirSystem
|
2631
2652
|
if oa_sys.is_initialized
|
@@ -2634,21 +2655,21 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2634
2655
|
return false # No OA system
|
2635
2656
|
end
|
2636
2657
|
oa_control = oa_sys.getControllerOutdoorAir
|
2637
|
-
|
2658
|
+
|
2638
2659
|
# Set the minimum OA schedule to follow occupancy
|
2639
|
-
oa_control.setMinimumOutdoorAirSchedule(occ_sch)
|
2640
|
-
|
2660
|
+
oa_control.setMinimumOutdoorAirSchedule(occ_sch)
|
2661
|
+
|
2641
2662
|
return true
|
2642
|
-
|
2643
|
-
end
|
2644
|
-
|
2663
|
+
|
2664
|
+
end
|
2665
|
+
|
2645
2666
|
# Remove a motorized OA damper by modifying the OA schedule
|
2646
2667
|
# to require full OA at all times. Whenever the fan operates,
|
2647
2668
|
# the damper will be open and OA will be brought into the building.
|
2648
2669
|
# This reflects the use of a backdraft gravity damper, and
|
2649
2670
|
# increases building loads unnecessarily during unoccupied hours.
|
2650
2671
|
def remove_motorized_oa_damper
|
2651
|
-
|
2672
|
+
|
2652
2673
|
# Get the OA system and OA controller
|
2653
2674
|
oa_sys = self.airLoopHVACOutdoorAirSystem
|
2654
2675
|
if oa_sys.is_initialized
|
@@ -2657,13 +2678,13 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2657
2678
|
return false # No OA system
|
2658
2679
|
end
|
2659
2680
|
oa_control = oa_sys.getControllerOutdoorAir
|
2660
|
-
|
2681
|
+
|
2661
2682
|
# Set the minimum OA schedule to always 1 (100%)
|
2662
2683
|
oa_control.setMinimumOutdoorAirSchedule(self.model.alwaysOnDiscreteSchedule)
|
2663
2684
|
|
2664
2685
|
return true
|
2665
|
-
|
2666
|
-
end
|
2686
|
+
|
2687
|
+
end
|
2667
2688
|
|
2668
2689
|
# This method creates a schedule where the value is zero when
|
2669
2690
|
# the overall occupancy for all zones on the airloop is below
|
@@ -2671,7 +2692,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2671
2692
|
# greater than or equal to the threshold. This method is designed
|
2672
2693
|
# to use the total number of people on the airloop, so if there is
|
2673
2694
|
# a zone that is continuously occupied by a few people, but other
|
2674
|
-
# zones that are intermittently occupied by many people, the
|
2695
|
+
# zones that are intermittently occupied by many people, the
|
2675
2696
|
# first zone doesn't drive the entire system.
|
2676
2697
|
#
|
2677
2698
|
# @param occupied_percentage_threshold [Double] the minimum fraction (0 to 1) that counts as occupied
|
@@ -2699,7 +2720,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2699
2720
|
num_ppl = people.getNumberOfPeople(space.floorArea)
|
2700
2721
|
if occ_schedules_num_occ[num_ppl_sch].nil?
|
2701
2722
|
occ_schedules_num_occ[num_ppl_sch] = num_ppl
|
2702
|
-
max_occ_on_airloop += num_ppl
|
2723
|
+
max_occ_on_airloop += num_ppl
|
2703
2724
|
else
|
2704
2725
|
occ_schedules_num_occ[num_ppl_sch] += num_ppl
|
2705
2726
|
max_occ_on_airloop += num_ppl
|
@@ -2718,7 +2739,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2718
2739
|
num_ppl = people.getNumberOfPeople(space.floorArea)
|
2719
2740
|
if occ_schedules_num_occ[num_ppl_sch].nil?
|
2720
2741
|
occ_schedules_num_occ[num_ppl_sch] = num_ppl
|
2721
|
-
max_occ_on_airloop += num_ppl
|
2742
|
+
max_occ_on_airloop += num_ppl
|
2722
2743
|
else
|
2723
2744
|
occ_schedules_num_occ[num_ppl_sch] += num_ppl
|
2724
2745
|
max_occ_on_airloop += num_ppl
|
@@ -2727,13 +2748,13 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2727
2748
|
end
|
2728
2749
|
end
|
2729
2750
|
end
|
2730
|
-
|
2751
|
+
|
2731
2752
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{self.name} has #{occ_schedules_num_occ.size} unique occ schedules.")
|
2732
2753
|
occ_schedules_num_occ.each do |occ_sch, num_occ|
|
2733
2754
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", " #{occ_sch.name} - #{num_occ.round} people")
|
2734
2755
|
end
|
2735
2756
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", " Total #{max_occ_on_airloop.round} people on #{self.name}")
|
2736
|
-
|
2757
|
+
|
2737
2758
|
# For each day of the year, determine
|
2738
2759
|
#time_value_pairs = []
|
2739
2760
|
year = self.model.getYearDescription
|
@@ -2745,12 +2766,12 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2745
2766
|
times_on_this_day = []
|
2746
2767
|
os_date = year.makeDate(i)
|
2747
2768
|
day_of_week = os_date.dayOfWeek.valueName
|
2748
|
-
|
2769
|
+
|
2749
2770
|
# Get the unique time indices and corresponding day schedules
|
2750
2771
|
occ_schedules_day_schs = {}
|
2751
2772
|
day_sch_num_occ = {}
|
2752
2773
|
occ_schedules_num_occ.each do |occ_sch, num_occ|
|
2753
|
-
|
2774
|
+
|
2754
2775
|
# Get the day schedules for this day
|
2755
2776
|
# (there should only be one)
|
2756
2777
|
day_schs = occ_sch.getDaySchedules(os_date, os_date)
|
@@ -2759,7 +2780,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2759
2780
|
times_on_this_day << time.toString
|
2760
2781
|
end
|
2761
2782
|
day_sch_num_occ[day_schs[0]] = num_occ
|
2762
|
-
|
2783
|
+
|
2763
2784
|
end
|
2764
2785
|
|
2765
2786
|
# Determine the total fraction for the airloop at each time
|
@@ -2777,7 +2798,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2777
2798
|
occ_frac = day_sch.getValue(os_time)
|
2778
2799
|
tot_occ_at_time += occ_frac * num_occ
|
2779
2800
|
end
|
2780
|
-
|
2801
|
+
|
2781
2802
|
# Total fraction for the airloop at each time
|
2782
2803
|
air_loop_occ_frac = tot_occ_at_time / max_occ_on_airloop
|
2783
2804
|
occ_status = 0 # unoccupied
|
@@ -2794,35 +2815,35 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2794
2815
|
end
|
2795
2816
|
|
2796
2817
|
# OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{daily_times.join(', ')} #{daily_values.join(', ')}")
|
2797
|
-
|
2818
|
+
|
2798
2819
|
# Simplify the daily times to eliminate intermediate
|
2799
2820
|
# points with the same value as the following point.
|
2800
2821
|
simple_daily_times = []
|
2801
2822
|
simple_daily_os_times = []
|
2802
2823
|
simple_daily_values = []
|
2803
2824
|
simple_daily_occs = []
|
2804
|
-
daily_values.each_with_index do |value, i|
|
2825
|
+
daily_values.each_with_index do |value, i|
|
2805
2826
|
next if value == daily_values[i+1]
|
2806
2827
|
simple_daily_times << daily_times[i]
|
2807
2828
|
simple_daily_os_times << daily_os_times[i]
|
2808
2829
|
simple_daily_values << daily_values[i]
|
2809
2830
|
simple_daily_occs << daily_occs[i]
|
2810
2831
|
end
|
2811
|
-
|
2832
|
+
|
2812
2833
|
# OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "#{simple_daily_times.join(', ')} {simple_daily_values.join(', ')}")
|
2813
|
-
|
2834
|
+
|
2814
2835
|
# Store the daily values
|
2815
2836
|
yearly_data << {'date'=>os_date,'day_of_week'=>day_of_week,'times'=>simple_daily_times,'values'=>simple_daily_values,'daily_os_times'=>simple_daily_os_times, 'daily_occs'=>simple_daily_occs}
|
2816
2837
|
|
2817
2838
|
end
|
2818
|
-
|
2839
|
+
|
2819
2840
|
# Create a TimeSeries from the data
|
2820
2841
|
#time_series = OpenStudio::TimeSeries.new(times, values, 'unitless')
|
2821
2842
|
|
2822
2843
|
# Make a schedule ruleset
|
2823
2844
|
sch_name = "#{self.name} Occ Sch"
|
2824
2845
|
sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(self.model)
|
2825
|
-
sch_ruleset.setName("#{sch_name}")
|
2846
|
+
sch_ruleset.setName("#{sch_name}")
|
2826
2847
|
|
2827
2848
|
# Default - All Occupied
|
2828
2849
|
day_sch = sch_ruleset.defaultDaySchedule
|
@@ -2834,7 +2855,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2834
2855
|
sch_ruleset.setWinterDesignDaySchedule(day_sch)
|
2835
2856
|
day_sch = sch_ruleset.winterDesignDaySchedule
|
2836
2857
|
day_sch.setName("#{sch_name} Winter Design Day")
|
2837
|
-
day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1)
|
2858
|
+
day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1)
|
2838
2859
|
|
2839
2860
|
# Summer Design Day - All Occupied
|
2840
2861
|
day_sch = OpenStudio::Model::ScheduleDay.new(self.model)
|
@@ -2842,7 +2863,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2842
2863
|
day_sch = sch_ruleset.summerDesignDaySchedule
|
2843
2864
|
day_sch.setName("#{sch_name} Summer Design Day")
|
2844
2865
|
day_sch.addValue(OpenStudio::Time.new(0, 24, 0, 0), 1)
|
2845
|
-
|
2866
|
+
|
2846
2867
|
# Create ruleset schedules, attempting to create
|
2847
2868
|
# the minimum number of unique rules.
|
2848
2869
|
['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'].each do |day_of_week|
|
@@ -2857,7 +2878,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2857
2878
|
times = daily_data['times']
|
2858
2879
|
values = daily_data['values']
|
2859
2880
|
daily_occs = daily_data['daily_occs']
|
2860
|
-
|
2881
|
+
|
2861
2882
|
# If the next (Monday, Tuesday, etc.)
|
2862
2883
|
# is the same as today, keep going.
|
2863
2884
|
# If the next is different, or if
|
@@ -2868,10 +2889,10 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2868
2889
|
next_day_values = yearly_data[i+7]['values']
|
2869
2890
|
next if times == next_day_times && values == next_day_values
|
2870
2891
|
end
|
2871
|
-
|
2892
|
+
|
2872
2893
|
daily_os_times = daily_data['daily_os_times']
|
2873
2894
|
daily_occs = daily_data['daily_occs']
|
2874
|
-
|
2895
|
+
|
2875
2896
|
# If here, we need to make a rule to cover from the previous
|
2876
2897
|
# rule to today
|
2877
2898
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", "Making a new rule for #{day_of_week} from #{end_of_prev_rule.to_s} to #{date}")
|
@@ -2885,7 +2906,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2885
2906
|
day_sch.addValue(time, value)
|
2886
2907
|
OpenStudio::logFree(OpenStudio::Debug, "openstudio.standards.AirLoopHVAC", " Adding value #{time}, #{value}")
|
2887
2908
|
end
|
2888
|
-
|
2909
|
+
|
2889
2910
|
# Set the dates when the rule applies
|
2890
2911
|
sch_rule.setStartDate(end_of_prev_rule)
|
2891
2912
|
sch_rule.setEndDate(date)
|
@@ -2898,28 +2919,28 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2898
2919
|
sch_rule.setApplyFriday(true) if day_of_week == 'Friday'
|
2899
2920
|
sch_rule.setApplySaturday(true) if day_of_week == 'Saturday'
|
2900
2921
|
sch_rule.setApplySunday(true) if day_of_week == 'Sunday'
|
2901
|
-
|
2922
|
+
|
2902
2923
|
# Reset the previous rule end date
|
2903
2924
|
end_of_prev_rule = date + OpenStudio::Time.new(0, 24, 0, 0)
|
2904
|
-
|
2925
|
+
|
2905
2926
|
end
|
2906
|
-
|
2927
|
+
|
2907
2928
|
end
|
2908
2929
|
|
2909
2930
|
return sch_ruleset
|
2910
|
-
|
2911
|
-
end
|
2912
|
-
|
2931
|
+
|
2932
|
+
end
|
2933
|
+
|
2913
2934
|
# Generate the EMS used to implement the economizer
|
2914
2935
|
# and staging controls for packaged single zone units.
|
2915
2936
|
# @note The resulting EMS doesn't actually get added to
|
2916
2937
|
# the IDF yet.
|
2917
2938
|
#
|
2918
2939
|
def apply_single_zone_controls(template, climate_zone)
|
2919
|
-
|
2940
|
+
|
2920
2941
|
# Number of stages is determined by the template
|
2921
2942
|
num_stages = nil
|
2922
|
-
case template
|
2943
|
+
case template
|
2923
2944
|
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', 'NECB 2011'
|
2924
2945
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No special economizer controls were modeled.")
|
2925
2946
|
return true
|
@@ -2928,19 +2949,19 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2928
2949
|
when '90.1-2010', '90.1-2013'
|
2929
2950
|
num_stages = 2
|
2930
2951
|
end
|
2931
|
-
|
2952
|
+
|
2932
2953
|
# Scrub special characters from the system name
|
2933
2954
|
sn = self.name.get.to_s
|
2934
2955
|
snc = sn.gsub(/\W/,'').gsub('_','')
|
2935
|
-
|
2956
|
+
|
2936
2957
|
# Get the zone name
|
2937
2958
|
zone = self.thermalZones[0]
|
2938
2959
|
zone_name = zone.name.get.to_s
|
2939
2960
|
zn_name_clean = zone_name.gsub(/\W/,'_')
|
2940
|
-
|
2961
|
+
|
2941
2962
|
# Zone air node
|
2942
|
-
zone_air_node_name = zone.zoneAirNode.name.get
|
2943
|
-
|
2963
|
+
zone_air_node_name = zone.zoneAirNode.name.get
|
2964
|
+
|
2944
2965
|
# Get the OA system and OA controller
|
2945
2966
|
oa_sys = self.airLoopHVACOutdoorAirSystem
|
2946
2967
|
if oa_sys.is_initialized
|
@@ -2951,7 +2972,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2951
2972
|
oa_control = oa_sys.getControllerOutdoorAir
|
2952
2973
|
oa_control_name = oa_control.name.get
|
2953
2974
|
oa_node_name = oa_sys.outboardOANode.get.name.get
|
2954
|
-
|
2975
|
+
|
2955
2976
|
# Get the name of the min oa schedule
|
2956
2977
|
min_oa_sch_name = nil
|
2957
2978
|
if oa_control.minimumOutdoorAirSchedule.is_initialized
|
@@ -2959,7 +2980,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2959
2980
|
else
|
2960
2981
|
min_oa_sch_name = self.model.alwaysOnDiscreteSchedule.name.get
|
2961
2982
|
end
|
2962
|
-
|
2983
|
+
|
2963
2984
|
# Get the supply fan
|
2964
2985
|
if self.supplyFan.empty?
|
2965
2986
|
OpenStudio::logFree(OpenStudio::Info, 'openstudio.standards.AirLoopHVAC', "For #{self.name}: No supply fan found, cannot apply DX fan/economizer control.")
|
@@ -2967,11 +2988,11 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2967
2988
|
end
|
2968
2989
|
fan = self.supplyFan.get
|
2969
2990
|
fan_name = fan.name.get
|
2970
|
-
|
2991
|
+
|
2971
2992
|
# Supply outlet node
|
2972
2993
|
sup_out_node = self.supplyOutletNode
|
2973
2994
|
sup_out_node_name = sup_out_node.name.get
|
2974
|
-
|
2995
|
+
|
2975
2996
|
# DX Cooling Coil
|
2976
2997
|
dx_coil = nil
|
2977
2998
|
self.supplyComponents.each do |equip|
|
@@ -2987,7 +3008,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
2987
3008
|
end
|
2988
3009
|
dx_coil_name = dx_coil.name.get
|
2989
3010
|
dx_coilsys_name = "#{dx_coil_name} CoilSystem"
|
2990
|
-
|
3011
|
+
|
2991
3012
|
# Heating Coil
|
2992
3013
|
htg_coil = nil
|
2993
3014
|
self.supplyComponents.each do |equip|
|
@@ -3004,7 +3025,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3004
3025
|
return false
|
3005
3026
|
end
|
3006
3027
|
htg_coil_name = htg_coil.name.get
|
3007
|
-
|
3028
|
+
|
3008
3029
|
# Create an economizer maximum OA fraction schedule with
|
3009
3030
|
# a maximum of 70% to reflect damper leakage per PNNL
|
3010
3031
|
max_oa_sch_name = "#{snc}maxOASch"
|
@@ -3013,14 +3034,14 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3013
3034
|
max_oa_sch.defaultDaySchedule.setName("#{max_oa_sch_name}Default")
|
3014
3035
|
max_oa_sch.defaultDaySchedule.addValue(OpenStudio::Time.new(0,24,0,0), 0.7)
|
3015
3036
|
oa_control.setMaximumFractionofOutdoorAirSchedule(max_oa_sch)
|
3016
|
-
|
3037
|
+
|
3017
3038
|
ems = "
|
3018
|
-
|
3039
|
+
|
3019
3040
|
! Sensors
|
3020
|
-
|
3041
|
+
|
3021
3042
|
EnergyManagementSystem:Sensor,
|
3022
|
-
#{snc}OASch,
|
3023
|
-
#{min_oa_sch_name}, !- Output:Variable or Output:Meter Index Key Name,
|
3043
|
+
#{snc}OASch,
|
3044
|
+
#{min_oa_sch_name}, !- Output:Variable or Output:Meter Index Key Name,
|
3024
3045
|
Schedule Value; !- Output:Variable or Output:Meter Name
|
3025
3046
|
|
3026
3047
|
EnergyManagementSystem:Sensor,
|
@@ -3029,32 +3050,32 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3029
3050
|
System Node Temperature; !- Output:Variable or Output:Meter Name
|
3030
3051
|
|
3031
3052
|
EnergyManagementSystem:Sensor,
|
3032
|
-
#{snc}OAFlowMass,
|
3053
|
+
#{snc}OAFlowMass,
|
3033
3054
|
#{oa_node_name}, !- Output:Variable or Output:Meter Index Key Name
|
3034
3055
|
System Node Mass Flow Rate; !- Output:Variable or Output:Meter Name
|
3035
3056
|
|
3036
3057
|
EnergyManagementSystem:Sensor,
|
3037
|
-
#{snc}HeatingRTF,
|
3058
|
+
#{snc}HeatingRTF,
|
3038
3059
|
#{htg_coil_name}, !- Output:Variable or Output:Meter Index Key Name
|
3039
3060
|
Heating Coil Runtime Fraction; !- Output:Variable or Output:Meter Name
|
3040
3061
|
|
3041
3062
|
EnergyManagementSystem:Sensor,
|
3042
|
-
#{snc}RTF,
|
3063
|
+
#{snc}RTF,
|
3043
3064
|
#{dx_coil_name}, !- Output:Variable or Output:Meter Index Key Name
|
3044
3065
|
Cooling Coil Runtime Fraction; !- Output:Variable or Output:Meter Name
|
3045
3066
|
|
3046
3067
|
EnergyManagementSystem:Sensor,
|
3047
|
-
#{snc}SpeedRatio,
|
3068
|
+
#{snc}SpeedRatio,
|
3048
3069
|
#{dx_coilsys_name}, !- Output:Variable or Output:Meter Index Key Name
|
3049
3070
|
Coil System Compressor Speed Ratio; !- Output:Variable or Output:Meter Name
|
3050
3071
|
|
3051
3072
|
EnergyManagementSystem:Sensor,
|
3052
|
-
#{snc}DATRqd,
|
3073
|
+
#{snc}DATRqd,
|
3053
3074
|
#{sup_out_node_name}, !- Output:Variable or Output:Meter Index Key Name
|
3054
3075
|
System Node Setpoint Temperature; !- Output:Variable or Output:Meter Name
|
3055
3076
|
|
3056
3077
|
EnergyManagementSystem:Sensor,
|
3057
|
-
#{snc}EconoStatus,
|
3078
|
+
#{snc}EconoStatus,
|
3058
3079
|
#{sn}, !- Output:Variable or Output:Meter Index Key Name
|
3059
3080
|
Air System Outdoor Air Economizer Status; !- Output:Variable or Output:Meter Name
|
3060
3081
|
|
@@ -3066,19 +3087,19 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3066
3087
|
Fan Nominal Pressure Rise; !- Internal Data Type
|
3067
3088
|
|
3068
3089
|
EnergyManagementSystem:InternalVariable,
|
3069
|
-
#{snc}DesignFlowMass,
|
3090
|
+
#{snc}DesignFlowMass,
|
3070
3091
|
#{oa_control_name},!- Internal Data Index Key Name
|
3071
3092
|
Outdoor Air Controller Maximum Mass Flow Rate; !- Internal Data Type
|
3072
3093
|
|
3073
3094
|
EnergyManagementSystem:InternalVariable,
|
3074
|
-
#{snc}OADesignMass,
|
3095
|
+
#{snc}OADesignMass,
|
3075
3096
|
#{oa_control_name},!- Internal Data Index Key Name
|
3076
3097
|
Outdoor Air Controller Minimum Mass Flow Rate; !- Internal Data Type
|
3077
3098
|
|
3078
3099
|
! Actuators
|
3079
3100
|
|
3080
3101
|
EnergyManagementSystem:Actuator,
|
3081
|
-
#{snc}FanPressure,
|
3102
|
+
#{snc}FanPressure,
|
3082
3103
|
#{fan_name}, !- Actuated Component Unique Name
|
3083
3104
|
Fan, !- Actuated Component Type
|
3084
3105
|
Fan Pressure Rise; !- Actuated Component Control Type
|
@@ -3093,36 +3114,36 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3093
3114
|
#{snc}FanPwrExp, !- Erl Variable 1 Name
|
3094
3115
|
#{snc}Stg1Spd, !- Erl Variable 2 Name
|
3095
3116
|
#{snc}Stg2Spd, !- Erl Variable 3 Name
|
3096
|
-
#{snc}HeatSpeed,
|
3097
|
-
#{snc}VenSpeed,
|
3098
|
-
#{snc}NumberofStages;
|
3117
|
+
#{snc}HeatSpeed,
|
3118
|
+
#{snc}VenSpeed,
|
3119
|
+
#{snc}NumberofStages;
|
3099
3120
|
|
3100
3121
|
EnergyManagementSystem:Program,
|
3101
3122
|
#{snc}EconomizerCTRLProg,
|
3102
|
-
SET #{snc}TimestepEconEff = 0.7,
|
3103
|
-
SET #{snc}MaxE = 0.7,
|
3123
|
+
SET #{snc}TimestepEconEff = 0.7,
|
3124
|
+
SET #{snc}MaxE = 0.7,
|
3104
3125
|
SET #{snc}DATRqd = (#{snc}DATRqd*1.8)+32,
|
3105
3126
|
SET OATF = (OATF*1.8)+32,
|
3106
3127
|
SET OAwbF = (OAwbF*1.8)+32,
|
3107
3128
|
IF #{snc}OAFlowMass > (#{snc}OADesignMass*#{snc}OASch),
|
3108
3129
|
SET #{snc}EconoActive = 1,
|
3109
|
-
ELSE,
|
3130
|
+
ELSE,
|
3110
3131
|
SET #{snc}EconoActive = 0,
|
3111
|
-
ENDIF,
|
3132
|
+
ENDIF,
|
3112
3133
|
SET #{snc}dTNeeded = 75-#{snc}DATRqd,
|
3113
3134
|
SET #{snc}CoolDesdT = ((98*0.15)+(75*(1-0.15)))-55,
|
3114
3135
|
SET #{snc}CoolLoad = #{snc}dTNeeded/ #{snc}CoolDesdT,
|
3115
|
-
IF #{snc}CoolLoad > 1,
|
3136
|
+
IF #{snc}CoolLoad > 1,
|
3116
3137
|
SET #{snc}CoolLoad = 1,
|
3117
3138
|
ELSEIF #{snc}CoolLoad < 0,
|
3118
3139
|
SET #{snc}CoolLoad = 0,
|
3119
|
-
ENDIF,
|
3140
|
+
ENDIF,
|
3120
3141
|
IF #{snc}EconoActive == 1,
|
3121
3142
|
SET #{snc}Stage = #{snc}NumberofStages,
|
3122
|
-
IF #{snc}Stage == 2,
|
3143
|
+
IF #{snc}Stage == 2,
|
3123
3144
|
IF #{snc}CoolLoad < 0.6,
|
3124
3145
|
SET #{snc}TimestepEconEff = #{snc}MaxE,
|
3125
|
-
ELSE,
|
3146
|
+
ELSE,
|
3126
3147
|
SET #{snc}ECOEff = 0-2.18919863612305,
|
3127
3148
|
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.674461284910428*#{snc}CoolLoad),
|
3128
3149
|
SET #{snc}ECOEff = #{snc}ECOEff+(0.000459106275872404*(OATF^2)),
|
@@ -3131,88 +3152,88 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3131
3152
|
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.00382838660261133*(OAwbF^2)),
|
3132
3153
|
SET #{snc}ECOEff = #{snc}ECOEff+(0.0000255567460240583*(OAwbF^3)),
|
3133
3154
|
SET #{snc}TimestepEconEff = #{snc}ECOEff,
|
3134
|
-
ENDIF,
|
3135
|
-
ELSE,
|
3155
|
+
ENDIF,
|
3156
|
+
ELSE,
|
3136
3157
|
SET #{snc}ECOEff = 2.36337942464462,
|
3137
3158
|
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.409939515512619*#{snc}CoolLoad),
|
3138
3159
|
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.0565205596792225*OAwbF),
|
3139
3160
|
SET #{snc}ECOEff = #{snc}ECOEff+(0-0.0000632612294169389*(OATF^2)),
|
3140
3161
|
SET #{snc}TimestepEconEff = #{snc}ECOEff+(0.000571724868775081*(OAwbF^2)),
|
3141
|
-
ENDIF,
|
3162
|
+
ENDIF,
|
3142
3163
|
IF #{snc}TimestepEconEff > #{snc}MaxE,
|
3143
3164
|
SET #{snc}TimestepEconEff = #{snc}MaxE,
|
3144
3165
|
ELSEIF #{snc}TimestepEconEff < (#{snc}OADesignMass*#{snc}OASch),
|
3145
3166
|
SET #{snc}TimestepEconEff = (#{snc}OADesignMass*#{snc}OASch),
|
3146
|
-
ENDIF,
|
3147
|
-
ENDIF;
|
3167
|
+
ENDIF,
|
3168
|
+
ENDIF;
|
3148
3169
|
|
3149
3170
|
EnergyManagementSystem:Program,
|
3150
|
-
#{snc}SetFanPar,
|
3151
|
-
IF #{snc}NumberofStages == 1,
|
3152
|
-
Return,
|
3153
|
-
ENDIF,
|
3171
|
+
#{snc}SetFanPar,
|
3172
|
+
IF #{snc}NumberofStages == 1,
|
3173
|
+
Return,
|
3174
|
+
ENDIF,
|
3154
3175
|
SET #{snc}FanPwrExp = 2.2,
|
3155
3176
|
SET #{snc}OAFrac = #{snc}OAFlowMass/#{snc}DesignFlowMass,
|
3156
3177
|
IF #{snc}OAFrac < 0.66,
|
3157
3178
|
SET #{snc}VenSpeed = 0.66,
|
3158
3179
|
SET #{snc}Stg1Spd = 0.66,
|
3159
|
-
ELSE,
|
3180
|
+
ELSE,
|
3160
3181
|
SET #{snc}VenSpeed = #{snc}OAFrac,
|
3161
3182
|
SET #{snc}Stg1Spd = #{snc}OAFrac,
|
3162
|
-
ENDIF,
|
3183
|
+
ENDIF,
|
3163
3184
|
SET #{snc}Stg2Spd = 1.0,
|
3164
3185
|
SET #{snc}HeatSpeed = 1.0;
|
3165
3186
|
|
3166
3187
|
EnergyManagementSystem:Program,
|
3167
|
-
#{snc}FanControl,
|
3168
|
-
IF #{snc}NumberofStages == 1,
|
3169
|
-
Return,
|
3170
|
-
ENDIF,
|
3188
|
+
#{snc}FanControl,
|
3189
|
+
IF #{snc}NumberofStages == 1,
|
3190
|
+
Return,
|
3191
|
+
ENDIF,
|
3171
3192
|
IF #{snc}HeatingRTF > 0,
|
3172
3193
|
SET #{snc}Heating = #{snc}HeatingRTF,
|
3173
3194
|
SET #{snc}Ven = 1-#{snc}HeatingRTF,
|
3174
|
-
SET #{snc}Eco = 0,
|
3175
|
-
SET #{snc}Stage1 = 0,
|
3176
|
-
SET #{snc}Stage2 = 0,
|
3177
|
-
ELSE,
|
3195
|
+
SET #{snc}Eco = 0,
|
3196
|
+
SET #{snc}Stage1 = 0,
|
3197
|
+
SET #{snc}Stage2 = 0,
|
3198
|
+
ELSE,
|
3178
3199
|
SET #{snc}Heating = 0,
|
3179
3200
|
SET #{snc}EcoSpeed = #{snc}VenSpeed,
|
3180
3201
|
IF #{snc}SpeedRatio == 0,
|
3181
|
-
IF #{snc}RTF > 0,
|
3202
|
+
IF #{snc}RTF > 0,
|
3182
3203
|
SET #{snc}Stage1 = #{snc}RTF,
|
3183
|
-
SET #{snc}Stage2 = 0,
|
3204
|
+
SET #{snc}Stage2 = 0,
|
3184
3205
|
SET #{snc}Ven = 1-#{snc}RTF,
|
3185
|
-
SET #{snc}Eco = 0,
|
3206
|
+
SET #{snc}Eco = 0,
|
3186
3207
|
IF #{snc}OAFlowMass > (#{snc}OADesignMass*#{snc}OASch),
|
3187
3208
|
SET #{snc}Stg1Spd = 1.0,
|
3188
|
-
ENDIF,
|
3189
|
-
ELSE,
|
3190
|
-
SET #{snc}Stage1 = 0,
|
3191
|
-
SET #{snc}Stage2 = 0,
|
3209
|
+
ENDIF,
|
3210
|
+
ELSE,
|
3211
|
+
SET #{snc}Stage1 = 0,
|
3212
|
+
SET #{snc}Stage2 = 0,
|
3192
3213
|
IF #{snc}OAFlowMass > (#{snc}OADesignMass*#{snc}OASch),
|
3193
|
-
SET #{snc}Eco = 1.0,
|
3194
|
-
SET #{snc}Ven = 0,
|
3195
|
-
!Calculate the expected discharge air temperature if the system runs at its low speed
|
3214
|
+
SET #{snc}Eco = 1.0,
|
3215
|
+
SET #{snc}Ven = 0,
|
3216
|
+
!Calculate the expected discharge air temperature if the system runs at its low speed
|
3196
3217
|
SET #{snc}ExpDAT = #{snc}DATRqd-(1-#{snc}VenSpeed)*#{zn_name_clean}Temp,
|
3197
3218
|
SET #{snc}ExpDAT = #{snc}ExpDAT/#{snc}VenSpeed,
|
3198
3219
|
IF OATF > #{snc}ExpDAT,
|
3199
3220
|
SET #{snc}EcoSpeed = #{snc}Stg2Spd,
|
3200
|
-
ENDIF,
|
3201
|
-
ELSE,
|
3202
|
-
SET #{snc}Eco = 0,
|
3203
|
-
SET #{snc}Ven = 1.0,
|
3204
|
-
ENDIF,
|
3205
|
-
ENDIF,
|
3206
|
-
ELSE,
|
3221
|
+
ENDIF,
|
3222
|
+
ELSE,
|
3223
|
+
SET #{snc}Eco = 0,
|
3224
|
+
SET #{snc}Ven = 1.0,
|
3225
|
+
ENDIF,
|
3226
|
+
ENDIF,
|
3227
|
+
ELSE,
|
3207
3228
|
SET #{snc}Stage1 = 1-#{snc}SpeedRatio,
|
3208
3229
|
SET #{snc}Stage2 = #{snc}SpeedRatio,
|
3209
|
-
SET #{snc}Ven = 0,
|
3210
|
-
SET #{snc}Eco = 0,
|
3230
|
+
SET #{snc}Ven = 0,
|
3231
|
+
SET #{snc}Eco = 0,
|
3211
3232
|
IF #{snc}OAFlowMass > (#{snc}OADesignMass*#{snc}OASch),
|
3212
3233
|
SET #{snc}Stg1Spd = 1.0,
|
3213
|
-
ENDIF,
|
3214
|
-
ENDIF,
|
3215
|
-
ENDIF,
|
3234
|
+
ENDIF,
|
3235
|
+
ENDIF,
|
3236
|
+
ENDIF,
|
3216
3237
|
! For each mode, (percent time in mode)*(fanSpeer^PwrExp) is the contribution to weighted fan power over time step
|
3217
3238
|
SET #{snc}FPR = #{snc}Ven*(#{snc}VenSpeed ^ #{snc}FanPwrExp),
|
3218
3239
|
SET #{snc}FPR = #{snc}FPR+#{snc}Eco*(#{snc}EcoSpeed^#{snc}FanPwrExp),
|
@@ -3235,7 +3256,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3235
3256
|
#{snc}SetNumberofStages; !- Program Name 1
|
3236
3257
|
|
3237
3258
|
EnergyManagementSystem:ProgramCallingManager,
|
3238
|
-
#{snc}ECOManager,
|
3259
|
+
#{snc}ECOManager,
|
3239
3260
|
InsideHVACSystemIterationLoop, !- EnergyPlus Model Calling Point
|
3240
3261
|
#{snc}EconomizerCTRLProg; !- Program Name 1
|
3241
3262
|
|
@@ -3250,19 +3271,19 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3250
3271
|
#{snc}FanControl;
|
3251
3272
|
|
3252
3273
|
"
|
3253
|
-
|
3274
|
+
|
3254
3275
|
# Write the ems out
|
3255
|
-
# File.open("#{Dir.pwd}/#{snc}_ems.idf", 'w') do |file|
|
3276
|
+
# File.open("#{Dir.pwd}/#{snc}_ems.idf", 'w') do |file|
|
3256
3277
|
# file.puts ems
|
3257
3278
|
# end
|
3258
|
-
|
3279
|
+
|
3259
3280
|
return ems
|
3260
|
-
|
3281
|
+
|
3261
3282
|
end
|
3262
|
-
|
3283
|
+
|
3263
3284
|
# Determine if static pressure reset is required for this
|
3264
3285
|
# system. For 90.1, this determination needs information
|
3265
|
-
# about whether or not the system has DDC control over the
|
3286
|
+
# about whether or not the system has DDC control over the
|
3266
3287
|
# VAV terminals.
|
3267
3288
|
#
|
3268
3289
|
# @todo Instead of requiring the input of whether a system
|
@@ -3276,11 +3297,11 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3276
3297
|
def is_static_pressure_reset_required(template, has_ddc)
|
3277
3298
|
|
3278
3299
|
sp_reset_required = false
|
3279
|
-
|
3300
|
+
|
3280
3301
|
# A big number of btu per hr as the minimum requirement
|
3281
3302
|
infinity_btu_per_hr = 999999999999
|
3282
3303
|
minimum_capacity_btu_per_hr = infinity_btu_per_hr
|
3283
|
-
|
3304
|
+
|
3284
3305
|
# Determine the minimum capacity that requires an economizer
|
3285
3306
|
case template
|
3286
3307
|
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004'
|
@@ -3295,9 +3316,9 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3295
3316
|
when 'NECB 2011'
|
3296
3317
|
# static pressure reset not required
|
3297
3318
|
end
|
3298
|
-
|
3319
|
+
|
3299
3320
|
return sp_reset_required
|
3300
|
-
|
3321
|
+
|
3301
3322
|
end
|
3302
3323
|
|
3303
3324
|
# Determine if a system's fans must shut off when
|
@@ -3306,23 +3327,23 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3306
3327
|
# @param template [String]
|
3307
3328
|
# @return [Bool] true if required, false if not
|
3308
3329
|
def is_unoccupied_fan_shutoff_required(template)
|
3309
|
-
|
3330
|
+
|
3310
3331
|
shutoff_required = true
|
3311
|
-
|
3332
|
+
|
3312
3333
|
# Per 90.1 6.4.3.4.5, systems less than 0.75 HP
|
3313
3334
|
# must turn off when unoccupied.
|
3314
3335
|
minimum_fan_hp = nil
|
3315
3336
|
case template
|
3316
|
-
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013'
|
3337
|
+
when 'DOE Ref Pre-1980', 'DOE Ref 1980-2004', '90.1-2004', '90.1-2007', '90.1-2010', '90.1-2013', 'NECB 2011'
|
3317
3338
|
minimum_fan_hp = 0.75
|
3318
3339
|
end
|
3319
|
-
|
3340
|
+
|
3320
3341
|
# Determine the system fan horsepower
|
3321
3342
|
total_hp = 0.0
|
3322
3343
|
self.supply_return_exhaust_relief_fans.each do |fan|
|
3323
3344
|
total_hp += fan.motorHorsepower
|
3324
3345
|
end
|
3325
|
-
|
3346
|
+
|
3326
3347
|
# Check the HP exception
|
3327
3348
|
if total_hp < minimum_fan_hp
|
3328
3349
|
shutoff_required = false
|
@@ -3330,9 +3351,9 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3330
3351
|
end
|
3331
3352
|
|
3332
3353
|
return shutoff_required
|
3333
|
-
|
3354
|
+
|
3334
3355
|
end
|
3335
|
-
|
3356
|
+
|
3336
3357
|
# Shut off the system during unoccupied periods.
|
3337
3358
|
# During these times, systems will cycle on briefly
|
3338
3359
|
# if temperature drifts below setpoint. For systems
|
@@ -3347,7 +3368,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3347
3368
|
# the system will be considered unoccupied.
|
3348
3369
|
# @return [Bool] true if successful, false if not
|
3349
3370
|
def enable_unoccupied_fan_shutoff(min_occ_pct = 0.15)
|
3350
|
-
|
3371
|
+
|
3351
3372
|
# Set the system to night cycle
|
3352
3373
|
night_cycle_type = 'CycleOnAny'
|
3353
3374
|
# For VAV with PFP boxes, cycle zone fans only
|
@@ -3355,14 +3376,14 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3355
3376
|
night_cycle_type = 'CycleOnAnyZoneFansOnly'
|
3356
3377
|
end
|
3357
3378
|
self.setNightCycleControlType(night_cycle_type)
|
3358
|
-
|
3379
|
+
|
3359
3380
|
# Check if already using a schedule other than always on
|
3360
3381
|
avail_sch = self.availabilitySchedule
|
3361
3382
|
unless avail_sch == self.model.alwaysOnDiscreteSchedule
|
3362
3383
|
OpenStudio::logFree(OpenStudio::Info, "openstudio.standards.AirLoopHVAC", "For #{self.name}: Availability schedule is already set to #{avail_sch.name}. Will assume this includes unoccupied shut down; no changes will be made.")
|
3363
3384
|
return true
|
3364
3385
|
end
|
3365
|
-
|
3386
|
+
|
3366
3387
|
# Get the airloop occupancy schedule
|
3367
3388
|
loop_occ_sch = self.get_occupancy_schedule(min_occ_pct)
|
3368
3389
|
flh = loop_occ_sch.annual_equivalent_full_load_hrs
|
@@ -3372,24 +3393,24 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3372
3393
|
self.setAvailabilitySchedule(loop_occ_sch)
|
3373
3394
|
|
3374
3395
|
return true
|
3375
|
-
|
3396
|
+
|
3376
3397
|
end
|
3377
|
-
|
3398
|
+
|
3378
3399
|
# Calculate the total floor area of all zones attached
|
3379
3400
|
# to the air loop, in m^2.
|
3380
3401
|
#
|
3381
3402
|
# return [Double] the total floor area of all zones attached
|
3382
3403
|
# to the air loop, in m^2.
|
3383
3404
|
def floor_area_served()
|
3384
|
-
|
3405
|
+
|
3385
3406
|
total_area = 0.0
|
3386
|
-
|
3407
|
+
|
3387
3408
|
self.thermalZones.each do |zone|
|
3388
3409
|
total_area += zone.floorArea
|
3389
3410
|
end
|
3390
3411
|
|
3391
3412
|
return total_area
|
3392
|
-
|
3413
|
+
|
3393
3414
|
end
|
3394
3415
|
|
3395
3416
|
# Calculate the total floor area of all zones attached
|
@@ -3398,9 +3419,9 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3398
3419
|
# return [Double] the total floor area of all zones attached
|
3399
3420
|
# to the air loop, in m^2.
|
3400
3421
|
def floor_area_served_interior_zones()
|
3401
|
-
|
3422
|
+
|
3402
3423
|
total_area = 0.0
|
3403
|
-
|
3424
|
+
|
3404
3425
|
self.thermalZones.each do |zone|
|
3405
3426
|
# Skip zones that have exterior surface area
|
3406
3427
|
next if zone.exteriorSurfaceArea > 0
|
@@ -3408,18 +3429,18 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3408
3429
|
end
|
3409
3430
|
|
3410
3431
|
return total_area
|
3411
|
-
|
3412
|
-
end
|
3413
|
-
|
3432
|
+
|
3433
|
+
end
|
3434
|
+
|
3414
3435
|
# Calculate the total floor area of all zones attached
|
3415
3436
|
# to the air loop that have at least one exterior surface, in m^2.
|
3416
3437
|
#
|
3417
3438
|
# return [Double] the total floor area of all zones attached
|
3418
3439
|
# to the air loop, in m^2.
|
3419
3440
|
def floor_area_served_exterior_zones()
|
3420
|
-
|
3441
|
+
|
3421
3442
|
total_area = 0.0
|
3422
|
-
|
3443
|
+
|
3423
3444
|
self.thermalZones.each do |zone|
|
3424
3445
|
# Skip zones that have no exterior surface area
|
3425
3446
|
next if zone.exteriorSurfaceArea == 0
|
@@ -3427,7 +3448,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3427
3448
|
end
|
3428
3449
|
|
3429
3450
|
return total_area
|
3430
|
-
|
3451
|
+
|
3431
3452
|
end
|
3432
3453
|
|
3433
3454
|
# find design_supply_air_flow_rate
|
@@ -3448,7 +3469,7 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3448
3469
|
return design_supply_air_flow_rate
|
3449
3470
|
|
3450
3471
|
end
|
3451
|
-
|
3472
|
+
|
3452
3473
|
# Determine how much data center
|
3453
3474
|
# area the airloop serves.
|
3454
3475
|
#
|
@@ -3459,9 +3480,9 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3459
3480
|
# of relying on the standards space type name to
|
3460
3481
|
# identify a data center.
|
3461
3482
|
def data_center_area_served()
|
3462
|
-
|
3483
|
+
|
3463
3484
|
dc_area_m2 = 0.0
|
3464
|
-
|
3485
|
+
|
3465
3486
|
self.thermalZones.each do |zone|
|
3466
3487
|
zone.spaces.each do |space|
|
3467
3488
|
# Skip spaces with no space type
|
@@ -3474,9 +3495,9 @@ class OpenStudio::Model::AirLoopHVAC
|
|
3474
3495
|
dc_area_m2 += space.floorArea
|
3475
3496
|
end
|
3476
3497
|
end
|
3477
|
-
|
3498
|
+
|
3478
3499
|
return dc_area_m2
|
3479
|
-
|
3500
|
+
|
3480
3501
|
end
|
3481
|
-
|
3502
|
+
|
3482
3503
|
end
|