openstudio-analysis 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -6
  3. data/README.md +55 -7
  4. data/Rakefile +10 -0
  5. data/lib/openstudio/analysis/algorithm_attributes.rb +22 -0
  6. data/lib/openstudio/analysis/formulation.rb +174 -0
  7. data/lib/openstudio/analysis/translator/excel.rb +65 -48
  8. data/lib/openstudio/analysis/version.rb +1 -1
  9. data/lib/openstudio/analysis/workflow.rb +197 -0
  10. data/lib/openstudio/analysis/workflow_step.rb +312 -0
  11. data/lib/openstudio/analysis.rb +8 -0
  12. data/lib/openstudio/helpers/string.rb +22 -22
  13. data/lib/openstudio/templates/analysis.json.erb +0 -1
  14. data/lib/openstudio-analysis.rb +9 -0
  15. data/spec/files/0_1_09_small_list_incomplete.xlsx +0 -0
  16. data/spec/files/0_2_0_template_simpletest.xlsx +0 -0
  17. data/spec/files/0_4_0_lhs_discrete_continuous.xlsx +0 -0
  18. data/spec/files/analysis/example_analysis_api.json +656 -0
  19. data/spec/files/analysis/examples/discrete_lhs_example.json +809 -0
  20. data/spec/files/analysis/examples/medium_office_example.json +1673 -0
  21. data/spec/files/analysis/lhs_discrete_and_continuous_variables_api.json +1230 -0
  22. data/spec/files/analysis/medium_office.json +91 -92
  23. data/spec/files/analysis/medium_office.zip +0 -0
  24. data/spec/files/analysis/name_goes_here_api.json +1681 -0
  25. data/spec/files/analysis/output_vars_api.json +632 -0
  26. data/spec/files/analysis/preflight_api.json +1518 -0
  27. data/spec/files/analysis/simple_test_api.json +519 -0
  28. data/spec/files/analysis/test_model_api.json +493 -0
  29. data/spec/files/export/analysis/0_1_09_outputvars.json +38 -39
  30. data/spec/files/export/analysis/0_1_09_outputvars.zip +0 -0
  31. data/spec/files/export/analysis/0_1_11_discrete_variables.json +46 -47
  32. data/spec/files/export/analysis/0_1_11_discrete_variables.zip +0 -0
  33. data/spec/files/export/analysis/0_1_12_discrete_dynamic_columns.json +8 -9
  34. data/spec/files/export/analysis/0_1_12_discrete_dynamic_columns.zip +0 -0
  35. data/spec/files/export/analysis/0_2_0_template_simpletest.json +19 -14
  36. data/spec/files/export/analysis/0_2_0_template_simpletest.zip +0 -0
  37. data/spec/files/export/analysis/0_3_0_outputs.json +99 -100
  38. data/spec/files/export/analysis/0_3_0_outputs.zip +0 -0
  39. data/spec/files/export/analysis/{6d6a08db-fdf8-4bb5-8ad3-18c471418c72.json → 276ccf51-ed22-4604-a380-8985cec5efe8.json} +103 -104
  40. data/spec/files/export/analysis/{6d6a08db-fdf8-4bb5-8ad3-18c471418c72.zip → 276ccf51-ed22-4604-a380-8985cec5efe8.zip} +0 -0
  41. data/spec/files/export/analysis/{55086845-70cf-487f-87f6-7a147cbf1e72.json → 639cb8a5-cdbb-4b69-955a-cbb650f6872b.json} +107 -108
  42. data/spec/files/export/analysis/{55086845-70cf-487f-87f6-7a147cbf1e72.zip → 639cb8a5-cdbb-4b69-955a-cbb650f6872b.zip} +0 -0
  43. data/spec/files/export/analysis/{10c791ce-cba7-4506-a863-3fb15703889b.json → 9560f95b-5730-4038-a95b-328c825c596b.json} +99 -100
  44. data/spec/files/export/analysis/{10c791ce-cba7-4506-a863-3fb15703889b.zip → 9560f95b-5730-4038-a95b-328c825c596b.zip} +0 -0
  45. data/spec/files/export/analysis/{f028bfbe-e30e-488d-adad-a60a62bbf7e0.json → c50f0062-cdfb-4dec-bc02-215f6c29af3c.json} +107 -108
  46. data/spec/files/export/analysis/{f028bfbe-e30e-488d-adad-a60a62bbf7e0.zip → c50f0062-cdfb-4dec-bc02-215f6c29af3c.zip} +0 -0
  47. data/spec/files/export/analysis/discrete_lhs_example.json +1185 -0
  48. data/spec/files/export/analysis/discrete_lhs_example.zip +0 -0
  49. data/spec/files/export/analysis/small_seed.json +38 -39
  50. data/spec/files/export/analysis/small_seed.zip +0 -0
  51. data/spec/files/export/workflow/analysis.json +23 -0
  52. data/spec/files/measures/ActualMeasureNoJson/measure.json +25 -0
  53. data/spec/files/measures/ActualMeasureNoJson/measure.rb +80 -0
  54. data/spec/files/measures/ActualMeasureNoJson/measure.xml +2 -0
  55. data/spec/files/measures/SetThermostatSchedules/measure.json +63 -0
  56. data/spec/files/measures/SetThermostatSchedules/measure.rb +254 -0
  57. data/spec/files/measures/SetThermostatSchedules/measure.xml +2 -0
  58. data/spec/openstudio/excel_spec.rb +11 -11
  59. data/spec/openstudio/formulation_spec.rb +107 -0
  60. data/spec/openstudio/workflow_spec.rb +90 -0
  61. data/spec/openstudio/workflow_step_spec.rb +116 -0
  62. data/spec/reports/SPEC-OpenStudio-Analysis-Formulation.xml +28 -0
  63. data/spec/reports/SPEC-OpenStudio-Analysis-ServerApi-create-a-new-object-instance.xml +2 -2
  64. data/spec/reports/SPEC-OpenStudio-Analysis-ServerApi-test-not-localhost.xml +2 -2
  65. data/spec/reports/SPEC-OpenStudio-Analysis-ServerApi.xml +1 -1
  66. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-discrete-variables.xml +30 -4
  67. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-discrete-with-dynamic-columns.xml +11 -3
  68. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-no-variables-defined.xml +6 -6
  69. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-proxy-setup-with-user.xml +2 -2
  70. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-proxy-setup.xml +2 -2
  71. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-setup-output-variables.xml +52 -8
  72. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-setup-version-0-1-9.xml +22 -5
  73. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-small-list-of-incomplete-variables.xml +2 -2
  74. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-small-list-of-variables-should-not-validate.xml +2 -2
  75. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-small-list-of-variables.xml +28 -5
  76. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-small-list-with-with-repeated-variable-names.xml +2 -2
  77. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-1-10.xml +3 -3
  78. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-2-0-simple.xml +13 -6
  79. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-2-0.xml +55 -5
  80. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-0-dynamic-uuid-assignments.xml +9 -4
  81. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-0-measure-existence-checks.xml +8 -3
  82. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-0-objective-functions.xml +13 -5
  83. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-3-and-short-display-names.xml +9 -4
  84. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-5-and-measure-paths.xml +9 -4
  85. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-7-and-worker-init-final-scripts.0.xml +40 -2
  86. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-7-and-worker-init-final-scripts.xml +9 -4
  87. data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel.xml +1 -1
  88. data/spec/reports/SPEC-OpenStudio-Analysis-Workflow.xml +31 -0
  89. data/spec/reports/SPEC-OpenStudio-Analysis-WorkflowStep.xml +29 -0
  90. data/spec/spec_helper.rb +1 -1
  91. metadata +87 -18
