openstudio-workflow 1.0.0.pat1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +16 -68
  4. data/Rakefile +9 -9
  5. data/bin/openstudio_cli +786 -0
  6. data/lib/openstudio/workflow/adapters/input/local.rb +97 -0
  7. data/lib/openstudio/workflow/adapters/output/local.rb +90 -0
  8. data/lib/openstudio/workflow/adapters/output/socket.rb +70 -0
  9. data/lib/openstudio/workflow/{jobs/run_preflight/run_preflight.rb → adapters/output/web.rb} +37 -19
  10. data/lib/openstudio/workflow/{adapter.rb → adapters/output_adapter.rb} +53 -51
  11. data/lib/openstudio/workflow/job.rb +22 -0
  12. data/lib/openstudio/workflow/jobs/{run_energyplus → resources}/monthly_report.idf +0 -0
  13. data/lib/openstudio/workflow/jobs/run_energyplus.rb +49 -0
  14. data/lib/openstudio/workflow/jobs/run_ep_measures.rb +55 -0
  15. data/lib/openstudio/workflow/jobs/run_initialization.rb +136 -0
  16. data/lib/openstudio/workflow/jobs/run_os_measures.rb +59 -0
  17. data/lib/openstudio/workflow/jobs/run_postprocess.rb +53 -0
  18. data/lib/openstudio/workflow/jobs/run_preprocess.rb +81 -0
  19. data/lib/openstudio/workflow/jobs/run_reporting_measures.rb +86 -0
  20. data/lib/openstudio/workflow/jobs/run_translation.rb +49 -0
  21. data/lib/openstudio/workflow/multi_delegator.rb +1 -3
  22. data/lib/openstudio/workflow/registry.rb +137 -0
  23. data/lib/openstudio/workflow/run.rb +182 -221
  24. data/lib/openstudio/workflow/time_logger.rb +1 -1
  25. data/lib/openstudio/workflow/util/energyplus.rb +564 -0
  26. data/lib/openstudio/workflow/util/io.rb +33 -0
  27. data/lib/openstudio/workflow/util/measure.rb +520 -0
  28. data/lib/openstudio/workflow/util/model.rb +100 -0
  29. data/lib/openstudio/workflow/util/post_process.rb +177 -0
  30. data/lib/openstudio/workflow/util/weather_file.rb +108 -0
  31. data/lib/openstudio/workflow/util.rb +14 -0
  32. data/lib/openstudio/workflow/version.rb +1 -1
  33. data/lib/openstudio/workflow_json.rb +399 -0
  34. data/lib/openstudio/workflow_runner.rb +213 -0
  35. data/lib/openstudio-workflow.rb +13 -118
  36. metadata +45 -85
  37. data/lib/openstudio/extended_runner.rb +0 -105
  38. data/lib/openstudio/workflow/adapters/local.rb +0 -101
  39. data/lib/openstudio/workflow/adapters/mongo.rb +0 -227
  40. data/lib/openstudio/workflow/jobs/lib/apply_measures.rb +0 -253
  41. data/lib/openstudio/workflow/jobs/run_energyplus/run_energyplus.rb +0 -314
  42. data/lib/openstudio/workflow/jobs/run_openstudio/run_openstudio.rb +0 -230
  43. data/lib/openstudio/workflow/jobs/run_postprocess/run_postprocess.rb +0 -110
  44. data/lib/openstudio/workflow/jobs/run_reporting_measures/run_reporting_measures.rb +0 -471
  45. data/lib/openstudio/workflow/jobs/run_runmanager/run_runmanager.rb +0 -247
  46. data/lib/openstudio/workflow/jobs/run_xml/run_xml.rb +0 -279
