Class: OpenStudio::Analysis::WorkflowStep

Inherits:
Object
  • Object
show all
Defined in:
lib/openstudio/analysis/workflow_step.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeObject

Create an instance of the OpenStudio::Analysis::WorkflowStep



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/openstudio/analysis/workflow_step.rb', line 59

def initialize
  @name = ''
  @display_name = ''

  # The type of item being added (RubyMeasure, EnergyPlusMeasure, ...)
  @type = nil

  @measure_definition_class_name = nil
  @measure_definition_directory = nil
  @measure_definition_directory_local = nil
  @measure_definition_display_name = nil
  @measure_definition_name = nil
  @measure_definition_name_xml = nil
  @measure_definition_uuid = nil
  @measure_definition_version_uuid = nil
  @arguments = []

  @arguments << {
    display_name: 'Skip Entire Measure',
    display_name_short: 'Skip',
    name: '__SKIP__',
    value_type: 'boolean',
    default_value: false,
    value: false
  }

  # TODO: eventually the variables should be its own class. This would then be an array of Variable objects.
  @variables = []
end

Instance Attribute Details

#argumentsObject (readonly)

Returns the value of attribute arguments.



53
54
55
# File 'lib/openstudio/analysis/workflow_step.rb', line 53

def arguments
  @arguments
end

#display_nameObject

Returns the value of attribute display_name.



43
44
45
# File 'lib/openstudio/analysis/workflow_step.rb', line 43

def display_name
  @display_name
end

#measure_definition_class_nameObject

Returns the value of attribute measure_definition_class_name.



45
46
47
# File 'lib/openstudio/analysis/workflow_step.rb', line 45

def measure_definition_class_name
  @measure_definition_class_name
end

#measure_definition_directoryObject

Returns the value of attribute measure_definition_directory.



46
47
48
# File 'lib/openstudio/analysis/workflow_step.rb', line 46

def measure_definition_directory
  @measure_definition_directory
end

#measure_definition_directory_localObject

Returns the value of attribute measure_definition_directory_local.



47
48
49
# File 'lib/openstudio/analysis/workflow_step.rb', line 47

def measure_definition_directory_local
  @measure_definition_directory_local
end

#measure_definition_display_nameObject

Returns the value of attribute measure_definition_display_name.



48
49
50
# File 'lib/openstudio/analysis/workflow_step.rb', line 48

def measure_definition_display_name
  @measure_definition_display_name
end

#measure_definition_nameObject

Returns the value of attribute measure_definition_name.



49
50
51
# File 'lib/openstudio/analysis/workflow_step.rb', line 49

def measure_definition_name
  @measure_definition_name
end

#measure_definition_name_xmlObject

Returns the value of attribute measure_definition_name_xml.



50
51
52
# File 'lib/openstudio/analysis/workflow_step.rb', line 50

def measure_definition_name_xml
  @measure_definition_name_xml
end

#measure_definition_uuidObject

Returns the value of attribute measure_definition_uuid.



51
52
53
# File 'lib/openstudio/analysis/workflow_step.rb', line 51

def measure_definition_uuid
  @measure_definition_uuid
end

#measure_definition_version_uuidObject

Returns the value of attribute measure_definition_version_uuid.



52
53
54
# File 'lib/openstudio/analysis/workflow_step.rb', line 52

def measure_definition_version_uuid
  @measure_definition_version_uuid
end

#nameObject

Returns the value of attribute name.



42
43
44
# File 'lib/openstudio/analysis/workflow_step.rb', line 42

def name
  @name
end

#typeObject

Returns the value of attribute type.



41
42
43
# File 'lib/openstudio/analysis/workflow_step.rb', line 41

def type
  @type
end

#variablesObject (readonly)

Returns the value of attribute variables.



54
55
56
# File 'lib/openstudio/analysis/workflow_step.rb', line 54

def variables
  @variables
end

Class Method Details

.from_analysis_hash(instance_name, instance_display_name, path_to_measure, hash, options = {}) ⇒ Object

Read the workflow item from a analysis hash. Can we combine measure hash and analysis hash?

