openstudio-workflow 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -72
  3. data/README.md +93 -48
  4. data/Rakefile +36 -36
  5. data/lib/openstudio-workflow.rb +49 -49
  6. data/lib/openstudio/workflow/adapters/input/local.rb +244 -240
  7. data/lib/openstudio/workflow/adapters/output/local.rb +95 -95
  8. data/lib/openstudio/workflow/adapters/output/socket.rb +91 -91
  9. data/lib/openstudio/workflow/adapters/output/web.rb +66 -66
  10. data/lib/openstudio/workflow/adapters/output_adapter.rb +147 -147
  11. data/lib/openstudio/workflow/job.rb +22 -22
  12. data/lib/openstudio/workflow/jobs/resources/monthly_report.idf +222 -222
  13. data/lib/openstudio/workflow/jobs/run_energyplus.rb +49 -49
  14. data/lib/openstudio/workflow/jobs/run_ep_measures.rb +55 -55
  15. data/lib/openstudio/workflow/jobs/run_initialization.rb +169 -167
  16. data/lib/openstudio/workflow/jobs/run_os_measures.rb +69 -69
  17. data/lib/openstudio/workflow/jobs/run_postprocess.rb +53 -53
  18. data/lib/openstudio/workflow/jobs/run_preprocess.rb +69 -69
  19. data/lib/openstudio/workflow/jobs/run_reporting_measures.rb +98 -98
  20. data/lib/openstudio/workflow/jobs/run_translation.rb +61 -61
  21. data/lib/openstudio/workflow/multi_delegator.rb +46 -46
  22. data/lib/openstudio/workflow/registry.rb +137 -137
  23. data/lib/openstudio/workflow/run.rb +299 -299
  24. data/lib/openstudio/workflow/time_logger.rb +53 -53
  25. data/lib/openstudio/workflow/util.rb +14 -14
  26. data/lib/openstudio/workflow/util/energyplus.rb +566 -564
  27. data/lib/openstudio/workflow/util/io.rb +33 -33
  28. data/lib/openstudio/workflow/util/measure.rb +588 -588
  29. data/lib/openstudio/workflow/util/model.rb +100 -100
  30. data/lib/openstudio/workflow/util/post_process.rb +187 -187
  31. data/lib/openstudio/workflow/util/weather_file.rb +108 -108
  32. data/lib/openstudio/workflow/version.rb +24 -24
  33. data/lib/openstudio/workflow_json.rb +426 -426
  34. data/lib/openstudio/workflow_runner.rb +233 -215
  35. metadata +3 -3