@@ -0,0 +1,399 @@
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 'pathname'
21
+
22
+ # Optional_Shim provides a wrapper that looks like an OpenStudio Optional
23
+ class Optional_Shim
24
+ def initialize(obj)
25
+ @obj = obj
26
+ end
27
+
28
+ def empty?
29
+ @obj.nil?
30
+ end
31
+
32
+ def is_initialized
33
+ !@obj.nil?
34
+ end
35
+
36
+ def get
37
+ raise 'Uninitialized Optional_Shim' if @obj.nil?
38
+ @obj
39
+ end
40
+ end
41
+
42
+ s = ''
43
+ unless s.respond_to?(:to_StepResult)
44
+ class String
45
+ def to_StepResult
46
+ self
47
+ end
48
+ end
49
+ end
50
+ unless s.respond_to?(:to_VariantType)
51
+ class String
52
+ def to_VariantType
53
+ self
54
+ end
55
+ end
56
+ end
57
+
58
+ # WorkflowStepResultValue_Shim provides a shim interface to the WorkflowStepResultValue class in OpenStudio 2.X when running in OpenStudio 1.X
59
+ class WorkflowStepResultValue_Shim
60
+ def initialize(name, value, type)
61
+ @name = name
62
+ @value = value
63
+ @type = type
64
+ end
65
+
66
+ attr_reader :name
67
+
68
+ attr_reader :value
69
+
70
+ def variantType
71
+ @type
72
+ end
73
+
74
+ def valueAsString
75
+ @value.to_s
76
+ end
77
+
78
+ def valueAsDouble
79
+ @value.to_f
80
+ end
81
+
82
+ def valueAsInteger
83
+ @value.to_i
84
+ end
85
+
86
+ def valueAsBoolean
87
+ @value
88
+ end
89
+ end
90
+
91
+ # WorkflowStepResult_Shim provides a shim interface to the WorkflowStepResult class in OpenStudio 2.X when running in OpenStudio 1.X
92
+ class WorkflowStepResult_Shim
93
+ def initialize(result)
94
+ @result = result
95
+ end
96
+
97
+ def stepErrors
98
+ return @result[:step_errors]
99
+ end
100
+
101
+ def stepWarnings
102
+ return @result[:step_warnings]
103
+ end
104
+
105
+ def stepInfo
106
+ return @result[:step_info]
107
+ end
108
+
109
+ def stepValues
110
+ result = []
111
+ @result[:step_values].each do |step_value|
112
+ result << WorkflowStepResultValue_Shim.new(step_value[:name], step_value[:value], step_value[:type])
113
+ end
114
+ return result
115
+ end
116
+
117
+ def stepResult
118
+ Optional_Shim.new(@result[:step_result])
119
+ end
120
+
121
+ def setStepResult(step_result)
122
+ @result[:step_result] = step_result
123
+ end
124
+ end
125
+
126
+ # WorkflowStep_Shim provides a shim interface to the WorkflowStep class in OpenStudio 2.X when running in OpenStudio 1.X
127
+ class WorkflowStep_Shim
128
+ def initialize(step)
129
+ @step = step
130
+ end
131
+
132
+ attr_reader :step
133
+
134
+ def result
135
+ if @step[:result]
136
+ Optional_Shim.new(WorkflowStepResult_Shim.new(@step[:result]))
137
+ else
138
+ Optional_Shim.new
139
+ end
140
+ end
141
+
142
+ # std::string measureDirName() const;
143
+ def measureDirName
144
+ @step[:measure_dir_name]
145
+ end
146
+
147
+ # std::map<std::string, Variant> arguments() const;
148
+ def arguments
149
+ # TODO: match C++
150
+ @step[:arguments]
151
+ end
152
+ end
153
+
154
+ # WorkflowJSON_Shim provides a shim interface to the WorkflowJSON class in OpenStudio 2.X when running in OpenStudio 1.X
155
+ class WorkflowJSON_Shim
156
+ def initialize(workflow, osw_dir)
157
+ @workflow = workflow
158
+ @osw_dir = osw_dir
159
+ @current_step_index = 0
160
+ end
161
+
162
+ # std::string string(bool includeHash=true) const;
163
+ def string
164
+ JSON.fast_generate(@workflow)
165
+ end
166
+
167
+ def timeString
168
+ ::Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
169
+ end
170
+
171
+ # Returns the absolute path to the directory this workflow was loaded from or saved to. Returns current working dir for new WorkflowJSON.
172
+ # openstudio::path oswDir() const;
173
+ def oswDir
174
+ OpenStudio.toPath(@osw_dir)
175
+ end
176
+
177
+ def saveAs(path)
178
+ File.open(path.to_s, 'w') do |file|
179
+ file << JSON.pretty_generate(@workflow)
180
+ end
181
+ end
182
+
183
+ # Sets the started at time.
184
+ def start
185
+ @workflow[:started_at] = timeString
186
+ end
187
+
188
+ # Get the current step index.
189
+ def currentStepIndex
190
+ @current_step_index
191
+ end
192
+
193
+ # Get the current step.
194
+ # boost::optional<WorkflowStep> currentStep() const;
195
+ def currentStep
196
+ steps = @workflow[:steps]
197
+
198
+ step = nil
199
+ if @current_step_index < steps.size
200
+ step = WorkflowStep_Shim.new(steps[@current_step_index])
201
+ end
202
+ return Optional_Shim.new(step)
203
+ end
204
+
205
+ # Increments current step, returns true if there is another step.
206
+ # bool incrementStep();
207
+ def incrementStep
208
+ @current_step_index += 1
209
+
210
+ if @current_step_index < @workflow[:steps].size
211
+ return true
212
+ end
213
+
214
+ return false
215
+ end
216
+
217
+ # Returns the root directory, default value is '.'. Evaluated relative to oswDir if not absolute.
218
+ # openstudio::path rootDir() const;
219
+ # openstudio::path absoluteRootDir() const;
220
+ def rootDir
221
+ if @workflow[:root_dir]
222
+ OpenStudio.toPath(@workflow[:root_dir])
223
+ else
224
+ OpenStudio.toPath(@osw_dir)
225
+ end
226
+ end
227
+
228
+ def absoluteRootDir
229
+ OpenStudio.toPath(File.absolute_path(rootDir.to_s, @osw_dir.to_s))
230
+ end
231
+
232
+ # Returns the run directory, default value is './run'. Evaluated relative to rootDir if not absolute.
233
+ # openstudio::path runDir() const;
234
+ # openstudio::path absoluteRunDir() const;
235
+ def runDir
236
+ if @workflow[:run_directory]
237
+ OpenStudio.toPath(@workflow[:run_directory])
238
+ else
239
+ OpenStudio.toPath('./run')
240
+ end
241
+ end
242
+
243
+ def absoluteRunDir
244
+ OpenStudio.toPath(File.absolute_path(runDir.to_s, rootDir.to_s))
245
+ end
246
+
247
+ def outPath
248
+ if @workflow[:out_name]
249
+ OpenStudio.toPath(@workflow[:out_name])
250
+ else
251
+ OpenStudio.toPath('./out.osw')
252
+ end
253
+ end
254
+
255
+ def absoluteOutPath
256
+ OpenStudio.toPath(File.absolute_path(outPath.to_s, oswDir.to_s))
257
+ end
258
+
259
+ # Returns the paths that will be searched in order for files, default value is './files/'. Evaluated relative to rootDir if not absolute.
260
+ # std::vector<openstudio::path> filePaths() const;
261
+ # std::vector<openstudio::path> absoluteFilePaths() const;
262
+ def filePaths
263
+ result = OpenStudio::PathVector.new
264
+ if @workflow[:file_paths]
265
+ @workflow[:file_paths].each do |file_path|
266
+ result << OpenStudio.toPath(file_path)
267
+ end
268
+ else
269
+ result << OpenStudio.toPath('./files')
270
+ result << OpenStudio.toPath('./weather')
271
+ result << OpenStudio.toPath('../../files')
272
+ result << OpenStudio.toPath('../../weather')
273
+ result << OpenStudio.toPath('./')
274
+ end
275
+ result
276
+ end
277
+
278
+ def absoluteFilePaths
279
+ result = OpenStudio::PathVector.new
280
+ filePaths.each do |file_path|
281
+ result << OpenStudio.toPath(File.absolute_path(file_path.to_s, rootDir.to_s))
282
+ end
283
+ result
284
+ end
285
+
286
+ # Attempts to find a file by name, searches through filePaths in order and returns first match.
287
+ # boost::optional<openstudio::path> findFile(const openstudio::path& file);
288
+ # boost::optional<openstudio::path> findFile(const std::string& fileName);
289
+ def findFile(file)
290
+ file = file.to_s
291
+
292
+ # check if absolute and exists
293
+ if Pathname.new(file).absolute?
294
+ if File.exist?(file)
295
+ return OpenStudio::OptionalPath.new(OpenStudio.toPath(file))
296
+ end
297
+
298
+ # absolute path does not exist
299
+ return OpenStudio::OptionalPath.new
300
+ end
301
+
302
+ absoluteFilePaths.each do |file_path|
303
+ result = File.join(file_path.to_s, file)
304
+ if File.exist?(result)
305
+ return OpenStudio::OptionalPath.new(OpenStudio.toPath(result))
306
+ end
307
+ end
308
+ OpenStudio::OptionalPath.new
309
+ end
310
+
311
+ # Returns the paths that will be searched in order for measures, default value is './measures/'. Evaluated relative to rootDir if not absolute.
312
+ # std::vector<openstudio::path> measurePaths() const;
313
+ # std::vector<openstudio::path> absoluteMeasurePaths() const;
314
+ def measurePaths
315
+ result = OpenStudio::PathVector.new
316
+ if @workflow[:measure_paths]
317
+ @workflow[:measure_paths].each do |measure_path|
318
+ result << OpenStudio.toPath(measure_path)
319
+ end
320
+ else
321
+ result << OpenStudio.toPath('./measures')
322
+ result << OpenStudio.toPath('../../measures')
323
+ result << OpenStudio.toPath('./')
324
+ end
325
+ result
326
+ end
327
+
328
+ def absoluteMeasurePaths
329
+ result = OpenStudio::PathVector.new
330
+ measurePaths.each do |measure_path|
331
+ result << OpenStudio.toPath(File.absolute_path(measure_path.to_s, rootDir.to_s))
332
+ end
333
+ result
334
+ end
335
+
336
+ # Attempts to find a measure by name, searches through measurePaths in order and returns first match. */
337
+ # boost::optional<openstudio::path> findMeasure(const openstudio::path& measureDir);
338
+ # boost::optional<openstudio::path> findMeasure(const std::string& measureDirName);
339
+ def findMeasure(measureDir)
340
+ measureDir = measureDir.to_s
341
+
342
+ # check if absolute and exists
343
+ if Pathname.new(measureDir).absolute?
344
+ if File.exist?(measureDir)
345
+ return OpenStudio::OptionalPath.new(OpenStudio.toPath(measureDir))
346
+ end
347
+
348
+ # absolute path does not exist
349
+ return OpenStudio::OptionalPath.new
350
+ end
351
+
352
+ absoluteMeasurePaths.each do |measure_path|
353
+ result = File.join(measure_path.to_s, measureDir)
354
+ if File.exist?(result)
355
+ return OpenStudio::OptionalPath.new(OpenStudio.toPath(result))
356
+ end
357
+ end
358
+ OpenStudio::OptionalPath.new
359
+ end
360
+
361
+ # Returns the seed file path. Evaluated relative to filePaths if not absolute.
362
+ # boost::optional<openstudio::path> seedFile() const;
363
+ def seedFile
364
+ result = OpenStudio::OptionalPath.new
365
+ if @workflow[:seed_file]
366
+ result = OpenStudio::OptionalPath.new(OpenStudio.toPath(@workflow[:seed_file]))
367
+ end
368
+ result
369
+ end
370
+
371
+ # Returns the weather file path. Evaluated relative to filePaths if not absolute.
372
+ # boost::optional<openstudio::path> weatherFile() const;
373
+ def weatherFile
374
+ result = OpenStudio::OptionalPath.new
375
+ if @workflow[:weather_file]
376
+ result = OpenStudio::OptionalPath.new(OpenStudio.toPath(@workflow[:weather_file]))
377
+ end
378
+ result
379
+ end
380
+
381
+ # Returns the workflow steps. */
382
+ # std::vector<WorkflowStep> workflowSteps() const;
383
+ def workflowSteps
384
+ result = []
385
+ @workflow[:steps].each do |step|
386
+ result << WorkflowStep_Shim.new(step)
387
+ end
388
+ result
389
+ end
390
+
391
+ def setCompletedStatus(status)
392
+ @workflow[:completed_status] = status
393
+ @workflow[:completed_at] = timeString
394
+ end
395
+
396
+ def setEplusoutErr(eplusout_err)
397
+ @workflow[:eplusout_err] = eplusout_err
398
+ end
399
+ end
@@ -0,0 +1,213 @@
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 'workflow_json'
21
+
22
+ # Extend OS Runner to persist measure information throughout the workflow
23
+ # Provide shims to support OpenStudio 2.X functionality in OpenStudio 1.X
24
+ class WorkflowRunner < OpenStudio::Ruleset::OSRunner
25
+ def initialize(multi_logger, workflow_json, openstudio_2)
26
+ @multi_logger = multi_logger
27
+ @workflow_json = workflow_json
28
+ @openstudio_2 = openstudio_2
29
+
30
+ begin
31
+ # OpenStudio 2.X
32
+ super(@workflow_json)
33
+ rescue Exception => e
34
+ # OpenStudio 1.X
35
+ @workflow = workflow_json
36
+ @units_preference = 'SI'
37
+ @language_preference = 'EN'
38
+ super()
39
+ end
40
+ end
41
+
42
+ def timeString
43
+ ::Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
44
+ end
45
+
46
+ # Returns the workflow currently being run. New in OS 2.0.
47
+ # WorkflowJSON workflow() const;
48
+ def workflow
49
+ if @openstudio_2
50
+ super
51
+ else
52
+ @workflow
53
+ end
54
+ end
55
+
56
+ # Returns preferred unit system, either 'IP' or 'SI'. New in OS 2.0. */
57
+ # std::string unitsPreference() const;
58
+ def unitsPreference
59
+ if @openstudio_2
60
+ super
61
+ else
62
+ @units_preference
63
+ end
64
+ end
65
+
66
+ # Returns preferred language, e.g. 'en' or 'fr'. New in OS 2.0. */
67
+ # std::string languagePreference() const;
68
+ def languagePreference
69
+ if @openstudio_2
70
+ super
71
+ else
72
+ @language_preference
73
+ end
74
+ end
75
+
76
+ # called right when each measure is run
77
+ # only called in OpenStudio 1.X
78
+ # virtual void prepareForUserScriptRun(const UserScript& userScript);
79
+ def prepareForUserScriptRun(userScript)
80
+ if @openstudio_2
81
+ prepareForMeasureRun(userScript)
82
+ else
83
+ current_step = @workflow.currentStep
84
+
85
+ unless current_step.empty?
86
+ current_step.get.step[:result] = {}
87
+ current_step.get.step[:result][:started_at] = timeString
88
+ end
89
+
90
+ # TODO: capture std out and err
91
+
92
+ # TODO: get initial list of files
93
+
94
+ super
95
+ end
96
+ end
97
+
98
+ def result
99
+ if @openstudio_2
100
+ super
101
+ else
102
+ os_result = super
103
+
104
+ current_step = @workflow.currentStep
105
+
106
+ if current_step.empty?
107
+ raise 'Cannot find current_step'
108
+ end
109
+ current_step = current_step.get
110
+
111
+ if current_step.step[:result].nil?
112
+ # skipped, prepareForUserScriptRun was not called
113
+ current_step.step[:result] = {}
114
+ current_step.step[:result][:started_at] = timeString
115
+ current_step.step[:result][:step_result] = 'Skip'
116
+ else
117
+ current_step.step[:result][:step_result] = os_result.value.valueName
118
+ end
119
+
120
+ current_step.step[:result][:completed_at] = timeString
121
+
122
+ # TODO: restore stdout and stderr
123
+
124
+ # TODO: check for created files
125
+
126
+ current_step.step[:result][:step_errors] = []
127
+ os_result.errors.each do |error|
128
+ current_step.step[:result][:step_errors] << error.logMessage
129
+ end
130
+
131
+ current_step.step[:result][:step_warnings] = []
132
+ os_result.warnings.each do |warning|
133
+ current_step.step[:result][:step_warnings] << warning.logMessage
134
+ end
135
+
136
+ current_step.step[:result][:step_info] = []
137
+ os_result.info.each do |info|
138
+ current_step.step[:result][:step_info] << info.logMessage
139
+ end
140
+
141
+ unless os_result.initialCondition.empty?
142
+ current_step.step[:result][:initial_condition] = os_result.initialCondition.get.logMessage
143
+ end
144
+
145
+ unless os_result.finalCondition.empty?
146
+ current_step.step[:result][:final_condition] = os_result.finalCondition.get.logMessage
147
+ end
148
+
149
+ current_step.step[:result][:step_values] = []
150
+ os_result.attributes.each do |attribute|
151
+ result = nil
152
+ if attribute.valueType == 'Boolean'.to_AttributeValueType
153
+ result = { name: attribute.name, value: attribute.valueAsBoolean, type: 'Boolean' }
154
+ elsif attribute.valueType == 'Double'.to_AttributeValueType
155
+ result = { name: attribute.name, value: attribute.valueAsDouble, type: 'Double' }
156
+ elsif attribute.valueType == 'Integer'.to_AttributeValueType
157
+ result = { name: attribute.name, value: attribute.valueAsInteger, type: 'Integer' }
158
+ elsif attribute.valueType == 'Unsigned'.to_AttributeValueType
159
+ result = { name: attribute.name, value: attribute.valueAsUnsigned, type: 'Integer' }
160
+ elsif attribute.valueType == 'String'.to_AttributeValueType
161
+ result = { name: attribute.name, value: attribute.valueAsString, type: 'String' }
162
+ end
163
+
164
+ current_step.step[:result][:step_values] << result unless result.nil?
165
+ end
166
+
167
+ return WorkflowStepResult_Shim.new(current_step.step[:result])
168
+ end
169
+ end
170
+
171
+ # incrementing step copies result to previous results
172
+ # void incrementStep();
173
+ def incrementStep
174
+ if @openstudio_2
175
+ super
176
+ else
177
+ # compute result
178
+ current_result = result
179
+
180
+ @workflow.incrementStep
181
+ end
182
+ end
183
+
184
+ # Overload registerInfo
185
+ def registerInfo(message)
186
+ super
187
+ @multi_logger.info message
188
+ end
189
+
190
+ # Overload registerInfo
191
+ def registerWarning(message)
192
+ super
193
+ @multi_logger.warn message
194
+ end
195
+
196
+ # Overload registerError
197
+ def registerError(message)
198
+ super
199
+ @multi_logger.error message
200
+ end
201
+
202
+ # Overload registerInitialCondition
203
+ def registerInitialCondition(message)
204
+ super
205
+ @multi_logger.info message
206
+ end
207
+
208
+ # Overload registerFinalCondition
209
+ def registerFinalCondition(message)
210
+ super
211
+ @multi_logger.info message
212
+ end
213
+ end