Parameters:

  • instance_name (String)

    Machine name of the instance

  • instance_display_name (String)

    Display name of the instance

  • path_to_measure (String)

    This is the local path to the measure directroy, relative or absolute. It is used when zipping up all the measures.

  • hash (Hash)

    Measure hash in the format of the measure.json (from the Analysis Spreadsheet project)

  • options (Hash) (defaults to: {})

    Optional arguments

Options Hash (options):

  • :ignore_not_found (Boolean)

    Do not raise an exception if the measure could not be found on the machine

Returns:

  • (Object)

    Returns the OpenStudio::Analysis::WorkflowStep



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/openstudio/analysis/workflow_step.rb', line 363

def self.from_analysis_hash(instance_name, instance_display_name, path_to_measure, hash, options = {})
  # TODO: Validate the hash
  # TODO: validate that the measure exists?

  if File.directory? path_to_measure
    path_to_measure = File.join(path_to_measure, 'measure.rb')
  end

  # verify that the path to the measure is a path and not a file. If it is make it a path
  if File.exist?(path_to_measure) && File.file?(path_to_measure)
    path_to_measure = File.dirname(path_to_measure)
  else
    raise "Could not find measure '#{instance_name}' in '#{path_to_measure}'" unless options[:ignore_not_found]
  end

  # Extract the directo
  path_to_measure_local = path_to_measure
  path_to_measure = "./measures/#{File.basename(path_to_measure)}"

  # map the BCL hash format into the OpenStudio WorkflowStep format
  s = OpenStudio::Analysis::WorkflowStep.new

  # add the instance and display name
  s.name = instance_name
  s.display_name = instance_display_name

  # definition of the measure
  s.measure_definition_class_name = hash[:measure_definition_class_name]
  s.measure_definition_directory = path_to_measure
  s.measure_definition_directory_local = path_to_measure_local
  s.measure_definition_display_name = hash[:measure_definition_display_name]
  s.measure_definition_name = hash[:measure_definition_name]
  # name_xml is not used right now but eventually should be used to compare the hash[:name] and the hash[:name_xml]
  s.measure_definition_name_xml = hash[:measure_definition_name_xml]
  s.measure_definition_uuid = hash[:measure_definition_uuid]
  s.measure_definition_version_uuid = hash[:measure_definition_version_uuid]

  s.type = hash[:measure_type] # this is actually the measure type
  if hash[:arguments]
    hash[:arguments].each do |arg|
      # warn the user to we need to deprecate variable_type and use value_type (which is what os server uses)
      var_type = arg[:value_type]

      if var_type == 'choice'
        # WARN the user that the measure had a "choice data type"
        var_type = 'string'
      end

      s.arguments << {
        display_name: arg[:display_name],
        display_name_short: arg[:display_name_short],
        name: arg[:name],
        value_type: var_type,
        default_value: arg[:default_value],
        value: arg[:value]
      }
    end
  end

  if hash[:variables]
    hash[:variables].each do |variable|
      # add the arguments first
      s.arguments << {
        display_name: variable[:argument][:display_name],
        display_name_short: variable[:argument][:display_name_short],
        name: variable[:argument][:name],
        value_type: variable[:argument][:value_type],
        default_value: variable[:argument][:default_value],
        value: variable[:argument][:default_value]
      }

      var_options = {}
      var_options[:variable_type] = variable[:variable_type]
      var_options[:variable_display_name_short] = variable[:display_name_short]
      var_options[:static_value] = variable[:static_value]
      distribution = variable[:uncertainty_description]
      distribution[:minimum] = variable[:minimum]
      distribution[:mean] = distribution[:attributes].find { |a| a[:name] == 'modes' }[:value]
      distribution[:maximum] = variable[:maximum]
      distribution[:standard_deviation] = distribution[:attributes].find { |a| a[:name] == 'stddev' }[:value]
      distribution[:step_size] = distribution[:attributes].find { |a| a[:name] == 'delta_x' }[:value]
      s.make_variable(variable[:argument][:name], variable[:display_name], distribution, var_options)
    end
  end

  s
