openstudio-load-flexibility-measures 0.2.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +36 -36
- data/.rubocop.yml +6 -9
- data/CHANGELOG.html +75 -75
- data/CHANGELOG.md +59 -37
- data/Gemfile +31 -7
- data/Jenkinsfile +11 -0
- data/README.md +53 -42
- data/Rakefile +15 -15
- data/doc_templates/LICENSE.md +26 -26
- data/doc_templates/README.md.erb +41 -41
- data/doc_templates/copyright_erb.txt +35 -35
- data/doc_templates/copyright_js.txt +3 -3
- data/doc_templates/copyright_ruby.txt +33 -33
- data/lib/measures/add_central_ice_storage/LICENSE.md +26 -26
- data/lib/measures/add_central_ice_storage/README.md +264 -264
- data/lib/measures/add_central_ice_storage/README.md.erb +41 -41
- data/lib/measures/add_central_ice_storage/measure.rb +1325 -1324
- data/lib/measures/add_central_ice_storage/measure.xml +503 -503
- data/lib/measures/add_central_ice_storage/resources/OsLib_Schedules.rb +171 -173
- data/lib/measures/add_central_ice_storage/tests/add_central_ice_storage_test.rb +203 -203
- data/lib/measures/add_central_ice_storage/tests/ice_test_model.osm +21523 -21523
- data/lib/measures/add_hpwh/LICENSE.md +26 -26
- data/lib/measures/add_hpwh/README.md +186 -186
- data/lib/measures/add_hpwh/README.md.erb +41 -41
- data/lib/measures/add_hpwh/docs/Flexible Domestic Hot Water Implementation Guide.pdf +0 -0
- data/lib/measures/add_hpwh/measure.rb +663 -647
- data/lib/measures/add_hpwh/measure.xml +402 -397
- data/lib/measures/add_hpwh/tests/SmallHotel-2A.osm +42893 -42893
- data/lib/measures/add_hpwh/tests/{add_hphw_test.rb → add_hpwh_test.rb} +137 -98
- data/lib/measures/add_packaged_ice_storage/LICENSE.md +26 -26
- data/lib/measures/add_packaged_ice_storage/README.html +185 -185
- data/lib/measures/add_packaged_ice_storage/README.md +189 -189
- data/lib/measures/add_packaged_ice_storage/measure.rb +694 -691
- data/lib/measures/add_packaged_ice_storage/measure.xml +245 -245
- data/lib/measures/add_packaged_ice_storage/resources/TESCurves.idf +1059 -1059
- data/lib/measures/add_packaged_ice_storage/tests/MeasureTest.osm +9507 -9507
- data/lib/measures/add_packaged_ice_storage/tests/add_packaged_ice_storage_test.rb +96 -96
- data/lib/openstudio/load_flexibility_measures/version.rb +40 -40
- data/lib/openstudio/load_flexibility_measures.rb +50 -50
- data/openstudio-load-flexibility-measures.gemspec +33 -32
- metadata +27 -27
@@ -1,691 +1,694 @@
|
|
1
|
-
# *******************************************************************************
|
2
|
-
# OpenStudio(R), Copyright (c) 2008-
|
3
|
-
# All rights reserved.
|
4
|
-
# Redistribution and use in source and binary forms, with or without
|
5
|
-
# modification, are permitted provided that the following conditions are met:
|
6
|
-
#
|
7
|
-
# (1) Redistributions of source code must retain the above copyright notice,
|
8
|
-
# this list of conditions and the following disclaimer.
|
9
|
-
#
|
10
|
-
# (2) Redistributions in binary form must reproduce the above copyright notice,
|
11
|
-
# this list of conditions and the following disclaimer in the documentation
|
12
|
-
# and/or other materials provided with the distribution.
|
13
|
-
#
|
14
|
-
# (3) Neither the name of the copyright holder nor the names of any contributors
|
15
|
-
# may be used to endorse or promote products derived from this software without
|
16
|
-
# specific prior written permission from the respective party.
|
17
|
-
#
|
18
|
-
# (4) Other than as required in clauses (1) and (2), distributions in any form
|
19
|
-
# of modifications or other derivative works may not use the "OpenStudio"
|
20
|
-
# trademark, "OS", "os", or any other confusingly similar designation without
|
21
|
-
# specific prior written permission from Alliance for Sustainable Energy, LLC.
|
22
|
-
#
|
23
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
|
24
|
-
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
25
|
-
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
26
|
-
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
|
27
|
-
# UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
|
28
|
-
# THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
29
|
-
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
30
|
-
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
31
|
-
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
32
|
-
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
33
|
-
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
34
|
-
# *******************************************************************************
|
35
|
-
|
36
|
-
# Revised - KH July 2019
|
37
|
-
# Measure Renamed, License Updated, and Code Cleaned - KH June 2020
|
38
|
-
|
39
|
-
# start the measure
|
40
|
-
class AddPackagedIceStorage < OpenStudio::Measure::EnergyPlusMeasure
|
41
|
-
# human readable name
|
42
|
-
def name
|
43
|
-
'Add Packaged Ice Storage'
|
44
|
-
end
|
45
|
-
|
46
|
-
# human readable description
|
47
|
-
def description
|
48
|
-
'This measure removes the cooling coils in the model and replaces them with packaged air conditioning units with integrated ice storage.'
|
49
|
-
end
|
50
|
-
|
51
|
-
# human readable description of modeling approach
|
52
|
-
def modeler_description
|
53
|
-
"This measure applies to packaged single zone air conditioning systems or packaged variable air volume systems that were originally modeled with CoilSystem:Cooling:DX or AirLoopHVAC:UnitarySystem container objects. It adds a Coil:Cooling:DX:SingleSpeed:ThermalStorage coil object to each user-selected thermal zone and deletes the existing cooling coil.
|
54
|
-
|
55
|
-
Users inputs are accepted for cooling coil size, ice storage size, system control method, modes of operation, and operating schedule.
|
56
|
-
|
57
|
-
The measure requires schedule objects and performance curves from an included resource file TESCurves.idf. Output variables of typical interest are included as well."
|
58
|
-
end
|
59
|
-
|
60
|
-
# define the arguments that the user will input
|
61
|
-
def arguments(workspace)
|
62
|
-
args = OpenStudio::Measure::OSArgumentVector.new
|
63
|
-
|
64
|
-
# # Add a delimiter for clarify
|
65
|
-
# delimiter = OpenStudio::Measure::OSArgument.makeStringArgument('delimiter', false)
|
66
|
-
# delimiter.setDisplayName('Select Coils to Replace:')
|
67
|
-
# delimiter.setDefaultValue('-----------------------------------------------------------------')
|
68
|
-
# args << delimiter
|
69
|
-
|
70
|
-
# get existing dx coils for user selection
|
71
|
-
coils = workspace.getObjectsByType('Coil:Cooling:DX:SingleSpeed'.to_IddObjectType)
|
72
|
-
coils += workspace.getObjectsByType('Coil:Cooling:DX:TwoSpeed'.to_IddObjectType)
|
73
|
-
coilhash = {}
|
74
|
-
c = coils.sort { |a, b| a.getString(0).get <=> b.getString(0).get }
|
75
|
-
c.each do |coil|
|
76
|
-
c_name = coil.name.to_s
|
77
|
-
coilhash[c_name] = true
|
78
|
-
end
|
79
|
-
|
80
|
-
# make boolean argument for selecting cooling coils to replace
|
81
|
-
coilhash.each do |k, v|
|
82
|
-
coil_selection = OpenStudio::Measure::OSArgument.makeBoolArgument(k, true)
|
83
|
-
coil_selection.setDisplayName(k)
|
84
|
-
coil_selection.setDefaultValue(v)
|
85
|
-
coil_selection.setDescription('Replace this coil?')
|
86
|
-
args << coil_selection
|
87
|
-
end
|
88
|
-
|
89
|
-
ice_cap = OpenStudio::Measure::OSArgument.makeStringArgument('ice_cap', true)
|
90
|
-
ice_cap.setDisplayName('Input the ice storage capacity [ton-hours]')
|
91
|
-
ice_cap.setDescription('To specify by coil, in alphabetical order, enter values for each separated by comma.')
|
92
|
-
ice_cap.setDefaultValue('AutoSize')
|
93
|
-
args << ice_cap
|
94
|
-
|
95
|
-
size_mult = OpenStudio::Measure::OSArgument.makeStringArgument('size_mult', false)
|
96
|
-
size_mult.setDisplayName('Enter a sizing multiplier to manually adjust the autosize results for ice tank capacities')
|
97
|
-
size_mult.setDefaultValue('1.0')
|
98
|
-
args << size_mult
|
99
|
-
|
100
|
-
# make argument for control method
|
101
|
-
ctl = OpenStudio::Measure::OSArgument.makeChoiceArgument('ctl', ['ScheduledModes', 'EMSControlled'], true)
|
102
|
-
ctl.setDisplayName('Select ice storage control method')
|
103
|
-
ctl.setDefaultValue('EMSControlled')
|
104
|
-
args << ctl
|
105
|
-
|
106
|
-
# obtain default schedule names in TESCurves.idf. This allows users to manually add schedules to the idf and be able to access them in OS or PAT
|
107
|
-
source_idf = OpenStudio::IdfFile.load(OpenStudio::Path.new(File.dirname(__FILE__)
|
108
|
-
schedules = source_idf.getObjectsByType('Schedule:Compact'.to_IddObjectType)
|
109
|
-
schedule_names = OpenStudio::StringVector.new
|
110
|
-
|
111
|
-
schedules.each do |sch|
|
112
|
-
schedule_names << sch.name.to_s
|
113
|
-
end
|
114
|
-
|
115
|
-
# make argument for TES operating mode schedule
|
116
|
-
sched = OpenStudio::Measure::OSArgument.makeChoiceArgument('sched', schedule_names, true)
|
117
|
-
sched.setDisplayName('Select the operating mode schedule for the new TES coils')
|
118
|
-
sched.setDescription('Use the fields below to set a simple daily ice charge/discharge schedule. Or, select from pre-defined options.')
|
119
|
-
sched.setDefaultValue('Simple User Sched')
|
120
|
-
args << sched
|
121
|
-
|
122
|
-
# make arguement for weekend TES operation
|
123
|
-
wknd = OpenStudio::Measure::OSArgument.makeBoolArgument('wknd', false)
|
124
|
-
wknd.setDisplayName('Run TES on the weekends')
|
125
|
-
wknd.setDescription('Select if building is occupied on weekends')
|
126
|
-
wknd.setDefaultValue(true)
|
127
|
-
args << wknd
|
128
|
-
|
129
|
-
# make arguments for operating season
|
130
|
-
season = OpenStudio::Measure::OSArgument.makeStringArgument('season', false)
|
131
|
-
season.setDisplayName('Select season during which the ice cooling may be used')
|
132
|
-
season.setDescription('Use MM/DD-MM/DD format')
|
133
|
-
season.setDefaultValue('01/01-12/31')
|
134
|
-
args << season
|
135
|
-
|
136
|
-
# make arguments for simple charging period
|
137
|
-
charge_start = OpenStudio::Measure::OSArgument.makeStringArgument('charge_start', false)
|
138
|
-
charge_start.setDisplayName('Input start time for ice charge (hr:min)')
|
139
|
-
charge_start.setDescription('Use 24 hour format')
|
140
|
-
charge_start.setDefaultValue('22:00')
|
141
|
-
args << charge_start
|
142
|
-
|
143
|
-
charge_end = OpenStudio::Measure::OSArgument.makeStringArgument('charge_end', false)
|
144
|
-
charge_end.setDisplayName('Input end time for ice charge (hr:min)')
|
145
|
-
charge_end.setDescription('Use 24 hour format')
|
146
|
-
charge_end.setDefaultValue('07:00')
|
147
|
-
args << charge_end
|
148
|
-
|
149
|
-
# make arguments for simple discharging period
|
150
|
-
discharge_start = OpenStudio::Measure::OSArgument.makeStringArgument('discharge_start', false)
|
151
|
-
discharge_start.setDisplayName('Input start time for ice discharge (hr:min)')
|
152
|
-
discharge_start.setDescription("Use 24hour format.\nIf 'AutoSize' is selected for ice capacity, these inputs set an ice capacity sizing factor. Otherwise, these only affect discharging schedule.")
|
153
|
-
discharge_start.setDefaultValue('12:00')
|
154
|
-
args << discharge_start
|
155
|
-
|
156
|
-
discharge_end = OpenStudio::Measure::OSArgument.makeStringArgument('discharge_end', false)
|
157
|
-
discharge_end.setDisplayName('Input target end time for ice discharge (hr:min)')
|
158
|
-
discharge_end.setDescription('Use 24 hour format')
|
159
|
-
discharge_end.setDefaultValue('18:00')
|
160
|
-
args << discharge_end
|
161
|
-
|
162
|
-
args
|
163
|
-
# end the arguments method
|
164
|
-
end
|
165
|
-
|
166
|
-
# define what happens when the measure is run
|
167
|
-
def run(workspace, runner, user_arguments)
|
168
|
-
super(workspace, runner, user_arguments)
|
169
|
-
|
170
|
-
# use the built-in error checking
|
171
|
-
unless runner.validateUserArguments(arguments(workspace), user_arguments)
|
172
|
-
return false
|
173
|
-
end
|
174
|
-
|
175
|
-
# load required TESCurves.idf. This contains all the TES performance curves and default schedules
|
176
|
-
source_idf = OpenStudio::IdfFile.load(OpenStudio::Path.new(File.dirname(__FILE__)
|
177
|
-
|
178
|
-
# workspace.addObjects(idf_obj_vector) does not work here. Add each obj individually.
|
179
|
-
source_idf.objects.each do |o|
|
180
|
-
workspace.addObject(o)
|
181
|
-
end
|
182
|
-
runner.registerInfo("#{source_idf.objects.size} performance curves, schedule objects, and output variables were imported from 'TESCurves.idf'.\n\n")
|
183
|
-
|
184
|
-
# assign user arguments to variables
|
185
|
-
ice_cap = runner.getStringArgumentValue('ice_cap', user_arguments) # ice capacity value (in ton-hours)
|
186
|
-
size_mult = runner.getStringArgumentValue('size_mult', user_arguments) # size multiplier for ice tank capacity - use if autosize is excessively oversizing
|
187
|
-
ctl = runner.getStringArgumentValue('ctl', user_arguments) # control method (schedule or EMS)
|
188
|
-
sched = runner.getStringArgumentValue('sched', user_arguments) # select operating mode schedule (schedule objects located in resources\TESCurves.idf)
|
189
|
-
wknd = runner.getBoolArgumentValue('wknd', user_arguments) # turn tes on/off for weekend operation
|
190
|
-
season = runner.getStringArgumentValue('season', user_arguments) # set operating season for Simple User Sched
|
191
|
-
charge_start = runner.getStringArgumentValue('charge_start', user_arguments) # time ice charging begins
|
192
|
-
charge_end = runner.getStringArgumentValue('charge_end', user_arguments) # time ice charging ends
|
193
|
-
discharge_start = runner.getStringArgumentValue('discharge_start', user_arguments) # time ice discharge begins
|
194
|
-
discharge_end = runner.getStringArgumentValue('discharge_end', user_arguments) # time ice discharge ends
|
195
|
-
|
196
|
-
# retrieve user selected coils and assign to vector
|
197
|
-
coils = workspace.getObjectsByType('Coil:Cooling:DX:SingleSpeed'.to_IddObjectType)
|
198
|
-
coils += workspace.getObjectsByType('Coil:Cooling:DX:TwoSpeed'.to_IddObjectType)
|
199
|
-
coilhash = {}
|
200
|
-
c = coils.sort { |a, b| a.getString(0).get <=> b.getString(0).get }
|
201
|
-
c.each do |coil|
|
202
|
-
c_name = coil.name.to_s
|
203
|
-
coilhash[c_name] = true
|
204
|
-
end
|
205
|
-
|
206
|
-
coil_selection = []
|
207
|
-
coilhash.each do |k, v|
|
208
|
-
temp_var = runner.getBoolArgumentValue(k, user_arguments)
|
209
|
-
coil_selection << k if temp_var
|
210
|
-
end
|
211
|
-
|
212
|
-
# create other useful variables
|
213
|
-
replacement_count = 0 # tracks number of coils replaced by measure
|
214
|
-
time_size_factor = '' # sets Storage Capacity Sizing Factor {hr}
|
215
|
-
discharge_cop = '63.6' # default COP for Ice Discharge
|
216
|
-
curve_d_shr_ft = 'Discharge-SHR-fT-NREL' # default curve for sensible heat ratio f(T) during ice discharge
|
217
|
-
|
218
|
-
# convert string time values into floats for math comparisons
|
219
|
-
# ds/de = discharge start/end, cs/ce = charge start/end
|
220
|
-
ds = discharge_start.split(':')[0].to_f + (discharge_start.split(':')[1].to_f / 0.6)
|
221
|
-
de = discharge_end.split(':')[0].to_f + (discharge_end.split(':')[1].to_f / 0.6)
|
222
|
-
cs = charge_start.split(':')[0].to_f + (charge_start.split(':')[1].to_f / 0.6)
|
223
|
-
ce = charge_end.split(':')[0].to_f + (charge_end.split(':')[1].to_f / 0.6)
|
224
|
-
|
225
|
-
# #Check User Inputs and Define Variables
|
226
|
-
hardcaps = []
|
227
|
-
if ice_cap != 'AutoSize'
|
228
|
-
if ice_cap == ''
|
229
|
-
runner.registerWarning("No ice capacity was entered for 'User Input' selection, 'AutoSize' was used instead.")
|
230
|
-
ice_cap = 'AutoSize'
|
231
|
-
elsif ice_cap.split(',').size > 1
|
232
|
-
runner.registerInfo('Ice storage tanks will be hardsized based on user inputs, assigned alphabetically.')
|
233
|
-
ice_cap.split(',').each { |i| hardcaps.push((i.to_f * 0.0126608).to_s) }
|
234
|
-
while hardcaps.size != coil_selection.size
|
235
|
-
runner.registerInfo("No user-defined thermal storage capacity for #{coil_selection[hardcaps.size]}; unit will be AutoSized.")
|
236
|
-
hardcaps.push('AutoSize')
|
237
|
-
end
|
238
|
-
else
|
239
|
-
ice_cap = (ice_cap.to_f * 0.0126608).to_s # convert units from ton-hours to GJ
|
240
|
-
end
|
241
|
-
elsif sched == 'Simple User Sched'
|
242
|
-
time_size_factor = ((de - ds) * size_mult.to_f).to_s
|
243
|
-
elsif sched == 'TES Sched 2: 1-5 Peak'
|
244
|
-
time_size_factor = (4.0 * size_mult.to_f).to_s
|
245
|
-
elsif sched == 'TES Sched 3: 3-8 Peak'
|
246
|
-
time_size_factor = (5.0 * size_mult.to_f).to_s
|
247
|
-
elsif sched == 'TES Sched 4: GSS-T'
|
248
|
-
time_size_factor = (3.0 * size_mult.to_f).to_s
|
249
|
-
else
|
250
|
-
time_size_factor = (4.0 * size_mult.to_f).to_s # sets default time size factor to 4 hours
|
251
|
-
end
|
252
|
-
|
253
|
-
# Check user schedule inputs and build schedule
|
254
|
-
if sched == 'Simple User Sched'
|
255
|
-
|
256
|
-
# find empty user input schedule object from TESCurves.idf import
|
257
|
-
user_schedules = workspace.getObjectsByName('Simple User Sched')
|
258
|
-
user_schedule = user_schedules[0]
|
259
|
-
|
260
|
-
# check ice discharge times to ensure end occurs after start. Exit gracefully if it doesn't.
|
261
|
-
if de < ds
|
262
|
-
runner.registerError('Ice discharge end time occurs before the start time. If ice discharge is desired overnight, create a schedule object in ../resources/TESCurves.idf. Measure was not applied.')
|
263
|
-
return false
|
264
|
-
end
|
265
|
-
|
266
|
-
# sets charge and discharge mode ** May be modified if Cool_Charge or Cool_Discharge modes become available
|
267
|
-
charge_mode = 4
|
268
|
-
discharge_mode = 5
|
269
|
-
|
270
|
-
# format user input for cooling season values
|
271
|
-
czn = season.split(/[\s-]/)
|
272
|
-
cool_start = czn[0].to_s
|
273
|
-
cool_end = czn[-1].to_s
|
274
|
-
|
275
|
-
# set cooling season start periods
|
276
|
-
a = 4 # index variable to ensure schedule is built properly under various conditions
|
277
|
-
c = 3 # index variable to help ensure a weekday-only schedule is properly built
|
278
|
-
|
279
|
-
if cool_start != '01/01'
|
280
|
-
user_schedule.setString(2, "Through: #{cool_start}")
|
281
|
-
user_schedule.setString(3, 'For: AllDays')
|
282
|
-
user_schedule.setString(4, 'Until: 24:00')
|
283
|
-
user_schedule.setString(5, '1')
|
284
|
-
user_schedule.setString(7, 'For: AllDays')
|
285
|
-
a = 8
|
286
|
-
c = 7
|
287
|
-
end
|
288
|
-
|
289
|
-
# build user defined schedule object
|
290
|
-
if cs > ce
|
291
|
-
# assign times to schedule fields
|
292
|
-
user_schedule.setString(a, "Until: #{charge_end}")
|
293
|
-
user_schedule.setString(a + 1, charge_mode.to_s)
|
294
|
-
user_schedule.setString(a + 2, "Until: #{discharge_start}")
|
295
|
-
user_schedule.setString(a + 3, '1')
|
296
|
-
user_schedule.setString(a + 4, "Until: #{discharge_end}")
|
297
|
-
user_schedule.setString(a + 5, discharge_mode.to_s)
|
298
|
-
user_schedule.setString(a + 6, "Until: #{charge_start}")
|
299
|
-
user_schedule.setString(a + 7, '1')
|
300
|
-
user_schedule.setString(a + 8, 'Until: 24:00')
|
301
|
-
user_schedule.setString(a + 9, charge_mode.to_s)
|
302
|
-
b = a + 10
|
303
|
-
elsif charge_start != '00:00'
|
304
|
-
user_schedule.setString(a, "Until: #{charge_end}")
|
305
|
-
user_schedule.setString(a + 1, charge_mode.to_s)
|
306
|
-
user_schedule.setString(a + 2, "Until: #{discharge_start}")
|
307
|
-
user_schedule.setString(a + 3, '1')
|
308
|
-
user_schedule.setString(a + 4, "Until: #{discharge_end}")
|
309
|
-
user_schedule.setString(a + 5, discharge_mode.to_s)
|
310
|
-
user_schedule.setString(a + 6, 'Until: 24:00')
|
311
|
-
user_schedule.setString(a + 7, '1')
|
312
|
-
b = a + 8
|
313
|
-
else
|
314
|
-
user_schedule.setString(a, "Until: #{charge_end}")
|
315
|
-
user_schedule.setString(a + 1, charge_mode.to_s)
|
316
|
-
user_schedule.setString(a + 2, "Until: #{discharge_start}")
|
317
|
-
user_schedule.setString(a + 3, '1')
|
318
|
-
user_schedule.setString(a + 4, "Until: #{discharge_end}")
|
319
|
-
user_schedule.setString(a + 5, discharge_mode.to_s)
|
320
|
-
user_schedule.setString(a + 6, 'Until: 24:00')
|
321
|
-
user_schedule.setString(a + 7, '1')
|
322
|
-
b = a + 8
|
323
|
-
end
|
324
|
-
|
325
|
-
# make weekend modification if necessary
|
326
|
-
unless wknd
|
327
|
-
user_schedule.setString(c, 'For: WeekDays')
|
328
|
-
user_schedule.setString(b, 'For: Weekends')
|
329
|
-
user_schedule.setString(b + 1, "Until: #{charge_end}")
|
330
|
-
user_schedule.setString(b + 2, charge_mode.to_s)
|
331
|
-
user_schedule.setString(b + 3, 'Until: 24:00')
|
332
|
-
user_schedule.setString(b + 4, '1')
|
333
|
-
b += 5
|
334
|
-
end
|
335
|
-
|
336
|
-
# complete cooling season schedule if not through 12/31
|
337
|
-
if cool_end != '12/31'
|
338
|
-
user_schedule.setString(c - 1, "Through: #{cool_end}")
|
339
|
-
user_schedule.setString(b, 'Through: 12/31')
|
340
|
-
user_schedule.setString(b + 1, 'For: AllDays')
|
341
|
-
user_schedule.setString(b + 2, 'Until: 24:00')
|
342
|
-
user_schedule.setString(b + 3, '1')
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
# find objects of interest in the model (used to identify container objects, air loops, and thermal zones)
|
347
|
-
cooling_coil_systems = workspace.getObjectsByType('CoilSystem:Cooling:DX'.to_IddObjectType)
|
348
|
-
air_loops = workspace.getObjectsByType('AirLoopHVAC'.to_IddObjectType)
|
349
|
-
branches = workspace.getObjectsByType('Branch'.to_IddObjectType)
|
350
|
-
hvac_zone_mixers = workspace.getObjectsByType('AirLoopHVAC:ZoneMixer'.to_IddObjectType)
|
351
|
-
zone_connections = workspace.getObjectsByType('ZoneHVAC:EquipmentConnections'.to_IddObjectType)
|
352
|
-
unitary_generic_obj = workspace.getObjectsByType('AirLoopHVAC:UnitarySystem'.to_IddObjectType)
|
353
|
-
node_lists = workspace.getObjectsByType('NodeList'.to_IddObjectType)
|
354
|
-
|
355
|
-
# create vector of all cooling system container objects
|
356
|
-
cooling_containers = OpenStudio::IdfObjectVector.new
|
357
|
-
cooling_coil_systems.each do |coil_sys|
|
358
|
-
if coil_selection.include?(coil_sys.getString(6).to_s)
|
359
|
-
cooling_containers << coil_sys
|
360
|
-
end
|
361
|
-
end
|
362
|
-
unitary_generic_obj.each do |unitary_sys|
|
363
|
-
if coil_selection.include?(unitary_sys.getString(15).to_s)
|
364
|
-
cooling_containers << unitary_sys
|
365
|
-
end
|
366
|
-
end
|
367
|
-
|
368
|
-
# exit gracefully if original model does not have coilSystem:Cooling objects
|
369
|
-
if cooling_containers.empty?
|
370
|
-
runner.registerError('This measure only operates on the following EnergyPlus container objects: CoilSystem:Cooling:DX and AirLoopHVAC:UnitarySystem. Measure was not applied.')
|
371
|
-
end
|
372
|
-
|
373
|
-
# create TES object string template for use in replacing existing coils; incorporates user input variables
|
374
|
-
new_tes_string =
|
375
|
-
"Coil:Cooling:DX:SingleSpeed:ThermalStorage,
|
376
|
-
NAME PLACEHOLDER, !- Name
|
377
|
-
ALWAYS_ON, !- Availability Schedule Name
|
378
|
-
#{ctl}, !- Operating Mode Control Method
|
379
|
-
#{sched}, !- Operation Mode Control Schedule Name
|
380
|
-
Ice, !- Storage Type
|
381
|
-
, !- User Defined Fluid Type
|
382
|
-
, !- Fluid Storage Volume {m3}
|
383
|
-
AutoSize, !- Ice Storage Capacity {GJ}
|
384
|
-
#{time_size_factor}, !- Storage Capacity Sizing Factor {hr}
|
385
|
-
AMBIENT NODE, !- Storage Tank Ambient Temperature Node Name
|
386
|
-
7.913, !- Storage Tank to Ambient U-value Times Area Heat Transfer Coefficient {W/K}
|
387
|
-
, !- Fluid Storage Tank Rating Temperature {C}
|
388
|
-
AutoSize, !- Rated Evaporator Air Flow Rate {m3/s}
|
389
|
-
EVAP IN NODE, !- Evaporator Air Inlet Node Name
|
390
|
-
EVAP OUT NODE, !- Evaporator Air Outlet Node Name
|
391
|
-
Yes, !- Cooling Only Mode Available
|
392
|
-
AutoSize, !- Cooling Only Mode Rated Total Evaporator Cooling Capacity {W} **IB40 Limits: 10551 W (3 ton) to 70337 W (20 ton)**
|
393
|
-
0.7, !- Cooling Only Mode Rated Sensible Heat Ratio
|
394
|
-
3.23372055845678, !- Cooling Only Mode Rated COP {W/W}
|
395
|
-
Cool-Cap-fT, !- Cooling Only Mode Total Evaporator Cooling Capacity Function of Temperature Curve Name
|
396
|
-
ConstantCubic, !- Cooling Only Mode Total Evaporator Cooling Capacity Function of Flow Fraction Curve Name
|
397
|
-
Cool-EIR-fT, !- Cooling Only Mode Energy Input Ratio Function of Temperature Curve Name
|
398
|
-
ConstantCubic, !- Cooling Only Mode Energy Input Ratio Function of Flow Fraction Curve Name
|
399
|
-
Cool-PLF-fPLR, !- Cooling Only Mode Part Load Fraction Correlation Curve Name
|
400
|
-
Cool-SHR-fT, !- Cooling Only Mode Sensible Heat Ratio Function of Temperature Curve Name
|
401
|
-
Cool-SHR-fFF, !- Cooling Only Mode Sensible Heat Ratio Function of Flow Fraction Curve Name
|
402
|
-
No, !- Cooling And Charge Mode Available
|
403
|
-
AutoSize, !- Cooling And Charge Mode Rated Total Evaporator Cooling Capacity
|
404
|
-
1.0, !- Cooling And Charge Mode Capacity Sizing Factor
|
405
|
-
AutoSize, !- Cooling And Charge Mode Rated Storage Charging Capacity
|
406
|
-
0.86, !- Cooling And Charge Mode Storage Capacity Sizing Factor
|
407
|
-
0.7, !- Cooling And Charge Mode Rated Sensible Heat Ratio
|
408
|
-
3.66668443E+00, !- Cooling And Charge Mode Cooling Rated COP
|
409
|
-
2.17, !- Cooling And Charge Mode Charging Rated COP
|
410
|
-
CoolCharge-Cool-Cap-fT, !- Cooling And Charge Mode Total Evaporator Cooling Capacity Function of Temperature Curve Name
|
411
|
-
ConstantCubic, !- Cooling And Charge Mode Total Evaporator Cooling Capacity Function of Flow Fraction Curve Name
|
412
|
-
CoolCharge-Cool-EIR-fT, !- Cooling And Charge Mode Evaporator Energy Input Ratio Function of Temperature Curve Name
|
413
|
-
ConstantCubic, !- Cooling And Charge Mode Evaporator Energy Input Ratio Function of Flow Fraction Curve Name
|
414
|
-
Cool-PLF-fPLR, !- Cooling And Charge Mode Evaporator Part Load Fraction Correlation Curve Name
|
415
|
-
CoolCharge-Charge-Cap-fT,!- Cooling And Charge Mode Storage Charge Capacity Function of Temperature Curve Name
|
416
|
-
ConstantCubic, !- Cooling And Charge Mode Storage Charge Capacity Function of Total Evaporator PLR Curve Name
|
417
|
-
CoolCharge-Charge-EIR-fT,!- Cooling And Charge Mode Storage Energy Input Ratio Function of Temperature Curve Name
|
418
|
-
ConstantCubic, !- Cooling And Charge Mode Storage Energy Input Ratio Function of Flow Fraction Curve Name
|
419
|
-
ConstantCubic, !- Cooling And Charge Mode Storage Energy Part Load Fraction Correlation Curve Name
|
420
|
-
Cool-SHR-fT, !- Cooling And Charge Mode Sensible Heat Ratio Function of Temperature Curve Name
|
421
|
-
Cool-SHR-fFF, !- Cooling And Charge Mode Sensible Heat Ratio Function of Flow Fraction Curve Name
|
422
|
-
No, !- Cooling And Discharge Mode Available
|
423
|
-
, !- Cooling And Discharge Mode Rated Total Evaporator Cooling Capacity {W}
|
424
|
-
, !- Cooling And Discharge Mode Evaporator Capacity Sizing Factor
|
425
|
-
, !- Cooling And Discharge Mode Rated Storage Discharging Capacity {W}
|
426
|
-
, !- Cooling And Discharge Mode Storage Discharge Capacity Sizing Factor
|
427
|
-
, !- Cooling And Discharge Mode Rated Sensible Heat Ratio
|
428
|
-
, !- Cooling And Discharge Mode Cooling Rated COP {W/W}
|
429
|
-
, !- Cooling And Discharge Mode Discharging Rated COP {W/W}
|
430
|
-
, !- Cooling And Discharge Mode Total Evaporator Cooling Capacity Function of Temperature Curve Name
|
431
|
-
, !- Cooling And Discharge Mode Total Evaporator Cooling Capacity Function of Flow Fraction Curve Name
|
432
|
-
, !- Cooling And Discharge Mode Evaporator Energy Input Ratio Function of Temperature Curve Name
|
433
|
-
, !- Cooling And Discharge Mode Evaporator Energy Input Ratio Function of Flow Fraction Curve Name
|
434
|
-
, !- Cooling And Discharge Mode Evaporator Part Load Fraction Correlation Curve Name
|
435
|
-
, !- Cooling And Discharge Mode Storage Discharge Capacity Function of Temperature Curve Name
|
436
|
-
, !- Cooling And Discharge Mode Storage Discharge Capacity Function of Flow Fraction Curve Name
|
437
|
-
, !- Cooling And Discharge Mode Storage Discharge Capacity Function of Total Evaporator PLR Curve Name
|
438
|
-
, !- Cooling And Discharge Mode Storage Energy Input Ratio Function of Temperature Curve Name
|
439
|
-
, !- Cooling And Discharge Mode Storage Energy Input Ratio Function of Flow Fraction Curve Name
|
440
|
-
, !- Cooling And Discharge Mode Storage Energy Part Load Fraction Correlation Curve Name
|
441
|
-
, !- Cooling And Discharge Mode Sensible Heat Ratio Function of Temperature Curve Name
|
442
|
-
, !- Cooling And Discharge Mode Sensible Heat Ratio Function of Flow Fraction Curve Name
|
443
|
-
Yes, !- Charge Only Mode Available
|
444
|
-
AutoSize, !- Charge Only Mode Rated Storage Charging Capacity {W}
|
445
|
-
0.8, !- Charge Only Mode Capacity Sizing Factor
|
446
|
-
3.09, !- Charge Only Mode Charging Rated COP {W/W}
|
447
|
-
ChargeOnly-Cap-fT, !- Charge Only Mode Storage Charge Capacity Function of Temperature Curve Name
|
448
|
-
ChargeOnly-EIR-fT, !- Charge Only Mode Storage Energy Input Ratio Function of Temperature Curve Name
|
449
|
-
Yes, !- Discharge Only Mode Available
|
450
|
-
AutoSize, !- Discharge Only Mode Rated Storage Discharging Capacity {W}
|
451
|
-
1.37, !- Discharge Only Mode Capacity Sizing Factor
|
452
|
-
0.64, !- Discharge Only Mode Rated Sensible Heat Ratio
|
453
|
-
#{discharge_cop}, !- Discharge Only Mode Rated COP {W/W}
|
454
|
-
Discharge-Cap-fT, !- Discharge Only Mode Storage Discharge Capacity Function of Temperature Curve Name
|
455
|
-
Discharge-Cap-fFF, !- Discharge Only Mode Storage Discharge Capacity Function of Flow Fraction Curve Name
|
456
|
-
ConstantBi, !- Discharge Only Mode Energy Input Ratio Function of Temperature Curve Name
|
457
|
-
ConstantCubic, !- Discharge Only Mode Energy Input Ratio Function of Flow Fraction Curve Name
|
458
|
-
ConstantCubic, !- Discharge Only Mode Part Load Fraction Correlation Curve Name
|
459
|
-
#{curve_d_shr_ft}, !- Discharge Only Mode Sensible Heat Ratio Function of Temperature Curve Name
|
460
|
-
Discharge-SHR-fFF, !- Discharge Only Mode Sensible Heat Ratio Function of Flow Fraction Curve Name
|
461
|
-
0.0, !- Ancillary Electric Power {W}
|
462
|
-
2.0, !- Cold Weather Operation Minimum Outdoor Air Temperature {C}
|
463
|
-
0.0, !- Cold Weather Operation Ancillary Power {W}
|
464
|
-
CONDENSER INLET NODE, !- Condenser Air Inlet Node Name
|
465
|
-
CONDENSER OUTLET NODE, !- Condenser Air Outlet Node Name
|
466
|
-
autocalculate, !- Condenser Design Air Flow Rate {m3/s}
|
467
|
-
1.25, !- Condenser Air Flow Sizing Factor
|
468
|
-
AirCooled, !- Condenser Type
|
469
|
-
, !- Evaporative Condenser Effectiveness {dimensionless}
|
470
|
-
, !- Evaporative Condenser Pump Rated Power Consumption {W}
|
471
|
-
, !- Basin Heater Capacity {W/K}
|
472
|
-
, !- Basin Heater Setpoint Temperature {C}
|
473
|
-
, !- Basin Heater Availability Schedule Name
|
474
|
-
, !- Supply Water Storage Tank Name
|
475
|
-
, !- Condensate Collection Water Storage Tank Name
|
476
|
-
, !- Storage Tank Plant Connection Inlet Node Name
|
477
|
-
, !- Storage Tank Plant Connection Outlet Node Name
|
478
|
-
, !- Storage Tank Plant Connection Design Flow Rate {m3/s}
|
479
|
-
, !- Storage Tank Plant Connection Heat Transfer Effectiveness
|
480
|
-
, !- Storage Tank Minimum Operating Limit Fluid Temperature {C}
|
481
|
-
; !- Storage Tank Maximum Operating Limit Fluid Temperature {C}"
|
482
|
-
# end of new TES coil string
|
483
|
-
|
484
|
-
# #Begin Coil Replacement
|
485
|
-
# iterate through all CoilSystem:Cooling objects and replace existing coils with TES coils
|
486
|
-
coil_selection.each do |sel_coil|
|
487
|
-
# get workspace object for selected coil from name
|
488
|
-
sel_coil = workspace.getObjectsByName(sel_coil)[0]
|
489
|
-
ice_cap = hardcaps[replacement_count] unless hardcaps.empty?
|
490
|
-
|
491
|
-
# get coil type in order to find get appropriate field keys
|
492
|
-
# fields of interest: (0 - Max Cap, 1 - Rated COP, 2 - Inlet Node, 3 - Outlet Node)
|
493
|
-
field_names = [] # may not be required. Check scope in Ruby documentation
|
494
|
-
sel_type = sel_coil.iddObject.type.valueDescription.to_s
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
inlet
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
oa_ambient
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
condenser_inlet
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
condenser_outlet
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
utss.
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
'
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
utss.setString(keys[
|
557
|
-
utss.setString(keys[
|
558
|
-
utss.setString(keys[
|
559
|
-
utss.setString(keys[
|
560
|
-
utss.setString(
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
utss.setString(
|
567
|
-
utss.setString(
|
568
|
-
utss.setString(
|
569
|
-
utss.setString(
|
570
|
-
utss.setString(
|
571
|
-
|
572
|
-
utss.setString(
|
573
|
-
|
574
|
-
utss.setString(
|
575
|
-
utss.setString(
|
576
|
-
utss.setString(
|
577
|
-
utss.setString(
|
578
|
-
utss.setString(
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
#
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
new_sched_sensor
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
#
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
new_sensor
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
new_actuator.
|
633
|
-
new_actuator.setString(
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
IF
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
new_pcm.
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
#
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
#
|
684
|
-
runner.
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
1
|
+
# *******************************************************************************
|
2
|
+
# OpenStudio(R), Copyright (c) 2008-2021, Alliance for Sustainable Energy, LLC.
|
3
|
+
# All rights reserved.
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# (1) Redistributions of source code must retain the above copyright notice,
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
#
|
10
|
+
# (2) Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
#
|
14
|
+
# (3) Neither the name of the copyright holder nor the names of any contributors
|
15
|
+
# may be used to endorse or promote products derived from this software without
|
16
|
+
# specific prior written permission from the respective party.
|
17
|
+
#
|
18
|
+
# (4) Other than as required in clauses (1) and (2), distributions in any form
|
19
|
+
# of modifications or other derivative works may not use the "OpenStudio"
|
20
|
+
# trademark, "OS", "os", or any other confusingly similar designation without
|
21
|
+
# specific prior written permission from Alliance for Sustainable Energy, LLC.
|
22
|
+
#
|
23
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS
|
24
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
25
|
+
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
26
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE
|
27
|
+
# UNITED STATES GOVERNMENT, OR THE UNITED STATES DEPARTMENT OF ENERGY, NOR ANY OF
|
28
|
+
# THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
29
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
30
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
31
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
32
|
+
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
33
|
+
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
34
|
+
# *******************************************************************************
|
35
|
+
|
36
|
+
# Revised - KH July 2019
|
37
|
+
# Measure Renamed, License Updated, and Code Cleaned - KH June 2020
|
38
|
+
|
39
|
+
# start the measure
|
40
|
+
class AddPackagedIceStorage < OpenStudio::Measure::EnergyPlusMeasure
|
41
|
+
# human readable name
|
42
|
+
def name
|
43
|
+
'Add Packaged Ice Storage'
|
44
|
+
end
|
45
|
+
|
46
|
+
# human readable description
|
47
|
+
def description
|
48
|
+
'This measure removes the cooling coils in the model and replaces them with packaged air conditioning units with integrated ice storage.'
|
49
|
+
end
|
50
|
+
|
51
|
+
# human readable description of modeling approach
|
52
|
+
def modeler_description
|
53
|
+
"This measure applies to packaged single zone air conditioning systems or packaged variable air volume systems that were originally modeled with CoilSystem:Cooling:DX or AirLoopHVAC:UnitarySystem container objects. It adds a Coil:Cooling:DX:SingleSpeed:ThermalStorage coil object to each user-selected thermal zone and deletes the existing cooling coil.
|
54
|
+
|
55
|
+
Users inputs are accepted for cooling coil size, ice storage size, system control method, modes of operation, and operating schedule.
|
56
|
+
|
57
|
+
The measure requires schedule objects and performance curves from an included resource file TESCurves.idf. Output variables of typical interest are included as well."
|
58
|
+
end
|
59
|
+
|
60
|
+
# define the arguments that the user will input
|
61
|
+
def arguments(workspace)
|
62
|
+
args = OpenStudio::Measure::OSArgumentVector.new
|
63
|
+
|
64
|
+
# # Add a delimiter for clarify
|
65
|
+
# delimiter = OpenStudio::Measure::OSArgument.makeStringArgument('delimiter', false)
|
66
|
+
# delimiter.setDisplayName('Select Coils to Replace:')
|
67
|
+
# delimiter.setDefaultValue('-----------------------------------------------------------------')
|
68
|
+
# args << delimiter
|
69
|
+
|
70
|
+
# get existing dx coils for user selection
|
71
|
+
coils = workspace.getObjectsByType('Coil:Cooling:DX:SingleSpeed'.to_IddObjectType)
|
72
|
+
coils += workspace.getObjectsByType('Coil:Cooling:DX:TwoSpeed'.to_IddObjectType)
|
73
|
+
coilhash = {}
|
74
|
+
c = coils.sort { |a, b| a.getString(0).get <=> b.getString(0).get }
|
75
|
+
c.each do |coil|
|
76
|
+
c_name = coil.name.to_s
|
77
|
+
coilhash[c_name] = true
|
78
|
+
end
|
79
|
+
|
80
|
+
# make boolean argument for selecting cooling coils to replace
|
81
|
+
coilhash.each do |k, v|
|
82
|
+
coil_selection = OpenStudio::Measure::OSArgument.makeBoolArgument(k, true)
|
83
|
+
coil_selection.setDisplayName(k)
|
84
|
+
coil_selection.setDefaultValue(v)
|
85
|
+
coil_selection.setDescription('Replace this coil?')
|
86
|
+
args << coil_selection
|
87
|
+
end
|
88
|
+
|
89
|
+
ice_cap = OpenStudio::Measure::OSArgument.makeStringArgument('ice_cap', true)
|
90
|
+
ice_cap.setDisplayName('Input the ice storage capacity [ton-hours]')
|
91
|
+
ice_cap.setDescription('To specify by coil, in alphabetical order, enter values for each separated by comma.')
|
92
|
+
ice_cap.setDefaultValue('AutoSize')
|
93
|
+
args << ice_cap
|
94
|
+
|
95
|
+
size_mult = OpenStudio::Measure::OSArgument.makeStringArgument('size_mult', false)
|
96
|
+
size_mult.setDisplayName('Enter a sizing multiplier to manually adjust the autosize results for ice tank capacities')
|
97
|
+
size_mult.setDefaultValue('1.0')
|
98
|
+
args << size_mult
|
99
|
+
|
100
|
+
# make argument for control method
|
101
|
+
ctl = OpenStudio::Measure::OSArgument.makeChoiceArgument('ctl', ['ScheduledModes', 'EMSControlled'], true)
|
102
|
+
ctl.setDisplayName('Select ice storage control method')
|
103
|
+
ctl.setDefaultValue('EMSControlled')
|
104
|
+
args << ctl
|
105
|
+
|
106
|
+
# obtain default schedule names in TESCurves.idf. This allows users to manually add schedules to the idf and be able to access them in OS or PAT
|
107
|
+
source_idf = OpenStudio::IdfFile.load(OpenStudio::Path.new("#{File.dirname(__FILE__)}/resources/TESCurves.idf")).get
|
108
|
+
schedules = source_idf.getObjectsByType('Schedule:Compact'.to_IddObjectType)
|
109
|
+
schedule_names = OpenStudio::StringVector.new
|
110
|
+
|
111
|
+
schedules.each do |sch|
|
112
|
+
schedule_names << sch.name.to_s
|
113
|
+
end
|
114
|
+
|
115
|
+
# make argument for TES operating mode schedule
|
116
|
+
sched = OpenStudio::Measure::OSArgument.makeChoiceArgument('sched', schedule_names, true)
|
117
|
+
sched.setDisplayName('Select the operating mode schedule for the new TES coils')
|
118
|
+
sched.setDescription('Use the fields below to set a simple daily ice charge/discharge schedule. Or, select from pre-defined options.')
|
119
|
+
sched.setDefaultValue('Simple User Sched')
|
120
|
+
args << sched
|
121
|
+
|
122
|
+
# make arguement for weekend TES operation
|
123
|
+
wknd = OpenStudio::Measure::OSArgument.makeBoolArgument('wknd', false)
|
124
|
+
wknd.setDisplayName('Run TES on the weekends')
|
125
|
+
wknd.setDescription('Select if building is occupied on weekends')
|
126
|
+
wknd.setDefaultValue(true)
|
127
|
+
args << wknd
|
128
|
+
|
129
|
+
# make arguments for operating season
|
130
|
+
season = OpenStudio::Measure::OSArgument.makeStringArgument('season', false)
|
131
|
+
season.setDisplayName('Select season during which the ice cooling may be used')
|
132
|
+
season.setDescription('Use MM/DD-MM/DD format')
|
133
|
+
season.setDefaultValue('01/01-12/31')
|
134
|
+
args << season
|
135
|
+
|
136
|
+
# make arguments for simple charging period
|
137
|
+
charge_start = OpenStudio::Measure::OSArgument.makeStringArgument('charge_start', false)
|
138
|
+
charge_start.setDisplayName('Input start time for ice charge (hr:min)')
|
139
|
+
charge_start.setDescription('Use 24 hour format')
|
140
|
+
charge_start.setDefaultValue('22:00')
|
141
|
+
args << charge_start
|
142
|
+
|
143
|
+
charge_end = OpenStudio::Measure::OSArgument.makeStringArgument('charge_end', false)
|
144
|
+
charge_end.setDisplayName('Input end time for ice charge (hr:min)')
|
145
|
+
charge_end.setDescription('Use 24 hour format')
|
146
|
+
charge_end.setDefaultValue('07:00')
|
147
|
+
args << charge_end
|
148
|
+
|
149
|
+
# make arguments for simple discharging period
|
150
|
+
discharge_start = OpenStudio::Measure::OSArgument.makeStringArgument('discharge_start', false)
|
151
|
+
discharge_start.setDisplayName('Input start time for ice discharge (hr:min)')
|
152
|
+
discharge_start.setDescription("Use 24hour format.\nIf 'AutoSize' is selected for ice capacity, these inputs set an ice capacity sizing factor. Otherwise, these only affect discharging schedule.")
|
153
|
+
discharge_start.setDefaultValue('12:00')
|
154
|
+
args << discharge_start
|
155
|
+
|
156
|
+
discharge_end = OpenStudio::Measure::OSArgument.makeStringArgument('discharge_end', false)
|
157
|
+
discharge_end.setDisplayName('Input target end time for ice discharge (hr:min)')
|
158
|
+
discharge_end.setDescription('Use 24 hour format')
|
159
|
+
discharge_end.setDefaultValue('18:00')
|
160
|
+
args << discharge_end
|
161
|
+
|
162
|
+
args
|
163
|
+
# end the arguments method
|
164
|
+
end
|
165
|
+
|
166
|
+
# define what happens when the measure is run
|
167
|
+
def run(workspace, runner, user_arguments)
|
168
|
+
super(workspace, runner, user_arguments)
|
169
|
+
|
170
|
+
# use the built-in error checking
|
171
|
+
unless runner.validateUserArguments(arguments(workspace), user_arguments)
|
172
|
+
return false
|
173
|
+
end
|
174
|
+
|
175
|
+
# load required TESCurves.idf. This contains all the TES performance curves and default schedules
|
176
|
+
source_idf = OpenStudio::IdfFile.load(OpenStudio::Path.new("#{File.dirname(__FILE__)}/resources/TESCurves.idf")).get
|
177
|
+
|
178
|
+
# workspace.addObjects(idf_obj_vector) does not work here. Add each obj individually.
|
179
|
+
source_idf.objects.each do |o|
|
180
|
+
workspace.addObject(o)
|
181
|
+
end
|
182
|
+
runner.registerInfo("#{source_idf.objects.size} performance curves, schedule objects, and output variables were imported from 'TESCurves.idf'.\n\n")
|
183
|
+
|
184
|
+
# assign user arguments to variables
|
185
|
+
ice_cap = runner.getStringArgumentValue('ice_cap', user_arguments) # ice capacity value (in ton-hours)
|
186
|
+
size_mult = runner.getStringArgumentValue('size_mult', user_arguments) # size multiplier for ice tank capacity - use if autosize is excessively oversizing
|
187
|
+
ctl = runner.getStringArgumentValue('ctl', user_arguments) # control method (schedule or EMS)
|
188
|
+
sched = runner.getStringArgumentValue('sched', user_arguments) # select operating mode schedule (schedule objects located in resources\TESCurves.idf)
|
189
|
+
wknd = runner.getBoolArgumentValue('wknd', user_arguments) # turn tes on/off for weekend operation
|
190
|
+
season = runner.getStringArgumentValue('season', user_arguments) # set operating season for Simple User Sched
|
191
|
+
charge_start = runner.getStringArgumentValue('charge_start', user_arguments) # time ice charging begins
|
192
|
+
charge_end = runner.getStringArgumentValue('charge_end', user_arguments) # time ice charging ends
|
193
|
+
discharge_start = runner.getStringArgumentValue('discharge_start', user_arguments) # time ice discharge begins
|
194
|
+
discharge_end = runner.getStringArgumentValue('discharge_end', user_arguments) # time ice discharge ends
|
195
|
+
|
196
|
+
# retrieve user selected coils and assign to vector
|
197
|
+
coils = workspace.getObjectsByType('Coil:Cooling:DX:SingleSpeed'.to_IddObjectType)
|
198
|
+
coils += workspace.getObjectsByType('Coil:Cooling:DX:TwoSpeed'.to_IddObjectType)
|
199
|
+
coilhash = {}
|
200
|
+
c = coils.sort { |a, b| a.getString(0).get <=> b.getString(0).get }
|
201
|
+
c.each do |coil|
|
202
|
+
c_name = coil.name.to_s
|
203
|
+
coilhash[c_name] = true
|
204
|
+
end
|
205
|
+
|
206
|
+
coil_selection = []
|
207
|
+
coilhash.each do |k, v|
|
208
|
+
temp_var = runner.getBoolArgumentValue(k, user_arguments)
|
209
|
+
coil_selection << k if temp_var
|
210
|
+
end
|
211
|
+
|
212
|
+
# create other useful variables
|
213
|
+
replacement_count = 0 # tracks number of coils replaced by measure
|
214
|
+
time_size_factor = '' # sets Storage Capacity Sizing Factor {hr}
|
215
|
+
discharge_cop = '63.6' # default COP for Ice Discharge
|
216
|
+
curve_d_shr_ft = 'Discharge-SHR-fT-NREL' # default curve for sensible heat ratio f(T) during ice discharge
|
217
|
+
|
218
|
+
# convert string time values into floats for math comparisons
|
219
|
+
# ds/de = discharge start/end, cs/ce = charge start/end
|
220
|
+
ds = discharge_start.split(':')[0].to_f + (discharge_start.split(':')[1].to_f / 0.6)
|
221
|
+
de = discharge_end.split(':')[0].to_f + (discharge_end.split(':')[1].to_f / 0.6)
|
222
|
+
cs = charge_start.split(':')[0].to_f + (charge_start.split(':')[1].to_f / 0.6)
|
223
|
+
ce = charge_end.split(':')[0].to_f + (charge_end.split(':')[1].to_f / 0.6)
|
224
|
+
|
225
|
+
# #Check User Inputs and Define Variables
|
226
|
+
hardcaps = []
|
227
|
+
if ice_cap != 'AutoSize'
|
228
|
+
if ice_cap == ''
|
229
|
+
runner.registerWarning("No ice capacity was entered for 'User Input' selection, 'AutoSize' was used instead.")
|
230
|
+
ice_cap = 'AutoSize'
|
231
|
+
elsif ice_cap.split(',').size > 1
|
232
|
+
runner.registerInfo('Ice storage tanks will be hardsized based on user inputs, assigned alphabetically.')
|
233
|
+
ice_cap.split(',').each { |i| hardcaps.push((i.to_f * 0.0126608).to_s) }
|
234
|
+
while hardcaps.size != coil_selection.size
|
235
|
+
runner.registerInfo("No user-defined thermal storage capacity for #{coil_selection[hardcaps.size]}; unit will be AutoSized.")
|
236
|
+
hardcaps.push('AutoSize')
|
237
|
+
end
|
238
|
+
else
|
239
|
+
ice_cap = (ice_cap.to_f * 0.0126608).to_s # convert units from ton-hours to GJ
|
240
|
+
end
|
241
|
+
elsif sched == 'Simple User Sched'
|
242
|
+
time_size_factor = ((de - ds) * size_mult.to_f).to_s
|
243
|
+
elsif sched == 'TES Sched 2: 1-5 Peak'
|
244
|
+
time_size_factor = (4.0 * size_mult.to_f).to_s
|
245
|
+
elsif sched == 'TES Sched 3: 3-8 Peak'
|
246
|
+
time_size_factor = (5.0 * size_mult.to_f).to_s
|
247
|
+
elsif sched == 'TES Sched 4: GSS-T'
|
248
|
+
time_size_factor = (3.0 * size_mult.to_f).to_s
|
249
|
+
else
|
250
|
+
time_size_factor = (4.0 * size_mult.to_f).to_s # sets default time size factor to 4 hours
|
251
|
+
end
|
252
|
+
|
253
|
+
# Check user schedule inputs and build schedule
|
254
|
+
if sched == 'Simple User Sched'
|
255
|
+
|
256
|
+
# find empty user input schedule object from TESCurves.idf import
|
257
|
+
user_schedules = workspace.getObjectsByName('Simple User Sched')
|
258
|
+
user_schedule = user_schedules[0]
|
259
|
+
|
260
|
+
# check ice discharge times to ensure end occurs after start. Exit gracefully if it doesn't.
|
261
|
+
if de < ds
|
262
|
+
runner.registerError('Ice discharge end time occurs before the start time. If ice discharge is desired overnight, create a schedule object in ../resources/TESCurves.idf. Measure was not applied.')
|
263
|
+
return false
|
264
|
+
end
|
265
|
+
|
266
|
+
# sets charge and discharge mode ** May be modified if Cool_Charge or Cool_Discharge modes become available
|
267
|
+
charge_mode = 4
|
268
|
+
discharge_mode = 5
|
269
|
+
|
270
|
+
# format user input for cooling season values
|
271
|
+
czn = season.split(/[\s-]/)
|
272
|
+
cool_start = czn[0].to_s
|
273
|
+
cool_end = czn[-1].to_s
|
274
|
+
|
275
|
+
# set cooling season start periods
|
276
|
+
a = 4 # index variable to ensure schedule is built properly under various conditions
|
277
|
+
c = 3 # index variable to help ensure a weekday-only schedule is properly built
|
278
|
+
|
279
|
+
if cool_start != '01/01'
|
280
|
+
user_schedule.setString(2, "Through: #{cool_start}")
|
281
|
+
user_schedule.setString(3, 'For: AllDays')
|
282
|
+
user_schedule.setString(4, 'Until: 24:00')
|
283
|
+
user_schedule.setString(5, '1')
|
284
|
+
user_schedule.setString(7, 'For: AllDays')
|
285
|
+
a = 8
|
286
|
+
c = 7
|
287
|
+
end
|
288
|
+
|
289
|
+
# build user defined schedule object
|
290
|
+
if cs > ce
|
291
|
+
# assign times to schedule fields
|
292
|
+
user_schedule.setString(a, "Until: #{charge_end}")
|
293
|
+
user_schedule.setString(a + 1, charge_mode.to_s)
|
294
|
+
user_schedule.setString(a + 2, "Until: #{discharge_start}")
|
295
|
+
user_schedule.setString(a + 3, '1')
|
296
|
+
user_schedule.setString(a + 4, "Until: #{discharge_end}")
|
297
|
+
user_schedule.setString(a + 5, discharge_mode.to_s)
|
298
|
+
user_schedule.setString(a + 6, "Until: #{charge_start}")
|
299
|
+
user_schedule.setString(a + 7, '1')
|
300
|
+
user_schedule.setString(a + 8, 'Until: 24:00')
|
301
|
+
user_schedule.setString(a + 9, charge_mode.to_s)
|
302
|
+
b = a + 10
|
303
|
+
elsif charge_start != '00:00'
|
304
|
+
user_schedule.setString(a, "Until: #{charge_end}")
|
305
|
+
user_schedule.setString(a + 1, charge_mode.to_s)
|
306
|
+
user_schedule.setString(a + 2, "Until: #{discharge_start}")
|
307
|
+
user_schedule.setString(a + 3, '1')
|
308
|
+
user_schedule.setString(a + 4, "Until: #{discharge_end}")
|
309
|
+
user_schedule.setString(a + 5, discharge_mode.to_s)
|
310
|
+
user_schedule.setString(a + 6, 'Until: 24:00')
|
311
|
+
user_schedule.setString(a + 7, '1')
|
312
|
+
b = a + 8
|
313
|
+
else
|
314
|
+
user_schedule.setString(a, "Until: #{charge_end}")
|
315
|
+
user_schedule.setString(a + 1, charge_mode.to_s)
|
316
|
+
user_schedule.setString(a + 2, "Until: #{discharge_start}")
|
317
|
+
user_schedule.setString(a + 3, '1')
|
318
|
+
user_schedule.setString(a + 4, "Until: #{discharge_end}")
|
319
|
+
user_schedule.setString(a + 5, discharge_mode.to_s)
|
320
|
+
user_schedule.setString(a + 6, 'Until: 24:00')
|
321
|
+
user_schedule.setString(a + 7, '1')
|
322
|
+
b = a + 8
|
323
|
+
end
|
324
|
+
|
325
|
+
# make weekend modification if necessary
|
326
|
+
unless wknd
|
327
|
+
user_schedule.setString(c, 'For: WeekDays')
|
328
|
+
user_schedule.setString(b, 'For: Weekends')
|
329
|
+
user_schedule.setString(b + 1, "Until: #{charge_end}")
|
330
|
+
user_schedule.setString(b + 2, charge_mode.to_s)
|
331
|
+
user_schedule.setString(b + 3, 'Until: 24:00')
|
332
|
+
user_schedule.setString(b + 4, '1')
|
333
|
+
b += 5
|
334
|
+
end
|
335
|
+
|
336
|
+
# complete cooling season schedule if not through 12/31
|
337
|
+
if cool_end != '12/31'
|
338
|
+
user_schedule.setString(c - 1, "Through: #{cool_end}")
|
339
|
+
user_schedule.setString(b, 'Through: 12/31')
|
340
|
+
user_schedule.setString(b + 1, 'For: AllDays')
|
341
|
+
user_schedule.setString(b + 2, 'Until: 24:00')
|
342
|
+
user_schedule.setString(b + 3, '1')
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# find objects of interest in the model (used to identify container objects, air loops, and thermal zones)
|
347
|
+
cooling_coil_systems = workspace.getObjectsByType('CoilSystem:Cooling:DX'.to_IddObjectType)
|
348
|
+
air_loops = workspace.getObjectsByType('AirLoopHVAC'.to_IddObjectType)
|
349
|
+
branches = workspace.getObjectsByType('Branch'.to_IddObjectType)
|
350
|
+
hvac_zone_mixers = workspace.getObjectsByType('AirLoopHVAC:ZoneMixer'.to_IddObjectType)
|
351
|
+
zone_connections = workspace.getObjectsByType('ZoneHVAC:EquipmentConnections'.to_IddObjectType)
|
352
|
+
unitary_generic_obj = workspace.getObjectsByType('AirLoopHVAC:UnitarySystem'.to_IddObjectType)
|
353
|
+
node_lists = workspace.getObjectsByType('NodeList'.to_IddObjectType)
|
354
|
+
|
355
|
+
# create vector of all cooling system container objects
|
356
|
+
cooling_containers = OpenStudio::IdfObjectVector.new
|
357
|
+
cooling_coil_systems.each do |coil_sys|
|
358
|
+
if coil_selection.include?(coil_sys.getString(6).to_s)
|
359
|
+
cooling_containers << coil_sys
|
360
|
+
end
|
361
|
+
end
|
362
|
+
unitary_generic_obj.each do |unitary_sys|
|
363
|
+
if coil_selection.include?(unitary_sys.getString(15).to_s)
|
364
|
+
cooling_containers << unitary_sys
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
# exit gracefully if original model does not have coilSystem:Cooling objects
|
369
|
+
if cooling_containers.empty?
|
370
|
+
runner.registerError('This measure only operates on the following EnergyPlus container objects: CoilSystem:Cooling:DX and AirLoopHVAC:UnitarySystem. Measure was not applied.')
|
371
|
+
end
|
372
|
+
|
373
|
+
# create TES object string template for use in replacing existing coils; incorporates user input variables
|
374
|
+
new_tes_string =
|
375
|
+
"Coil:Cooling:DX:SingleSpeed:ThermalStorage,
|
376
|
+
NAME PLACEHOLDER, !- Name
|
377
|
+
ALWAYS_ON, !- Availability Schedule Name
|
378
|
+
#{ctl}, !- Operating Mode Control Method
|
379
|
+
#{sched}, !- Operation Mode Control Schedule Name
|
380
|
+
Ice, !- Storage Type
|
381
|
+
, !- User Defined Fluid Type
|
382
|
+
, !- Fluid Storage Volume {m3}
|
383
|
+
AutoSize, !- Ice Storage Capacity {GJ}
|
384
|
+
#{time_size_factor}, !- Storage Capacity Sizing Factor {hr}
|
385
|
+
AMBIENT NODE, !- Storage Tank Ambient Temperature Node Name
|
386
|
+
7.913, !- Storage Tank to Ambient U-value Times Area Heat Transfer Coefficient {W/K}
|
387
|
+
, !- Fluid Storage Tank Rating Temperature {C}
|
388
|
+
AutoSize, !- Rated Evaporator Air Flow Rate {m3/s}
|
389
|
+
EVAP IN NODE, !- Evaporator Air Inlet Node Name
|
390
|
+
EVAP OUT NODE, !- Evaporator Air Outlet Node Name
|
391
|
+
Yes, !- Cooling Only Mode Available
|
392
|
+
AutoSize, !- Cooling Only Mode Rated Total Evaporator Cooling Capacity {W} **IB40 Limits: 10551 W (3 ton) to 70337 W (20 ton)**
|
393
|
+
0.7, !- Cooling Only Mode Rated Sensible Heat Ratio
|
394
|
+
3.23372055845678, !- Cooling Only Mode Rated COP {W/W}
|
395
|
+
Cool-Cap-fT, !- Cooling Only Mode Total Evaporator Cooling Capacity Function of Temperature Curve Name
|
396
|
+
ConstantCubic, !- Cooling Only Mode Total Evaporator Cooling Capacity Function of Flow Fraction Curve Name
|
397
|
+
Cool-EIR-fT, !- Cooling Only Mode Energy Input Ratio Function of Temperature Curve Name
|
398
|
+
ConstantCubic, !- Cooling Only Mode Energy Input Ratio Function of Flow Fraction Curve Name
|
399
|
+
Cool-PLF-fPLR, !- Cooling Only Mode Part Load Fraction Correlation Curve Name
|
400
|
+
Cool-SHR-fT, !- Cooling Only Mode Sensible Heat Ratio Function of Temperature Curve Name
|
401
|
+
Cool-SHR-fFF, !- Cooling Only Mode Sensible Heat Ratio Function of Flow Fraction Curve Name
|
402
|
+
No, !- Cooling And Charge Mode Available
|
403
|
+
AutoSize, !- Cooling And Charge Mode Rated Total Evaporator Cooling Capacity
|
404
|
+
1.0, !- Cooling And Charge Mode Capacity Sizing Factor
|
405
|
+
AutoSize, !- Cooling And Charge Mode Rated Storage Charging Capacity
|
406
|
+
0.86, !- Cooling And Charge Mode Storage Capacity Sizing Factor
|
407
|
+
0.7, !- Cooling And Charge Mode Rated Sensible Heat Ratio
|
408
|
+
3.66668443E+00, !- Cooling And Charge Mode Cooling Rated COP
|
409
|
+
2.17, !- Cooling And Charge Mode Charging Rated COP
|
410
|
+
CoolCharge-Cool-Cap-fT, !- Cooling And Charge Mode Total Evaporator Cooling Capacity Function of Temperature Curve Name
|
411
|
+
ConstantCubic, !- Cooling And Charge Mode Total Evaporator Cooling Capacity Function of Flow Fraction Curve Name
|
412
|
+
CoolCharge-Cool-EIR-fT, !- Cooling And Charge Mode Evaporator Energy Input Ratio Function of Temperature Curve Name
|
413
|
+
ConstantCubic, !- Cooling And Charge Mode Evaporator Energy Input Ratio Function of Flow Fraction Curve Name
|
414
|
+
Cool-PLF-fPLR, !- Cooling And Charge Mode Evaporator Part Load Fraction Correlation Curve Name
|
415
|
+
CoolCharge-Charge-Cap-fT,!- Cooling And Charge Mode Storage Charge Capacity Function of Temperature Curve Name
|
416
|
+
ConstantCubic, !- Cooling And Charge Mode Storage Charge Capacity Function of Total Evaporator PLR Curve Name
|
417
|
+
CoolCharge-Charge-EIR-fT,!- Cooling And Charge Mode Storage Energy Input Ratio Function of Temperature Curve Name
|
418
|
+
ConstantCubic, !- Cooling And Charge Mode Storage Energy Input Ratio Function of Flow Fraction Curve Name
|
419
|
+
ConstantCubic, !- Cooling And Charge Mode Storage Energy Part Load Fraction Correlation Curve Name
|
420
|
+
Cool-SHR-fT, !- Cooling And Charge Mode Sensible Heat Ratio Function of Temperature Curve Name
|
421
|
+
Cool-SHR-fFF, !- Cooling And Charge Mode Sensible Heat Ratio Function of Flow Fraction Curve Name
|
422
|
+
No, !- Cooling And Discharge Mode Available
|
423
|
+
, !- Cooling And Discharge Mode Rated Total Evaporator Cooling Capacity {W}
|
424
|
+
, !- Cooling And Discharge Mode Evaporator Capacity Sizing Factor
|
425
|
+
, !- Cooling And Discharge Mode Rated Storage Discharging Capacity {W}
|
426
|
+
, !- Cooling And Discharge Mode Storage Discharge Capacity Sizing Factor
|
427
|
+
, !- Cooling And Discharge Mode Rated Sensible Heat Ratio
|
428
|
+
, !- Cooling And Discharge Mode Cooling Rated COP {W/W}
|
429
|
+
, !- Cooling And Discharge Mode Discharging Rated COP {W/W}
|
430
|
+
, !- Cooling And Discharge Mode Total Evaporator Cooling Capacity Function of Temperature Curve Name
|
431
|
+
, !- Cooling And Discharge Mode Total Evaporator Cooling Capacity Function of Flow Fraction Curve Name
|
432
|
+
, !- Cooling And Discharge Mode Evaporator Energy Input Ratio Function of Temperature Curve Name
|
433
|
+
, !- Cooling And Discharge Mode Evaporator Energy Input Ratio Function of Flow Fraction Curve Name
|
434
|
+
, !- Cooling And Discharge Mode Evaporator Part Load Fraction Correlation Curve Name
|
435
|
+
, !- Cooling And Discharge Mode Storage Discharge Capacity Function of Temperature Curve Name
|
436
|
+
, !- Cooling And Discharge Mode Storage Discharge Capacity Function of Flow Fraction Curve Name
|
437
|
+
, !- Cooling And Discharge Mode Storage Discharge Capacity Function of Total Evaporator PLR Curve Name
|
438
|
+
, !- Cooling And Discharge Mode Storage Energy Input Ratio Function of Temperature Curve Name
|
439
|
+
, !- Cooling And Discharge Mode Storage Energy Input Ratio Function of Flow Fraction Curve Name
|
440
|
+
, !- Cooling And Discharge Mode Storage Energy Part Load Fraction Correlation Curve Name
|
441
|
+
, !- Cooling And Discharge Mode Sensible Heat Ratio Function of Temperature Curve Name
|
442
|
+
, !- Cooling And Discharge Mode Sensible Heat Ratio Function of Flow Fraction Curve Name
|
443
|
+
Yes, !- Charge Only Mode Available
|
444
|
+
AutoSize, !- Charge Only Mode Rated Storage Charging Capacity {W}
|
445
|
+
0.8, !- Charge Only Mode Capacity Sizing Factor
|
446
|
+
3.09, !- Charge Only Mode Charging Rated COP {W/W}
|
447
|
+
ChargeOnly-Cap-fT, !- Charge Only Mode Storage Charge Capacity Function of Temperature Curve Name
|
448
|
+
ChargeOnly-EIR-fT, !- Charge Only Mode Storage Energy Input Ratio Function of Temperature Curve Name
|
449
|
+
Yes, !- Discharge Only Mode Available
|
450
|
+
AutoSize, !- Discharge Only Mode Rated Storage Discharging Capacity {W}
|
451
|
+
1.37, !- Discharge Only Mode Capacity Sizing Factor
|
452
|
+
0.64, !- Discharge Only Mode Rated Sensible Heat Ratio
|
453
|
+
#{discharge_cop}, !- Discharge Only Mode Rated COP {W/W}
|
454
|
+
Discharge-Cap-fT, !- Discharge Only Mode Storage Discharge Capacity Function of Temperature Curve Name
|
455
|
+
Discharge-Cap-fFF, !- Discharge Only Mode Storage Discharge Capacity Function of Flow Fraction Curve Name
|
456
|
+
ConstantBi, !- Discharge Only Mode Energy Input Ratio Function of Temperature Curve Name
|
457
|
+
ConstantCubic, !- Discharge Only Mode Energy Input Ratio Function of Flow Fraction Curve Name
|
458
|
+
ConstantCubic, !- Discharge Only Mode Part Load Fraction Correlation Curve Name
|
459
|
+
#{curve_d_shr_ft}, !- Discharge Only Mode Sensible Heat Ratio Function of Temperature Curve Name
|
460
|
+
Discharge-SHR-fFF, !- Discharge Only Mode Sensible Heat Ratio Function of Flow Fraction Curve Name
|
461
|
+
0.0, !- Ancillary Electric Power {W}
|
462
|
+
2.0, !- Cold Weather Operation Minimum Outdoor Air Temperature {C}
|
463
|
+
0.0, !- Cold Weather Operation Ancillary Power {W}
|
464
|
+
CONDENSER INLET NODE, !- Condenser Air Inlet Node Name
|
465
|
+
CONDENSER OUTLET NODE, !- Condenser Air Outlet Node Name
|
466
|
+
autocalculate, !- Condenser Design Air Flow Rate {m3/s}
|
467
|
+
1.25, !- Condenser Air Flow Sizing Factor
|
468
|
+
AirCooled, !- Condenser Type
|
469
|
+
, !- Evaporative Condenser Effectiveness {dimensionless}
|
470
|
+
, !- Evaporative Condenser Pump Rated Power Consumption {W}
|
471
|
+
, !- Basin Heater Capacity {W/K}
|
472
|
+
, !- Basin Heater Setpoint Temperature {C}
|
473
|
+
, !- Basin Heater Availability Schedule Name
|
474
|
+
, !- Supply Water Storage Tank Name
|
475
|
+
, !- Condensate Collection Water Storage Tank Name
|
476
|
+
, !- Storage Tank Plant Connection Inlet Node Name
|
477
|
+
, !- Storage Tank Plant Connection Outlet Node Name
|
478
|
+
, !- Storage Tank Plant Connection Design Flow Rate {m3/s}
|
479
|
+
, !- Storage Tank Plant Connection Heat Transfer Effectiveness
|
480
|
+
, !- Storage Tank Minimum Operating Limit Fluid Temperature {C}
|
481
|
+
; !- Storage Tank Maximum Operating Limit Fluid Temperature {C}"
|
482
|
+
# end of new TES coil string
|
483
|
+
|
484
|
+
# #Begin Coil Replacement
|
485
|
+
# iterate through all CoilSystem:Cooling objects and replace existing coils with TES coils
|
486
|
+
coil_selection.each do |sel_coil|
|
487
|
+
# get workspace object for selected coil from name
|
488
|
+
sel_coil = workspace.getObjectsByName(sel_coil)[0]
|
489
|
+
ice_cap = hardcaps[replacement_count] unless hardcaps.empty?
|
490
|
+
|
491
|
+
# get coil type in order to find get appropriate field keys
|
492
|
+
# fields of interest: (0 - Max Cap, 1 - Rated COP, 2 - Inlet Node, 3 - Outlet Node)
|
493
|
+
field_names = [] # may not be required. Check scope in Ruby documentation
|
494
|
+
sel_type = sel_coil.iddObject.type.valueDescription.to_s
|
495
|
+
case sel_type
|
496
|
+
when 'Coil:Cooling:DX:SingleSpeed'
|
497
|
+
field_names = ['Gross Rated Total Cooling Capacity', 'Gross Rated Cooling COP',
|
498
|
+
'Air Inlet Node Name', 'Air Outlet Node Name']
|
499
|
+
when 'Coil:Cooling:DX:TwoSpeed'
|
500
|
+
field_names = ['High Speed Gross Rated Total Cooling Capacity', 'High Speed Gross Rated Cooling COP',
|
501
|
+
'Air Inlet Node Name', 'Air Outlet Node Name']
|
502
|
+
end
|
503
|
+
|
504
|
+
# get field indices associated with desired keys
|
505
|
+
keys = []
|
506
|
+
field_names.each do |fn|
|
507
|
+
keys << sel_coil.iddObject.getFieldIndex(fn).to_i
|
508
|
+
end
|
509
|
+
|
510
|
+
# get old coil name and create new coil name
|
511
|
+
old_coil_name = sel_coil.getString(0).to_s
|
512
|
+
utss_name = "UTSS Coil #{replacement_count}"
|
513
|
+
|
514
|
+
# grab inlet and outlet air nodes form selected coil
|
515
|
+
inlet = sel_coil.getString(keys[2]).to_s
|
516
|
+
outlet = sel_coil.getString(keys[3]).to_s
|
517
|
+
|
518
|
+
# update inlet and outlet node names - not needed possibly add later if names become confusing
|
519
|
+
|
520
|
+
# create local ambient node
|
521
|
+
idf_oa_ambient = OpenStudio::IdfObject.new('OutdoorAir:Node'.to_IddObjectType)
|
522
|
+
ws_oa_ambient = workspace.addObject(idf_oa_ambient)
|
523
|
+
oa_ambient = ws_oa_ambient.get
|
524
|
+
oa_ambient.setString(0, "#{utss_name} OA Ambient Node")
|
525
|
+
|
526
|
+
# create new condenser inlet node
|
527
|
+
idf_condenser_inlet = OpenStudio::IdfObject.new('OutdoorAir:Node'.to_IddObjectType)
|
528
|
+
ws_condenser_inlet = workspace.addObject(idf_condenser_inlet)
|
529
|
+
condenser_inlet = ws_condenser_inlet.get
|
530
|
+
condenser_inlet.setString(0, "#{utss_name} Condenser Inlet Node")
|
531
|
+
|
532
|
+
# create new condenser inlet node
|
533
|
+
idf_condenser_outlet = OpenStudio::IdfObject.new('OutdoorAir:Node'.to_IddObjectType)
|
534
|
+
ws_condenser_outlet = workspace.addObject(idf_condenser_outlet)
|
535
|
+
condenser_outlet = ws_condenser_outlet.get
|
536
|
+
condenser_outlet.setString(0, "#{utss_name} Condenser Out Node")
|
537
|
+
|
538
|
+
# create a new UTSS object
|
539
|
+
idf_coil_object = OpenStudio::IdfObject.load(new_tes_string)
|
540
|
+
utss_obj = idf_coil_object.get
|
541
|
+
ws_tes_obj = workspace.addObject(utss_obj)
|
542
|
+
utss = ws_tes_obj.get
|
543
|
+
utss.setString(0, utss_name)
|
544
|
+
|
545
|
+
# get indices for required utss fields - reused variable names, consider changing if confusing
|
546
|
+
field_names = ['Evaporator Air Inlet Node Name', 'Evaporator Air Outlet Node Name',
|
547
|
+
'Storage Tank Ambient Temperature Node Name',
|
548
|
+
'Condenser Air Inlet Node Name', 'Condenser Air Outlet Node Name']
|
549
|
+
|
550
|
+
keys = []
|
551
|
+
field_names.each do |fn|
|
552
|
+
keys << utss.iddObject.getFieldIndex(fn).to_i
|
553
|
+
end
|
554
|
+
|
555
|
+
# updated required fields in utss object
|
556
|
+
utss.setString(keys[0], inlet) # air inlet node
|
557
|
+
utss.setString(keys[1], outlet) # air outlet node
|
558
|
+
utss.setString(keys[2], oa_ambient.name.to_s) # outdoor ambient node
|
559
|
+
utss.setString(keys[3], condenser_inlet.name.to_s) # condenser inlet node
|
560
|
+
utss.setString(keys[4], condenser_outlet.name.to_s) # condenser outlet node
|
561
|
+
utss.setString(7, ice_cap) # hardsized thermal storage capacity
|
562
|
+
|
563
|
+
# copy old coil information over to TES object (use low-speed info for 2spd coils)
|
564
|
+
case sel_coil.iddObject.name
|
565
|
+
when 'Coil:Cooling:DX:SingleSpeed'
|
566
|
+
utss.setString(16, sel_coil.getString(2).get)
|
567
|
+
utss.setString(18, sel_coil.getString(4).get)
|
568
|
+
utss.setString(19, sel_coil.getString(9).get)
|
569
|
+
utss.setString(20, sel_coil.getString(10).get)
|
570
|
+
utss.setString(21, sel_coil.getString(11).get)
|
571
|
+
utss.setString(22, sel_coil.getString(12).get)
|
572
|
+
utss.setString(23, sel_coil.getString(13).get)
|
573
|
+
when 'Coil:Cooling:DX:TwoSpeed'
|
574
|
+
utss.setString(16, sel_coil.getString(14).get)
|
575
|
+
utss.setString(18, sel_coil.getString(16).get)
|
576
|
+
utss.setString(19, sel_coil.getString(18).get)
|
577
|
+
utss.setString(20, sel_coil.getString(10).get)
|
578
|
+
utss.setString(21, sel_coil.getString(19).get)
|
579
|
+
utss.setString(22, sel_coil.getString(12).get)
|
580
|
+
utss.setString(23, sel_coil.getString(13).get)
|
581
|
+
end
|
582
|
+
|
583
|
+
# identify container object in which the coil is used
|
584
|
+
cooling_containers.each do |cont|
|
585
|
+
case cont.iddObject.type.valueDescription.to_s
|
586
|
+
when 'CoilSystem:Cooling:DX'
|
587
|
+
if cont.getString(6).to_s == old_coil_name
|
588
|
+
cont.setString(5, 'Coil:Cooling:DX:SingleSpeed:ThermalStorage')
|
589
|
+
cont.setString(6, utss_name)
|
590
|
+
break
|
591
|
+
end
|
592
|
+
when 'AirLoopHVAC:UnitarySystem'
|
593
|
+
if cont.getString(15).to_s == old_coil_name
|
594
|
+
cont.setString(14, 'Coil:Cooling:DX:SingleSpeed:ThermalStorage')
|
595
|
+
cont.setString(15, utss_name)
|
596
|
+
break
|
597
|
+
end
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
# remove old coil
|
602
|
+
workspace.removeObject(sel_coil.handle)
|
603
|
+
|
604
|
+
# increment replacement count
|
605
|
+
replacement_count += 1
|
606
|
+
|
607
|
+
## Add EMS Controller Components
|
608
|
+
# create EMS intended schedule sensor once
|
609
|
+
if replacement_count == 1
|
610
|
+
idf_sched_sensor = OpenStudio::IdfObject.new('EnergyManagementSystem:Sensor'.to_IddObjectType)
|
611
|
+
ws_sched_sensor = workspace.addObject(idf_sched_sensor)
|
612
|
+
new_sched_sensor = ws_sched_sensor.get
|
613
|
+
new_sched_sensor.setString(0, 'TESIntendedSchedule')
|
614
|
+
new_sched_sensor.setString(1, sched)
|
615
|
+
new_sched_sensor.setString(2, 'Schedule Value')
|
616
|
+
end
|
617
|
+
|
618
|
+
# clean-up variable names for EMS purposes (no spaces allowed)
|
619
|
+
u_name = utss_name.gsub(/\s/, '_')
|
620
|
+
|
621
|
+
# add EMS sensor for TES control
|
622
|
+
idf_sensor = OpenStudio::IdfObject.new('EnergyManagementSystem:Sensor'.to_IddObjectType)
|
623
|
+
ws_sensor = workspace.addObject(idf_sensor)
|
624
|
+
new_sensor = ws_sensor.get
|
625
|
+
new_sensor.setString(0, "#{u_name}_sTES")
|
626
|
+
new_sensor.setString(1, utss_name)
|
627
|
+
new_sensor.setString(2, 'Cooling Coil Ice Thermal Storage End Fraction')
|
628
|
+
|
629
|
+
# add EMS actuator for TES control
|
630
|
+
idf_actuator = OpenStudio::IdfObject.new('EnergyManagementSystem:Actuator'.to_IddObjectType)
|
631
|
+
ws_actuator = workspace.addObject(idf_actuator)
|
632
|
+
new_actuator = ws_actuator.get
|
633
|
+
new_actuator.setString(0, "#{u_name}_OpMode")
|
634
|
+
new_actuator.setString(1, utss_name)
|
635
|
+
new_actuator.setString(2, 'Coil:Cooling:DX:SingleSpeed:ThermalStorage')
|
636
|
+
new_actuator.setString(3, 'Operating Mode')
|
637
|
+
|
638
|
+
# add Global Variable to track min SOC from previous use
|
639
|
+
idf_gvar = OpenStudio::IdfObject.new('EnergyManagementSystem:GlobalVariable'.to_IddObjectType)
|
640
|
+
ws_gvar = workspace.addObject(idf_gvar)
|
641
|
+
new_gvar = ws_gvar.get
|
642
|
+
new_gvar.setString(0, "#{u_name}_MinSOC")
|
643
|
+
|
644
|
+
# add EMS program for TES control
|
645
|
+
program_string = "
|
646
|
+
EnergyManagementSystem:Program,
|
647
|
+
#{u_name}_Control, !- Name
|
648
|
+
SET #{u_name}_OpMode = TESIntendedSchedule,
|
649
|
+
IF CurrentEnvironment == 1,
|
650
|
+
SET #{u_name}_MinSOC = 1,
|
651
|
+
ENDIF,
|
652
|
+
IF (#{u_name}_OpMode == 5),
|
653
|
+
IF ( #{u_name}_sTES < 0.05 ),
|
654
|
+
SET #{u_name}_OpMode = 1,
|
655
|
+
ENDIF,
|
656
|
+
SET #{u_name}_MinSOC = #{u_name}_sTES,
|
657
|
+
ENDIF,
|
658
|
+
IF (#{u_name}_OpMode == 4),
|
659
|
+
IF ( #{u_name}_sTES > 0.99 ),
|
660
|
+
SET #{u_name}_OpMode = 1,
|
661
|
+
ENDIF,
|
662
|
+
ENDIF;"
|
663
|
+
|
664
|
+
idf_program = OpenStudio::IdfObject.load(program_string)
|
665
|
+
idf_prgm = idf_program.get
|
666
|
+
workspace.addObject(idf_prgm)
|
667
|
+
|
668
|
+
# add EMS program calling manager for TES control
|
669
|
+
idf_pcm = OpenStudio::IdfObject.new('EnergyManagementSystem:ProgramCallingManager'.to_IddObjectType)
|
670
|
+
ws_pcm = workspace.addObject(idf_pcm)
|
671
|
+
new_pcm = ws_pcm.get
|
672
|
+
new_pcm.setString(0, "#{u_name}_TES_PrgmCallMgr")
|
673
|
+
new_pcm.setString(1, 'AfterPredictorAfterHVACManagers')
|
674
|
+
new_pcm.setString(2, "#{u_name}_Control")
|
675
|
+
|
676
|
+
# register info
|
677
|
+
# Coil replaced, Coil Added, EMS Program Added
|
678
|
+
runner.registerInfo("Coil '#{old_coil_name}' was replaced with a unitary thermal storage system named" \
|
679
|
+
"'#{utss.name}' with a capacity of #{ice_cap} GJ.\n")
|
680
|
+
# end of coil replacement routine
|
681
|
+
end
|
682
|
+
|
683
|
+
# additional output for schedule verification
|
684
|
+
runner.registerInfo("The user-selected schedule for the ice unit operation is:\n\n#{user_schedule}")
|
685
|
+
|
686
|
+
# register initial and final conditions
|
687
|
+
runner.registerInitialCondition("The building started with #{cooling_containers.size} cooling coils.")
|
688
|
+
runner.registerFinalCondition("A total of #{replacement_count} cooling coils were replaced with thermal storage coil systems.")
|
689
|
+
true
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
# register the measure to be used by the application
|
694
|
+
AddPackagedIceStorage.new.registerWithApplication
|