@@ -0,0 +1,197 @@
1
+ # OpenStudio::Analysis::Workflow configured the list of measures to run and in what order
2
+ module OpenStudio
3
+ module Analysis
4
+ class Workflow
5
+ attr_reader :items
6
+ # allow users to access the items via the measures attribute accessor
7
+ alias_method :measures, :items
8
+
9
+ # Create an instance of the OpenStudio::Analysis::Workflow
10
+ #
11
+ # @return [Object] An OpenStudio::Analysis::Workflow object
12
+ def initialize
13
+ @items = []
14
+ end
15
+
16
+ # Remove all the items in the workflow
17
+ def clear
18
+ @items.clear
19
+ end
20
+
21
+ # Add a measure to the workflow from a path. Inside the path it is expecting to have a measure.json file
22
+ # if not, the BCL gem is used to create the measure.json file.
23
+ #
24
+ # @params instance_name [String] The name of the instance. This allows for multiple measures to be added to the worklow with unique names
25
+ # @params instance_display_name [String] The display name of the instance. This allows for multiple measures to be added to the worklow with unique names
26
+ # @param path_to_measure [String] This is the local path to the measure directory, relative or absolute. It is used when zipping up all the measures.
27
+ # @return [Object] Returns the measure that was added as an OpenStudio::AnalysisWorkflowStep object
28
+ def add_measure_from_path(instance_name, instance_display_name, path_to_measure)
29
+ measure_filename = 'measure.rb'
30
+ if File.exist?(path_to_measure) && File.file?(path_to_measure)
31
+ measure_filename = File.basename(path_to_measure)
32
+ path_to_measure = File.dirname(path_to_measure)
33
+ end
34
+
35
+ if Dir.exist?(path_to_measure) && File.directory?(path_to_measure)
36
+ # Watch out for namespace conflicts (use ::BCL)
37
+ b = ::BCL::ComponentMethods.new
38
+ measure_hash = nil
39
+ unless File.exist?(File.join(path_to_measure, 'measure.json'))
40
+ measure_hash = b.parse_measure_file(nil, File.join(path_to_measure, measure_filename))
41
+ File.open(File.join(path_to_measure, 'measure.json'), 'w') { |f| f << JSON.pretty_generate(measure_hash) }
42
+ warn("measure.json not found in #{path_to_measure}, will parse measure file using BCL gem")
43
+ end
44
+
45
+ if measure_hash.nil? && File.exist?(File.join(path_to_measure, 'measure.json'))
46
+ measure_hash = JSON.parse(File.read(File.join(path_to_measure, 'measure.json')), symbolize_names: true)
47
+ elsif measure_hash.nil?
48
+ fail 'measure.json was not found and was not automatically created'
49
+ end
50
+
51
+ add_measure(instance_name, instance_display_name, path_to_measure, measure_hash)
52
+ else
53
+ fail "could not find measure to add to workflow #{path_to_measure}"
54
+ end
55
+
56
+ @items.last
57
+ end
58
+
59
+ # Add a measure from the custom hash format without reading the measure.rb or measure.json file
60
+ #
61
+ # @params instance_name [String] The name of the instance. This allows for multiple measures to be added to the worklow with unique names
62
+ # @params instance_display_name [String] The display name of the instance. This allows for multiple measures to be added to the worklow with unique names
63
+ # @param path_to_measure [String] This is the local path to the measure directory, relative or absolute. It is used when zipping up all the measures.
64
+ # @param measure_metadata [Hash] Format of the measure.json
65
+ # @return [Object] Returns the measure that was added as an OpenStudio::AnalysisWorkflowStep object
66
+ def add_measure(instance_name, instance_display_name, path_to_measure, measure_metadata)
67
+ @items << OpenStudio::Analysis::WorkflowStep.from_measure_hash(instance_name, instance_display_name, path_to_measure, measure_metadata)
68
+
69
+ @items.last
70
+ end
71
+
72
+ # Add a measure from the format that Excel parses into. This is a helper method to map the excel data to the new
73
+ # programmatic interface format
74
+ #
75
+ # @params measure [Hash] The measure in the format of the Excel translator
76
+ # @return [Object] Returns the measure that was added as an OpenStudio::AnalysisWorkflowStep object
77
+ def add_measure_from_excel(measure)
78
+ hash = {}
79
+ hash[:classname] = measure['measure_file_name']
80
+ hash[:name] = measure['name']
81
+ hash[:display_name] = measure['display_name']
82
+ hash[:measure_type] = measure['measure_type']
83
+ hash[:uid] = measure['uid'] ? measure['uid'] : SecureRandom.uuid
84
+ hash[:version_id] = measure['version_id'] ? measure['version_id'] : SecureRandom.uuid
85
+
86
+ # map the arguments - this can be a variable or argument, add them all as arguments first
87
+ args = []
88
+ measure['variables'].each do |variable|
89
+ args << {
90
+ local_variable: variable['name'],
91
+ variable_type: variable['type'],
92
+ name: variable['name'],
93
+ display_name: variable['display_name'],
94
+ display_name_short: variable['display_name_short'],
95
+ units: variable['units'],
96
+ default_value: variable['distribution']['static_value'],
97
+ value: variable['distribution']['static_value']
98
+ }
99
+ end
100
+ hash[:arguments] = args
101
+
102
+ m = add_measure(measure['name'], measure['display_name'], "./spec/files/measures/#{measure['measure_file_name_directory']}", hash)
103
+
104
+ measure['variables'].each do |variable|
105
+ next unless variable['variable_type'] == 'variable'
106
+
107
+ dist = {
108
+ type: variable['distribution']['type'],
109
+ minimum: variable['distribution']['min'],
110
+ maximum: variable['distribution']['max'],
111
+ mean: variable['distribution']['mean'],
112
+ standard_deviation: variable['distribution']['stddev'],
113
+ values: variable['distribution']['discrete_values'],
114
+ weights: variable['distribution']['discrete_weights'],
115
+ step_size: variable['distribution']['delta_x']
116
+ }
117
+ opt = {
118
+ variable_type: variable['variable_type'],
119
+ variable_display_name_short: variable['display_name_short'],
120
+ static_value: variable['distribution']['static_value']
121
+ }
122
+
123
+ m.make_variable(variable['name'], variable['display_name'], dist)
124
+ end
125
+ end
126
+
127
+ # Find the measure by its instance name
128
+ #
129
+ # @params instance_name [String] instance name of the measure
130
+ # @return [Object] The WorkflowStep with the instance_name
131
+ def find_measure(instance_name)
132
+ @items.find { |i| i.name == instance_name }
133
+ end
134
+ alias_method :find_workflow_step, :find_measure
135
+
136
+ # Return all the variables in the analysis as an array. The list that is returned is read only.
137
+ #
138
+ # @return [Array] All variables in the workflow
139
+ def all_variables
140
+ @items.map(&:variables).flatten
141
+ end
142
+
143
+ # Save the workflow to a hash object
144
+ def to_hash(version = 1)
145
+ h = nil
146
+ if version == 1
147
+ arr = []
148
+ @items.each_with_index do |item, index|
149
+ temp_h = item.to_hash(version)
150
+ temp_h[:workflow_index] = index
151
+
152
+ arr << temp_h
153
+ end
154
+
155
+ h = arr
156
+ else
157
+ fail "Version #{version} not yet implemented for to_hash"
158
+ end
159
+
160
+ h
161
+ end
162
+
163
+ # Save the workflow to a JSON string
164
+ #
165
+ # @return [String] JSON formatted string
166
+ def to_json(version = 1)
167
+ if version == 1
168
+ JSON.pretty_generate(to_hash(version))
169
+ else
170
+ fail "Version #{version} not yet implemented for to_json"
171
+ end
172
+ end
173
+
174
+ # Read the Workflow description from a persisted file. The format at the moment is the current analysis.json
175
+ #
176
+ # @params filename [String] Path to file with the analysis.json to load
177
+ # @return [Object] Return an instance of the workflow object
178
+ def self.from_file(filename)
179
+ if File.exist? filename
180
+ j = JSON.parse(File.read(filename), symbolize_names: true)
181
+
182
+ # get the version of the file
183
+ file_format_version = j[:file_format_version] ? j[:file_format_version] : 1
184
+
185
+ puts "Parsing file version #{file_format_version}"
186
+
187
+ else
188
+ fail "Could not find workflow file #{filename}"
189
+ end
190
+
191
+ o = OpenStudio::Analysis::Workflow.new
192
+ # put the JSON into the right format
193
+ o
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,312 @@
1
+ # OpenStudio::Analysis::WorkflowStep is a class container for storing a measure. The generic name of step may be used later
2
+ # to include a workflow step on running EnergyPlus, radiance, etc.
3
+ module OpenStudio
4
+ module Analysis
5
+ class WorkflowStep
6
+ attr_accessor :type
7
+ attr_accessor :name
8
+ attr_accessor :display_name
9
+
10
+ attr_accessor :measure_definition_class_name
11
+ attr_accessor :measure_definition_directory
12
+ attr_accessor :measure_definition_display_name
13
+ attr_accessor :measure_definition_name
14
+ attr_accessor :measure_definition_name_xml
15
+ attr_accessor :measure_definition_uuid
16
+ attr_accessor :measure_definition_version_uuid
17
+ attr_reader :arguments
18
+ attr_reader :variables
19
+
20
+ # Create an instance of the OpenStudio::Analysis::WorkflowStep
21
+ #
22
+ # @return [Object] An OpenStudio::Analysis::WorkflowStep object
23
+ def initialize
24
+ @name = ''
25
+ @display_name = ''
26
+
27
+ # The type of item being added (RubyMeasure, EnergyPlusMeasure, ...)
28
+ @type = nil
29
+
30
+ @measure_definition_class_name = nil
31
+ @measure_definition_directory = nil
32
+ @measure_definition_display_name = nil
33
+ @measure_definition_name = nil
34
+ @measure_definition_name_xml = nil
35
+ @measure_definition_uuid = nil
36
+ @measure_definition_version_uuid = nil
37
+ @arguments = []
38
+
39
+ # TODO: eventually the variables should be its own class. This would then be an array of Variable objects.
40
+ @variables = []
41
+ end
42
+
43
+ # Return an array of the argument names
44
+ #
45
+ # @return [Array] Listing of argument names.
46
+ def argument_names
47
+ @arguments.map { |a| a[:name] }
48
+ end
49
+
50
+ # Set the value of an argument to `value`. The user is required to know the data type and pass it in accordingly
51
+ #
52
+ # @param argument_name [String] The machine name of the argument that you want to set the value to
53
+ # @param value [] The value to assign the argument
54
+ # @return [Boolean] True/false if it assigned it
55
+ def argument_value(argument_name, value)
56
+ a = @arguments.find_all { |a| a[:name] == argument_name }
57
+ fail "could not find argument_name of #{argument_name} in measure #{name}" if a.empty?
58
+ fail "more than one argument with the same name of #{argument_name} in measure #{name}" if a.size > 1
59
+
60
+ a = a.first
61
+
62
+ a[:value] = value
63
+
64
+ a[:value] == value
65
+ end
66
+ # Tag a measure's argument as a variable.
67
+ #
68
+ # @param 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.
69
+ # @param 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.
70
+ # @param distribution [Hash] Hash describing the distribution of the variable.
71
+ # @option distribution [String] :type Type of distribution. `discrete`, `uniform`, `triangle`, `normal`, `lognormal`
72
+ # @option distribution [String] :units Units of the variable. This is legacy as previous OpenStudio measures did not specify units separately.
73
+ # @option distribution [String] :minimum Minimum value of the distribution, required for all distributions
74
+ # @option distribution [String] :maximum Maximum value of the distribution, required for all distributions
75
+ # @option distribution [String] :standard_deviation The standard deviation, if the distribution requires it.
76
+ # @option distribution [String] :mode The mean/mode of the distribution (if required)
77
+ # @option distribution [String] :mean Alias for the mode. If this is used it will override the mode
78
+ # @option distribution [String] :relation_to_output How is the variable correlates to the output of interest (for continuous distributions)
79
+ # @option distribution [String] :step_size Minimum step size (delta_x) of the variable (for continuous distributions)
80
+ # @option distribution [String] :values If discrete, then the values to run
81
+ # @option distribution [String] :weights 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.
82
+ # @param variable_type [String] What type of variable, variable or pivot. Typically this is variable.
83
+ # @param options [Hash] Values that define the variable.
84
+ # @option options [String] :variable_type The type of variable, `variable` or `pivot`. By default this is a variable.
85
+ # @option options [String] :variable_display_name_short The short display name of the variable. Will be defaulted to the variable_display_name if not passed
86
+ # @option options [String] :static_value Static/Default value of the variable. If not defined it will use the default value for the argument. This can be set later as well using the `argument_value` method.
87
+ # @return [Boolean] True / False if it was able to tag the measure argument
88
+ def make_variable(argument_name, variable_display_name, distribution, options = {})
89
+ options = { variable_type: 'variable' }.merge(options)
90
+ distribution[:mode] = distribution[:mean] if distribution.key? :mean
91
+
92
+ a = @arguments.find_all { |a| a[:name] == argument_name }
93
+ fail "could not find argument_name of #{argument_name} in measure #{name}" if a.empty?
94
+ fail "more than one argument with the same name of #{argument_name} in measure #{name}" if a.size > 1
95
+
96
+ if distribution_valid?(distribution)
97
+ # grab the argument hash
98
+ a = a.first
99
+
100
+ # add more information to the argument
101
+ v = {}
102
+ v[:argument] = a
103
+ v[:display_name] = variable_display_name
104
+ v[:display_name_short] = options[:variable_display_name_short] ? options[:variable_display_name_short] : variable_display_name
105
+
106
+ v[:type] = distribution[:type]
107
+ v[:units] = distribution[:units] ? distribution[:units] : nil
108
+ v[:minimum] = distribution[:minimum]
109
+ v[:maximum] = distribution[:maximum]
110
+ v[:relation_to_output] = distribution[:relation_to_output] ? distribution[:relation_to_output] : nil
111
+ v[:mode] = distribution[:mode]
112
+ v[:static_value] = distribution[:static_value] if distribution[:static_value]
113
+ # TODO: Static value should be named default value or just value
114
+
115
+ if distribution[:type] =~ /discrete/
116
+ v[:weights] = distribution[:weights]
117
+ v[:values] = distribution[:values]
118
+ elsif distribution[:type] =~ /uniform/
119
+ # all the data should be present
120
+ elsif distribution[:type] =~ /triangle/
121
+ v[:step_size] = distribution[:step_size] ? distribution[:step_size] : nil
122
+ # stddev is not saves when triangular
123
+ elsif distribution[:type] =~ /normal/
124
+ v[:step_size] = distribution[:step_size] ? distribution[:step_size] : nil
125
+ v[:standard_deviation] = distribution[:standard_deviation]
126
+ end
127
+
128
+ # assign uuid and version id to the variable
129
+ v[:uuid] = SecureRandom.uuid
130
+ v[:version_uuid] = SecureRandom.uuid
131
+ @variables << v
132
+ end
133
+ true
134
+ end
135
+
136
+ # Convert the class into a hash. TODO: Make this smart based on the :type eventually
137
+ #
138
+ # @return [Hash] Returns the hash
139
+ def to_hash(version = 1, *a)
140
+ hash = {}
141
+ if version == 1
142
+ instance_variables.each do |var|
143
+ if var.to_s == '@type'
144
+ hash[:measure_type] = instance_variable_get(var)
145
+ elsif var.to_s == '@arguments'
146
+ hash[:arguments] = []
147
+ @arguments.each do |a|
148
+ # This will change in version 2 but right now, if the argument is a variable, then the argument will
149
+ # be in the variables hash, not the arguments hash.
150
+ next unless @variables.find { |v| v[:argument][:name] == a[:name] }.nil?
151
+ hash[:arguments] << a
152
+ end
153
+ elsif var.to_s == '@variables'
154
+ # skip until after looping over instance_variables
155
+ elsif var.to_s == '@__swigtype__'
156
+ # skip the swig variables caused by using the same namespace as OpenStudio
157
+ else
158
+ hash[var.to_s.delete('@')] = instance_variable_get(var)
159
+ end
160
+
161
+ # TODO: warn that we are no longer writing out "variable_type": "RubyContinuousVariable",
162
+ # TODO: iterate over the variables and create UUIDs, or not?
163
+ end
164
+
165
+ # fix everything to support the legacy version
166
+ hash[:variables] = @variables
167
+
168
+ # Clean up the variables to match the legacy format
169
+ hash[:variables].each_with_index do |v, index|
170
+ v[:variable_type] == 'pivot' ? v[:pivot] = true : v[:variable] = true
171
+ v[:variable] = true
172
+ v[:static_value] = v[:argument][:default_value] unless v[:static_value]
173
+
174
+ v[:uncertainty_description] = {}
175
+ v[:uncertainty_description][:type] = v[:type] =~ /uncertain/ ? "#{v[:type]}" : "#{v[:type]}_uncertain"
176
+ warn "Deprecation Warning. In Version 0.5 the _uncertain text will be removed from distribution types: #{v[:uncertainty_description][:type]}"
177
+ warn 'Deprecation Warning. RubyContinuousVariable (OpenStudio called this the variable_type) is no longer persisted'
178
+
179
+ # This is not neatly coded. This should be a new object that knows how to write itself out.
180
+ v[:uncertainty_description][:attributes] = []
181
+ if v[:type] =~ /discrete/
182
+ new_h = {}
183
+ new_h[:name] = 'discrete'
184
+ new_h[:values_and_weights] = v.delete(:values).zip(v.delete(:weights)).map { |w| { value: w[0], weight: w[1] } }
185
+ v[:uncertainty_description][:attributes] << new_h
186
+
187
+ v[:uncertainty_description][:attributes] << { name: 'lower_bounds', value: v[:minimum] }
188
+ v[:uncertainty_description][:attributes] << { name: 'upper_bounds', value: v[:maximum] }
189
+ v[:uncertainty_description][:attributes] << { name: 'modes', value: v[:mode] }
190
+ elsif v[:type] =~ /uniform/
191
+ v[:uncertainty_description][:attributes] << { name: 'lower_bounds', value: v[:minimum] }
192
+ v[:uncertainty_description][:attributes] << { name: 'upper_bounds', value: v[:maximum] }
193
+ v[:uncertainty_description][:attributes] << { name: 'modes', value: v[:mode] }
194
+ else
195
+ v[:uncertainty_description][:attributes] << { name: 'lower_bounds', value: v[:minimum] }
196
+ v[:uncertainty_description][:attributes] << { name: 'upper_bounds', value: v[:maximum] }
197
+ v[:uncertainty_description][:attributes] << { name: 'modes', value: v[:mode] }
198
+ v[:uncertainty_description][:attributes] << { name: 'delta_x', value: v[:step_size] ? v[:step_size] : nil }
199
+ v[:uncertainty_description][:attributes] << { name: 'stddev', value: v[:standard_deviation] ? v[:standard_deviation] : nil }
200
+ end
201
+
202
+ v[:workflow_index] = index
203
+ warn 'Deprecation Warning. workflow_step_type is no longer persisted'
204
+
205
+ # remove some remaining items
206
+ v.delete(:type)
207
+ v.delete(:mode) if v.key?(:mode)
208
+ v.delete(:step_size) if v.key?(:step_size)
209
+ v.delete(:standard_deviation) if v.key?(:standard_deviation)
210
+ end
211
+
212
+ else
213
+ fail "Do not know how to create the Hash for Version #{version}"
214
+ end
215
+
216
+ hash
217
+ end
218
+
219
+ # Read the workflow item from a measure hash.
220
+ #
221
+ # @param instance_name [String] Machine name of the instance
222
+ # @param instance_display_name [String] Display name of the instance
223
+ # @param 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.
224
+ # @param hash [Hash] Measure hash in the format of the measure.json (from the Analysis Spreadsheet project)
225
+
226
+ # @return [Object] Returns the OpenStudio::Analysis::WorkflowStep
227
+ def self.from_measure_hash(instance_name, instance_display_name, path_to_measure, hash)
228
+ # TODO: Validate the hash
229
+ # TODO: validate that the measure exists?
230
+
231
+ # verify that the path to the measure is a path and not a file.
232
+ if File.exist?(path_to_measure) && File.file?(path_to_measure)
233
+ path_to_measure = File.dirname(path_to_measure)
234
+ end
235
+
236
+ # map the BCL hash format into the OpenStudio WorkflowStep format
237
+ s = OpenStudio::Analysis::WorkflowStep.new
238
+
239
+ # add the instance and display name
240
+ s.name = instance_name
241
+ s.display_name = instance_display_name
242
+
243
+ # definition of the measure
244
+ s.measure_definition_class_name = hash[:classname]
245
+ s.measure_definition_directory = path_to_measure
246
+ s.measure_definition_display_name = hash[:display_name]
247
+ s.measure_definition_name = hash[:name]
248
+ # name_xml is not used right now but eventually should be used to compare the hash[:name] and the hash[:name_xml]
249
+ s.measure_definition_name_xml = hash[:name_xml]
250
+ s.measure_definition_uuid = hash[:uid]
251
+ s.measure_definition_version_uuid = hash[:version_id]
252
+
253
+ # do not allow the choice variable_type
254
+
255
+ s.type = hash[:measure_type] # this is actually the measure type
256
+ if hash[:arguments]
257
+ hash[:arguments].each do |arg|
258
+ var_type = arg[:variable_type].downcase
259
+ if var_type == 'choice'
260
+ # WARN the user that the measure had a "choice data type"
261
+ var_type = 'string'
262
+ end
263
+
264
+ s.arguments << {
265
+ display_name: arg[:display_name],
266
+ display_name_short: arg[:display_name],
267
+ name: arg[:local_variable],
268
+ value_type: var_type,
269
+ default_value: arg[:default_value],
270
+ value: arg[:default_value]
271
+ }
272
+ end
273
+ end
274
+
275
+ s
276
+ end
277
+
278
+ private
279
+
280
+ # validate the arguments of the distribution
281
+ def distribution_valid?(d)
282
+ # regardless of uncertainty description the following must be defined
283
+ fail 'No distribution defined for variable' unless d.key? :type
284
+ fail 'No minimum defined for variable' unless d.key? :minimum
285
+ fail 'No maximum defined for variable' unless d.key? :maximum
286
+ fail 'No mean/mode defined for variable' unless d.key? :mode
287
+
288
+ if d[:type] =~ /uniform/
289
+ # Do we need to tell the user that we don't really need the mean/mode for uniform?
290
+ elsif d[:type] =~ /discrete/
291
+ # require min, max, mode
292
+ fail 'No values passed for discrete distribution' unless d[:values] || d[:values].empty?
293
+ if d[:weights]
294
+ fail 'Weights are not the same length as values' unless d[:values].size == d[:weights].size
295
+ fail 'Weights do not sum up to one' unless d[:weights].reduce(:+).between?(0.99, 1.01) # allow a small error for now
296
+ else
297
+ fraction = 1 / d[:values].size.to_f
298
+ d[:weights] = [fraction] * d[:values].size
299
+ end
300
+ elsif d[:type] =~ /triangle/
301
+ # requires min, max, mode
302
+
303
+ elsif d[:type] =~ /normal/ # both normal and lognormal
304
+ # require min, max, mode, stddev
305
+ fail 'No standard deviation for variable' unless d[:standard_deviation]
306
+ end
307
+
308
+ true
309
+ end
310
+ end
311
+ end
312
+ end
@@ -0,0 +1,8 @@
1
+ # OpenStudio::Analysis Module instantiates versions of formulations
2
+ module OpenStudio
3
+ module Analysis
4
+ def self.create(display_name)
5
+ OpenStudio::Analysis::Formulation.new(display_name)
6
+ end
7
+ end
8
+ end
@@ -3,10 +3,10 @@
3
3
  class String