end

.from_measure_hash(instance_name, instance_display_name, path_to_measure, hash, options = {}) ⇒ Object

Read the workflow item from a measure hash.

Parameters:

  • instance_name (String)

    Machine name of the instance

  • instance_display_name (String)

    Display name of the instance

  • path_to_measure (String)

    This is the local path to the measure directroy, relative or absolute. It is used when zipping up all the measures.

  • hash (Hash)

    Measure hash in the format of the measure.json (from the Analysis Spreadsheet project)

  • options (Hash) (defaults to: {})

    Optional arguments

Options Hash (options):

  • :ignore_not_found (Boolean)

    Do not raise an exception if the measure could not be found on the machine

Returns:

  • (Object)

    Returns the OpenStudio::Analysis::WorkflowStep



275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/openstudio/analysis/workflow_step.rb', line 275

def self.from_measure_hash(instance_name, instance_display_name, path_to_measure, hash, options = {})
  # TODO: Validate the hash
  # TODO: validate that the measure exists?

  if File.directory? path_to_measure
    path_to_measure = File.join(path_to_measure, 'measure.rb')
  end

  # verify that the path to the measure is a path and not a file. If it is make it a path
  if File.exist?(path_to_measure) && File.file?(path_to_measure)
    path_to_measure = File.dirname(path_to_measure)
  else
    raise "Could not find measure '#{instance_name}' in '#{path_to_measure}'" unless options[:ignore_not_found]
  end

  # Extract the directory
  path_to_measure_local = path_to_measure
  path_to_measure = "./measures/#{File.basename(path_to_measure)}"

  # map the BCL hash format into the OpenStudio WorkflowStep format
  s = OpenStudio::Analysis::WorkflowStep.new

  # add the instance and display name
  s.name = instance_name
  s.display_name = instance_display_name

  # definition of the measure
  s.measure_definition_class_name = hash[:classname]
  s.measure_definition_directory = path_to_measure
  s.measure_definition_directory_local = path_to_measure_local
  s.measure_definition_display_name = hash[:display_name]
  s.measure_definition_name = hash[:name]
  # name_xml is not used right now but eventually should be used to compare the hash[:name] and the hash[:name_xml]
  s.measure_definition_name_xml = hash[:name_xml]
  s.measure_definition_uuid = hash[:uid]
  s.measure_definition_version_uuid = hash[:version_id]

  # do not allow the choice variable_type

  s.type = hash[:measure_type] # this is actually the measure type
  if hash[:arguments]
    hash[:arguments].each do |arg|
      # warn the user to we need to deprecate variable_type and use value_type (which is what os server uses)
      var_type = arg[:variable_type] ? arg[:variable_type].downcase : arg[:value_type]

      if var_type == 'choice'
        # WARN the user that the measure had a "choice data type"
        var_type = 'string'
      end

      s.arguments << {
        display_name: arg[:display_name],
        display_name_short: arg[:display_name_short],
        name: arg[:name],
        value_type: var_type,
        default_value: arg[:default_value],
        value: arg[:default_value]
      }
    end
  end

  # Load the arguments of variables, but do not make them variables. This format is more about arugments, than variables
  if hash[:variables]
    hash[:variables].each do |variable|
      # add the arguments first
      s.arguments << {
        display_name: variable[:argument][:display_name],
        display_name_short: variable[:argument][:display_name_short],
        name: variable[:argument][:name],
        value_type: variable[:argument][:value_type],
        default_value: variable[:argument][:default_value],
        value: variable[:argument][:default_value]
      }
    end
  end

  s
end

Instance Method Details

#argument_namesArray

Return an array of the argument names

Returns:

  • (Array)

    Listing of argument names.



92
93
94
# File 'lib/openstudio/analysis/workflow_step.rb', line 92

def argument_names
  @arguments.map { |a| a[:name] }
end

#argument_value(argument_name, value) ⇒ Boolean

