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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -6
- data/README.md +55 -7
- data/Rakefile +10 -0
- data/lib/openstudio/analysis/algorithm_attributes.rb +22 -0
- data/lib/openstudio/analysis/formulation.rb +174 -0
- data/lib/openstudio/analysis/translator/excel.rb +65 -48
- data/lib/openstudio/analysis/version.rb +1 -1
- data/lib/openstudio/analysis/workflow.rb +197 -0
- data/lib/openstudio/analysis/workflow_step.rb +312 -0
- data/lib/openstudio/analysis.rb +8 -0
- data/lib/openstudio/helpers/string.rb +22 -22
- data/lib/openstudio/templates/analysis.json.erb +0 -1
- data/lib/openstudio-analysis.rb +9 -0
- data/spec/files/0_1_09_small_list_incomplete.xlsx +0 -0
- data/spec/files/0_2_0_template_simpletest.xlsx +0 -0
- data/spec/files/0_4_0_lhs_discrete_continuous.xlsx +0 -0
- data/spec/files/analysis/example_analysis_api.json +656 -0
- data/spec/files/analysis/examples/discrete_lhs_example.json +809 -0
- data/spec/files/analysis/examples/medium_office_example.json +1673 -0
- data/spec/files/analysis/lhs_discrete_and_continuous_variables_api.json +1230 -0
- data/spec/files/analysis/medium_office.json +91 -92
- data/spec/files/analysis/medium_office.zip +0 -0
- data/spec/files/analysis/name_goes_here_api.json +1681 -0
- data/spec/files/analysis/output_vars_api.json +632 -0
- data/spec/files/analysis/preflight_api.json +1518 -0
- data/spec/files/analysis/simple_test_api.json +519 -0
- data/spec/files/analysis/test_model_api.json +493 -0
- data/spec/files/export/analysis/0_1_09_outputvars.json +38 -39
- data/spec/files/export/analysis/0_1_09_outputvars.zip +0 -0
- data/spec/files/export/analysis/0_1_11_discrete_variables.json +46 -47
- data/spec/files/export/analysis/0_1_11_discrete_variables.zip +0 -0
- data/spec/files/export/analysis/0_1_12_discrete_dynamic_columns.json +8 -9
- data/spec/files/export/analysis/0_1_12_discrete_dynamic_columns.zip +0 -0
- data/spec/files/export/analysis/0_2_0_template_simpletest.json +19 -14
- data/spec/files/export/analysis/0_2_0_template_simpletest.zip +0 -0
- data/spec/files/export/analysis/0_3_0_outputs.json +99 -100
- data/spec/files/export/analysis/0_3_0_outputs.zip +0 -0
- data/spec/files/export/analysis/{6d6a08db-fdf8-4bb5-8ad3-18c471418c72.json → 276ccf51-ed22-4604-a380-8985cec5efe8.json} +103 -104
- data/spec/files/export/analysis/{6d6a08db-fdf8-4bb5-8ad3-18c471418c72.zip → 276ccf51-ed22-4604-a380-8985cec5efe8.zip} +0 -0
- data/spec/files/export/analysis/{55086845-70cf-487f-87f6-7a147cbf1e72.json → 639cb8a5-cdbb-4b69-955a-cbb650f6872b.json} +107 -108
- data/spec/files/export/analysis/{55086845-70cf-487f-87f6-7a147cbf1e72.zip → 639cb8a5-cdbb-4b69-955a-cbb650f6872b.zip} +0 -0
- data/spec/files/export/analysis/{10c791ce-cba7-4506-a863-3fb15703889b.json → 9560f95b-5730-4038-a95b-328c825c596b.json} +99 -100
- data/spec/files/export/analysis/{10c791ce-cba7-4506-a863-3fb15703889b.zip → 9560f95b-5730-4038-a95b-328c825c596b.zip} +0 -0
- data/spec/files/export/analysis/{f028bfbe-e30e-488d-adad-a60a62bbf7e0.json → c50f0062-cdfb-4dec-bc02-215f6c29af3c.json} +107 -108
- data/spec/files/export/analysis/{f028bfbe-e30e-488d-adad-a60a62bbf7e0.zip → c50f0062-cdfb-4dec-bc02-215f6c29af3c.zip} +0 -0
- data/spec/files/export/analysis/discrete_lhs_example.json +1185 -0
- data/spec/files/export/analysis/discrete_lhs_example.zip +0 -0
- data/spec/files/export/analysis/small_seed.json +38 -39
- data/spec/files/export/analysis/small_seed.zip +0 -0
- data/spec/files/export/workflow/analysis.json +23 -0
- data/spec/files/measures/ActualMeasureNoJson/measure.json +25 -0
- data/spec/files/measures/ActualMeasureNoJson/measure.rb +80 -0
- data/spec/files/measures/ActualMeasureNoJson/measure.xml +2 -0
- data/spec/files/measures/SetThermostatSchedules/measure.json +63 -0
- data/spec/files/measures/SetThermostatSchedules/measure.rb +254 -0
- data/spec/files/measures/SetThermostatSchedules/measure.xml +2 -0
- data/spec/openstudio/excel_spec.rb +11 -11
- data/spec/openstudio/formulation_spec.rb +107 -0
- data/spec/openstudio/workflow_spec.rb +90 -0
- data/spec/openstudio/workflow_step_spec.rb +116 -0
- data/spec/reports/SPEC-OpenStudio-Analysis-Formulation.xml +28 -0
- data/spec/reports/SPEC-OpenStudio-Analysis-ServerApi-create-a-new-object-instance.xml +2 -2
- data/spec/reports/SPEC-OpenStudio-Analysis-ServerApi-test-not-localhost.xml +2 -2
- data/spec/reports/SPEC-OpenStudio-Analysis-ServerApi.xml +1 -1
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-discrete-variables.xml +30 -4
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-discrete-with-dynamic-columns.xml +11 -3
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-no-variables-defined.xml +6 -6
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-proxy-setup-with-user.xml +2 -2
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-proxy-setup.xml +2 -2
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-setup-output-variables.xml +52 -8
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-setup-version-0-1-9.xml +22 -5
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-small-list-of-incomplete-variables.xml +2 -2
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-small-list-of-variables-should-not-validate.xml +2 -2
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-small-list-of-variables.xml +28 -5
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-small-list-with-with-repeated-variable-names.xml +2 -2
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-1-10.xml +3 -3
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-2-0-simple.xml +13 -6
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-2-0.xml +55 -5
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-0-dynamic-uuid-assignments.xml +9 -4
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-0-measure-existence-checks.xml +8 -3
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-0-objective-functions.xml +13 -5
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-3-and-short-display-names.xml +9 -4
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-5-and-measure-paths.xml +9 -4
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-7-and-worker-init-final-scripts.0.xml +40 -2
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel-version-0-3-7-and-worker-init-final-scripts.xml +9 -4
- data/spec/reports/SPEC-OpenStudio-Analysis-Translator-Excel.xml +1 -1
- data/spec/reports/SPEC-OpenStudio-Analysis-Workflow.xml +31 -0
- data/spec/reports/SPEC-OpenStudio-Analysis-WorkflowStep.xml +29 -0
- data/spec/spec_helper.rb +1 -1
- 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
|
@@ -3,10 +3,10 @@
|
|
3
3
|
class String
|
4
4
|
def underscore
|
5
5
|
gsub(/::/, '/')
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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 "
|
41
|
-
|
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
|
data/lib/openstudio-analysis.rb
CHANGED
@@ -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
|
|
Binary file
|
Binary file
|
Binary file
|