@@ -1,299 +1,299 @@
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
- require_relative 'registry'
21
- require_relative 'adapters/input/local'
22
- require_relative 'adapters/output/local'
23
-
24
- require 'logger'
25
- require 'pathname'
26
-
27
- # Run Class for OpenStudio workflow. All comments here need some love, as well as the code itself
28
- module OpenStudio
29
- module Workflow
30
- class Run
31
- attr_accessor :registry
32
-
33
- attr_reader :options
34
- attr_reader :input_adapter
35
- attr_reader :output_adapter
36
- attr_reader :final_message
37
- attr_reader :job_results
38
- attr_reader :current_state
39
-
40
- # Define the default set of jobs. Note that the states of :queued of :finished need to exist for all job arrays.
41
- #
42
- def self.default_jobs
43
- [
44
- { state: :queued, next_state: :initialization, options: { initial: true } },
45
- { state: :initialization, next_state: :os_measures, job: :RunInitialization,
46
- file: 'openstudio/workflow/jobs/run_initialization', options: {} },
47
- { state: :os_measures, next_state: :translator, job: :RunOpenStudioMeasures,
48
- file: 'openstudio/workflow/jobs/run_os_measures.rb', options: {} },
49
- { state: :translator, next_state: :ep_measures, job: :RunTranslation,
50
- file: 'openstudio/workflow/jobs/run_translation.rb', options: {} },
51
- { state: :ep_measures, next_state: :preprocess, job: :RunEnergyPlusMeasures,
52
- file: 'openstudio/workflow/jobs/run_ep_measures.rb', options: {} },
53
- { state: :preprocess, next_state: :simulation, job: :RunPreprocess,
54
- file: 'openstudio/workflow/jobs/run_preprocess.rb', options: {} },
55
- { state: :simulation, next_state: :reporting_measures, job: :RunEnergyPlus,
56
- file: 'openstudio/workflow/jobs/run_energyplus.rb', options: {} },
57
- { state: :reporting_measures, next_state: :postprocess, job: :RunReportingMeasures,
58
- file: 'openstudio/workflow/jobs/run_reporting_measures.rb', options: {} },
59
- { state: :postprocess, next_state: :finished, job: :RunPostprocess,
60
- file: 'openstudio/workflow/jobs/run_postprocess.rb', options: {} },
61
- { state: :finished },
62
- { state: :errored }
63
- ]
64
- end
65
-
66
- # Initialize a new run class
67
- #
68
- # @param [String] osw_path the path to the OSW file to run. It is highly recommended that this be an absolute
69
- # path, however if not it will be made absolute relative to the current working directory
70
- # @param [Hash] user_options ({}) A set of user-specified options that are used to override default behaviors.
71
- # @option user_options [Hash] :cleanup Remove unneccessary files during post processing, overrides OSW option if set, defaults to true
72
- # @option user_options [Hash] :debug Print debugging messages, overrides OSW option if set, defaults to false
73
- # @option user_options [Hash] :energyplus_path Specifies path to energyplus executable, defaults to empty
74
- # @option user_options [Hash] :jobs Simulation workflow, overrides jobs in OSW if set, defaults to default_jobs
75
- # @option user_options [Hash] :output_adapter Output adapter to use, overrides output adapter in OSW if set, defaults to local adapter
76
- # @option user_options [Hash] :preserve_run_dir Prevents run directory from being cleaned prior to run, overrides OSW option if set, defaults to false - DLM, Deprecate
77
- # @option user_options [Hash] :profile Produce additional output for profiling simulations, defaults to false
78
- # @option user_options [Hash] :targets Log targets to write to, defaults to standard out and run.log
79
- # @option user_options [Hash] :verify_osw Check OSW for correctness, defaults to true
80
- # @option user_options [Hash] :weather_file Initial weather file to load, overrides OSW option if set, defaults to empty
81
- def initialize(osw_path, user_options = {})
82
- # DLM - what is final_message?
83
- @final_message = ''
84
- @current_state = nil
85
- @options = {}
86
-
87
- # Registry is a large hash of objects that are populated during the run, the number of objects in the registry should be reduced over time, especially if the functionality can be added to the WorkflowJSON class
88
- # - analysis - the current OSA parsed as a Ruby Hash
89
- # - datapoint - the current OSD parsed as a Ruby Hash
90
- # - log_targets - IO devices that are being logged to
91
- # - logger - general logger
92
- # - model - the current OpenStudio Model object, updated after each step
93
- # - model_idf - the current EnergyPlus Workspace object, updated after each step
94
- # - openstudio_2 - true if we are running in OpenStudio 2.X environment
95
- # - osw_path - the path the OSW was loaded from as a string
96
- # - osw_dir - the directory the OSW was loaded from as a string
97
- # - output_attributes - added during simulation time
98
- # - results - objective function values
99
- # - root_dir - the root directory in the OSW as a string
100
- # - run_dir - the run directory for the simulation as a string
101
- # - runner - the current OSRunner object
102
- # - sql - the path to the current EnergyPlus SQL file as a string
103
- # - time_logger - logger for doing profiling - time to run each step will be captured in OSResult, deprecate
104
- # - wf - the path to the current weather file as a string, updated after each step
105
- # - workflow - the current OSW parsed as a Ruby Hash
106
- # - workflow_json - the current WorkflowJSON object
107
- @registry = Registry.new
108
-
109
- openstudio_2 = false
110
- begin
111
- # OpenStudio 2.X test
112
- OpenStudio::WorkflowJSON.new
113
- openstudio_2 = true
114
- rescue NameError => e
115
- end
116
- @registry.register(:openstudio_2) { openstudio_2 }
117
-
118
- # get the input osw
119
- @input_adapter = OpenStudio::Workflow::InputAdapter::Local.new(osw_path)
120
-
121
- # DLM: need to check that we have correct permissions to all these paths
122
- @registry.register(:osw_path) { Pathname.new(@input_adapter.osw_path).realpath }
123
- @registry.register(:osw_dir) { Pathname.new(@input_adapter.osw_dir).realpath }
124
- @registry.register(:run_dir) { Pathname.new(@input_adapter.run_dir).cleanpath } # run dir might not yet exist, calling realpath will throw
125
-
126
- # get info to set up logging first in case of failures later
127
- @options[:debug] = @input_adapter.debug(user_options, false)
128
- @options[:preserve_run_dir] = @input_adapter.preserve_run_dir(user_options, false)
129
- @options[:profile] = @input_adapter.profile(user_options, false)
130
-
131
- # if running in osw dir, force preserve run dir
132
- if @registry[:osw_dir] == @registry[:run_dir]
133
- # force preserving the run directory
134
- @options[:preserve_run_dir] = true
135
- end
136
-
137
- # By default blow away the entire run directory every time and recreate it
138
- unless @options[:preserve_run_dir]
139
- if File.exist?(@registry[:run_dir])
140
- # logger is not initialized yet (it needs run dir to exist for log)
141
- puts "Removing existing run directory #{@registry[:run_dir]}" if @options[:debug]
142
-
143
- # DLM: this is dangerous, we are calling rm_rf on a user entered directory, need to check this first
144
- # TODO: Echoing Dan's comment
145
- FileUtils.rm_rf(@registry[:run_dir])
146
- end
147
- end
148
- FileUtils.mkdir_p(@registry[:run_dir])
149
-
150
- # set up logging after cleaning run dir
151
- if user_options[:targets]
152
- @options[:targets] = user_options[:targets]
153
- else
154
- # don't create these files unless we want to use them
155
- # DLM: TODO, make sure that run.log will be closed later
156
- @options[:targets] = [STDOUT, File.open(File.join(@registry[:run_dir], 'run.log'), 'a')]
157
- end
158
-
159
- @registry.register(:log_targets) { @options[:targets] }
160
- @registry.register(:time_logger) { TimeLogger.new } if @options[:profile]
161
-
162
- # Initialize the MultiDelegator logger
163
- logger_level = @options[:debug] ? ::Logger::DEBUG : ::Logger::WARN
164
- @logger = ::Logger.new(MultiDelegator.delegate(:write, :close).to(*@options[:targets])) # * is the splat operator
165
- @logger.level = logger_level
166
- @registry.register(:logger) { @logger }
167
-
168
- @logger.info "openstudio_2 = #{@registry[:openstudio_2]}"
169
-
170
- # get the output adapter
171
- default_output_adapter = OpenStudio::Workflow::OutputAdapter::Local.new(output_directory: @input_adapter.run_dir)
172
- @output_adapter = @input_adapter.output_adapter(user_options, default_output_adapter, @logger)
173
-
174
- # get the jobs
175
- default_jobs = OpenStudio::Workflow::Run.default_jobs
176
- @jobs = @input_adapter.jobs(user_options, default_jobs, @logger)
177
-
178
- # get other run options out of user_options and into permanent options
179
- @options[:cleanup] = @input_adapter.cleanup(user_options, true)
180
- @options[:energyplus_path] = @input_adapter.energyplus_path(user_options, nil)
181
- @options[:verify_osw] = @input_adapter.verify_osw(user_options, true)
182
- @options[:weather_file] = @input_adapter.weather_file(user_options, nil)
183
-
184
- openstudio_dir = "unknown"
185
- begin
186
- openstudio_dir = $OpenStudio_Dir
187
- if openstudio_dir.nil?
188
- openstudio_dir = OpenStudio::getOpenStudioModuleDirectory.to_s
189
- end
190
- rescue
191
- end
192
- @logger.info "openstudio_dir = #{openstudio_dir}"
193
-
194
- @logger.info "Initializing directory #{@registry[:run_dir]} for simulation with options #{@options}"
195
-
196
- # Define the state and transitions
197
- @current_state = :queued
198
- end
199
-
200
- # execute the workflow defined in the state object
201
- #
202
- # @todo add a catch if any job fails
203
- # @todo make a block method to provide feedback
204
- def run
205
- @logger.info "Starting workflow in #{@registry[:run_dir]}"
206
- begin
207
- next_state
208
- while @current_state != :finished && @current_state != :errored
209
- sleep 2
210
- step
211
- end
212
-
213
- @logger.info 'Finished workflow - communicating results and zipping files'
214
- @output_adapter.communicate_results(@registry[:run_dir], @registry[:results])
215
- rescue => e
216
- @logger.info "Error occurred during running with #{e.message}"
217
- ensure
218
- @logger.info 'Workflow complete'
219
-
220
- if @current_state == :errored
221
- @registry[:workflow_json].setCompletedStatus('Fail') if @registry[:workflow_json]
222
- else
223
- @registry[:workflow_json].setCompletedStatus('Success')
224
- end
225
-
226
- # save all files before calling output adapter
227
- @registry[:log_targets].each(&:flush)
228
-
229
- # save workflow with results
230
- if @registry[:workflow_json]
231
- out_path = @registry[:workflow_json].absoluteOutPath
232
- @registry[:workflow_json].saveAs(out_path)
233
- end
234
-
235
- # Write out the TimeLogger to the filesystem
236
- @registry[:time_logger].save(File.join(@registry[:run_dir], 'profile.json')) if @registry[:time_logger]
237
-
238
- if @current_state == :errored
239
- @output_adapter.communicate_failure
240
- else
241
- @output_adapter.communicate_complete
242
- end
243
-
244
- end
245
-
246
- @current_state
247
- end
248
-
249
- # Step through the states, if there is an error (e.g. exception) then go to error
250
- #
251
- def step
252
- step_instance = @jobs.find { |h| h[:state] == @current_state }
253
- require step_instance[:file]
254
- klass = OpenStudio::Workflow.new_class(step_instance[:job], @input_adapter, @output_adapter, @registry, @options)
255
- @output_adapter.communicate_transition("Starting state #{@current_state}", :state)
256
- state_return = klass.perform
257
- if state_return
258
- @output_adapter.communicate_transition("Returned from state #{@current_state} with message #{state_return}", :state)
259
- else
260
- @output_adapter.communicate_transition("Returned from state #{@current_state}", :state)
261
- end
262
- next_state
263
- rescue => e
264
- step_error("#{e.message}:#{e.backtrace.join("\n")}")
265
- end
266
-
267
- # Error handling for when there is an exception running any of the state transitions
268
- #
269
- def step_error(*args)
270
- # Make sure to set the instance variable @error to true in order to stop the :step
271
- # event from being fired.
272
- @final_message = "Found error in state '#{@current_state}' with message #{args}}"
273
- @logger.error @final_message
274
-
275
- # transition to an error state
276
- @current_state = :errored
277
- end
278
-
279
- # Return the finished state and exit
280
- #
281
- def run_finished(_, _, _)
282
- logger.info "Running #{__method__}"
283
-
284
- @current_state
285
- end
286
-
287
- private
288
-
289
- # Advance the @current_state to the next state
290
- #
291
- def next_state
292
- @logger.info "Current state: '#{@current_state}'"
293
- ns = @jobs.find { |h| h[:state] == @current_state }[:next_state]
294
- @logger.info "Next state will be: '#{ns}'"
295
- @current_state = ns
296
- end
297
- end
298
- end
299
- end
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
+ require_relative 'registry'
21
+ require_relative 'adapters/input/local'
22
+ require_relative 'adapters/output/local'
23
+
24
+ require 'logger'
25
+ require 'pathname'
26
+
27
+ # Run Class for OpenStudio workflow. All comments here need some love, as well as the code itself
28
+ module OpenStudio
29
+ module Workflow
30
+ class Run
31
+ attr_accessor :registry
32
+
33
+ attr_reader :options
34
+ attr_reader :input_adapter
35
+ attr_reader :output_adapter
36
+ attr_reader :final_message
37
+ attr_reader :job_results
38
+ attr_reader :current_state
39
+
40
+ # Define the default set of jobs. Note that the states of :queued of :finished need to exist for all job arrays.
41
+ #
42
+ def self.default_jobs
43
+ [
44
+ { state: :queued, next_state: :initialization, options: { initial: true } },
45
+ { state: :initialization, next_state: :os_measures, job: :RunInitialization,
46
+ file: 'openstudio/workflow/jobs/run_initialization', options: {} },
47
+ { state: :os_measures, next_state: :translator, job: :RunOpenStudioMeasures,
48
+ file: 'openstudio/workflow/jobs/run_os_measures.rb', options: {} },
49
+ { state: :translator, next_state: :ep_measures, job: :RunTranslation,
50
+ file: 'openstudio/workflow/jobs/run_translation.rb', options: {} },
51
+ { state: :ep_measures, next_state: :preprocess, job: :RunEnergyPlusMeasures,
52
+ file: 'openstudio/workflow/jobs/run_ep_measures.rb', options: {} },
53
+ { state: :preprocess, next_state: :simulation, job: :RunPreprocess,
54
+ file: 'openstudio/workflow/jobs/run_preprocess.rb', options: {} },
55
+ { state: :simulation, next_state: :reporting_measures, job: :RunEnergyPlus,
56
+ file: 'openstudio/workflow/jobs/run_energyplus.rb', options: {} },
57
+ { state: :reporting_measures, next_state: :postprocess, job: :RunReportingMeasures,
58
+ file: 'openstudio/workflow/jobs/run_reporting_measures.rb', options: {} },
59
+ { state: :postprocess, next_state: :finished, job: :RunPostprocess,
60
+ file: 'openstudio/workflow/jobs/run_postprocess.rb', options: {} },
61
+ { state: :finished },
62
+ { state: :errored }
63
+ ]
64
+ end
65
+
66
+ # Initialize a new run class
67
+ #
68
+ # @param [String] osw_path the path to the OSW file to run. It is highly recommended that this be an absolute
69
+ # path, however if not it will be made absolute relative to the current working directory
70
+ # @param [Hash] user_options ({}) A set of user-specified options that are used to override default behaviors.
71
+ # @option user_options [Hash] :cleanup Remove unneccessary files during post processing, overrides OSW option if set, defaults to true
72
+ # @option user_options [Hash] :debug Print debugging messages, overrides OSW option if set, defaults to false
73
+ # @option user_options [Hash] :energyplus_path Specifies path to energyplus executable, defaults to empty
74
+ # @option user_options [Hash] :jobs Simulation workflow, overrides jobs in OSW if set, defaults to default_jobs
75
+ # @option user_options [Hash] :output_adapter Output adapter to use, overrides output adapter in OSW if set, defaults to local adapter
76
+ # @option user_options [Hash] :preserve_run_dir Prevents run directory from being cleaned prior to run, overrides OSW option if set, defaults to false - DLM, Deprecate
77
+ # @option user_options [Hash] :profile Produce additional output for profiling simulations, defaults to false
78
+ # @option user_options [Hash] :targets Log targets to write to, defaults to standard out and run.log
79
+ # @option user_options [Hash] :verify_osw Check OSW for correctness, defaults to true
80
+ # @option user_options [Hash] :weather_file Initial weather file to load, overrides OSW option if set, defaults to empty
81
+ def initialize(osw_path, user_options = {})
82
+ # DLM - what is final_message?
83
+ @final_message = ''
84
+ @current_state = nil
85
+ @options = {}
86
+
87
+ # Registry is a large hash of objects that are populated during the run, the number of objects in the registry should be reduced over time, especially if the functionality can be added to the WorkflowJSON class
88
+ # - analysis - the current OSA parsed as a Ruby Hash
89
+ # - datapoint - the current OSD parsed as a Ruby Hash
90
+ # - log_targets - IO devices that are being logged to
91
+ # - logger - general logger
92
+ # - model - the current OpenStudio Model object, updated after each step
93
+ # - model_idf - the current EnergyPlus Workspace object, updated after each step
94
+ # - openstudio_2 - true if we are running in OpenStudio 2.X environment
95
+ # - osw_path - the path the OSW was loaded from as a string
96
+ # - osw_dir - the directory the OSW was loaded from as a string
97
+ # - output_attributes - added during simulation time
98
+ # - results - objective function values
99
+ # - root_dir - the root directory in the OSW as a string
100
+ # - run_dir - the run directory for the simulation as a string
101
+ # - runner - the current OSRunner object
102
+ # - sql - the path to the current EnergyPlus SQL file as a string
103
+ # - time_logger - logger for doing profiling - time to run each step will be captured in OSResult, deprecate
104
+ # - wf - the path to the current weather file as a string, updated after each step
105
+ # - workflow - the current OSW parsed as a Ruby Hash
106
+ # - workflow_json - the current WorkflowJSON object
107
+ @registry = Registry.new
108
+
109
+ openstudio_2 = false
110
+ begin
111
+ # OpenStudio 2.X test
112
+ OpenStudio::WorkflowJSON.new
113
+ openstudio_2 = true
114
+ rescue NameError => e
115
+ end
116
+ @registry.register(:openstudio_2) { openstudio_2 }
117
+
118
+ # get the input osw
119
+ @input_adapter = OpenStudio::Workflow::InputAdapter::Local.new(osw_path)
120
+
121
+ # DLM: need to check that we have correct permissions to all these paths
122
+ @registry.register(:osw_path) { Pathname.new(@input_adapter.osw_path).realpath }
123
+ @registry.register(:osw_dir) { Pathname.new(@input_adapter.osw_dir).realpath }
124
+ @registry.register(:run_dir) { Pathname.new(@input_adapter.run_dir).cleanpath } # run dir might not yet exist, calling realpath will throw
125
+
126
+ # get info to set up logging first in case of failures later
127
+ @options[:debug] = @input_adapter.debug(user_options, false)
128
+ @options[:preserve_run_dir] = @input_adapter.preserve_run_dir(user_options, false)
129
+ @options[:profile] = @input_adapter.profile(user_options, false)
130
+
131
+ # if running in osw dir, force preserve run dir
132
+ if @registry[:osw_dir] == @registry[:run_dir]
133
+ # force preserving the run directory
134
+ @options[:preserve_run_dir] = true
135
+ end
136
+
137
+ # By default blow away the entire run directory every time and recreate it
138
+ unless @options[:preserve_run_dir]
139
+ if File.exist?(@registry[:run_dir])
140
+ # logger is not initialized yet (it needs run dir to exist for log)
141
+ puts "Removing existing run directory #{@registry[:run_dir]}" if @options[:debug]
142
+
143
+ # DLM: this is dangerous, we are calling rm_rf on a user entered directory, need to check this first
144
+ # TODO: Echoing Dan's comment
145
+ FileUtils.rm_rf(@registry[:run_dir])
146
+ end
147
+ end
148
+ FileUtils.mkdir_p(@registry[:run_dir])
149
+
150
+ # set up logging after cleaning run dir
151
+ if user_options[:targets]
152
+ @options[:targets] = user_options[:targets]
153
+ else
154
+ # don't create these files unless we want to use them
155
+ # DLM: TODO, make sure that run.log will be closed later
156
+ @options[:targets] = [STDOUT, File.open(File.join(@registry[:run_dir], 'run.log'), 'a')]
157
+ end
158
+
159
+ @registry.register(:log_targets) { @options[:targets] }
160
+ @registry.register(:time_logger) { TimeLogger.new } if @options[:profile]
161
+
162
+ # Initialize the MultiDelegator logger
163
+ logger_level = @options[:debug] ? ::Logger::DEBUG : ::Logger::WARN
164
+ @logger = ::Logger.new(MultiDelegator.delegate(:write, :close).to(*@options[:targets])) # * is the splat operator
165
+ @logger.level = logger_level
166
+ @registry.register(:logger) { @logger }
167
+
168
+ @logger.info "openstudio_2 = #{@registry[:openstudio_2]}"
169
+
170
+ # get the output adapter
171
+ default_output_adapter = OpenStudio::Workflow::OutputAdapter::Local.new(output_directory: @input_adapter.run_dir)
172
+ @output_adapter = @input_adapter.output_adapter(user_options, default_output_adapter, @logger)
173
+
174
+ # get the jobs
175
+ default_jobs = OpenStudio::Workflow::Run.default_jobs
176
+ @jobs = @input_adapter.jobs(user_options, default_jobs, @logger)
177
+
178
+ # get other run options out of user_options and into permanent options
179
+ @options[:cleanup] = @input_adapter.cleanup(user_options, true)
180
+ @options[:energyplus_path] = @input_adapter.energyplus_path(user_options, nil)
181
+ @options[:verify_osw] = @input_adapter.verify_osw(user_options, true)
182
+ @options[:weather_file] = @input_adapter.weather_file(user_options, nil)
183
+
184
+ openstudio_dir = "unknown"
185
+ begin
186
+ openstudio_dir = $OpenStudio_Dir
187
+ if openstudio_dir.nil?
188
+ openstudio_dir = OpenStudio::getOpenStudioModuleDirectory.to_s
189
+ end
190
+ rescue
191
+ end
192
+ @logger.info "openstudio_dir = #{openstudio_dir}"
193
+
194
+ @logger.info "Initializing directory #{@registry[:run_dir]} for simulation with options #{@options}"
195
+
196
+ # Define the state and transitions
197
+ @current_state = :queued
198
+ end
199
+
200
+ # execute the workflow defined in the state object
201
+ #
202
+ # @todo add a catch if any job fails
203
+ # @todo make a block method to provide feedback
204
+ def run
205
+ @logger.info "Starting workflow in #{@registry[:run_dir]}"
206
+ begin
207
+ next_state
208
+ while @current_state != :finished && @current_state != :errored
209
+ sleep 2
210
+ step
211
+ end
212
+
213
+ @logger.info 'Finished workflow - communicating results and zipping files'
214
+ @output_adapter.communicate_results(@registry[:run_dir], @registry[:results])
215
+ rescue => e
216
+ @logger.info "Error occurred during running with #{e.message}"
217
+ ensure
218
+ @logger.info 'Workflow complete'
219
+
220
+ if @current_state == :errored
221
+ @registry[:workflow_json].setCompletedStatus('Fail') if @registry[:workflow_json]
222
+ else
223
+ @registry[:workflow_json].setCompletedStatus('Success')
224
+ end
225
+
226
+ # save all files before calling output adapter
227
+ @registry[:log_targets].each(&:flush)
228
+
229
+ # save workflow with results
230
+ if @registry[:workflow_json]
231
+ out_path = @registry[:workflow_json].absoluteOutPath
232
+ @registry[:workflow_json].saveAs(out_path)
233
+ end
234
+
235
+ # Write out the TimeLogger to the filesystem
236
+ @registry[:time_logger].save(File.join(@registry[:run_dir], 'profile.json')) if @registry[:time_logger]
237
+
238
+ if @current_state == :errored
239
+ @output_adapter.communicate_failure
240
+ else
241
+ @output_adapter.communicate_complete
242
+ end
243
+
244
+ end
245
+
246
+ @current_state
247
+ end
248
+
249
+ # Step through the states, if there is an error (e.g. exception) then go to error
250
+ #
251
+ def step
252
+ step_instance = @jobs.find { |h| h[:state] == @current_state }
253
+ require step_instance[:file]
254
+ klass = OpenStudio::Workflow.new_class(step_instance[:job], @input_adapter, @output_adapter, @registry, @options)
255
+ @output_adapter.communicate_transition("Starting state #{@current_state}", :state)
256
+ state_return = klass.perform
257
+ if state_return
258
+ @output_adapter.communicate_transition("Returned from state #{@current_state} with message #{state_return}", :state)
259
+ else
260
+ @output_adapter.communicate_transition("Returned from state #{@current_state}", :state)
261
+ end
262
+ next_state
263
+ rescue => e
264
+ step_error("#{e.message}:#{e.backtrace.join("\n")}")
265
+ end
266
+
267
+ # Error handling for when there is an exception running any of the state transitions
268
+ #
269
+ def step_error(*args)
270
+ # Make sure to set the instance variable @error to true in order to stop the :step
271
+ # event from being fired.
272
+ @final_message = "Found error in state '#{@current_state}' with message #{args}}"
273
+ @logger.error @final_message
274
+
275
+ # transition to an error state
276
+ @current_state = :errored
277
+ end
278
+
279
+ # Return the finished state and exit
280
+ #
281
+ def run_finished(_, _, _)
282
+ logger.info "Running #{__method__}"
283
+
284
+ @current_state
285
+ end
286
+
287
+ private
288
+
289
+ # Advance the @current_state to the next state
290
+ #
291
+ def next_state
292
+ @logger.info "Current state: '#{@current_state}'"
293
+ ns = @jobs.find { |h| h[:state] == @current_state }[:next_state]
294
+ @logger.info "Next state will be: '#{ns}'"
295
+ @current_state = ns
296
+ end
297
+ end
298
+ end
299
+ end