Set the value of an argument to ‘value`. The user is required to know the data type and pass it in accordingly

Parameters:

  • argument_name (String)

    The machine name of the argument that you want to set the value to

  • value

    The value to assign the argument

Returns:

  • (Boolean)

    True/false if it assigned it



101
102
103
104
105
106
107
108
109
110
111
# File 'lib/openstudio/analysis/workflow_step.rb', line 101

def argument_value(argument_name, value)
  a = @arguments.find_all { |a| a[:name] == argument_name }
  raise "could not find argument_name of #{argument_name} in measure #{name}. Valid argument names are #{argument_names}." if a.empty?
  raise "more than one argument with the same name of #{argument_name} in measure #{name}" if a.size > 1

  a = a.first

  a[:value] = value

  a[:value] == value
end

#find_variable_by_name(name) ⇒ Object

Return a variable by its name.

Parameters:

  • name (String)

    Name of the arugment that makes the variable.

Returns:

  • (Object)

    The variable object



117
118
119
120
121
# File 'lib/openstudio/analysis/workflow_step.rb', line 117

def find_variable_by_name(name)
  v = @variables.find { |v| v[:argument][:name] == name }

  v
end

#make_variable(argument_name, variable_display_name, distribution, options = {}) ⇒ Boolean

Tag a measure’s argument as a variable.

Parameters:

  • argument_name (String)

    The instance_name of the measure argument that is to be tagged. This is the same name as the argument’s variable in the measure.rb file.

  • variable_display_name (String)

    What the variable is called. It is best if the display name is self describing (i.e. does not need any other context). It can be the same as the argument display name.

  • distribution (Hash)

    Hash describing the distribution of the variable.

  • variable_type (String)

    What type of variable, variable or pivot. Typically this is variable.

  • options (Hash) (defaults to: {})

    Values that define the variable.

Options Hash (distribution):

  • :type (String)

    Type of distribution. ‘discrete`, `uniform`, `triangle`, `normal`, `lognormal`, `integer_sequence`

  • :units (String)

    Units of the variable. This is legacy as previous OpenStudio measures did not specify units separately.

  • :minimum (String)

    Minimum value of the distribution, required for all distributions

  • :maximum (String)

    Maximum value of the distribution, required for all distributions

  • :standard_deviation (String)

    The standard deviation, if the distribution requires it.

  • :mode (String)

    The mean/mode of the distribution (if required)

  • :mean (String)

    Alias for the mode. If this is used it will override the mode

  • :relation_to_output (String)

    How is the variable correlates to the output of interest (for continuous distributions)

  • :step_size (String)

    Minimum step size (delta_x) of the variable (for continuous distributions)

  • :values (String)

    If discrete, then the values to run

  • :weights (String)

    If discrete, then the weights for each of the discrete values, must be the same length as values, and sum to 1. If empty, then it will create this automatically to be uniform.

