openstudio-geb 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/doc_templates/LICENSE.md +7 -21
- data/doc_templates/copyright_erb.txt +2 -32
- data/doc_templates/copyright_js.txt +2 -2
- data/doc_templates/copyright_ruby.txt +3 -33
- data/lib/measures/Add Output Variable/LICENSE.md +7 -21
- data/lib/measures/Add Output Variable/measure.rb +3 -33
- data/lib/measures/Add Output Variable/tests/AddOutputVariable_Test.rb +2 -32
- data/lib/measures/AddElectricVehicleChargingLoad/LICENSE.md +13 -1
- data/lib/measures/AddElectricVehicleChargingLoad/measure.rb +16 -52
- data/lib/measures/AddElectricVehicleChargingLoad/tests/add_electric_vehicle_charging_load_test.rb +2 -32
- data/lib/measures/AdjustThermostatSetpointsByDegreesForPeakHours/LICENSE.md +7 -21
- data/lib/measures/AdjustThermostatSetpointsByDegreesForPeakHours/measure.rb +14 -35
- data/lib/measures/AdjustThermostatSetpointsByDegreesForPeakHours/tests/AdjustThermostatSetpointsByDegreesForPeakHours_Test.rb +2 -32
- data/lib/measures/Enable Demand Controlled Ventilation/LICENSE.md +7 -21
- data/lib/measures/Enable Demand Controlled Ventilation/measure.rb +2 -32
- data/lib/measures/Enable Demand Controlled Ventilation/tests/EnableDemandControlledVentilation_Test.rb +2 -32
- data/lib/measures/GEB Metrics Report/LICENSE.md +13 -1
- data/lib/measures/GEB Metrics Report/measure.rb +4 -39
- data/lib/measures/GEB Metrics Report/resources/os_lib_helper_methods.rb +2 -32
- data/lib/measures/GEB Metrics Report/resources/os_lib_reporting.rb +2 -32
- data/lib/measures/GEB Metrics Report/tests/geb_metrics_report_test.rb +2 -32
- data/lib/measures/add_ceiling_fan/LICENSE.md +13 -0
- data/lib/measures/add_ceiling_fan/README.md +119 -0
- data/lib/measures/add_ceiling_fan/README.md.erb +45 -0
- data/lib/measures/add_ceiling_fan/docs/.gitkeep +0 -0
- data/lib/measures/add_ceiling_fan/measure.rb +565 -0
- data/lib/measures/add_ceiling_fan/measure.xml +226 -0
- data/lib/measures/add_ceiling_fan/tests/CZ06RV2.epw +8768 -0
- data/lib/measures/add_ceiling_fan/tests/MediumOffice-90.1-2010-ASHRAE 169-2013-5A.osm +13669 -0
- data/lib/measures/add_ceiling_fan/tests/SFD_1story_UB_UA_ASHP2_HPWH.osm +13110 -0
- data/lib/measures/add_ceiling_fan/tests/add_ceiling_fan_test.rb +82 -0
- data/lib/measures/add_chilled_water_storage_tank/LICENSE.md +13 -1
- data/lib/measures/add_chilled_water_storage_tank/measure.rb +56 -56
- data/lib/measures/add_chilled_water_storage_tank/tests/add_chilled_water_storage_tank_test.rb +4 -34
- data/lib/measures/add_electrochromic_window/LICENSE.md +13 -1
- data/lib/measures/add_electrochromic_window/measure.rb +2 -32
- data/lib/measures/add_electrochromic_window/tests/add_electrochromic_window_test.rb +2 -32
- data/lib/measures/add_exterior_blinds_and_control/LICENSE.md +13 -1
- data/lib/measures/add_exterior_blinds_and_control/measure.rb +2 -34
- data/lib/measures/add_exterior_blinds_and_control/tests/add_exterior_blinds_and_control_test.rb +2 -34
- data/lib/measures/add_heat_pump_water_heater/LICENSE.md +7 -21
- data/lib/measures/add_heat_pump_water_heater/measure.rb +2 -32
- data/lib/measures/add_heat_pump_water_heater/tests/add_hpwh_test.rb +2 -32
- data/lib/measures/add_interior_blinds_and_control/LICENSE.md +13 -1
- data/lib/measures/add_interior_blinds_and_control/measure.rb +2 -32
- data/lib/measures/add_interior_blinds_and_control/tests/add_exterior_blinds_and_control_test.rb +2 -32
- data/lib/measures/add_natural_ventilation_with_hybrid_control/LICENSE.md +13 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/README.md +133 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/README.md.erb +45 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/docs/.gitkeep +0 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/measure.rb +453 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/measure.xml +241 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/MediumOffice-90.1-2010-ASHRAE 169-2013-5A.osm +13669 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/SmallHotel-2A.osm +42899 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/USA_NY_Buffalo.Niagara.Intl.AP.725280_TMY3.epw +8768 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/USA_TX_Houston-Bush.Intercontinental.AP.722430_TMY3.epw +8768 -0
- data/lib/measures/add_natural_ventilation_with_hybrid_control/tests/add_natural_ventilation_with_hybrid_control_test.rb +95 -0
- data/lib/measures/add_rooftop_pv_simple/LICENSE.md +7 -21
- data/lib/measures/add_rooftop_pv_simple/measure.rb +2 -32
- data/lib/measures/add_rooftop_pv_simple/tests/add_rooftop_pv_test.rb +2 -32
- data/lib/measures/adjust_dhw_setpoint/LICENSE.md +13 -1
- data/lib/measures/adjust_dhw_setpoint/measure.rb +2 -32
- data/lib/measures/adjust_dhw_setpoint/tests/adjust_dhw_setpoint_test.rb +2 -32
- data/lib/measures/average_ventilation_for_peak_hours/LICENSE.md +13 -1
- data/lib/measures/average_ventilation_for_peak_hours/measure.rb +2 -32
- data/lib/measures/average_ventilation_for_peak_hours/tests/average_ventilation_for_peak_hours_test.rb +2 -32
- data/lib/measures/enable_occupancy_driven_lighting/LICENSE.md +13 -1
- data/lib/measures/enable_occupancy_driven_lighting/measure.rb +2 -32
- data/lib/measures/enable_occupancy_driven_lighting/tests/enable_occupancy_driven_lighting_test.rb +2 -32
- data/lib/measures/precooling/LICENSE.md +7 -21
- data/lib/measures/precooling/measure.rb +2 -33
- data/lib/measures/preheating/LICENSE.md +7 -21
- data/lib/measures/preheating/measure.rb +2 -33
- data/lib/measures/reduce_domestic_hot_water_use_for_peak_hours/LICENSE.md +13 -1
- data/lib/measures/reduce_domestic_hot_water_use_for_peak_hours/measure.rb +2 -32
- data/lib/measures/reduce_domestic_hot_water_use_for_peak_hours/tests/reduce_domestic_hot_water_use_for_peak_hours_test.rb +2 -32
- data/lib/measures/reduce_epd_by_percentage_for_peak_hours/LICENSE.md +13 -1
- data/lib/measures/reduce_epd_by_percentage_for_peak_hours/measure.rb +2 -32
- data/lib/measures/reduce_epd_by_percentage_for_peak_hours/tests/reduce_epd_by_percentage_for_peak_hours_copy_test.rb +2 -32
- data/lib/measures/reduce_exterior_lighting_loads/LICENSE.md +13 -0
- data/lib/measures/reduce_exterior_lighting_loads/README.md +79 -0
- data/lib/measures/reduce_exterior_lighting_loads/README.md.erb +45 -0
- data/lib/measures/reduce_exterior_lighting_loads/docs/.gitkeep +0 -0
- data/lib/measures/reduce_exterior_lighting_loads/measure.rb +385 -0
- data/lib/measures/reduce_exterior_lighting_loads/measure.xml +195 -0
- data/lib/measures/reduce_exterior_lighting_loads/tests/CZ06RV2.epw +8768 -0
- data/lib/measures/reduce_exterior_lighting_loads/tests/SFD_1story_UB_UA_ASHP2_HPWH.osm +13110 -0
- data/lib/measures/reduce_exterior_lighting_loads/tests/example_model.osm +8077 -0
- data/lib/measures/reduce_exterior_lighting_loads/tests/reduce_exterior_lighting_loads_test.rb +81 -0
- data/lib/measures/reduce_lpd_by_percentage_for_peak_hours/LICENSE.md +13 -1
- data/lib/measures/reduce_lpd_by_percentage_for_peak_hours/measure.rb +2 -32
- data/lib/measures/reduce_lpd_by_percentage_for_peak_hours/tests/reduce_lpd_by_percentage_for_peak_hours_test.rb +2 -32
- data/lib/openstudio/geb/extension.rb +2 -32
- data/lib/openstudio/geb/logging.rb +2 -32
- data/lib/openstudio/geb/run.rb +2 -32
- data/lib/openstudio/geb/utilities.rb +2 -32
- data/lib/openstudio/geb/version.rb +3 -33
- data/lib/openstudio/geb.rb +2 -32
- data/lib/openstudio-geb.rb +5 -0
- data/openstudio-geb.gemspec +2 -3
- metadata +38 -6
@@ -0,0 +1,453 @@
|
|
1
|
+
# *******************************************************************************
|
2
|
+
# OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
|
3
|
+
# See also https://openstudio.net/license
|
4
|
+
# *******************************************************************************
|
5
|
+
|
6
|
+
# see the URL below for information on how to write OpenStudio measures
|
7
|
+
# http://nrel.github.io/OpenStudio-user-documentation/reference/measure_writing_guide/
|
8
|
+
|
9
|
+
# start the measure
|
10
|
+
class AddNaturalVentilationWithHybridControl < OpenStudio::Measure::ModelMeasure
|
11
|
+
require 'time'
|
12
|
+
require 'json'
|
13
|
+
|
14
|
+
# human readable name
|
15
|
+
def name
|
16
|
+
# Measure name should be the title case of the class name.
|
17
|
+
return 'Add natural ventilation with hybrid control'
|
18
|
+
end
|
19
|
+
|
20
|
+
# human readable description
|
21
|
+
def description
|
22
|
+
return 'This measure adds natural ventilation to all the zones with operable windows, and controls natural ' \
|
23
|
+
'ventilation together with HVAC in a hybrid manner. More specifically, HVAC will be disabled ' \
|
24
|
+
'when windows are open, and HVAC will be available when windows are closed.'
|
25
|
+
end
|
26
|
+
|
27
|
+
# human readable description of modeling approach
|
28
|
+
def modeler_description
|
29
|
+
return 'This measures adds ZoneVentilation:WindandStackOpenArea objects to zones with operable windwos to model ' \
|
30
|
+
'natural ventilation, then adds AvailabilityManager:HybridVentilation to each zone with natural ventilation and ' \
|
31
|
+
'control HVAC and natural ventilation in a hybrid manner. When windows are open, HVAC will be disabled; ' \
|
32
|
+
'when windows are closed, HVAC will be available. HVAC can be an airloop system or a zonal system. '
|
33
|
+
end
|
34
|
+
|
35
|
+
# define the arguments that the user will input
|
36
|
+
def arguments(model)
|
37
|
+
args = OpenStudio::Measure::OSArgumentVector.new
|
38
|
+
|
39
|
+
# make an argument for window's open area fraction
|
40
|
+
open_area_fraction = OpenStudio::Measure::OSArgument::makeDoubleArgument('open_area_fraction', true)
|
41
|
+
open_area_fraction.setDisplayName('Window Open Area Fraction (0-1)')
|
42
|
+
open_area_fraction.setDescription('A typical operable window does not open fully. The actual opening area in a zone is the product of the area of operable windows and the open area fraction schedule. Default 50% open.')
|
43
|
+
open_area_fraction.setDefaultValue(0.5)
|
44
|
+
args << open_area_fraction
|
45
|
+
|
46
|
+
# make an argument for min indoor temp
|
47
|
+
min_indoor_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('min_indoor_temp', false)
|
48
|
+
min_indoor_temp.setDisplayName('Minimum Indoor Temperature (degC)')
|
49
|
+
min_indoor_temp.setDescription('The indoor temperature below which ventilation is shutoff.')
|
50
|
+
min_indoor_temp.setDefaultValue(20)
|
51
|
+
args << min_indoor_temp
|
52
|
+
|
53
|
+
# make an argument for maximum indoor temperature
|
54
|
+
max_indoor_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('max_indoor_temp', false)
|
55
|
+
max_indoor_temp.setDisplayName('Maximum Indoor Temperature (degC)')
|
56
|
+
max_indoor_temp.setDescription('The indoor temperature above which ventilation is shutoff.')
|
57
|
+
max_indoor_temp.setDefaultValue(24)
|
58
|
+
args << max_indoor_temp
|
59
|
+
|
60
|
+
# make an argument for delta temperature
|
61
|
+
delta_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('delta_temp', false)
|
62
|
+
delta_temp.setDisplayName('Maximum Indoor-Outdoor Temperature Difference (degC)')
|
63
|
+
delta_temp.setDescription('This is the temperature difference between the indoor and outdoor air dry-bulb '\
|
64
|
+
'temperatures below which ventilation is shutoff. For example, a delta temperature '\
|
65
|
+
'of 2 degC means ventilation is available if the outside air temperature is at least '\
|
66
|
+
'2 degC cooler than the zone air temperature. Values can be negative.')
|
67
|
+
delta_temp.setDefaultValue(2.0)
|
68
|
+
args << delta_temp
|
69
|
+
|
70
|
+
# make an argument for minimum outdoor temperature
|
71
|
+
min_outdoor_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('min_outdoor_temp', true)
|
72
|
+
min_outdoor_temp.setDisplayName('Minimum Outdoor Temperature (degC)')
|
73
|
+
min_outdoor_temp.setDescription('The outdoor temperature below which ventilation is shut off.')
|
74
|
+
min_outdoor_temp.setDefaultValue(20)
|
75
|
+
args << min_outdoor_temp
|
76
|
+
|
77
|
+
# make an argument for maximum outdoor temperature
|
78
|
+
max_outdoor_temp = OpenStudio::Measure::OSArgument::makeDoubleArgument('max_outdoor_temp', true)
|
79
|
+
max_outdoor_temp.setDisplayName('Maximum Outdoor Temperature (degC)')
|
80
|
+
max_outdoor_temp.setDescription('The outdoor temperature above which ventilation is shut off.')
|
81
|
+
max_outdoor_temp.setDefaultValue(24)
|
82
|
+
args << max_outdoor_temp
|
83
|
+
|
84
|
+
# make an argument for maximum wind speed
|
85
|
+
max_wind_speed = OpenStudio::Measure::OSArgument::makeDoubleArgument('max_wind_speed', false)
|
86
|
+
max_wind_speed.setDisplayName('Maximum Wind Speed (m/s)')
|
87
|
+
max_wind_speed.setDescription('This is the wind speed above which ventilation is shut off. The default values assume windows are closed when wind is above a gentle breeze to avoid blowing around papers in the space.')
|
88
|
+
max_wind_speed.setDefaultValue(40)
|
89
|
+
args << max_wind_speed
|
90
|
+
|
91
|
+
# make an argument for the start time of natural ventilation
|
92
|
+
nv_starttime = OpenStudio::Measure::OSArgument.makeStringArgument('nv_starttime', false)
|
93
|
+
nv_starttime.setDisplayName('Daily Start Time for natural ventilation')
|
94
|
+
nv_starttime.setDescription('Use 24 hour format (HR:MM)')
|
95
|
+
nv_starttime.setDefaultValue('07:00')
|
96
|
+
args << nv_starttime
|
97
|
+
|
98
|
+
# make an argument for the end time of natural ventilation
|
99
|
+
nv_endtime = OpenStudio::Measure::OSArgument.makeStringArgument('nv_endtime', false)
|
100
|
+
nv_endtime.setDisplayName('Daily End Time for natural ventilation')
|
101
|
+
nv_endtime.setDescription('Use 24 hour format (HR:MM)')
|
102
|
+
nv_endtime.setDefaultValue('21:00')
|
103
|
+
args << nv_endtime
|
104
|
+
|
105
|
+
# make an argument for the start date of natural ventilation
|
106
|
+
nv_startdate = OpenStudio::Measure::OSArgument.makeStringArgument('nv_startdate', false)
|
107
|
+
nv_startdate.setDisplayName('Start Date for natural ventilation')
|
108
|
+
nv_startdate.setDescription('In MM-DD format')
|
109
|
+
nv_startdate.setDefaultValue('03-01')
|
110
|
+
args << nv_startdate
|
111
|
+
|
112
|
+
# make an argument for the end date of natural ventilation
|
113
|
+
nv_enddate = OpenStudio::Measure::OSArgument.makeStringArgument('nv_enddate', false)
|
114
|
+
nv_enddate.setDisplayName('End Date for natural ventilation')
|
115
|
+
nv_enddate.setDescription('In MM-DD format')
|
116
|
+
nv_enddate.setDefaultValue('10-31')
|
117
|
+
args << nv_enddate
|
118
|
+
|
119
|
+
# Make boolean arguments for natural ventilation schedule on weekends
|
120
|
+
wknds = OpenStudio::Measure::OSArgument.makeBoolArgument('wknds', false)
|
121
|
+
wknds.setDisplayName('Allow Natural Ventilation on Weekends')
|
122
|
+
wknds.setDefaultValue(true)
|
123
|
+
args << wknds
|
124
|
+
|
125
|
+
return args
|
126
|
+
end
|
127
|
+
|
128
|
+
# define what happens when the measure is run
|
129
|
+
def run(model, runner, user_arguments)
|
130
|
+
super(model, runner, user_arguments)
|
131
|
+
|
132
|
+
# use the built-in error checking
|
133
|
+
if !runner.validateUserArguments(arguments(model), user_arguments)
|
134
|
+
return false
|
135
|
+
end
|
136
|
+
|
137
|
+
# assign the user inputs to variables
|
138
|
+
open_area_fraction = runner.getDoubleArgumentValue('open_area_fraction', user_arguments)
|
139
|
+
min_indoor_temp = runner.getDoubleArgumentValue('min_indoor_temp', user_arguments)
|
140
|
+
max_indoor_temp = runner.getDoubleArgumentValue('max_indoor_temp', user_arguments)
|
141
|
+
delta_temp = runner.getDoubleArgumentValue('delta_temp', user_arguments)
|
142
|
+
min_outdoor_temp = runner.getDoubleArgumentValue('min_outdoor_temp', user_arguments)
|
143
|
+
max_outdoor_temp = runner.getDoubleArgumentValue('max_outdoor_temp', user_arguments)
|
144
|
+
max_wind_speed = runner.getDoubleArgumentValue('max_wind_speed', user_arguments)
|
145
|
+
nv_starttime = runner.getStringArgumentValue('nv_starttime', user_arguments)
|
146
|
+
nv_endtime = runner.getStringArgumentValue('nv_endtime', user_arguments)
|
147
|
+
nv_startdate = runner.getStringArgumentValue('nv_startdate', user_arguments)
|
148
|
+
nv_enddate = runner.getStringArgumentValue('nv_enddate', user_arguments)
|
149
|
+
wknds = runner.getBoolArgumentValue('wknds', user_arguments)
|
150
|
+
|
151
|
+
# check open_area_fraction input
|
152
|
+
if open_area_fraction < 0
|
153
|
+
runner.registerError('Window open area fraction should be between 0 and 1. Please double check your input.')
|
154
|
+
return false
|
155
|
+
elsif open_area_fraction > 1
|
156
|
+
runner.registerError("open_area_fraction #{open_area_fraction} is a larger than 1. Window open area fraction should be between 0 and 1. Please double check your input.")
|
157
|
+
return false
|
158
|
+
elsif open_area_fraction == 0
|
159
|
+
runner.registerWarning("open_area_fraction #{open_area_fraction} is set as 0, meaning the windows will not be opened at all. Please double check your input.")
|
160
|
+
end
|
161
|
+
|
162
|
+
# check temp limit inputs
|
163
|
+
if min_indoor_temp >= max_indoor_temp
|
164
|
+
runner.registerError('Minimum indoor temperature should be lower than maximum outdoor temperature. Please double check your input.')
|
165
|
+
return false
|
166
|
+
end
|
167
|
+
|
168
|
+
if min_outdoor_temp >= max_outdoor_temp
|
169
|
+
runner.registerError('Minimum outdoor temperature should be lower than maximum outdoor temperature. Please double check your input.')
|
170
|
+
return false
|
171
|
+
end
|
172
|
+
|
173
|
+
# check max wind speed input
|
174
|
+
if max_wind_speed <= 0
|
175
|
+
runner.registerError('Maximum wind speed should be positive. Please double check your input.')
|
176
|
+
return false
|
177
|
+
end
|
178
|
+
|
179
|
+
# check delta temperature input
|
180
|
+
if delta_temp < 0
|
181
|
+
runner.registerWarning("delta_temp #{delta_temp} is negative. Normally delta temperature should be positive "\
|
182
|
+
"or at least 0 to enable free cooling and avoid introducing extra cooling load. "\
|
183
|
+
"Please double check your input.")
|
184
|
+
end
|
185
|
+
|
186
|
+
# check time format
|
187
|
+
begin
|
188
|
+
nv_starttime = Time.strptime(nv_starttime, '%H:%M')
|
189
|
+
nv_endtime = Time.strptime(nv_endtime, '%H:%M')
|
190
|
+
rescue ArgumentError
|
191
|
+
runner.registerError('Natural ventilation start and end time are required, and should be in format of %H:%M, e.g., 16:00.')
|
192
|
+
return false
|
193
|
+
end
|
194
|
+
if nv_starttime > nv_endtime
|
195
|
+
runner.registerInfo('Natural ventilation start time is later than end time, referring to overnight ' \
|
196
|
+
'natural ventilation. Make sure this is expected.')
|
197
|
+
end
|
198
|
+
|
199
|
+
# check date format
|
200
|
+
md = /(\d\d)-(\d\d)/.match(nv_startdate)
|
201
|
+
if md
|
202
|
+
nv_start_month = md[1].to_i
|
203
|
+
nv_start_day = md[2].to_i
|
204
|
+
else
|
205
|
+
runner.registerError('Start date must be in MM-DD format.')
|
206
|
+
return false
|
207
|
+
end
|
208
|
+
|
209
|
+
md = /(\d\d)-(\d\d)/.match(nv_enddate)
|
210
|
+
if md
|
211
|
+
nv_end_month = md[1].to_i
|
212
|
+
nv_end_day = md[2].to_i
|
213
|
+
else
|
214
|
+
runner.registerError('End date must be in MM-DD format.')
|
215
|
+
return false
|
216
|
+
end
|
217
|
+
|
218
|
+
nv_startdate_os = model.getYearDescription.makeDate(nv_start_month, nv_start_day)
|
219
|
+
nv_enddate_os = model.getYearDescription.makeDate(nv_end_month, nv_end_day)
|
220
|
+
|
221
|
+
# if_overnight: 1 or 0; wknds (if applicable to weekends): 1 or 0
|
222
|
+
# by default schedule on value is 1, sometimes need specify, e.g., natural ventilation window open area fraction
|
223
|
+
def create_sch(model, sch_name, start_time, end_time, start_date, end_date, wknds, sch_on_value=1, sch_off_value=0)
|
224
|
+
day_start_time = Time.strptime("00:00", '%H:%M')
|
225
|
+
# create discharging schedule
|
226
|
+
new_sch_ruleset = OpenStudio::Model::ScheduleRuleset.new(model)
|
227
|
+
new_sch_ruleset.setName(sch_name)
|
228
|
+
new_sch_ruleset.defaultDaySchedule.setName(sch_name + ' default')
|
229
|
+
if start_time > end_time
|
230
|
+
if_overnight = sch_on_value # assigned as schedule on value
|
231
|
+
else
|
232
|
+
if_overnight = sch_off_value # assigned as schedule off value
|
233
|
+
end
|
234
|
+
|
235
|
+
for min in 1..24*60
|
236
|
+
if ((end_time - day_start_time)/60).to_i == min
|
237
|
+
time = OpenStudio::Time.new(0, 0, min)
|
238
|
+
new_sch_ruleset.defaultDaySchedule.addValue(time, sch_on_value)
|
239
|
+
elsif ((start_time - day_start_time)/60).to_i == min
|
240
|
+
time = OpenStudio::Time.new(0, 0, min)
|
241
|
+
new_sch_ruleset.defaultDaySchedule.addValue(time, sch_off_value)
|
242
|
+
elsif min == 24*60
|
243
|
+
time = OpenStudio::Time.new(0, 0, min)
|
244
|
+
new_sch_ruleset.defaultDaySchedule.addValue(time, if_overnight)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
start_month = start_date.monthOfYear.value
|
249
|
+
start_day = start_date.dayOfMonth
|
250
|
+
end_month = end_date.monthOfYear.value
|
251
|
+
end_day = end_date.dayOfMonth
|
252
|
+
ts_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_sch_ruleset.defaultDaySchedule)
|
253
|
+
ts_rule.setName("#{new_sch_ruleset.name} #{start_month}/#{start_day}-#{end_month}/#{end_day} Rule")
|
254
|
+
ts_rule.setStartDate(start_date)
|
255
|
+
ts_rule.setEndDate(end_date)
|
256
|
+
ts_rule.setApplyWeekdays(true)
|
257
|
+
if wknds
|
258
|
+
ts_rule.setApplyWeekends(true)
|
259
|
+
else
|
260
|
+
ts_rule.setApplyWeekends(false)
|
261
|
+
end
|
262
|
+
|
263
|
+
unless start_month == 1 && start_day == 1
|
264
|
+
new_rule_day = OpenStudio::Model::ScheduleDay.new(model)
|
265
|
+
new_rule_day.addValue(OpenStudio::Time.new(0,24), 0)
|
266
|
+
new_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_rule_day)
|
267
|
+
new_rule.setName("#{new_sch_ruleset.name} 01/01-#{start_month}/#{start_day} Rule")
|
268
|
+
new_rule.setStartDate(model.getYearDescription.makeDate(1, 1))
|
269
|
+
new_rule.setEndDate(model.getYearDescription.makeDate(start_month, start_day))
|
270
|
+
new_rule.setApplyAllDays(true)
|
271
|
+
end
|
272
|
+
|
273
|
+
unless end_month == 12 && end_day == 31
|
274
|
+
new_rule_day = OpenStudio::Model::ScheduleDay.new(model)
|
275
|
+
new_rule_day.addValue(OpenStudio::Time.new(0,24), 0)
|
276
|
+
new_rule = OpenStudio::Model::ScheduleRule.new(new_sch_ruleset, new_rule_day)
|
277
|
+
new_rule.setName("#{new_sch_ruleset.name} #{end_month}/#{end_day}-12/31 Rule")
|
278
|
+
new_rule.setStartDate(model.getYearDescription.makeDate(end_month, end_day))
|
279
|
+
new_rule.setEndDate(model.getYearDescription.makeDate(12, 31))
|
280
|
+
new_rule.setApplyAllDays(true)
|
281
|
+
end
|
282
|
+
|
283
|
+
return new_sch_ruleset
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
# report initial condition of model
|
288
|
+
runner.registerInitialCondition("The building started with #{model.getZoneVentilationWindandStackOpenAreas.size} "\
|
289
|
+
"ZoneVentilation:WindandStackOpenArea objects and #{model.getZoneVentilationDesignFlowRates.size}.")
|
290
|
+
|
291
|
+
|
292
|
+
|
293
|
+
#################### STEP 1: add zone ventilation objects ################
|
294
|
+
# TODO: update
|
295
|
+
# for spaces with existing NV object (1. ZoneVentilation:DesignFlowRate with 'Natural' as the ventilation type; or 2. ZoneVentilation:WindandStackOpenArea), skip
|
296
|
+
|
297
|
+
nv_zone_list = {} # if NV object is added, add the key-value (zone name-NV object) to this hash
|
298
|
+
window_open_area_sch = create_sch(model, "window open area fraction sch", nv_starttime, nv_endtime, nv_startdate_os, nv_enddate_os, wknds, open_area_fraction, 0)
|
299
|
+
model.getSpaces.each do |space|
|
300
|
+
next if space.thermalZone.empty?
|
301
|
+
thermal_zone = space.thermalZone.get
|
302
|
+
|
303
|
+
# check if this space has existing NV object, if yes then skip
|
304
|
+
has_nv = false
|
305
|
+
nv_objs = []
|
306
|
+
thermal_zone.equipment.each do |zone_equipment|
|
307
|
+
if zone_equipment.to_ZoneVentilationDesignFlowRate.is_initialized
|
308
|
+
if zone_equipment.to_ZoneVentilationDesignFlowRate.get.ventilationType.lowercase == "natural"
|
309
|
+
has_nv = true
|
310
|
+
nv_objs << zone_equipment.to_ZoneVentilationDesignFlowRate.get
|
311
|
+
end
|
312
|
+
elsif zone_equipment.to_ZoneVentilationWindandStackOpenArea.is_initialized
|
313
|
+
has_nv = true
|
314
|
+
nv_objs << zone_equipment.to_ZoneVentilationWindandStackOpenArea.get
|
315
|
+
end
|
316
|
+
end
|
317
|
+
if has_nv
|
318
|
+
nv_zone_list[thermal_zone.name.to_s] = nv_objs
|
319
|
+
next
|
320
|
+
end
|
321
|
+
|
322
|
+
# add NV objects
|
323
|
+
space.surfaces.sort.each do |surface|
|
324
|
+
surface.subSurfaces.sort.each do |subsurface|
|
325
|
+
if (subsurface.subSurfaceType == 'OperableWindow' || subsurface.subSurfaceType == 'FixedWindow') && subsurface.outsideBoundaryCondition == 'Outdoors'
|
326
|
+
window_azimuth_deg = OpenStudio.convert(subsurface.azimuth, 'rad', 'deg').get
|
327
|
+
window_area = subsurface.netArea
|
328
|
+
|
329
|
+
##### Add the "ZoneVentilation:WindandStackOpenArea" for NV.
|
330
|
+
zn_vent_wind_and_stack = OpenStudio::Model::ZoneVentilationWindandStackOpenArea.new(model)
|
331
|
+
zn_vent_wind_and_stack.setName(subsurface.name.to_s + " NV object")
|
332
|
+
zn_vent_wind_and_stack.setOpeningArea(window_area)
|
333
|
+
zn_vent_wind_and_stack.setOpeningAreaFractionSchedule(window_open_area_sch)
|
334
|
+
zn_vent_wind_and_stack.setEffectiveAngle(window_azimuth_deg)
|
335
|
+
zn_vent_wind_and_stack.setMinimumIndoorTemperature(min_indoor_temp)
|
336
|
+
zn_vent_wind_and_stack.setMaximumIndoorTemperature(max_indoor_temp)
|
337
|
+
zn_vent_wind_and_stack.setMinimumOutdoorTemperature(min_outdoor_temp)
|
338
|
+
zn_vent_wind_and_stack.setMaximumOutdoorTemperature(max_outdoor_temp)
|
339
|
+
zn_vent_wind_and_stack.setDeltaTemperature(delta_temp)
|
340
|
+
zn_vent_wind_and_stack.setMaximumWindSpeed(max_wind_speed)
|
341
|
+
zn_vent_wind_and_stack.addToThermalZone(thermal_zone)
|
342
|
+
nv_zone_list[thermal_zone.name.to_s] = [] unless nv_zone_list.key?(thermal_zone.name.to_s)
|
343
|
+
nv_zone_list[thermal_zone.name.to_s] << zn_vent_wind_and_stack
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
#################### STEP 2: add hybrid ventilation control objects ################
|
350
|
+
|
351
|
+
# TODO: Simple Airflow Control Type Schedule Name set as 1 denote group control
|
352
|
+
# if a zone has more than one windows, group control allows them to be operated simultaneously
|
353
|
+
# if an airloopHVAC controls more than one zone, only one AvailabilityManagerHybridVentilation is allowed for an airloop, group control will
|
354
|
+
# decides the zones controlled by this airloop based on the selected ZoneVentilation object input
|
355
|
+
|
356
|
+
vent_control_sch = create_sch(model, "ventilation control sch", nv_starttime, nv_endtime, nv_startdate_os, nv_enddate_os, wknds, 1, 0)
|
357
|
+
simple_airflow_control_type_sch = OpenStudio::Model::ScheduleConstant.new(model)
|
358
|
+
simple_airflow_control_type_sch.setName("simple airflow control type sch - group control")
|
359
|
+
simple_airflow_control_type_sch.setValue(1)
|
360
|
+
|
361
|
+
# part1: loop through all the airloopHVAC and add hybrid ventilation control
|
362
|
+
model.getAirLoopHVACs.sort.each do |air_loop|
|
363
|
+
max_zone_area = 0
|
364
|
+
nv_zone_with_max_area = nil
|
365
|
+
air_loop.thermalZones.each do |thermal_zone|
|
366
|
+
if max_zone_area < thermal_zone.floorArea && nv_zone_list.key?(thermal_zone.name.to_s)
|
367
|
+
max_zone_area = thermal_zone.floorArea
|
368
|
+
nv_zone_with_max_area = thermal_zone.name.to_s
|
369
|
+
end
|
370
|
+
end
|
371
|
+
next if nv_zone_with_max_area.nil? # if there is no NV zone in this airloop, skip
|
372
|
+
has_hybrid_avail_manager = false
|
373
|
+
air_loop.availabilityManagers.sort.each do |avail_mgr|
|
374
|
+
next if avail_mgr.to_AvailabilityManagerHybridVentilation.empty?
|
375
|
+
if avail_mgr.to_AvailabilityManagerHybridVentilation.is_initialized
|
376
|
+
has_hybrid_avail_manager = true
|
377
|
+
avail_mgr_hybr_vent = avail_mgr.to_AvailabilityManagerHybridVentilation.get
|
378
|
+
avail_mgr_hybr_vent.setVentilationControlModeSchedule(vent_control_sch)
|
379
|
+
avail_mgr_hybr_vent.setControlledZone(model.getThermalZoneByName(nv_zone_with_max_area).get)
|
380
|
+
avail_mgr_hybr_vent.setMinimumOutdoorTemperature(min_outdoor_temp)
|
381
|
+
avail_mgr_hybr_vent.setMaximumOutdoorTemperature(max_outdoor_temp)
|
382
|
+
avail_mgr_hybr_vent.setMaximumWindSpeed(max_wind_speed)
|
383
|
+
avail_mgr_hybr_vent.setSimpleAirflowControlTypeSchedule(simple_airflow_control_type_sch)
|
384
|
+
|
385
|
+
# if zone has more than one window, set control based on the biggest window
|
386
|
+
nv_objs = nv_zone_list[nv_zone_with_max_area]
|
387
|
+
max_open_area = 0
|
388
|
+
nv_objs.each do |nv_obj|
|
389
|
+
max_open_area = nv_obj.openingArea if max_open_area < nv_obj.openingArea
|
390
|
+
avail_mgr_hybr_vent.setZoneVentilationObject(nv_obj)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
unless has_hybrid_avail_manager
|
396
|
+
avail_mgr_hybr_vent = OpenStudio::Model::AvailabilityManagerHybridVentilation.new(model)
|
397
|
+
avail_mgr_hybr_vent.setName(air_loop.name.to_s + " HybridVentilation AvailabilityManager")
|
398
|
+
avail_mgr_hybr_vent.setVentilationControlModeSchedule(vent_control_sch)
|
399
|
+
avail_mgr_hybr_vent.setControlledZone(model.getThermalZoneByName(nv_zone_with_max_area).get)
|
400
|
+
avail_mgr_hybr_vent.setMinimumOutdoorTemperature(min_outdoor_temp)
|
401
|
+
avail_mgr_hybr_vent.setMaximumOutdoorTemperature(max_outdoor_temp)
|
402
|
+
avail_mgr_hybr_vent.setMaximumWindSpeed(max_wind_speed)
|
403
|
+
avail_mgr_hybr_vent.setSimpleAirflowControlTypeSchedule(simple_airflow_control_type_sch)
|
404
|
+
|
405
|
+
# if controlled zone has more than one window, set control based on the biggest window
|
406
|
+
nv_objs = nv_zone_list[nv_zone_with_max_area]
|
407
|
+
max_open_area = 0
|
408
|
+
nv_objs.each do |nv_obj|
|
409
|
+
max_open_area = nv_obj.openingArea if max_open_area < nv_obj.openingArea
|
410
|
+
avail_mgr_hybr_vent.setZoneVentilationObject(nv_obj)
|
411
|
+
end
|
412
|
+
air_loop.addAvailabilityManager(avail_mgr_hybr_vent)
|
413
|
+
end
|
414
|
+
|
415
|
+
# remove thermal zones in this airloop from the nv_zone_list hash
|
416
|
+
air_loop.thermalZones.each do |thermal_zone|
|
417
|
+
if nv_zone_list.key?(thermal_zone.name.to_s)
|
418
|
+
nv_zone_list.delete(thermal_zone.name.to_s)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
# part2: loop through all spaces, add hybrid vent control to zones that are not connected to airloopHVAC but uses zonal equipment
|
424
|
+
nv_zone_list.each do |zone_name, nv_objs|
|
425
|
+
avail_mgr_hybr_vent = OpenStudio::Model::AvailabilityManagerHybridVentilation.new(model)
|
426
|
+
avail_mgr_hybr_vent.setName(zone_name + " HybridVentilation AvailabilityManager")
|
427
|
+
avail_mgr_hybr_vent.setVentilationControlModeSchedule(vent_control_sch)
|
428
|
+
avail_mgr_hybr_vent.setControlledZone(model.getThermalZoneByName(zone_name).get)
|
429
|
+
avail_mgr_hybr_vent.setMinimumOutdoorTemperature(min_outdoor_temp)
|
430
|
+
avail_mgr_hybr_vent.setMaximumOutdoorTemperature(max_outdoor_temp)
|
431
|
+
avail_mgr_hybr_vent.setMaximumWindSpeed(max_wind_speed)
|
432
|
+
avail_mgr_hybr_vent.setSimpleAirflowControlTypeSchedule(simple_airflow_control_type_sch)
|
433
|
+
|
434
|
+
# if controlled zone has more than one window, set control based on the biggest window
|
435
|
+
max_open_area = 0
|
436
|
+
nv_objs.each do |nv_obj|
|
437
|
+
max_open_area = nv_obj.openingArea if max_open_area < nv_obj.openingArea
|
438
|
+
avail_mgr_hybr_vent.setZoneVentilationObject(nv_obj)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
# echo added AvailabilityManagerHybridVentilation object number to the user
|
443
|
+
runner.registerInfo("A total of #{model.getAvailabilityManagerHybridVentilations.size} AvailabilityManagerHybridVentilations were added.")
|
444
|
+
|
445
|
+
# report final condition of model
|
446
|
+
runner.registerFinalCondition("The building finished with #{model.getAvailabilityManagerHybridVentilations.size} AvailabilityManagerHybridVentilations.")
|
447
|
+
|
448
|
+
return true
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
# register the measure to be used by the application
|
453
|
+
AddNaturalVentilationWithHybridControl.new.registerWithApplication
|