openstudio-workflow 1.0.0.pat1 → 1.0.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 +12 -0
- data/README.md +16 -68
- data/Rakefile +9 -9
- data/bin/openstudio_cli +786 -0
- data/lib/openstudio/workflow/adapters/input/local.rb +97 -0
- data/lib/openstudio/workflow/adapters/output/local.rb +90 -0
- data/lib/openstudio/workflow/adapters/output/socket.rb +70 -0
- data/lib/openstudio/workflow/{jobs/run_preflight/run_preflight.rb → adapters/output/web.rb} +37 -19
- data/lib/openstudio/workflow/{adapter.rb → adapters/output_adapter.rb} +53 -51
- data/lib/openstudio/workflow/job.rb +22 -0
- data/lib/openstudio/workflow/jobs/{run_energyplus → resources}/monthly_report.idf +0 -0
- data/lib/openstudio/workflow/jobs/run_energyplus.rb +49 -0
- data/lib/openstudio/workflow/jobs/run_ep_measures.rb +55 -0
- data/lib/openstudio/workflow/jobs/run_initialization.rb +136 -0
- data/lib/openstudio/workflow/jobs/run_os_measures.rb +59 -0
- data/lib/openstudio/workflow/jobs/run_postprocess.rb +53 -0
- data/lib/openstudio/workflow/jobs/run_preprocess.rb +81 -0
- data/lib/openstudio/workflow/jobs/run_reporting_measures.rb +86 -0
- data/lib/openstudio/workflow/jobs/run_translation.rb +49 -0
- data/lib/openstudio/workflow/multi_delegator.rb +1 -3
- data/lib/openstudio/workflow/registry.rb +137 -0
- data/lib/openstudio/workflow/run.rb +182 -221
- data/lib/openstudio/workflow/time_logger.rb +1 -1
- data/lib/openstudio/workflow/util/energyplus.rb +564 -0
- data/lib/openstudio/workflow/util/io.rb +33 -0
- data/lib/openstudio/workflow/util/measure.rb +520 -0
- data/lib/openstudio/workflow/util/model.rb +100 -0
- data/lib/openstudio/workflow/util/post_process.rb +177 -0
- data/lib/openstudio/workflow/util/weather_file.rb +108 -0
- data/lib/openstudio/workflow/util.rb +14 -0
- data/lib/openstudio/workflow/version.rb +1 -1
- data/lib/openstudio/workflow_json.rb +399 -0
- data/lib/openstudio/workflow_runner.rb +213 -0
- data/lib/openstudio-workflow.rb +13 -118
- metadata +45 -85
- data/lib/openstudio/extended_runner.rb +0 -105
- data/lib/openstudio/workflow/adapters/local.rb +0 -101
- data/lib/openstudio/workflow/adapters/mongo.rb +0 -227
- data/lib/openstudio/workflow/jobs/lib/apply_measures.rb +0 -253
- data/lib/openstudio/workflow/jobs/run_energyplus/run_energyplus.rb +0 -314
- data/lib/openstudio/workflow/jobs/run_openstudio/run_openstudio.rb +0 -230
- data/lib/openstudio/workflow/jobs/run_postprocess/run_postprocess.rb +0 -110
- data/lib/openstudio/workflow/jobs/run_reporting_measures/run_reporting_measures.rb +0 -471
- data/lib/openstudio/workflow/jobs/run_runmanager/run_runmanager.rb +0 -247
- data/lib/openstudio/workflow/jobs/run_xml/run_xml.rb +0 -279
@@ -1,314 +0,0 @@
|
|
1
|
-
######################################################################
|
2
|
-
# Copyright (c) 2008-2014, Alliance for Sustainable Energy.
|
3
|
-
# All rights reserved.
|
4
|
-
#
|
5
|
-
# This library is free software; you can redistribute it and/or
|
6
|
-
# modify it under the terms of the GNU Lesser General Public
|
7
|
-
# License as published by the Free Software Foundation; either
|
8
|
-
# version 2.1 of the License, or (at your option) any later version.
|
9
|
-
#
|
10
|
-
# This library is distributed in the hope that it will be useful,
|
11
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
-
# Lesser General Public License for more details.
|
14
|
-
#
|
15
|
-
# You should have received a copy of the GNU Lesser General Public
|
16
|
-
# License along with this library; if not, write to the Free Software
|
17
|
-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
-
######################################################################
|
19
|
-
|
20
|
-
class RunEnergyplus
|
21
|
-
ENERGYPLUS_REGEX = /^energyplus\D{0,4}$/i
|
22
|
-
EXPAND_OBJECTS_REGEX = /^expandobjects\D{0,4}$/i
|
23
|
-
|
24
|
-
# Initialize
|
25
|
-
# param directory: base directory where the simulation files are prepared
|
26
|
-
# param logger: logger object in which to write log messages
|
27
|
-
def initialize(directory, logger, time_logger, adapter, workflow_arguments, past_results, options = {})
|
28
|
-
@logger = logger
|
29
|
-
|
30
|
-
energyplus_path = find_energyplus
|
31
|
-
defaults = {
|
32
|
-
energyplus_path: energyplus_path
|
33
|
-
}
|
34
|
-
@options = defaults.merge(options)
|
35
|
-
|
36
|
-
# TODO: use openstudio tool finder for this
|
37
|
-
@directory = directory
|
38
|
-
@run_directory = "#{@directory}/run"
|
39
|
-
@adapter = adapter
|
40
|
-
@time_logger = time_logger
|
41
|
-
@workflow_arguments = workflow_arguments
|
42
|
-
@past_results = past_results
|
43
|
-
@results = {}
|
44
|
-
|
45
|
-
# container for storing the energyplus files there were copied into the local directory. These will be
|
46
|
-
# removed at the end of the simulation.
|
47
|
-
@energyplus_files = []
|
48
|
-
@energyplus_exe = nil
|
49
|
-
@expand_objects_exe = nil
|
50
|
-
|
51
|
-
@logger.info "#{self.class} passed the following options #{@options}"
|
52
|
-
end
|
53
|
-
|
54
|
-
def perform
|
55
|
-
@logger.info "Calling #{__method__} in the #{self.class} class"
|
56
|
-
@logger.info "Current directory is #{@directory}"
|
57
|
-
|
58
|
-
# Ensure that the directory is created (but it should already be at this point)
|
59
|
-
FileUtils.mkdir_p(@run_directory)
|
60
|
-
|
61
|
-
# if the weather file is already in the directory, then just use that weather file
|
62
|
-
weather_file_name = nil
|
63
|
-
weather_files = Dir["#{@directory}/*.epw"]
|
64
|
-
if weather_files.size > 1
|
65
|
-
@logger.info 'Multiple weather files in the directory. Will rely on the weather file name in the openstudio model'
|
66
|
-
elsif weather_files.size == 1
|
67
|
-
weather_file_name = weather_files.first
|
68
|
-
end
|
69
|
-
|
70
|
-
# verify that the OSM, IDF, and the Weather files are in the run directory as the 'in.*' format
|
71
|
-
if !weather_file_name &&
|
72
|
-
@options[:run_openstudio] &&
|
73
|
-
@options[:run_openstudio][:weather_filename] &&
|
74
|
-
File.exist?(@options[:run_openstudio][:weather_filename])
|
75
|
-
weather_file_name = @options[:run_openstudio][:weather_filename]
|
76
|
-
end
|
77
|
-
|
78
|
-
if weather_file_name
|
79
|
-
# verify that it is named in.epw
|
80
|
-
@logger.info "Weather file for EnergyPlus simulation is #{weather_file_name}"
|
81
|
-
FileUtils.copy(weather_file_name, "#{@run_directory}/in.epw")
|
82
|
-
else
|
83
|
-
fail "EPW file not found or not sent to #{self.class}"
|
84
|
-
end
|
85
|
-
|
86
|
-
# check if the run folder has an IDF. If not then check if the parent folder does.
|
87
|
-
idf_file_name = nil
|
88
|
-
if File.exist?("#{@run_directory}/in.idf")
|
89
|
-
@logger.info 'IDF (in.idf) already exists in the run directory'
|
90
|
-
else
|
91
|
-
# glob for idf at the directory level
|
92
|
-
idfs = Dir["#{@directory}/*.idf"]
|
93
|
-
if idfs.size > 1
|
94
|
-
@logger.info 'Multiple IDF files in the directory. Cannot continue'
|
95
|
-
elsif idfs.size == 1
|
96
|
-
idf_file_name = idfs.first
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# Need to check the in.idf and in.osm
|
101
|
-
# FileUtils.copy(options[:osm], "#{@run_directory}/in.osm")
|
102
|
-
if idf_file_name
|
103
|
-
FileUtils.copy(idf_file_name, "#{@run_directory}/in.idf")
|
104
|
-
end
|
105
|
-
|
106
|
-
# can't create symlinks because the /vagrant mount is actually a windows mount
|
107
|
-
@time_logger.start('Copying EnergyPlus files')
|
108
|
-
prepare_energyplus_dir
|
109
|
-
@time_logger.stop('Copying EnergyPlus files')
|
110
|
-
|
111
|
-
@time_logger.start('Running EnergyPlus Preprocess Script')
|
112
|
-
energyplus_preprocess("#{@run_directory}/in.idf")
|
113
|
-
@time_logger.start('Running EnergyPlus Preprocess Script')
|
114
|
-
|
115
|
-
@time_logger.start('Running EnergyPlus')
|
116
|
-
@results = call_energyplus
|
117
|
-
@time_logger.stop('Running EnergyPlus')
|
118
|
-
|
119
|
-
@results
|
120
|
-
end
|
121
|
-
|
122
|
-
private
|
123
|
-
|
124
|
-
def find_energyplus
|
125
|
-
if ENV['ENERGYPLUSDIR']
|
126
|
-
return ENV['ENERGYPLUSDIR']
|
127
|
-
# TODO: check if method exists! first
|
128
|
-
elsif OpenStudio.respond_to? :getEnergyPlusDirectory
|
129
|
-
return OpenStudio.getEnergyPlusDirectory.to_s
|
130
|
-
elsif ENV['RUBYLIB'] =~ /OpenStudio/
|
131
|
-
warn 'Finding EnergyPlus by RUBYLIB parsing will not be supported in the near future. Use either ENERGYPLUSDIR'\
|
132
|
-
'env variable or a newer OpenStudio version that has the getEnergyPlusDirectory method'
|
133
|
-
path = ENV['RUBYLIB'].split(':')
|
134
|
-
path = File.dirname(path.find { |p| p =~ /OpenStudio/ })
|
135
|
-
# Grab the version out of the openstudio path
|
136
|
-
path += '/sharedresources/EnergyPlus-8-3-0'
|
137
|
-
|
138
|
-
return path
|
139
|
-
else
|
140
|
-
if /cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
|
141
|
-
return 'C:/EnergyPlus-8-3-0'
|
142
|
-
else
|
143
|
-
return '/usr/local/EnergyPlus-8-3-0'
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def clean_directory
|
149
|
-
@logger.info 'Removing any copied EnergyPlus files'
|
150
|
-
@energyplus_files.each do |file|
|
151
|
-
if File.exist? file
|
152
|
-
FileUtils.rm_f file
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
paths_to_rm = []
|
157
|
-
paths_to_rm << "#{@run_directory}/packaged_measures"
|
158
|
-
paths_to_rm << "#{@run_directory}/Energy+.ini"
|
159
|
-
paths_to_rm.each { |p| FileUtils.rm_rf(p) if File.exist?(p) }
|
160
|
-
end
|
161
|
-
|
162
|
-
# Prepare the directory to run EnergyPlus. In EnergyPlus < 8.2, we have to copy all the files into the directory.
|
163
|
-
#
|
164
|
-
# @return [Boolean] Returns true is there is more than one file copied
|
165
|
-
def prepare_energyplus_dir
|
166
|
-
@logger.info "Copying EnergyPlus files to run directory: #{@run_directory}"
|
167
|
-
Dir["#{@options[:energyplus_path]}/*"].each do |file|
|
168
|
-
next if File.directory? file
|
169
|
-
next if File.extname(file).downcase =~ /.pdf|.app|.html|.gif|.txt|.xlsx/
|
170
|
-
|
171
|
-
dest_file = "#{@run_directory}/#{File.basename(file)}"
|
172
|
-
@energyplus_files << dest_file
|
173
|
-
|
174
|
-
@energyplus_exe = File.basename(dest_file) if File.basename(dest_file) =~ ENERGYPLUS_REGEX
|
175
|
-
@expand_objects_exe = File.basename(dest_file) if File.basename(dest_file) =~ EXPAND_OBJECTS_REGEX
|
176
|
-
FileUtils.copy file, dest_file
|
177
|
-
end
|
178
|
-
|
179
|
-
fail "Could not find EnergyPlus executable in #{@options[:energyplus_path]}" unless @energyplus_exe
|
180
|
-
fail "Could not find ExpandObjects executable in #{@options[:energyplus_path]}" unless @expand_objects_exe
|
181
|
-
|
182
|
-
@energyplus_files.size > 0
|
183
|
-
end
|
184
|
-
|
185
|
-
def call_energyplus
|
186
|
-
begin
|
187
|
-
current_dir = Dir.pwd
|
188
|
-
Dir.chdir(@run_directory)
|
189
|
-
@logger.info "Starting simulation in run directory: #{Dir.pwd}"
|
190
|
-
|
191
|
-
File.open('stdout-expandobject', 'w') do |file|
|
192
|
-
IO.popen("./#{@expand_objects_exe}") do |io|
|
193
|
-
while (line = io.gets)
|
194
|
-
file << line
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
# Check if expand objects did anythying
|
200
|
-
if File.exist? 'expanded.idf'
|
201
|
-
FileUtils.mv('in.idf', 'pre-expand.idf', force: true) if File.exist?('in.idf')
|
202
|
-
FileUtils.mv('expanded.idf', 'in.idf', force: true)
|
203
|
-
end
|
204
|
-
|
205
|
-
# create stdout
|
206
|
-
File.open('stdout-energyplus', 'w') do |file|
|
207
|
-
IO.popen("./#{@energyplus_exe} 2>&1") do |io|
|
208
|
-
while (line = io.gets)
|
209
|
-
file << line
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
r = $?
|
214
|
-
|
215
|
-
@logger.info "EnergyPlus returned '#{r}'"
|
216
|
-
unless r == 0
|
217
|
-
@logger.warn 'EnergyPlus returned a non-zero exit code. Check the stdout-energyplus log.'
|
218
|
-
end
|
219
|
-
|
220
|
-
if File.exist? 'eplusout.end'
|
221
|
-
f = File.read('eplusout.end').force_encoding('ISO-8859-1').encode('utf-8', replace: nil)
|
222
|
-
warnings_count = f[/(\d*).Warning/, 1]
|
223
|
-
error_count = f[/(\d*).Severe.Errors/, 1]
|
224
|
-
@logger.info "EnergyPlus finished with #{warnings_count} warnings and #{error_count} severe errors"
|
225
|
-
if f =~ /EnergyPlus Terminated--Fatal Error Detected/
|
226
|
-
fail 'EnergyPlus Terminated with a Fatal Error. Check eplusout.err log.'
|
227
|
-
end
|
228
|
-
else
|
229
|
-
fail 'EnergyPlus failed and did not create an eplusout.end file. Check the stdout-energyplus log.'
|
230
|
-
end
|
231
|
-
|
232
|
-
if File.exist? 'eplusout.err'
|
233
|
-
eplus_err = File.read('eplusout.err').force_encoding('ISO-8859-1').encode('utf-8', replace: nil)
|
234
|
-
if eplus_err =~ /EnergyPlus Terminated--Fatal Error Detected/
|
235
|
-
fail 'EnergyPlus Terminated with a Fatal Error. Check eplusout.err log.'
|
236
|
-
end
|
237
|
-
end
|
238
|
-
rescue => e
|
239
|
-
log_message = "#{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
|
240
|
-
@logger.error log_message
|
241
|
-
raise log_message
|
242
|
-
ensure
|
243
|
-
@logger.info "Ensuring 'clean' directory"
|
244
|
-
clean_directory
|
245
|
-
|
246
|
-
Dir.chdir(current_dir)
|
247
|
-
@logger.info 'EnergyPlus Completed'
|
248
|
-
end
|
249
|
-
|
250
|
-
{}
|
251
|
-
end
|
252
|
-
|
253
|
-
# Run this code before running energyplus to make sure the reporting variables are setup correctly
|
254
|
-
def energyplus_preprocess(idf_filename)
|
255
|
-
@logger.info 'Running EnergyPlus Preprocess'
|
256
|
-
|
257
|
-
fail "Could not find IDF file in run directory (#{idf_filename})" unless File.exist? idf_filename
|
258
|
-
|
259
|
-
new_objects = []
|
260
|
-
needs_monthlyoutput = false
|
261
|
-
|
262
|
-
idf = OpenStudio::IdfFile.load(idf_filename).get
|
263
|
-
# save the pre-preprocess file
|
264
|
-
File.open("#{File.dirname(idf_filename)}/pre-preprocess.idf", 'w') { |f| f << idf.to_s }
|
265
|
-
|
266
|
-
needs_sqlobj = idf.getObjectsByType('Output:SQLite'.to_IddObjectType).empty?
|
267
|
-
|
268
|
-
needs_monthlyoutput = idf.getObjectsByName('Building Energy Performance - Natural Gas').empty? ||
|
269
|
-
idf.getObjectsByName('Building Energy Performance - Electricity').empty? ||
|
270
|
-
idf.getObjectsByName('Building Energy Performance - District Heating').empty? ||
|
271
|
-
idf.getObjectsByName('Building Energy Performance - District Cooling').empty?
|
272
|
-
|
273
|
-
# this is a workaround for issue #1699 -- remove when 1699 is closed.
|
274
|
-
new_objects << 'Output:Variable,*,Zone Air Temperature,Hourly;'
|
275
|
-
new_objects << 'Output:Variable,*,Zone Air Relative Humidity,Daily;'
|
276
|
-
new_objects << 'Output:Variable,*,Site Outdoor Air Drybulb Temperature,Monthly;'
|
277
|
-
new_objects << 'Output:Variable,*,Site Outdoor Air Wetbulb Temperature,Timestep;'
|
278
|
-
|
279
|
-
if needs_sqlobj
|
280
|
-
@logger.info 'Adding SQL Output to IDF'
|
281
|
-
new_objects << '
|
282
|
-
Output:SQLite,
|
283
|
-
SimpleAndTabular; ! Option Type
|
284
|
-
'
|
285
|
-
end
|
286
|
-
|
287
|
-
if needs_monthlyoutput
|
288
|
-
monthly_report_idf = File.join(File.dirname(__FILE__), 'monthly_report.idf')
|
289
|
-
idf_file = OpenStudio::IdfFile.load(File.read(monthly_report_idf), 'EnergyPlus'.to_IddFileType).get
|
290
|
-
idf.addObjects(idf_file.objects)
|
291
|
-
end
|
292
|
-
|
293
|
-
# These are supposedly needed for the calibration report
|
294
|
-
new_objects << 'Output:Meter:MeterFileOnly,Gas:Facility,Daily;'
|
295
|
-
new_objects << 'Output:Meter:MeterFileOnly,Electricity:Facility,Timestep;'
|
296
|
-
new_objects << 'Output:Meter:MeterFileOnly,Electricity:Facility,Daily;'
|
297
|
-
|
298
|
-
# Always add in the timestep facility meters
|
299
|
-
new_objects << 'Output:Meter,Electricity:Facility,Timestep;'
|
300
|
-
new_objects << 'Output:Meter,Gas:Facility,Timestep;'
|
301
|
-
new_objects << 'Output:Meter,DistrictCooling:Facility,Timestep;'
|
302
|
-
new_objects << 'Output:Meter,DistrictHeating:Facility,Timestep;'
|
303
|
-
|
304
|
-
new_objects.each do |obj|
|
305
|
-
object = OpenStudio::IdfObject.load(obj).get
|
306
|
-
idf.addObject(object)
|
307
|
-
end
|
308
|
-
|
309
|
-
# save the file
|
310
|
-
File.open(idf_filename, 'w') { |f| f << idf.to_s }
|
311
|
-
|
312
|
-
@logger.info 'Finished EnergyPlus Preprocess'
|
313
|
-
end
|
314
|
-
end
|
@@ -1,230 +0,0 @@
|
|
1
|
-
######################################################################
|
2
|
-
# Copyright (c) 2008-2014, Alliance for Sustainable Energy.
|
3
|
-
# All rights reserved.
|
4
|
-
#
|
5
|
-
# This library is free software; you can redistribute it and/or
|
6
|
-
# modify it under the terms of the GNU Lesser General Public
|
7
|
-
# License as published by the Free Software Foundation; either
|
8
|
-
# version 2.1 of the License, or (at your option) any later version.
|
9
|
-
#
|
10
|
-
# This library is distributed in the hope that it will be useful,
|
11
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
-
# Lesser General Public License for more details.
|
14
|
-
#
|
15
|
-
# You should have received a copy of the GNU Lesser General Public
|
16
|
-
# License along with this library; if not, write to the Free Software
|
17
|
-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
-
######################################################################
|
19
|
-
|
20
|
-
class RunOpenstudio
|
21
|
-
# Mixin the MeasureApplication module to apply measures
|
22
|
-
include OpenStudio::Workflow::ApplyMeasures
|
23
|
-
|
24
|
-
# Initialize
|
25
|
-
# param directory: base directory where the simulation files are prepared
|
26
|
-
# param logger: logger object in which to write log messages
|
27
|
-
def initialize(directory, logger, time_logger, adapter, workflow_arguments, past_results, options = {})
|
28
|
-
defaults = { format: 'hash', analysis_root_path: '.' }
|
29
|
-
warn 'Option of use_monthly_reports is deprecated. Monthly reports are always generated.' if options[:use_monthly_reports]
|
30
|
-
|
31
|
-
@options = defaults.merge(options)
|
32
|
-
@directory = directory
|
33
|
-
# TODO: there is a base number of arguments that each job will need including @run_directory. abstract it out.
|
34
|
-
@run_directory = "#{@directory}/run"
|
35
|
-
@adapter = adapter
|
36
|
-
@results = {}
|
37
|
-
@logger = logger
|
38
|
-
@time_logger = time_logger
|
39
|
-
@workflow_arguments = workflow_arguments
|
40
|
-
@past_results = past_results
|
41
|
-
@logger.info "#{self.class} passed the following options #{@options}"
|
42
|
-
|
43
|
-
# initialize instance variables that are needed in the perform section
|
44
|
-
@model = nil
|
45
|
-
@model_idf = nil
|
46
|
-
@initial_weather_file = nil
|
47
|
-
@weather_file_path = nil
|
48
|
-
@analysis_json = nil
|
49
|
-
# TODO: rename datapoint_json to just datapoint
|
50
|
-
@datapoint_json = nil
|
51
|
-
@output_attributes = {}
|
52
|
-
@report_measures = []
|
53
|
-
end
|
54
|
-
|
55
|
-
def perform
|
56
|
-
@logger.info "Calling #{__method__} in the #{self.class} class"
|
57
|
-
@logger.info "Current directory is #{@directory}"
|
58
|
-
|
59
|
-
@logger.info 'Retrieving datapoint and problem'
|
60
|
-
@datapoint_json = @adapter.get_datapoint(@directory, @options)
|
61
|
-
@analysis_json = @adapter.get_problem(@directory, @options)
|
62
|
-
|
63
|
-
if @analysis_json && @analysis_json[:analysis]
|
64
|
-
@model = load_seed_model
|
65
|
-
|
66
|
-
load_weather_file
|
67
|
-
|
68
|
-
apply_measures(:openstudio_measure)
|
69
|
-
@logger.info('Finished applying OpenStudio measures.')
|
70
|
-
|
71
|
-
@time_logger.start('Translating to EnergyPlus')
|
72
|
-
translate_to_energyplus
|
73
|
-
@time_logger.stop('Translating to EnergyPlus')
|
74
|
-
|
75
|
-
apply_measures(:energyplus_measure)
|
76
|
-
@logger.info('Finished applying EnergyPlus measures.')
|
77
|
-
|
78
|
-
# check if the weather file has changed. This is cheesy for now. Should have a default measure that
|
79
|
-
# always sets the weather file so that it can be better controlled
|
80
|
-
updated_weather_file = get_weather_file_from_model
|
81
|
-
unless updated_weather_file == @initial_weather_file
|
82
|
-
# reset the result hash so the future processes know which weather file to run
|
83
|
-
@logger.info "Updating the weather file to be '#{updated_weather_file}'"
|
84
|
-
if (Pathname.new updated_weather_file).absolute? && (Pathname.new updated_weather_file).exist?
|
85
|
-
@results[:weather_filename] = updated_weather_file
|
86
|
-
else
|
87
|
-
@results[:weather_filename] = "#{@weather_file_path}/#{updated_weather_file}"
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
@logger.info 'Saving measure output attributes JSON'
|
92
|
-
File.open("#{@run_directory}/measure_attributes.json", 'w') do |f|
|
93
|
-
f << JSON.pretty_generate(@output_attributes)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
@time_logger.start('Saving OSM and IDF')
|
98
|
-
save_osm_and_idf
|
99
|
-
@time_logger.stop('Saving OSM and IDF')
|
100
|
-
|
101
|
-
@results
|
102
|
-
end
|
103
|
-
|
104
|
-
private
|
105
|
-
|
106
|
-
def save_osm_and_idf
|
107
|
-
# save the data
|
108
|
-
osm_filename = "#{@run_directory}/in.osm"
|
109
|
-
File.open(osm_filename, 'w') { |f| f << @model.to_s }
|
110
|
-
|
111
|
-
idf_filename = "#{@run_directory}/in.idf"
|
112
|
-
File.open(idf_filename, 'w') { |f| f << @model_idf.to_s }
|
113
|
-
|
114
|
-
@results[:osm] = File.expand_path(osm_filename)
|
115
|
-
@results[:idf] = File.expand_path(idf_filename)
|
116
|
-
end
|
117
|
-
|
118
|
-
def load_seed_model
|
119
|
-
model = nil
|
120
|
-
@logger.info 'Loading seed model'
|
121
|
-
|
122
|
-
baseline_model_path = nil
|
123
|
-
# Unique case to use the previous generated OSM as the seed
|
124
|
-
if @options[:run_xml] && @options[:run_xml][:osm_filename]
|
125
|
-
if File.exist? @options[:run_xml][:osm_filename]
|
126
|
-
baseline_model_path = @options[:run_xml][:osm_filename]
|
127
|
-
end
|
128
|
-
elsif @analysis_json[:analysis][:seed]
|
129
|
-
@logger.info "Seed model is #{@analysis_json[:analysis][:seed]}"
|
130
|
-
if @analysis_json[:analysis][:seed][:path]
|
131
|
-
|
132
|
-
# assume that the seed model has been placed in the directory
|
133
|
-
baseline_model_path = File.expand_path(
|
134
|
-
File.join(@options[:analysis_root_path], @analysis_json[:analysis][:seed][:path]))
|
135
|
-
else
|
136
|
-
fail 'No seed model path in JSON defined'
|
137
|
-
end
|
138
|
-
else
|
139
|
-
# TODO: create a blank model and return
|
140
|
-
fail 'No seed model block'
|
141
|
-
end
|
142
|
-
|
143
|
-
if baseline_model_path
|
144
|
-
if File.exist? baseline_model_path
|
145
|
-
@logger.info "Reading in baseline model #{baseline_model_path}"
|
146
|
-
translator = OpenStudio::OSVersion::VersionTranslator.new
|
147
|
-
model = translator.loadModel(baseline_model_path)
|
148
|
-
fail 'OpenStudio model is empty or could not be loaded' if model.empty?
|
149
|
-
model = model.get
|
150
|
-
else
|
151
|
-
fail "Seed model '#{baseline_model_path}' did not exist"
|
152
|
-
end
|
153
|
-
else
|
154
|
-
fail 'No baseline/seed model found'
|
155
|
-
end
|
156
|
-
|
157
|
-
model
|
158
|
-
end
|
159
|
-
|
160
|
-
# Save the weather file to the instance variable
|
161
|
-
def load_weather_file
|
162
|
-
@initial_weather_file = get_weather_file_from_model
|
163
|
-
|
164
|
-
weather_filename = nil
|
165
|
-
if @options[:run_xml] && @options[:run_xml][:weather_filename]
|
166
|
-
if File.exist? @options[:run_xml][:weather_filename]
|
167
|
-
weather_filename = @options[:run_xml][:weather_filename]
|
168
|
-
end
|
169
|
-
elsif @analysis_json[:analysis][:weather_file]
|
170
|
-
if @analysis_json[:analysis][:weather_file][:path]
|
171
|
-
weather_filename = File.expand_path(
|
172
|
-
File.join(@options[:analysis_root_path], @analysis_json[:analysis][:weather_file][:path])
|
173
|
-
)
|
174
|
-
@weather_file_path = File.dirname(weather_filename)
|
175
|
-
else
|
176
|
-
fail 'No weather file path defined'
|
177
|
-
end
|
178
|
-
else
|
179
|
-
fail 'No weather file block defined'
|
180
|
-
end
|
181
|
-
|
182
|
-
unless File.exist?(weather_filename)
|
183
|
-
fail "Could not find weather file for simulation #{weather_filename}"
|
184
|
-
end
|
185
|
-
|
186
|
-
@results[:weather_filename] = weather_filename
|
187
|
-
|
188
|
-
weather_filename
|
189
|
-
end
|
190
|
-
|
191
|
-
# return the weather file from the model. If the weather file is defined in the model, then
|
192
|
-
# it checks the file paths to check if the model exists. This allows for a user to upload a
|
193
|
-
# weather file in a measure and then have the measure's path be used for the weather file.
|
194
|
-
def get_weather_file_from_model
|
195
|
-
wf = nil
|
196
|
-
# grab the weather file out of the OSM if it exists
|
197
|
-
if @model.weatherFile.empty?
|
198
|
-
@logger.info 'No weather file in model'
|
199
|
-
else
|
200
|
-
p = @model.weatherFile.get.path.get.to_s.gsub('file://', '')
|
201
|
-
if File.exist? p
|
202
|
-
# use this entire path
|
203
|
-
@logger.info "Full path to weather file exists #{p}"
|
204
|
-
wf = p
|
205
|
-
else
|
206
|
-
# this is the weather file from the OSM model
|
207
|
-
wf = File.basename(@model.weatherFile.get.path.get.to_s)
|
208
|
-
end
|
209
|
-
|
210
|
-
# @logger.info "Initial model weather file is #{wf}" # unless model.weatherFile.empty?
|
211
|
-
end
|
212
|
-
|
213
|
-
wf
|
214
|
-
end
|
215
|
-
|
216
|
-
# Forward translate to energyplus
|
217
|
-
def translate_to_energyplus
|
218
|
-
if @model_idf.nil?
|
219
|
-
@logger.info 'Translate object to EnergyPlus IDF in Prep for EnergyPlus Measure'
|
220
|
-
a = Time.now
|
221
|
-
# ensure objects exist for reporting purposes
|
222
|
-
@model.getFacility
|
223
|
-
@model.getBuilding
|
224
|
-
forward_translator = OpenStudio::EnergyPlus::ForwardTranslator.new
|
225
|
-
@model_idf = forward_translator.translateModel(@model)
|
226
|
-
b = Time.now
|
227
|
-
@logger.info "Translate object to EnergyPlus IDF took #{b.to_f - a.to_f}"
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
@@ -1,110 +0,0 @@
|
|
1
|
-
######################################################################
|
2
|
-
# Copyright (c) 2008-2014, Alliance for Sustainable Energy.
|
3
|
-
# All rights reserved.
|
4
|
-
#
|
5
|
-
# This library is free software; you can redistribute it and/or
|
6
|
-
# modify it under the terms of the GNU Lesser General Public
|
7
|
-
# License as published by the Free Software Foundation; either
|
8
|
-
# version 2.1 of the License, or (at your option) any later version.
|
9
|
-
#
|
10
|
-
# This library is distributed in the hope that it will be useful,
|
11
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
-
# Lesser General Public License for more details.
|
14
|
-
#
|
15
|
-
# You should have received a copy of the GNU Lesser General Public
|
16
|
-
# License along with this library; if not, write to the Free Software
|
17
|
-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
-
######################################################################
|
19
|
-
|
20
|
-
# Run precanned post processing to extract object functions
|
21
|
-
# TODO: I hear that measures can step on each other if not run in their own directory
|
22
|
-
|
23
|
-
require 'csv'
|
24
|
-
require 'ostruct'
|
25
|
-
|
26
|
-
class RunPostprocess
|
27
|
-
# Mixin the MeasureApplication module to apply measures
|
28
|
-
include OpenStudio::Workflow::ApplyMeasures
|
29
|
-
|
30
|
-
def initialize(directory, logger, time_logger, adapter, workflow_arguments, past_results, options = {})
|
31
|
-
defaults = {}
|
32
|
-
@options = defaults.merge(options)
|
33
|
-
@directory = directory
|
34
|
-
@run_directory = "#{@directory}/run"
|
35
|
-
@adapter = adapter
|
36
|
-
@logger = logger
|
37
|
-
@time_logger = time_logger
|
38
|
-
@workflow_arguments = workflow_arguments
|
39
|
-
@past_results = past_results
|
40
|
-
@results = {}
|
41
|
-
@output_attributes = {}
|
42
|
-
|
43
|
-
@logger.info "#{self.class} passed the following options #{@options}"
|
44
|
-
end
|
45
|
-
|
46
|
-
def perform
|
47
|
-
@logger.info "Calling #{__method__} in the #{self.class} class"
|
48
|
-
@logger.info 'RunPostProcess Retrieving datapoint and problem'
|
49
|
-
|
50
|
-
begin
|
51
|
-
cleanup
|
52
|
-
rescue => e
|
53
|
-
log_message = "Runner error #{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
|
54
|
-
@logger.error log_message
|
55
|
-
# raise log_message
|
56
|
-
end
|
57
|
-
|
58
|
-
@results
|
59
|
-
end
|
60
|
-
|
61
|
-
def cleanup
|
62
|
-
# move any of the reporting file to the 'reports' directory for serverside access
|
63
|
-
eplus_search_path = nil
|
64
|
-
FileUtils.mkdir_p "#{@directory}/reports"
|
65
|
-
|
66
|
-
# try to find the energyplus result file
|
67
|
-
eplus_html = "#{@run_directory}/eplustbl.htm"
|
68
|
-
unless File.exist? eplus_html
|
69
|
-
eplus_html = Dir["#{@directory}/*EnergyPlus*/eplustbl.htm"].last || nil
|
70
|
-
end
|
71
|
-
|
72
|
-
if eplus_html
|
73
|
-
if File.exist? eplus_html
|
74
|
-
# do some encoding on the html if possible
|
75
|
-
html = File.read(eplus_html)
|
76
|
-
html = html.force_encoding('ISO-8859-1').encode('utf-8', replace: nil)
|
77
|
-
File.open("#{@directory}/reports/eplustbl.html", 'w') { |f| f << html }
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# Also, find any "report*.*" files
|
82
|
-
Dir["#{@run_directory}/*/report*.*"].each do |report|
|
83
|
-
# get the parent directory of the file and snake case it
|
84
|
-
# do i need to force encoding on this as well?
|
85
|
-
measure_class_name = File.basename(File.dirname(report)).to_underscore
|
86
|
-
file_ext = File.extname(report)
|
87
|
-
append_str = File.basename(report, '.*')
|
88
|
-
new_file_name = "#{@directory}/reports/#{measure_class_name}_#{append_str}#{file_ext}"
|
89
|
-
FileUtils.copy report, new_file_name
|
90
|
-
end
|
91
|
-
|
92
|
-
# Remove empty directories in run folder
|
93
|
-
Dir["#{@run_directory}/*"].select { |d| File.directory? d }.select { |d| (Dir.entries(d) - %w(. ..)).empty? }.each do |d|
|
94
|
-
@logger.info "Removing empty directory #{d}"
|
95
|
-
Dir.rmdir d
|
96
|
-
end
|
97
|
-
|
98
|
-
paths_to_rm = []
|
99
|
-
# paths_to_rm << Pathname.glob("#{@run_directory}/*.osm")
|
100
|
-
# paths_to_rm << Pathname.glob("#{@run_directory}/*.idf") # keep the idfs
|
101
|
-
# paths_to_rm << Pathname.glob("*.audit")
|
102
|
-
# paths_to_rm << Pathname.glob("*.bnd")
|
103
|
-
# paths_to_rm << Pathname.glob("#{@run_directory}/*.eso")
|
104
|
-
paths_to_rm << Pathname.glob("#{@run_directory}/*.mtr")
|
105
|
-
paths_to_rm << Pathname.glob("#{@run_directory}/*.epw")
|
106
|
-
paths_to_rm << Pathname.glob("#{@run_directory}/*.mtd")
|
107
|
-
paths_to_rm << Pathname.glob("#{@run_directory}/*.rdd")
|
108
|
-
paths_to_rm.each { |p| FileUtils.rm_rf(p) }
|
109
|
-
end
|
110
|
-
end
|