Options Hash (options):

  • :variable_type (String)

    The type of variable, ‘variable` or `pivot`. By default this is a variable.

  • :variable_display_name_short (String)

    The short display name of the variable. Will be defaulted to the variable_display_name if not passed

Returns:

  • (Boolean)

    True / False if it was able to tag the measure argument



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/openstudio/analysis/workflow_step.rb', line 144

def make_variable(argument_name, variable_display_name, distribution, options = {})
  options = { variable_type: 'variable' }.merge(options)
  distribution[:mode] = distribution[:mean] if distribution.key? :mean

  raise "Set the static value in the options 'options[:static_value]', not the distribution" if distribution[:static_value]

  a = @arguments.find_all { |a| a[:name] == argument_name }
  raise "could not find argument_name of #{argument_name} in measure #{name}. Valid argument names are #{argument_names}." if a.empty?
  raise "more than one argument with the same name of #{argument_name} in measure #{name}" if a.size > 1

  if distribution_valid?(distribution)
    # grab the argument hash
    a = a.first

    # add more information to the argument
    v = {}
    v[:argument] = a
    v[:display_name] = variable_display_name
    v[:display_name_short] = options[:variable_display_name_short] ? options[:variable_display_name_short] : variable_display_name
    v[:variable_type] = options[:variable_type]

    v[:type] = distribution[:type]
    v[:units] = distribution[:units] ? distribution[:units] : nil
    v[:minimum] = distribution[:minimum]
    v[:maximum] = distribution[:maximum]
    v[:relation_to_output] = distribution[:relation_to_output] ? distribution[:relation_to_output] : nil
    v[:mode] = distribution[:mode]
    v[:static_value] = options[:static_value] if options[:static_value]
    # TODO: Static value should be named default value or just value

    # Always look for these attributes even if the distribution does not need them
    v[:weights] = distribution[:weights] if distribution[:weights]
    v[:values] = distribution[:values] if distribution[:values]
    v[:standard_deviation] = distribution[:standard_deviation] if distribution[:standard_deviation]
    v[:step_size] = distribution[:step_size] ? distribution[:step_size] : nil

    # assign uuid and version id to the variable
    v[:uuid] = SecureRandom.uuid
    v[:version_uuid] = SecureRandom.uuid
    @variables << v
  end

  true
end

#to_hash(version = 1, *a) ⇒ Hash

Convert the class into a hash. TODO: Make this smart based on the :type eventually

Returns:

  • (Hash)

    Returns the hash



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/openstudio/analysis/workflow_step.rb', line 192

def to_hash(version = 1, *a)
  hash = {}
  if version == 1
    instance_variables.each do |var|
      if var.to_s == '@type'
        hash[:measure_type] = instance_variable_get(var)
      elsif var.to_s == '@arguments'
        hash[:arguments] = []
        @arguments.each do |a|
          # This will change in version 2 but right now, if the argument is a variable, then the argument will
          # be in the variables hash, not the arguments hash.
          next unless @variables.find { |v| v[:argument][:name] == a[:name] }.nil?
          hash[:arguments] << a
        end
      elsif var.to_s == '@variables'
        # skip until after looping over instance_variables
      elsif var.to_s == '@__swigtype__'
        # skip the swig variables caused by using the same namespace as OpenStudio
      else
        hash[var.to_s.delete('@')] = instance_variable_get(var)
      end

      # TODO: iterate over the variables and create UUIDs, or not?
    end

    # fix everything to support the legacy version
    hash[:variables] = @variables

    # Clean up the variables to match the legacy format
    hash[:variables].each_with_index do |v, index|
      v[:variable_type] == 'pivot' ? v[:pivot] = true : v[:variable] = true
      v[:static_value] = v[:argument][:default_value] unless v[:static_value]

      v[:uncertainty_description] = {}
      # In Version 0.5 the _uncertain text will be removed from distribution
      if v[:type] =~ /uncertain/
        v[:type].delete!('_uncertain')
      end
      v[:uncertainty_description][:type] = v[:type]

      # This is not neatly coded. This should be a new object that knows how to write itself out.
      v[:uncertainty_description][:attributes] = []
      if v[:type] =~ /discrete/
        new_h = {}
        new_h[:name] = 'discrete'

        # check the weights
        new_h[:values_and_weights] = v.delete(:values).zip(v.delete(:weights)).map { |w| { value: w[0], weight: w[1] } }
        v[:uncertainty_description][:attributes] << new_h
      end

      # always write out these attributes
      v[:uncertainty_description][:attributes] << { name: 'lower_bounds', value: v[:minimum] }
      v[:uncertainty_description][:attributes] << { name: 'upper_bounds', value: v[:maximum] }
      v[:uncertainty_description][:attributes] << { name: 'modes', value: v[:mode] }
      v[:uncertainty_description][:attributes] << { name: 'delta_x', value: v[:step_size] ? v[:step_size] : nil }
      v[:uncertainty_description][:attributes] << { name: 'stddev', value: v[:standard_deviation] ? v[:standard_deviation] : nil }

      v[:workflow_index] = index

      # remove some remaining items
      v.delete(:type)
      v.delete(:mode) if v.key?(:mode)
      v.delete(:step_size) if v.key?(:step_size)
      v.delete(:standard_deviation) if v.key?(:standard_deviation)
    end

  else
    raise "Do not know how to create the Hash for Version #{version}"
  end

  hash
end