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,33 +1,33 @@
1
- module OpenStudio
2
- module Workflow
3
- module Util
4
- module IO
5
- def is_windows?
6
- win_patterns = [
7
- /bccwin/i,
8
- /cygwin/i,
9
- /djgpp/i,
10
- /mingw/i,
11
- /mswin/i,
12
- /wince/i
13
- ]
14
-
15
- case RUBY_PLATFORM
16
- when *win_patterns
17
- return true
18
- else
19
- return false
20
- end
21
- end
22
-
23
- def popen_command(command)
24
- result = command
25
- if is_windows?
26
- result = command.tr('/', '\\')
27
- end
28
- return result
29
- end
30
- end
31
- end
32
- end
33
- end
1
+ module OpenStudio
2
+ module Workflow
3
+ module Util
4
+ module IO
5
+ def is_windows?
6
+ win_patterns = [
7
+ /bccwin/i,
8
+ /cygwin/i,
9
+ /djgpp/i,
10
+ /mingw/i,
11
+ /mswin/i,
12
+ /wince/i
13
+ ]
14
+
15
+ case RUBY_PLATFORM
16
+ when *win_patterns
17
+ return true
18
+ else
19
+ return false
20
+ end
21
+ end
22
+
23
+ def popen_command(command)
24
+ result = command
25
+ if is_windows?
26
+ result = command.tr('/', '\\')
27
+ end
28
+ return result
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,588 +1,588 @@
1
- module OpenStudio
2
- module Workflow
3
- module Util
4
-
5
- # Handles all interaction with measure objects in the gem. This includes measure.xml and measure.rb files
6
- #
7
- module Measure
8
-
9
- # Wrapper method around #apply_measure to allow all measures of a type to be executed
10
- #
11
- # @param [String] measure_type Accepts OpenStudio::MeasureType argument
12
- # @param [Object] registry Hash access to objects
13
- # @param [Hash] options ({}) User-specified options used to override defaults
14
- # @option options [Object] :time_logger A special logger used to debug performance issues
15
- # @option options [Object] :output_adapter An output adapter to register measure transitions to
16
- # @param [Boolean] energyplus_output_requests If true then the energyPlusOutputRequests is called instead of the run method
17
- # @return [Void]
18
- #
19
- def apply_measures(measure_type, registry, options = {}, energyplus_output_requests = false)
20
-
21
- # DLM: time_logger is in the registry but docs say it is in options?
22
- registry[:time_logger].start "#{measure_type.valueName}:apply_measures" if registry[:time_logger]
23
-
24
- logger = registry[:logger]
25
- workflow_json = registry[:workflow_json]
26
-
27
- workflow_steps = workflow_json.workflowSteps
28
- fail "The 'steps' array of the OSW is required." unless workflow_steps
29
-
30
- logger.debug "Finding measures of type #{measure_type.valueName}"
31
- workflow_steps.each_index do |step_index|
32
-
33
- step = workflow_steps[step_index]
34
-
35
- if @registry[:openstudio_2]
36
- if !step.to_MeasureStep.empty?
37
- step = step.to_MeasureStep.get
38
- end
39
- end
40
-
41
- measure_dir_name = step.measureDirName
42
-
43
- measure_dir = workflow_json.findMeasure(measure_dir_name)
44
- fail "Cannot find #{measure_dir_name}" if measure_dir.empty?
45
- measure_dir = measure_dir.get
46
-
47
- measure = OpenStudio::BCLMeasure.load(measure_dir)
48
- fail "Cannot load measure at #{measure_dir}" if measure.empty?
49
- measure = measure.get
50
-
51
- class_name = measure.className
52
- measure_instance_type = measure.measureType
53
- if measure_instance_type == measure_type
54
- if energyplus_output_requests
55
- logger.info "Found measure #{class_name} of type #{measure_type.valueName}. Collecting EnergyPlus Output Requests now."
56
- apply_measure(registry, step, options, energyplus_output_requests)
57
- else
58
- logger.info "Found measure #{class_name} of type #{measure_type.valueName}. Applying now."
59
-
60
- # fast forward current step index to this index, skips any previous steps
61
- while workflow_json.currentStepIndex < step_index
62
- workflow_json.incrementStep
63
- end
64
-
65
- # DLM: why is output_adapter in options instead of registry?
66
- options[:output_adapter].communicate_transition("Applying #{class_name}", :measure) if options[:output_adapter]
67
- apply_measure(registry, step, options)
68
- options[:output_adapter].communicate_transition("Applied #{class_name}", :measure) if options[:output_adapter]
69
- end
70
-
71
- logger.info 'Moving to the next workflow step.'
72
- else
73
- logger.debug "Passing measure #{class_name} of type #{measure_type.valueName}"
74
- end
75
- end
76
-
77
- registry[:time_logger].stop "#{measure_type.valueName}:apply_measures" if registry[:time_logger]
78
- end
79
-
80
- # Determine if a given workflow can find and load all measures defined in steps
81
- #
82
- # @param [Hash] workflow See the schema for an OSW defined in the spec folder of this repo. Note that this
83
- # method requires the OSW to have been loaded with symbolized keys
84
- # @param [String] directory The directory that will be passed to the find_measure_dir method
85
- # @return [true] If the method doesn't fail the workflow measures were validated
86
- #
87
- def validate_measures(registry, logger)
88
-
89
- logger = registry[:logger] if logger.nil?
90
- workflow_json = registry[:workflow_json]
91
-
92
- state = 'ModelMeasure'.to_MeasureType
93
- steps = workflow_json.workflowSteps
94
- steps.each_with_index do |step, index|
95
- begin
96
- logger.debug "Validating step #{index}"
97
-
98
- if @registry[:openstudio_2]
99
- if !step.to_MeasureStep.empty?
100
- step = step.to_MeasureStep.get
101
- end
102
- end
103
-
104
- # Verify the existence of the required files
105
- measure_dir_name = step.measureDirName
106
-
107
- measure_dir = workflow_json.findMeasure(measure_dir_name)
108
- fail "Cannot find measure #{measure_dir_name}" if measure_dir.empty?
109
- measure_dir = measure_dir.get
110
-
111
- measure = OpenStudio::BCLMeasure.load(measure_dir)
112
- fail "Cannot load measure at #{measure_dir}" if measure.empty?
113
- measure = measure.get
114
-
115
- class_name = measure.className
116
- measure_instance_type = measure.measureType
117
-
118
- # Ensure that measures are in order, i.e. no OS after E+, E+ or OS after Reporting
119
- if measure_instance_type == 'ModelMeasure'.to_MeasureType
120
- fail "OpenStudio measure #{measure_dir} called after transition to EnergyPlus." if state == 'EnergyPlusMeasure'.to_MeasureType
121
- fail "OpenStudio measure #{measure_dir} called after after Energyplus simulation." if state == 'ReportingMeasure'.to_MeasureType
122
- elsif measure_instance_type == "EnergyPlusMeasure".to_MeasureType
123
- state = 'EnergyPlusMeasure'.to_MeasureType if state == 'ModelMeasure'.to_MeasureType
124
- fail "EnergyPlus measure #{measure_dir} called after Energyplus simulation." if state == 'ReportingMeasure'.to_MeasureType
125
- elsif measure_instance_type == 'ReportingMeasure'.to_MeasureType
126
- state = 'ReportingMeasure'.to_MeasureType if state != 'ReportingMeasure'.to_MeasureType
127
- else
128
- fail "Error: MeasureType #{measure_instance_type.valueName} of measure #{measure_dir} is not supported"
129
- end
130
-
131
- logger.debug "Validated step #{index}"
132
- end
133
- end
134
- end
135
-
136
- # Sets the argument map for argument_map argument pair
137
- #
138
- # @param [Object] argument_map See the OpenStudio SDK for a description of the OSArgumentMap structure
139
- # @param [Object] argument_name, user defined argument name
140
- # @param [Object] argument_value, user defined argument value
141
- # @param [Object] logger, logger object
142
- # @return [Object] Returns an updated ArgumentMap object
143
- #
144
- def apply_arguments(argument_map, argument_name, argument_value, logger)
145
- unless argument_value.nil?
146
- logger.info "Setting argument value '#{argument_name}' to '#{argument_value}'"
147
-
148
- v = argument_map[argument_name.to_s]
149
- fail "Could not find argument '#{argument_name}' in argument_map" unless v
150
- value_set = v.setValue(argument_value)
151
- fail "Could not set argument '#{argument_name}' to value '#{argument_value}'" unless value_set
152
- argument_map[argument_name.to_s] = v.clone
153
- else
154
- logger.warn "Value for argument '#{argument_name}' not set in argument list therefore will use default"
155
- end
156
- end
157
-
158
- def apply_arguments_2(argument_map, argument_name, argument_value, logger)
159
- unless argument_value.nil?
160
- logger.info "Setting argument value '#{argument_name}' to '#{argument_value}'"
161
-
162
- v = argument_map[argument_name.to_s]
163
- fail "Could not find argument '#{argument_name}' in argument_map" unless v
164
- value_set = false
165
- variant_type = argument_value.variantType
166
- if variant_type == "String".to_VariantType
167
- argument_value = argument_value.valueAsString
168
- value_set = v.setValue(argument_value)
169
- elsif variant_type == "Double".to_VariantType
170
- argument_value = argument_value.valueAsDouble
171
- value_set = v.setValue(argument_value)
172
- elsif variant_type == "Integer".to_VariantType
173
- argument_value = argument_value.valueAsInteger
174
- value_set = v.setValue(argument_value)
175
- elsif variant_type == "Boolean".to_VariantType
176
- argument_value = argument_value.valueAsBoolean
177
- value_set = v.setValue(argument_value)
178
- end
179
- fail "Could not set argument '#{argument_name}' to value '#{argument_value}'" unless value_set
180
- argument_map[argument_name.to_s] = v.clone
181
- else
182
- logger.warn "Value for argument '#{argument_name}' not set in argument list therefore will use default"
183
- end
184
- end
185
-
186
- # Method to add measure info to WorkflowStepResult
187
- #
188
- # @param [Object] result Current WorkflowStepResult
189
- # @param [Object] measure Current BCLMeasure
190
- def add_result_measure_info(result, measure)
191
- begin
192
- result.setMeasureType(measure.measureType)
193
- result.setMeasureName(measure.name)
194
- result.setMeasureId(measure.uid)
195
- result.setMeasureVersionId(measure.versionId)
196
- version_modified = measure.versionModified
197
- if !version_modified.empty?
198
- result.setMeasureVersionModified(version_modified.get)
199
- end
200
- result.setMeasureXmlChecksum(measure.xmlChecksum)
201
- result.setMeasureClassName(measure.className)
202
- result.setMeasureDisplayName(measure.displayName)
203
- result.setMeasureTaxonomy(measure.taxonomyTag)
204
- rescue NameError
205
- end
206
- end
207
-
208
- # Method to allow for a single measure of any type to be run
209
- #
210
- # @param [String] directory Location of the datapoint directory to run. This is needed
211
- # independent of the adapter that is being used. Note that the simulation will actually run in 'run'
212
- # @param [Object] adapter An instance of the adapter class
213
- # @param [String] current_weather_filepath The path which will be used to set the runner and returned to update
214
- # the OSW for future measures and the simulation
215
- # @param [Object] model The model object being used in the measure, either a OSM or IDF
216
- # @param [Hash] step Definition of the to be run by the workflow
217
- # @option step [String] :measure_dir_name The name of the directory which contains the measure files
218
- # @option step [Object] :arguments name value hash which defines the arguments to the measure, e.g.
219
- # {has_bool: true, cost: 3.1}
220
- # @param output_attributes [Hash] The results of previous measure applications which are persisted through the
221
- # runner to allow measures to react to previous events in the workflow
222
- # @param [Hash] options ({}) User-specified options used to override defaults
223
- # @option options [Array] :measure_search_array Ordered set of measure directories used to search for
224
- # step[:measure_dir_name], e.g. ['measures', '../../measures']
225
- # @option options [Object] :time_logger Special logger used to debug performance issues
226
- # @param [Boolean] energyplus_output_requests If true then the energyPlusOutputRequests is called instead of the run method
227
- # @return [Hash, String] Returns two objects. The first is the (potentially) updated output_attributes hash, and
228
- # the second is the (potentially) updated current_weather_filepath
229
- #
230
- def apply_measure(registry, step, options = {}, energyplus_output_requests = false)
231
-
232
- logger = registry[:logger]
233
- runner = registry[:runner]
234
- workflow_json = registry[:workflow_json]
235
- measure_dir_name = step.measureDirName
236
-
237
- run_dir = registry[:run_dir]
238
- fail 'No run directory set in the registry' unless run_dir
239
-
240
- output_attributes = registry[:output_attributes]
241
-
242
- # todo: get weather file from appropriate location
243
- @wf = registry[:wf]
244
- @model = registry[:model]
245
- @model_idf = registry[:model_idf]
246
- @sql_filename = registry[:sql]
247
-
248
- runner.setLastOpenStudioModel(@model) if @model
249
- #runner.setLastOpenStudioModelPath(const openstudio::path& lastOpenStudioModelPath); #DLM - deprecate?
250
- runner.setLastEnergyPlusWorkspace(@model_idf) if @model_idf
251
- #runner.setLastEnergyPlusWorkspacePath(const openstudio::path& lastEnergyPlusWorkspacePath); #DLM - deprecate?
252
- runner.setLastEnergyPlusSqlFilePath(@sql_filename) if @sql_filename
253
- runner.setLastEpwFilePath(@wf) if @wf
254
-
255
- logger.debug "Starting #{__method__} for #{measure_dir_name}"
256
- registry[:time_logger].start("Measure:#{measure_dir_name}") if registry[:time_logger]
257
- current_dir = Dir.pwd
258
-
259
- success = nil
260
- begin
261
-
262
- measure_dir = workflow_json.findMeasure(measure_dir_name)
263
- fail "Cannot find #{measure_dir_name}" if measure_dir.empty?
264
- measure_dir = measure_dir.get
265
-
266
- measure = OpenStudio::BCLMeasure.load(measure_dir)
267
- fail "Cannot load measure at #{measure_dir}" if measure.empty?
268
- measure = measure.get
269
-
270
- step_index = workflow_json.currentStepIndex
271
-
272
- measure_run_dir = File.join(run_dir, "#{step_index.to_s.rjust(3,'0')}_#{measure_dir_name}")
273
- logger.debug "Creating run directory for measure in #{measure_run_dir}"
274
- FileUtils.mkdir_p measure_run_dir
275
- Dir.chdir measure_run_dir
276
-
277
- if energyplus_output_requests
278
- logger.debug "energyPlusOutputRequests running in #{Dir.pwd}"
279
- else
280
- logger.debug "Apply measure running in #{Dir.pwd}"
281
- end
282
-
283
- class_name = measure.className
284
- measure_type = measure.measureType
285
-
286
- measure_path = measure.primaryRubyScriptPath
287
- fail "Measure does not have a primary ruby script specified" if measure_path.empty?
288
- measure_path = measure_path.get
289
- fail "#{measure_path} file does not exist" unless File.exist?(measure_path.to_s)
290
-
291
- logger.debug "Loading Measure from #{measure_path}"
292
-
293
- measure_object = nil
294
- result = nil
295
- begin
296
- load measure_path.to_s
297
- measure_object = Object.const_get(class_name).new
298
- rescue => e
299
-
300
- # add the error to the osw.out
301
- runner.registerError("#{e.message}\n\t#{e.backtrace.join("\n\t")}")
302
-
303
- # @todo (rhorsey) Clean up the error class here.
304
- log_message = "Error requiring measure #{__FILE__}. Failed with #{e.message}, #{e.backtrace.join("\n")}"
305
- raise log_message
306
- end
307
-
308
- arguments = nil
309
- skip_measure = false
310
- begin
311
-
312
- # Initialize arguments which may be model dependent, don't allow arguments method access to real model in case it changes something
313
- if measure_type == 'ModelMeasure'.to_MeasureType
314
- arguments = measure_object.arguments(@model.clone(true).to_Model)
315
- elsif measure_type == 'EnergyPlusMeasure'.to_MeasureType
316
- arguments = measure_object.arguments(@model_idf.clone(true))
317
- else measure_type == 'ReportingMeasure'.to_MeasureType
318
- arguments = measure_object.arguments
319
- end
320
-
321
- # Create argument map and initialize all the arguments
322
- argument_map = OpenStudio::Ruleset::OSArgumentMap.new
323
- if arguments
324
- arguments.each do |v|
325
- argument_map[v.name] = v.clone
326
- end
327
- end
328
-
329
- # Set argument values if they exist
330
- logger.debug "Iterating over arguments for workflow item '#{measure_dir_name}'"
331
- if step.arguments
332
- step.arguments.each do |argument_name, argument_value|
333
- if argument_name.to_s == '__SKIP__'
334
- if registry[:openstudio_2]
335
- variant_type = argument_value.variantType
336
- if variant_type == "String".to_VariantType
337
- argument_value = argument_value.valueAsString
338
- elsif variant_type == "Double".to_VariantType
339
- argument_value = argument_value.valueAsDouble
340
- elsif variant_type == "Integer".to_VariantType
341
- argument_value = argument_value.valueAsInteger
342
- elsif variant_type == "Boolean".to_VariantType
343
- argument_value = argument_value.valueAsBoolean
344
- end
345
- end
346
-
347
- if argument_value.class == String
348
- argument_value = argument_value.downcase
349
- if argument_value == "false"
350
- skip_measure = false
351
- else
352
- skip_measure = true
353
- end
354
- elsif argument_value.class == Fixnum
355
- skip_measure = (argument_value != 0)
356
- elsif argument_value.class == Float
357
- skip_measure = (argument_value != 0.0)
358
- elsif argument_value.class == FalseClass
359
- skip_measure = false
360
- elsif argument_value.class == TrueClass
361
- skip_measure = true
362
- elsif argument_value.class == NilClass
363
- skip_measure = false
364
- end
365
- else
366
- # regular argument
367
- if registry[:openstudio_2]
368
- success = apply_arguments_2(argument_map, argument_name, argument_value, logger)
369
- else
370
- success = apply_arguments(argument_map, argument_name, argument_value, logger)
371
- end
372
- fail 'Could not set arguments' unless success
373
- end
374
- end
375
- end
376
-
377
- # map any choice display names to choice values, in either set values or defaults
378
- argument_map.each_key do |argument_name|
379
- v = argument_map[argument_name]
380
- choice_values = v.choiceValues
381
- if !choice_values.empty?
382
- value = nil
383
- value = v.defaultValueAsString if v.hasDefaultValue
384
- value = v.valueAsString if v.hasValue
385
- if value && choice_values.index(value).nil?
386
- display_names = v.choiceValueDisplayNames
387
- i = display_names.index(value)
388
- if i && choice_values[i]
389
- logger.debug "Mapping display name '#{value}' to value '#{choice_values[i]}' for argument '#{argument_name}'"
390
- value_set = v.setValue(choice_values[i])
391
- fail "Could not set argument '#{argument_name}' to mapped value '#{choice_values[i]}'" unless value_set
392
- argument_map[argument_name.to_s] = v.clone
393
- end
394
- end
395
- end
396
- end
397
-
398
- rescue => e
399
-
400
- # add the error to the osw.out
401
- runner.registerError("#{e.message}\n\t#{e.backtrace.join("\n\t")}")
402
-
403
- log_message = "Error assigning argument in measure #{__FILE__}. Failed with #{e.message}, #{e.backtrace.join("\n")}"
404
- raise log_message
405
- end
406
-
407
- if skip_measure
408
- if !energyplus_output_requests
409
- # just increment
410
- logger.debug "Skipping measure '#{measure_dir_name}'"
411
-
412
- # required to update current step
413
- runner.prepareForUserScriptRun(measure_object)
414
-
415
- # don't want to log errors about arguments passed to skipped measures
416
- #runner.validateUserArguments(arguments, argument_map)
417
-
418
- current_result = runner.result
419
- runner.incrementStep
420
- add_result_measure_info(current_result, measure)
421
- current_result.setStepResult('Skip'.to_StepResult)
422
- end
423
- else
424
-
425
- begin
426
- if energyplus_output_requests
427
- logger.debug "Calling measure.energyPlusOutputRequests for '#{measure_dir_name}'"
428
- idf_objects = measure_object.energyPlusOutputRequests(runner, argument_map)
429
- num_added = 0
430
- idf_objects.each do |idf_object|
431
- num_added += OpenStudio::Workflow::Util::EnergyPlus.add_energyplus_output_request(@model_idf, idf_object)
432
- end
433
- logger.debug "Finished measure.energyPlusOutputRequests for '#{measure_dir_name}', #{num_added} output requests added"
434
- else
435
- logger.debug "Calling measure.run for '#{measure_dir_name}'"
436
- if measure_type == 'ModelMeasure'.to_MeasureType
437
- measure_object.run(@model, runner, argument_map)
438
- elsif measure_type == 'EnergyPlusMeasure'.to_MeasureType
439
- measure_object.run(@model_idf, runner, argument_map)
440
- elsif measure_type == 'ReportingMeasure'.to_MeasureType
441
- measure_object.run(runner, argument_map)
442
- end
443
- logger.debug "Finished measure.run for '#{measure_dir_name}'"
444
- end
445
-
446
- # Run garbage collector after every measure to help address race conditions
447
- GC.start
448
- rescue => e
449
-
450
- # add the error to the osw.out
451
- runner.registerError("#{e.message}\n\t#{e.backtrace.join("\n\t")}")
452
-
453
- result = runner.result
454
-
455
- if !energyplus_output_requests
456
- # incrementStep must be called after run
457
- runner.incrementStep
458
-
459
- add_result_measure_info(result, measure)
460
- end
461
-
462
- options[:output_adapter].communicate_measure_result(result) if options[:output_adapter]
463
-
464
- log_message = "Runner error #{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
465
- raise log_message
466
- end
467
-
468
- # if doing output requests we are done now
469
- if energyplus_output_requests
470
- registry.register(:model_idf) { @model_idf }
471
- return
472
- end
473
-
474
- result = nil
475
- begin
476
- result = runner.result
477
-
478
- # incrementStep must be called after run
479
- runner.incrementStep
480
-
481
- add_result_measure_info(result, measure)
482
-
483
- options[:output_adapter].communicate_measure_result(result) if options[:output_adapter]
484
-
485
- errors = result.stepErrors
486
-
487
- fail "Measure #{measure_dir_name} reported an error with #{errors}" if errors.size != 0
488
- logger.debug "Running of measure '#{measure_dir_name}' completed. Post-processing measure output"
489
-
490
- # TODO: fix this
491
- #unless @wf == runner.weatherfile_path
492
- # logger.debug "Updating the weather file to be '#{runner.weatherfile_path}'"
493
- # registry.register(:wf) { runner.weatherfile_path }
494
- #end
495
-
496
- # @todo add note about why reassignment and not eval
497
- registry.register(:model) { @model }
498
- registry.register(:model_idf) { @model_idf }
499
- registry.register(:sql) { @sql_filename }
500
-
501
- if measure_type == 'ModelMeasure'.to_MeasureType
502
- # check if weather file has changed
503
- weather_file = @model.getOptionalWeatherFile
504
- if !weather_file.empty?
505
- weather_file_path = weather_file.get.path
506
- if weather_file_path.empty?
507
- logger.debug "Weather file object found in model but no path is given"
508
- else
509
- weather_file_path2 = workflow_json.findFile(weather_file_path.get)
510
- if weather_file_path2.empty?
511
- logger.warn "Could not find weather file '#{weather_file_path}' referenced in model"
512
- else
513
- if weather_file_path2.get.to_s != @wf
514
- logger.debug "Updating weather file path to '#{weather_file_path2.get.to_s}'"
515
- @wf = weather_file_path2.get.to_s
516
- registry.register(:wf) { @wf }
517
- end
518
- end
519
- end
520
- end
521
- end
522
-
523
- rescue => e
524
- log_message = "Runner error #{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
525
- raise log_message
526
- end
527
-
528
- # DLM: this section creates the measure_attributes.json file which should be deprecated
529
- begin
530
- # DLM: which name do we want?
531
- measure_name = class_name
532
- #measure_name = measure_dir_name
533
-
534
- # DLM: do measure results from sequential measures with the same name clobber each other?
535
- output_attributes[measure_name.to_sym] = {} if output_attributes[measure_name.to_sym].nil?
536
-
537
- result.stepValues.each do |step_value|
538
- step_value_name = step_value.name
539
- step_value_type = step_value.variantType
540
-
541
- value = nil
542
- if (step_value_type == "String".to_VariantType)
543
- value = step_value.valueAsString
544
- elsif (step_value_type == "Double".to_VariantType)
545
- value = step_value.valueAsDouble
546
- elsif (step_value_type == "Integer".to_VariantType)
547
- value = step_value.valueAsInteger
548
- elsif (step_value_type == "Boolean".to_VariantType)
549
- value = step_value.valueAsBoolean
550
- end
551
-
552
- output_attributes[measure_name.to_sym][step_value_name] = value
553
- end
554
-
555
- # Add an applicability flag to all the measure results
556
- step_result = result.stepResult
557
- fail "Step Result not set" if step_result.empty?
558
- step_result = step_result.get
559
-
560
- if (step_result == "Skip".to_StepResult) || (step_result == "NA".to_StepResult)
561
- output_attributes[measure_name.to_sym][:applicable] = false
562
- else
563
- output_attributes[measure_name.to_sym][:applicable] = true
564
- end
565
- registry.register(:output_attributes) { output_attributes }
566
- rescue => e
567
- log_message = "#{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
568
- logger.error log_message
569
- raise log_message
570
- end
571
-
572
- end
573
-
574
- rescue => e
575
- log_message = "#{__FILE__} failed with message #{e.message} in #{e.backtrace.join("\n")}"
576
- logger.error log_message
577
- raise log_message
578
- ensure
579
- Dir.chdir current_dir
580
- registry[:time_logger].stop("Measure:#{measure_dir_name}") if registry[:time_logger]
581
-
582
- logger.info "Finished #{__method__} for #{measure_dir_name} in #{@registry[:time_logger].delta("Measure:#{measure_dir_name}")} s" if registry[:time_logger]
583
- end
584
- end
585
- end
586
- end
587
- end
588
- end
1
+ module OpenStudio
2
+ module Workflow
3
+ module Util
4
+
5
+ # Handles all interaction with measure objects in the gem. This includes measure.xml and measure.rb files
6
+ #
7
+ module Measure
8
+
9
+ # Wrapper method around #apply_measure to allow all measures of a type to be executed
10
+ #
11
+ # @param [String] measure_type Accepts OpenStudio::MeasureType argument
12
+ # @param [Object] registry Hash access to objects
13
+ # @param [Hash] options ({}) User-specified options used to override defaults
14
+ # @option options [Object] :time_logger A special logger used to debug performance issues
15
+ # @option options [Object] :output_adapter An output adapter to register measure transitions to
16
+ # @param [Boolean] energyplus_output_requests If true then the energyPlusOutputRequests is called instead of the run method
17
+ # @return [Void]
18
+ #
19
+ def apply_measures(measure_type, registry, options = {}, energyplus_output_requests = false)
20
+
21
+ # DLM: time_logger is in the registry but docs say it is in options?
22
+ registry[:time_logger].start "#{measure_type.valueName}:apply_measures" if registry[:time_logger]
23
+
24
+ logger = registry[:logger]
25
+ workflow_json = registry[:workflow_json]
26
+
27
+ workflow_steps = workflow_json.workflowSteps
28
+ fail "The 'steps' array of the OSW is required." unless workflow_steps
29
+
30
+ logger.debug "Finding measures of type #{measure_type.valueName}"
31
+ workflow_steps.each_index do |step_index|
32
+
33
+ step = workflow_steps[step_index]
34
+
35
+ if @registry[:openstudio_2]
36
+ if !step.to_MeasureStep.empty?
37
+ step = step.to_MeasureStep.get
38
+ end
39
+ end
40
+
41
+ measure_dir_name = step.measureDirName
42
+
43
+ measure_dir = workflow_json.findMeasure(measure_dir_name)
44
+ fail "Cannot find #{measure_dir_name}" if measure_dir.empty?
45
+ measure_dir = measure_dir.get
46
+
47
+ measure = OpenStudio::BCLMeasure.load(measure_dir)
48
+ fail "Cannot load measure at #{measure_dir}" if measure.empty?
49
+ measure = measure.get
50
+
51
+ class_name = measure.className
52
+ measure_instance_type = measure.measureType
53
+ if measure_instance_type == measure_type
54
+ if energyplus_output_requests
55
+ logger.info "Found measure #{class_name} of type #{measure_type.valueName}. Collecting EnergyPlus Output Requests now."
56
+ apply_measure(registry, step, options, energyplus_output_requests)
57
+ else
58
+ logger.info "Found measure #{class_name} of type #{measure_type.valueName}. Applying now."
59
+
60
+ # fast forward current step index to this index, skips any previous steps
61
+ while workflow_json.currentStepIndex < step_index
62
+ workflow_json.incrementStep
63
+ end
64
+
65
+ # DLM: why is output_adapter in options instead of registry?
66
+ options[:output_adapter].communicate_transition("Applying #{class_name}", :measure) if options[:output_adapter]
67
+ apply_measure(registry, step, options)
68
+ options[:output_adapter].communicate_transition("Applied #{class_name}", :measure) if options[:output_adapter]
69
+ end
70
+
71
+ logger.info 'Moving to the next workflow step.'
72
+ else
73
+ logger.debug "Passing measure #{class_name} of type #{measure_type.valueName}"
74
+ end
75
+ end
76
+
77
+ registry[:time_logger].stop "#{measure_type.valueName}:apply_measures" if registry[:time_logger]
78
+ end
79
+
80
+ # Determine if a given workflow can find and load all measures defined in steps
81
+ #
82
+ # @param [Hash] workflow See the schema for an OSW defined in the spec folder of this repo. Note that this
83
+ # method requires the OSW to have been loaded with symbolized keys
84
+ # @param [String] directory The directory that will be passed to the find_measure_dir method
85
+ # @return [true] If the method doesn't fail the workflow measures were validated
86
+ #
87
+ def validate_measures(registry, logger)
88
+
89
+ logger = registry[:logger] if logger.nil?
90
+ workflow_json = registry[:workflow_json]
91
+
92
+ state = 'ModelMeasure'.to_MeasureType
93
+ steps = workflow_json.workflowSteps
94
+ steps.each_with_index do |step, index|
95
+ begin
96
+ logger.debug "Validating step #{index}"
97
+
98
+ if @registry[:openstudio_2]
99
+ if !step.to_MeasureStep.empty?
100
+ step = step.to_MeasureStep.get
101
+ end
102
+ end
103
+
104
+ # Verify the existence of the required files
105
+ measure_dir_name = step.measureDirName
106
+
107
+ measure_dir = workflow_json.findMeasure(measure_dir_name)
108
+ fail "Cannot find measure #{measure_dir_name}" if measure_dir.empty?
109
+ measure_dir = measure_dir.get
110
+
111
+ measure = OpenStudio::BCLMeasure.load(measure_dir)
112
+ fail "Cannot load measure at #{measure_dir}" if measure.empty?
113
+ measure = measure.get
114
+
115
+ class_name = measure.className
116
+ measure_instance_type = measure.measureType
117
+
118
+ # Ensure that measures are in order, i.e. no OS after E+, E+ or OS after Reporting
119
+ if measure_instance_type == 'ModelMeasure'.to_MeasureType
120
+ fail "OpenStudio measure #{measure_dir} called after transition to EnergyPlus." if state == 'EnergyPlusMeasure'.to_MeasureType
121
+ fail "OpenStudio measure #{measure_dir} called after after Energyplus simulation." if state == 'ReportingMeasure'.to_MeasureType
122
+ elsif measure_instance_type == "EnergyPlusMeasure".to_MeasureType
123
+ state = 'EnergyPlusMeasure'.to_MeasureType if state == 'ModelMeasure'.to_MeasureType
124
+ fail "EnergyPlus measure #{measure_dir} called after Energyplus simulation." if state == 'ReportingMeasure'.to_MeasureType
125
+ elsif measure_instance_type == 'ReportingMeasure'.to_MeasureType
126
+ state = 'ReportingMeasure'.to_MeasureType if state != 'ReportingMeasure'.to_MeasureType
127
+ else
128
+ fail "Error: MeasureType #{measure_instance_type.valueName} of measure #{measure_dir} is not supported"
129
+ end
130
+
131
+ logger.debug "Validated step #{index}"
132
+ end
133
+ end
134
+ end
135
+
136
+ # Sets the argument map for argument_map argument pair
137
+ #
138
+ # @param [Object] argument_map See the OpenStudio SDK for a description of the OSArgumentMap structure
139
+ # @param [Object] argument_name, user defined argument name
140
+ # @param [Object] argument_value, user defined argument value
141
+ # @param [Object] logger, logger object
142
+ # @return [Object] Returns an updated ArgumentMap object
143
+ #
144
+ def apply_arguments(argument_map, argument_name, argument_value, logger)
145
+ unless argument_value.nil?
146
+ logger.info "Setting argument value '#{argument_name}' to '#{argument_value}'"
147
+
148
+ v = argument_map[argument_name.to_s]
149
+ fail "Could not find argument '#{argument_name}' in argument_map" unless v
150
+ value_set = v.setValue(argument_value)
151
+ fail "Could not set argument '#{argument_name}' to value '#{argument_value}'" unless value_set
152
+ argument_map[argument_name.to_s] = v.clone
153
+ else
154
+ logger.warn "Value for argument '#{argument_name}' not set in argument list therefore will use default"
155
+ end
156
+ end
157
+
158
+ def apply_arguments_2(argument_map, argument_name, argument_value, logger)
159
+ unless argument_value.nil?
160
+ logger.info "Setting argument value '#{argument_name}' to '#{argument_value}'"
161
+
162
+ v = argument_map[argument_name.to_s]
163
+ fail "Could not find argument '#{argument_name}' in argument_map" unless v
164
+ value_set = false
165
+ variant_type = argument_value.variantType
166
+ if variant_type == "String".to_VariantType
167
+ argument_value = argument_value.valueAsString
168
+ value_set = v.setValue(argument_value)
169
+ elsif variant_type == "Double".to_VariantType
170
+ argument_value = argument_value.valueAsDouble
171
+ value_set = v.setValue(argument_value)
172
+ elsif variant_type == "Integer".to_VariantType
173
+ argument_value = argument_value.valueAsInteger
174
+ value_set = v.setValue(argument_value)
175
+ elsif variant_type == "Boolean".to_VariantType
176
+ argument_value = argument_value.valueAsBoolean
177
+ value_set = v.setValue(argument_value)
178
+ end
179
+ fail "Could not set argument '#{argument_name}' to value '#{argument_value}'" unless value_set
180
+ argument_map[argument_name.to_s] = v.clone
181
+ else
182
+ logger.warn "Value for argument '#{argument_name}' not set in argument list therefore will use default"
183
+ end
184
+ end
185
+
186
+ # Method to add measure info to WorkflowStepResult
187
+ #
188
+ # @param [Object] result Current WorkflowStepResult
189
+ # @param [Object] measure Current BCLMeasure
190
+ def add_result_measure_info(result, measure)
191
+ begin
192
+ result.setMeasureType(measure.measureType)
193
+ result.setMeasureName(measure.name)
194
+ result.setMeasureId(measure.uid)
195
+ result.setMeasureVersionId(measure.versionId)
196
+ version_modified = measure.versionModified
197
+ if !version_modified.empty?
198
+ result.setMeasureVersionModified(version_modified.get)
199
+ end
200
+ result.setMeasureXmlChecksum(measure.xmlChecksum)
201
+ result.setMeasureClassName(measure.className)
202
+ result.setMeasureDisplayName(measure.displayName)
203
+ result.setMeasureTaxonomy(measure.taxonomyTag)
204
+ rescue NameError
205
+ end
206
+ end
207
+
208
+ # Method to allow for a single measure of any type to be run
209
+ #
210
+ # @param [String] directory Location of the datapoint directory to run. This is needed
211
+ # independent of the adapter that is being used. Note that the simulation will actually run in 'run'
212
+ # @param [Object] adapter An instance of the adapter class
213
+ # @param [String] current_weather_filepath The path which will be used to set the runner and returned to update
214
+ # the OSW for future measures and the simulation
215
+ # @param [Object] model The model object being used in the measure, either a OSM or IDF
216
+ # @param [Hash] step Definition of the to be run by the workflow
217
+ # @option step [String] :measure_dir_name The name of the directory which contains the measure files
218
+ # @option step [Object] :arguments name value hash which defines the arguments to the measure, e.g.
219
+ # {has_bool: true, cost: 3.1}
220
+ # @param output_attributes [Hash] The results of previous measure applications which are persisted through the
221
+ # runner to allow measures to react to previous events in the workflow
222
+ # @param [Hash] options ({}) User-specified options used to override defaults
223
+ # @option options [Array] :measure_search_array Ordered set of measure directories used to search for
224
+ # step[:measure_dir_name], e.g. ['measures', '../../measures']
225
+ # @option options [Object] :time_logger Special logger used to debug performance issues
226
+ # @param [Boolean] energyplus_output_requests If true then the energyPlusOutputRequests is called instead of the run method
227
+ # @return [Hash, String] Returns two objects. The first is the (potentially) updated output_attributes hash, and
228
+ # the second is the (potentially) updated current_weather_filepath
229
+ #
230
+ def apply_measure(registry, step, options = {}, energyplus_output_requests = false)
231
+
232
+ logger = registry[:logger]
233
+ runner = registry[:runner]
234
+ workflow_json = registry[:workflow_json]
235
+ measure_dir_name = step.measureDirName
236
+
237
+ run_dir = registry[:run_dir]
238
+ fail 'No run directory set in the registry' unless run_dir
239
+
240
+ output_attributes = registry[:output_attributes]
241
+
242
+ # todo: get weather file from appropriate location
243
+ @wf = registry[:wf]
244
+ @model = registry[:model]
245
+ @model_idf = registry[:model_idf]
246
+ @sql_filename = registry[:sql]
247
+
248
+ runner.setLastOpenStudioModel(@model) if @model
249
+ #runner.setLastOpenStudioModelPath(const openstudio::path& lastOpenStudioModelPath); #DLM - deprecate?
250
+ runner.setLastEnergyPlusWorkspace(@model_idf) if @model_idf
251
+ #runner.setLastEnergyPlusWorkspacePath(const openstudio::path& lastEnergyPlusWorkspacePath); #DLM - deprecate?
252
+ runner.setLastEnergyPlusSqlFilePath(@sql_filename) if @sql_filename
253
+ runner.setLastEpwFilePath(@wf) if @wf
254
+
255
+ logger.debug "Starting #{__method__} for #{measure_dir_name}"
256
+ registry[:time_logger].start("Measure:#{measure_dir_name}") if registry[:time_logger]
257
+ current_dir = Dir.pwd
258
+
259
+ success = nil
260
+ begin
261
+
262
+ measure_dir = workflow_json.findMeasure(measure_dir_name)
263
+ fail "Cannot find #{measure_dir_name}" if measure_dir.empty?
264
+ measure_dir = measure_dir.get
265
+
266
+ measure = OpenStudio::BCLMeasure.load(measure_dir)
267
+ fail "Cannot load measure at #{measure_dir}" if measure.empty?
268
+ measure = measure.get
269
+
270
+ step_index = workflow_json.currentStepIndex
271
+
272
+ measure_run_dir = File.join(run_dir, "#{step_index.to_s.rjust(3,'0')}_#{measure_dir_name}")
273
+ logger.debug "Creating run directory for measure in #{measure_run_dir}"
274
+ FileUtils.mkdir_p measure_run_dir
275
+ Dir.chdir measure_run_dir
276
+
277
+ if energyplus_output_requests
278
+ logger.debug "energyPlusOutputRequests running in #{Dir.pwd}"
279
+ else
280
+ logger.debug "Apply measure running in #{Dir.pwd}"
281
+ end
282
+
283
+ class_name = measure.className
284
+ measure_type = measure.measureType
285
+
286
+ measure_path = measure.primaryRubyScriptPath
287
+ fail "Measure does not have a primary ruby script specified" if measure_path.empty?
288
+ measure_path = measure_path.get
289
+ fail "#{measure_path} file does not exist" unless File.exist?(measure_path.to_s)
290
+
291
+ logger.debug "Loading Measure from #{measure_path}"
292
+
293
+ measure_object = nil
294
+ result = nil
295
+ begin
296
+ load measure_path.to_s
297
+ measure_object = Object.const_get(class_name).new
298
+ rescue => e
299
+
300
+ # add the error to the osw.out
301
+ runner.registerError("#{e.message}\n\t#{e.backtrace.join("\n\t")}")
302
+
303
+ # @todo (rhorsey) Clean up the error class here.
304
+ log_message = "Error requiring measure #{__FILE__}. Failed with #{e.message}, #{e.backtrace.join("\n")}"
305
+ raise log_message
306
+ end
307
+
308
+ arguments = nil
309
+ skip_measure = false
310
+ begin
311
+
312
+ # Initialize arguments which may be model dependent, don't allow arguments method access to real model in case it changes something
313
+ if measure_type == 'ModelMeasure'.to_MeasureType
314
+ arguments = measure_object.arguments(@model.clone(true).to_Model)
315
+ elsif measure_type == 'EnergyPlusMeasure'.to_MeasureType
316
+ arguments = measure_object.arguments(@model_idf.clone(true))
317
+ else measure_type == 'ReportingMeasure'.to_MeasureType
318
+ arguments = measure_object.arguments
319
+ end
320
+
321
+ # Create argument map and initialize all the arguments
322
+ argument_map = OpenStudio::Ruleset::OSArgumentMap.new
323
+ if arguments
324
+ arguments.each do |v|
325
+ argument_map[v.name] = v.clone
326
+ end
327
+ end
328
+
329
+ # Set argument values if they exist
330
+ logger.debug "Iterating over arguments for workflow item '#{measure_dir_name}'"
331
+ if step.arguments
332
+ step.arguments.each do |argument_name, argument_value|
333
+ if argument_name.to_s == '__SKIP__'
334
+ if registry[:openstudio_2]
335
+ variant_type = argument_value.variantType
336
+ if variant_type == "String".to_VariantType
337
+ argument_value = argument_value.valueAsString
338
+ elsif variant_type == "Double".to_VariantType
339
+ argument_value = argument_value.valueAsDouble
340
+ elsif variant_type == "Integer".to_VariantType
341
+ argument_value = argument_value.valueAsInteger
342
+ elsif variant_type == "Boolean".to_VariantType
343
+ argument_value = argument_value.valueAsBoolean
344
+ end
345
+ end
346
+
347
+ if argument_value.class == String
348
+ argument_value = argument_value.downcase
349
+ if argument_value == "false"
350
+ skip_measure = false
351
+ else
352
+ skip_measure = true
353
+ end
354
+ elsif argument_value.class == Fixnum
355
+ skip_measure = (argument_value != 0)
356
+ elsif argument_value.class == Float
357
+ skip_measure = (argument_value != 0.0)
358
+ elsif argument_value.class == FalseClass
359
+ skip_measure = false
360
+ elsif argument_value.class == TrueClass
361
+ skip_measure = true
362
+ elsif argument_value.class == NilClass
363
+ skip_measure = false
364
+ end
365
+ else
366
+ # regular argument
367
+ if registry[:openstudio_2]
368
+ success = apply_arguments_2(argument_map, argument_name, argument_value, logger)
369
+ else
370
+ success = apply_arguments(argument_map, argument_name, argument_value, logger)
371
+ end
372
+ fail 'Could not set arguments' unless success
373
+ end
374
+ end
375
+ end
376
+
377
+ # map any choice display names to choice values, in either set values or defaults
378
+ argument_map.each_key do |argument_name|
379
+ v = argument_map[argument_name]
380
+ choice_values = v.choiceValues
381
+ if !choice_values.empty?
382
+ value = nil
383
+ value = v.defaultValueAsString if v.hasDefaultValue
384
+ value = v.valueAsString if v.hasValue
385
+ if value && choice_values.index(value).nil?
386
+ display_names = v.choiceValueDisplayNames
387
+ i = display_names.index(value)
388
+ if i && choice_values[i]
389
+ logger.debug "Mapping display name '#{value}' to value '#{choice_values[i]}' for argument '#{argument_name}'"
390
+ value_set = v.setValue(choice_values[i])
391
+ fail "Could not set argument '#{argument_name}' to mapped value '#{choice_values[i]}'" unless value_set
392
+ argument_map[argument_name.to_s] = v.clone
393
+ end
394
+ end
395
+ end
396
+ end
397
+
398
+ rescue => e
399
+
400
+ # add the error to the osw.out
401
+ runner.registerError("#{e.message}\n\t#{e.backtrace.join("\n\t")}")
402
+
403
+ log_message = "Error assigning argument in measure #{__FILE__}. Failed with #{e.message}, #{e.backtrace.join("\n")}"
404
+ raise log_message
405
+ end
406
+
407
+ if skip_measure
408
+ if !energyplus_output_requests
409
+ # just increment
410
+ logger.debug "Skipping measure '#{measure_dir_name}'"
411
+
412
+ # required to update current step
413
+ runner.prepareForUserScriptRun(measure_object)
414
+
415
+ # don't want to log errors about arguments passed to skipped measures
416
+ #runner.validateUserArguments(arguments, argument_map)
417
+
418
+ current_result = runner.result
419
+ runner.incrementStep
420
+ add_result_measure_info(current_result, measure)
421
+ current_result.setStepResult('Skip'.to_StepResult)
422
+ end
423
+ else
424
+
425
+ begin
426
+ if energyplus_output_requests
427
+ logger.debug "Calling measure.energyPlusOutputRequests for '#{measure_dir_name}'"
428
+ idf_objects = measure_object.energyPlusOutputRequests(runner, argument_map)
429
+ num_added = 0
430
+ idf_objects.each do |idf_object|
431
+ num_added += OpenStudio::Workflow::Util::EnergyPlus.add_energyplus_output_request(@model_idf, idf_object)
432
+ end
433
+ logger.debug "Finished measure.energyPlusOutputRequests for '#{measure_dir_name}', #{num_added} output requests added"
434
+ else
435
+ logger.debug "Calling measure.run for '#{measure_dir_name}'"
436
+ if measure_type == 'ModelMeasure'.to_MeasureType
437
+ measure_object.run(@model, runner, argument_map)
438
+ elsif measure_type == 'EnergyPlusMeasure'.to_MeasureType
439
+ measure_object.run(@model_idf, runner, argument_map)
440
+ elsif measure_type == 'ReportingMeasure'.to_MeasureType
441
+ measure_object.run(runner, argument_map)
442
+ end
443
+ logger.debug "Finished measure.run for '#{measure_dir_name}'"
444
+ end
445
+
446
+ # Run garbage collector after every measure to help address race conditions
447
+ GC.start
448
+ rescue => e
449
+
450
+ # add the error to the osw.out
451
+ runner.registerError("#{e.message}\n\t#{e.backtrace.join("\n\t")}")
452
+
453
+ result = runner.result
454
+
455
+ if !energyplus_output_requests
456
+ # incrementStep must be called after run
457
+ runner.incrementStep
458
+
459
+ add_result_measure_info(result, measure)
460
+ end
461
+
462
+ options[:output_adapter].communicate_measure_result(result) if options[:output_adapter]
463
+
464
+ log_message = "Runner error #{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
465
+ raise log_message
466
+ end
467
+
468
+ # if doing output requests we are done now
469
+ if energyplus_output_requests
470
+ registry.register(:model_idf) { @model_idf }
471
+ return
472
+ end
473
+
474
+ result = nil
475
+ begin
476
+ result = runner.result
477
+
478
+ # incrementStep must be called after run
479
+ runner.incrementStep
480
+
481
+ add_result_measure_info(result, measure)
482
+
483
+ options[:output_adapter].communicate_measure_result(result) if options[:output_adapter]
484
+
485
+ errors = result.stepErrors
486
+
487
+ fail "Measure #{measure_dir_name} reported an error with #{errors}" if errors.size != 0
488
+ logger.debug "Running of measure '#{measure_dir_name}' completed. Post-processing measure output"
489
+
490
+ # TODO: fix this
491
+ #unless @wf == runner.weatherfile_path
492
+ # logger.debug "Updating the weather file to be '#{runner.weatherfile_path}'"
493
+ # registry.register(:wf) { runner.weatherfile_path }
494
+ #end
495
+
496
+ # @todo add note about why reassignment and not eval
497
+ registry.register(:model) { @model }
498
+ registry.register(:model_idf) { @model_idf }
499
+ registry.register(:sql) { @sql_filename }
500
+
501
+ if measure_type == 'ModelMeasure'.to_MeasureType
502
+ # check if weather file has changed
503
+ weather_file = @model.getOptionalWeatherFile
504
+ if !weather_file.empty?
505
+ weather_file_path = weather_file.get.path
506
+ if weather_file_path.empty?
507
+ logger.debug "Weather file object found in model but no path is given"
508
+ else
509
+ weather_file_path2 = workflow_json.findFile(weather_file_path.get)
510
+ if weather_file_path2.empty?
511
+ logger.warn "Could not find weather file '#{weather_file_path}' referenced in model"
512
+ else
513
+ if weather_file_path2.get.to_s != @wf
514
+ logger.debug "Updating weather file path to '#{weather_file_path2.get.to_s}'"
515
+ @wf = weather_file_path2.get.to_s
516
+ registry.register(:wf) { @wf }
517
+ end
518
+ end
519
+ end
520
+ end
521
+ end
522
+
523
+ rescue => e
524
+ log_message = "Runner error #{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
525
+ raise log_message
526
+ end
527
+
528
+ # DLM: this section creates the measure_attributes.json file which should be deprecated
529
+ begin
530
+ # DLM: which name do we want?
531
+ measure_name = class_name
532
+ #measure_name = measure_dir_name
533
+
534
+ # DLM: do measure results from sequential measures with the same name clobber each other?
535
+ output_attributes[measure_name.to_sym] = {} if output_attributes[measure_name.to_sym].nil?
536
+
537
+ result.stepValues.each do |step_value|
538
+ step_value_name = step_value.name
539
+ step_value_type = step_value.variantType
540
+
541
+ value = nil
542
+ if (step_value_type == "String".to_VariantType)
543
+ value = step_value.valueAsString
544
+ elsif (step_value_type == "Double".to_VariantType)
545
+ value = step_value.valueAsDouble
546
+ elsif (step_value_type == "Integer".to_VariantType)
547
+ value = step_value.valueAsInteger
548
+ elsif (step_value_type == "Boolean".to_VariantType)
549
+ value = step_value.valueAsBoolean
550
+ end
551
+
552
+ output_attributes[measure_name.to_sym][step_value_name] = value
553
+ end
554
+
555
+ # Add an applicability flag to all the measure results
556
+ step_result = result.stepResult
557
+ fail "Step Result not set" if step_result.empty?
558
+ step_result = step_result.get
559
+
560
+ if (step_result == "Skip".to_StepResult) || (step_result == "NA".to_StepResult)
561
+ output_attributes[measure_name.to_sym][:applicable] = false
562
+ else
563
+ output_attributes[measure_name.to_sym][:applicable] = true
564
+ end
565
+ registry.register(:output_attributes) { output_attributes }
566
+ rescue => e
567
+ log_message = "#{__FILE__} failed with #{e.message}, #{e.backtrace.join("\n")}"
568
+ logger.error log_message
569
+ raise log_message
570
+ end
571
+
572
+ end
573
+
574
+ rescue => e
575
+ log_message = "#{__FILE__} failed with message #{e.message} in #{e.backtrace.join("\n")}"
576
+ logger.error log_message
577
+ raise log_message
578
+ ensure
579
+ Dir.chdir current_dir
580
+ registry[:time_logger].stop("Measure:#{measure_dir_name}") if registry[:time_logger]
581
+
582
+ logger.info "Finished #{__method__} for #{measure_dir_name} in #{@registry[:time_logger].delta("Measure:#{measure_dir_name}")} s" if registry[:time_logger]
583
+ end
584
+ end
585
+ end
586
+ end
587
+ end
588
+ end