4
4
  def underscore
5
5
  gsub(/::/, '/')
6
- .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
7
- .gsub(/([a-z\d])([A-Z])/, '\1_\2')
8
- .tr('-', '_')
9
- .downcase
6
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
7
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
8
+ .tr('-', '_')
9
+ .downcase
10
10
  end
11
11
 
12
12
  def snake_case
@@ -22,25 +22,25 @@ end
22
22
 
23
23
  def typecast_value(variable_type, value, inspect_string = false)
24
24
  out_value = nil
25
- case variable_type.downcase
26
- when 'double'
27
- out_value = value.to_f
28
- when 'integer'
29
- out_value = value.to_i
30
- when 'string'
31
- out_value = inspect_string ? value.inspect : value.to_s
32
- when 'choice'
33
- out_value = value.inspect
34
- when 'bool', 'boolean'
35
- if value.downcase == 'true'
36
- out_value = true
37
- elsif value.downcase == 'false'
38
- out_value = false
25
+ unless value.nil?
26
+ case variable_type.downcase
27
+ when 'double'
28
+ out_value = value.to_f
29
+ when 'integer'
30
+ out_value = value.to_i
31
+ when 'string', 'choice'
32
+ out_value = inspect_string ? value.inspect : value.to_s
33
+ when 'bool', 'boolean'
34
+ if value.downcase == 'true'
35
+ out_value = true
36
+ elsif value.downcase == 'false'
37
+ out_value = false
38
+ else
39
+ fail "Can't cast to a bool from a value of '#{value}' of class '#{value.class}'"
40
+ end
39
41
  else
40
- fail "Can't cast to a bool from a value of '#{value}' of class '#{value.class}'"
41
- end
42
- else
43
- fail "Unknown variable type of '#{@variable['type']}'"
42
+ fail "Unknown variable type of '#{@variable['type']}'"
43
+ end
44
44
  end
45
45
 
46
46
  out_value
@@ -6,7 +6,6 @@
6
6
  "problem": {
7
7
  "algorithm": {
8
8
  },
9
- "name": "Problem",
10
9
  "workflow": []
11
10
  },
12
11
  "seed": {
@@ -12,10 +12,19 @@ require 'zip'
12
12
  require 'semantic'
13
13
  require 'semantic/core_ext'
14
14
 
15
+ require 'bcl'
16
+
15
17
  # core
16
18
  require 'openstudio/analysis/server_api'
17
19
  require 'openstudio/analysis/version'
18
20
 
21
+ # analysis classes
22
+ require 'openstudio/analysis'
23
+ require 'openstudio/analysis/formulation'
24
+ require 'openstudio/analysis/workflow'
25
+ require 'openstudio/analysis/workflow_step'
26
+ require 'openstudio/analysis/algorithm_attributes'
27
+
19
28
  # translators
20
29
  require 'openstudio/analysis/translator/excel'
21
30