urbanopt-reopt 0.2.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/pull_request_template.md +2 -2
- data/.gitignore +3 -0
- data/.rdoc_options +1 -1
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +40 -0
- data/CONTRIBUTING.md +3 -3
- data/Gemfile +12 -22
- data/LICENSE.md +1 -1
- data/RDOC_MAIN.md +13 -13
- data/README.md +15 -15
- data/Rakefile +31 -1
- data/a.txt +1 -0
- data/doc_templates/LICENSE.md +1 -1
- data/doc_templates/copyright_erb.txt +1 -1
- data/doc_templates/copyright_js.txt +1 -1
- data/doc_templates/copyright_ruby.txt +1 -1
- data/docs/README.md +16 -16
- data/index.md +13 -13
- data/lib/urbanopt-reopt.rb +1 -1
- data/lib/urbanopt/reopt.rb +1 -1
- data/lib/urbanopt/reopt/extension.rb +1 -1
- data/lib/urbanopt/reopt/feature_report_adapter.rb +47 -33
- data/lib/urbanopt/reopt/reopt_lite_api.rb +132 -13
- data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
- data/lib/urbanopt/reopt/reopt_post_processor.rb +58 -25
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +1 -1
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +65 -44
- data/lib/urbanopt/reopt/version.rb +2 -2
- data/lib/urbanopt/reopt_scenario.rb +1 -1
- data/urbanopt-reopt.gemspec +11 -7
- metadata +55 -27
- data/.travis.yml +0 -22
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -29,9 +29,8 @@
|
|
29
29
|
# *********************************************************************************
|
30
30
|
|
31
31
|
require 'bundler/setup'
|
32
|
-
require 'urbanopt/
|
32
|
+
require 'urbanopt/reporting/default_reports'
|
33
33
|
require 'urbanopt/reopt/reopt_logger'
|
34
|
-
require 'urbanopt/reopt'
|
35
34
|
require 'csv'
|
36
35
|
|
37
36
|
module URBANopt # :nodoc:
|
@@ -43,7 +42,7 @@ module URBANopt # :nodoc:
|
|
43
42
|
#
|
44
43
|
# [*parameters:*]
|
45
44
|
#
|
46
|
-
# * +scenario_report+ - _ScenarioReport_ - Optional. A scenario report that has been returned from the URBANopt::
|
45
|
+
# * +scenario_report+ - _ScenarioReport_ - Optional. A scenario report that has been returned from the URBANopt::Reporting::ScenarioDefaultPostProcessor - used in creating default output file names in \REopt Lite optimizations.
|
47
46
|
# * +scenario_reopt_assumptions_file+ - _String_ - Optional. JSON file formatted for a \REopt Lite analysis containing custom input parameters for optimizations at the Scenario Report level
|
48
47
|
# * +reopt_feature_assumptions+ - _Array_ - Optional. A list of JSON file formatted for a \REopt Lite analysis containing custom input parameters for optimizations at the Feature Report level. The order and number of files must match the Feature Reports in the scenario_report input.
|
49
48
|
# * +use_localhost+ - _Bool_ - If this is true, requests will be sent to a version of the \REopt Lite API running on localhost. Default is false, such that the production version of \REopt Lite is accessed.
|
@@ -70,8 +69,9 @@ module URBANopt # :nodoc:
|
|
70
69
|
if !scenario_report.nil?
|
71
70
|
@scenario_report = scenario_report
|
72
71
|
|
73
|
-
if
|
72
|
+
if !Dir.exist?(File.join(@scenario_report.directory_name, "reopt"))
|
74
73
|
Dir.mkdir(File.join(@scenario_report.directory_name, "reopt"))
|
74
|
+
@@logger.info("Created directory: " + File.join(@scenario_report.directory_name, "reopt"))
|
75
75
|
end
|
76
76
|
|
77
77
|
@scenario_reopt_default_output_file = File.join(@scenario_report.directory_name, "reopt/scenario_report_#{@scenario_report.id}_reopt_run.json")
|
@@ -80,6 +80,7 @@ module URBANopt # :nodoc:
|
|
80
80
|
@scenario_report.feature_reports.each do |fr|
|
81
81
|
if !Dir.exist?(File.join(fr.directory_name, "reopt"))
|
82
82
|
Dir.mkdir(File.join(fr.directory_name, "reopt"))
|
83
|
+
@@logger.info("Created directory: " + File.join(fr.directory_name, "reopt"))
|
83
84
|
end
|
84
85
|
@feature_reports_reopt_default_output_files << File.join(fr.directory_name, "reopt/feature_report_#{fr.id}_reopt_run.json")
|
85
86
|
end
|
@@ -111,25 +112,36 @@ module URBANopt # :nodoc:
|
|
111
112
|
#
|
112
113
|
# [*parameters:*]
|
113
114
|
#
|
114
|
-
# * +feature_report+ - _URBANopt::
|
115
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport which will be used in creating and then updated by a \REopt Lite opimization response.
|
115
116
|
# * +reopt_assumptions_hash+ - _Hash_ - Optional. A \REopt Lite formatted hash containing default parameters (i.e. utility rate, escalation rate) which will be updated by the FeatureReport (i.e. location, roof availability)
|
116
117
|
# * +reopt_output_file+ - _String_ - Optional. Path to a file at which REpopt Lite responses will be saved.
|
117
118
|
# * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the FeatureReport will be saved.
|
118
119
|
#
|
119
|
-
# [*return:*] _URBANopt::
|
120
|
+
# [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
|
120
121
|
##
|
121
|
-
def run_feature_report(feature_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil)
|
122
|
+
def run_feature_report(feature_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil, run_resilience:true)
|
122
123
|
api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
|
123
124
|
adapter = URBANopt::REopt::FeatureReportAdapter.new
|
124
125
|
|
125
126
|
reopt_input = adapter.reopt_json_from_feature_report(feature_report, reopt_assumptions_hash)
|
126
127
|
if reopt_output_file.nil?
|
127
|
-
reopt_output_file = feature_report.directory_name
|
128
|
+
reopt_output_file = File.join(feature_report.directory_name, 'reopt')
|
128
129
|
end
|
129
130
|
reopt_output = api.reopt_request(reopt_input, reopt_output_file)
|
130
|
-
|
131
|
+
if run_resilience
|
132
|
+
run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
|
133
|
+
if File.directory? reopt_output_file
|
134
|
+
resilience_stats = api.resilience_request(run_uuid, reopt_output_file)
|
135
|
+
else
|
136
|
+
resilience_stats = api.resilience_request(run_uuid, reopt_output_file.sub!('.json','_resilience.json'))
|
137
|
+
end
|
138
|
+
else
|
139
|
+
resilience_stats = nil
|
140
|
+
end
|
141
|
+
result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path, resilience_stats)
|
131
142
|
if !save_name.nil?
|
132
|
-
result.save_feature_report save_name
|
143
|
+
#result.save_feature_report save_name
|
144
|
+
result.save_json_report(save_name)
|
133
145
|
end
|
134
146
|
return result
|
135
147
|
end
|
@@ -140,13 +152,13 @@ module URBANopt # :nodoc:
|
|
140
152
|
#
|
141
153
|
# [*parameters:*]
|
142
154
|
#
|
143
|
-
# * +feature_report+ - _URBANopt::
|
155
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport which will be used in creating and then updated by a \REopt Lite opimization response.
|
144
156
|
# * +reopt_assumptions_hash+ - _Hash_ - Optional. A \REopt Lite formatted hash containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability)
|
145
157
|
# * +reopt_output_file+ - _String_ - Optional. Path to a file at which REpopt Lite responses will be saved.
|
146
158
|
# * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
|
147
159
|
#
|
148
160
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
|
149
|
-
def run_scenario_report(scenario_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil)
|
161
|
+
def run_scenario_report(scenario_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil, run_resilience:true)
|
150
162
|
if !reopt_assumptions_hash.nil?
|
151
163
|
@scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
|
152
164
|
end
|
@@ -163,8 +175,18 @@ module URBANopt # :nodoc:
|
|
163
175
|
reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash)
|
164
176
|
|
165
177
|
reopt_output = api.reopt_request(reopt_input, @scenario_reopt_default_output_file)
|
178
|
+
if run_resilience
|
179
|
+
run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
|
180
|
+
if File.directory? @scenario_reopt_default_output_file
|
181
|
+
resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file)
|
182
|
+
else
|
183
|
+
resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file.sub!('.json','_resilience.json'))
|
184
|
+
end
|
185
|
+
else
|
186
|
+
resilience_stats = nil
|
187
|
+
end
|
166
188
|
|
167
|
-
result = adapter.update_scenario_report(scenario_report, reopt_output, @scenario_timeseries_default_output_file)
|
189
|
+
result = adapter.update_scenario_report(scenario_report, reopt_output, @scenario_timeseries_default_output_file, resilience_stats)
|
168
190
|
if !save_name.nil?
|
169
191
|
result.save save_name
|
170
192
|
end
|
@@ -176,13 +198,13 @@ module URBANopt # :nodoc:
|
|
176
198
|
#
|
177
199
|
# [*parameters:*]
|
178
200
|
#
|
179
|
-
# * +feature_reports+ - _Array_ - An array of _URBANopt::
|
201
|
+
# * +feature_reports+ - _Array_ - An array of _URBANopt::Reporting::DefaultReports::FeatureReport_ objetcs which will each be used to create (and are subsquently updated by) a \REopt Lite opimization response.
|
180
202
|
# * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of \REopt Lite formatted hashes containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability). The number and order of the hashes should match the feature_reports array.
|
181
203
|
# * +reopt_output_files+ - _Array_ - Optional. A array of paths to files at which REpopt Lite responses will be saved. The number and order of the paths should match the feature_reports array.
|
182
204
|
# * +timeseries_csv_path+ - _Array_ - Optional. A array of paths to files at which the new timeseries CSV for the FeatureReports will be saved. The number and order of the paths should match the feature_reports array.
|
183
205
|
#
|
184
206
|
# [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
|
185
|
-
def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil)
|
207
|
+
def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil, run_resilience:true)
|
186
208
|
|
187
209
|
if !reopt_assumptions_hashes.empty?
|
188
210
|
@feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
|
@@ -215,20 +237,31 @@ module URBANopt # :nodoc:
|
|
215
237
|
begin
|
216
238
|
reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx])
|
217
239
|
reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
|
218
|
-
|
240
|
+
if run_resilience
|
241
|
+
run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
|
242
|
+
if File.directory? @feature_reports_reopt_default_output_files[idx]
|
243
|
+
resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx])
|
244
|
+
else
|
245
|
+
resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx].sub!('.json','_resilience.json'))
|
246
|
+
end
|
247
|
+
else
|
248
|
+
resilience_stats = nil
|
249
|
+
end
|
250
|
+
new_feature_report = feature_adapter.update_feature_report(feature_report, reopt_output, @feature_reports_timeseries_default_output_files[idx], resilience_stats)
|
219
251
|
new_feature_reports.push(new_feature_report)
|
220
252
|
if !save_names.nil?
|
221
253
|
if save_names.length == feature_reports.length
|
222
|
-
new_feature_report.save_feature_report save_names[idx]
|
254
|
+
#new_feature_report.save_feature_report save_names[idx]
|
255
|
+
new_feature_report.save_json_report save_names[idx]
|
223
256
|
else
|
224
257
|
warn "Could not save feature reports - the number of save names provided did not match the number of feature reports"
|
225
258
|
end
|
226
|
-
|
259
|
+
end
|
227
260
|
rescue StandardError
|
228
261
|
@@logger.info("Could not optimize Feature Report #{feature_report.name} #{feature_report.id}")
|
229
262
|
end
|
230
263
|
end
|
231
|
-
|
264
|
+
|
232
265
|
return new_feature_reports
|
233
266
|
end
|
234
267
|
|
@@ -237,22 +270,22 @@ module URBANopt # :nodoc:
|
|
237
270
|
#
|
238
271
|
# [*parameters:*]
|
239
272
|
#
|
240
|
-
# * +scenario_report+ - _Array_ - A _URBANopt::
|
273
|
+
# * +scenario_report+ - _Array_ - A _URBANopt::Reporting::DefaultReports::ScenarioReport_ which will each be used to create (and is subsquently updated by) \REopt Lite opimization responses for each of its FeatureReports.
|
241
274
|
# * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of \REopt Lite formatted hashes containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability). The number and order of the hashes should match the array in ScenarioReport.feature_reports.
|
242
275
|
# * +reopt_output_files+ - _Array_ - Optional. An array of paths to files at which REpopt Lite responses will be saved. The number and order of the paths should match the array in ScenarioReport.feature_reports.
|
243
276
|
# * +feature_report_timeseries_csv_paths+ - _Array_ - Optional. An array of paths to files at which the new timeseries CSV for the FeatureReports will be saved. The number and order of the paths should match the array in ScenarioReport.feature_reports.
|
244
277
|
#
|
245
278
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
|
246
|
-
def run_scenario_report_features(scenario_report:, reopt_assumptions_hashes:[], reopt_output_files:[], feature_report_timeseries_csv_paths:[], save_names_feature_reports:nil, save_name_scenario_report:nil)
|
247
|
-
new_feature_reports = run_feature_reports(feature_reports:scenario_report.feature_reports, reopt_assumptions_hashes:reopt_assumptions_hashes, reopt_output_files:reopt_output_files, timeseries_csv_paths:feature_report_timeseries_csv_paths,save_names:save_names_feature_reports)
|
279
|
+
def run_scenario_report_features(scenario_report:, reopt_assumptions_hashes:[], reopt_output_files:[], feature_report_timeseries_csv_paths:[], save_names_feature_reports:nil, save_name_scenario_report:nil, run_resilience:true)
|
280
|
+
new_feature_reports = run_feature_reports(feature_reports:scenario_report.feature_reports, reopt_assumptions_hashes:reopt_assumptions_hashes, reopt_output_files:reopt_output_files, timeseries_csv_paths:feature_report_timeseries_csv_paths,save_names:save_names_feature_reports, run_resilience:run_resilience)
|
248
281
|
|
249
|
-
new_scenario_report = URBANopt::
|
282
|
+
new_scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new
|
250
283
|
new_scenario_report.id = scenario_report.id
|
251
284
|
new_scenario_report.name = scenario_report.name
|
252
285
|
new_scenario_report.directory_name = scenario_report.directory_name
|
253
286
|
|
254
287
|
timeseries_hash = { column_names: scenario_report.timeseries_csv.column_names }
|
255
|
-
new_scenario_report.timeseries_csv = URBANopt::
|
288
|
+
new_scenario_report.timeseries_csv = URBANopt::Reporting::DefaultReports::TimeseriesCSV.new(timeseries_hash)
|
256
289
|
|
257
290
|
new_feature_reports.each do |feature_report|
|
258
291
|
new_scenario_report.add_feature_report(feature_report)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
3
|
# contributors. All rights reserved.
|
4
4
|
#
|
5
5
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -28,7 +28,7 @@
|
|
28
28
|
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
29
|
# *********************************************************************************
|
30
30
|
|
31
|
-
require 'urbanopt/
|
31
|
+
require 'urbanopt/reporting/default_reports'
|
32
32
|
require 'urbanopt/reopt/reopt_logger'
|
33
33
|
require 'matrix'
|
34
34
|
require 'csv'
|
@@ -51,7 +51,7 @@ module URBANopt # :nodoc:
|
|
51
51
|
#
|
52
52
|
# [*parameters:*]
|
53
53
|
#
|
54
|
-
# * +scenario_report+ - _URBANopt::
|
54
|
+
# * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting the +reopt_assumptions_hash+, if provided, to a \REopt Lite post. Otherwise, if the +reopt_assumptions_hash+ is nil a default post will be updated from this ScenarioReport and submitted to the \REopt Lite API.
|
55
55
|
# * +reopt_assumptions_hash+ - _Hash_ - Optional. A hash formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API.
|
56
56
|
#
|
57
57
|
# [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
|
@@ -70,31 +70,33 @@ module URBANopt # :nodoc:
|
|
70
70
|
end
|
71
71
|
|
72
72
|
# Update required info
|
73
|
-
if scenario_report.location.
|
73
|
+
if scenario_report.location.latitude_deg.nil? || scenario_report.location.longitude_deg.nil? || (scenario_report.location.latitude_deg == 0) || (scenario_report.location.longitude_deg == 0)
|
74
74
|
if !scenario_report.feature_reports.nil? && (scenario_report.feature_reports != [])
|
75
75
|
lats = []
|
76
76
|
longs = []
|
77
77
|
scenario_report.feature_reports.each do |x|
|
78
|
-
|
79
|
-
|
80
|
-
|
78
|
+
puts " ERROR: #{x.location.latitude_deg}"
|
79
|
+
if ![nil].include?(x.location.latitude_deg) && ![nil].include?(x.location.longitude_deg)
|
80
|
+
lats.push(x.location.latitude_deg)
|
81
|
+
longs.push(x.location.longitude_deg)
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
84
85
|
if !lats.empty? && !longs.empty?
|
85
|
-
scenario_report.location.
|
86
|
-
scenario_report.location.
|
86
|
+
scenario_report.location.latitude_deg = lats.reduce(:+) / lats.size.to_f
|
87
|
+
scenario_report.location.longitude_deg = longs.reduce(:+) / longs.size.to_f
|
87
88
|
end
|
88
89
|
end
|
89
90
|
end
|
90
91
|
|
91
92
|
# Update required info
|
92
93
|
requireds_names = ['latitude', 'longitude']
|
93
|
-
requireds = [scenario_report.location.
|
94
|
+
requireds = [scenario_report.location.latitude_deg, scenario_report.location.longitude_deg]
|
95
|
+
|
94
96
|
|
95
97
|
if requireds.include?(nil) || requireds.include?(0)
|
96
|
-
requireds.each_with_index do |
|
97
|
-
if [nil
|
98
|
+
requireds.each_with_index do |x, i|
|
99
|
+
if [nil].include? x
|
98
100
|
n = requireds_names[i]
|
99
101
|
raise "Missing value for #{n} - this is a required input"
|
100
102
|
end
|
@@ -103,33 +105,38 @@ module URBANopt # :nodoc:
|
|
103
105
|
|
104
106
|
reopt_inputs[:Scenario][:description] = description
|
105
107
|
|
106
|
-
reopt_inputs[:Scenario][:Site][:latitude] = scenario_report.location.
|
107
|
-
reopt_inputs[:Scenario][:Site][:longitude] = scenario_report.location.
|
108
|
+
reopt_inputs[:Scenario][:Site][:latitude] = scenario_report.location.latitude_deg
|
109
|
+
reopt_inputs[:Scenario][:Site][:longitude] = scenario_report.location.longitude_deg
|
108
110
|
|
109
111
|
# Update optional info
|
110
|
-
|
111
|
-
|
112
|
+
# REK: attribute names should be updated
|
113
|
+
if !scenario_report.program.roof_area_sqft.nil?
|
114
|
+
reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area_sqft[:available_roof_area]
|
115
|
+
end
|
116
|
+
|
117
|
+
if !scenario_report.program.site_area_sqft.nil?
|
118
|
+
reopt_inputs[:Scenario][:Site][:land_acres] = scenario_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
|
112
119
|
end
|
113
120
|
|
114
|
-
|
115
|
-
reopt_inputs[:Scenario][:
|
121
|
+
unless scenario_report.timesteps_per_hour.nil?
|
122
|
+
reopt_inputs[:Scenario][:time_steps_per_hour] = scenario_report.timesteps_per_hour
|
116
123
|
end
|
117
124
|
|
118
125
|
# Update load profile info
|
119
126
|
begin
|
120
127
|
col_num = scenario_report.timeseries_csv.column_names.index('Electricity:Facility(kWh)')
|
121
128
|
t = CSV.read(scenario_report.timeseries_csv.path, headers: true, converters: :numeric)
|
122
|
-
|
123
|
-
if
|
129
|
+
energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * scenario_report.timesteps_per_hour || 0) ) }
|
130
|
+
if energy_timeseries_kw.length < (scenario_report.timesteps_per_hour * 8760)
|
124
131
|
start_date = Time.parse(t.by_col["Datetime"][0])
|
125
|
-
start_ts = (((start_date.yday * 60.0 * 60.0 * 24) + (start_date.hour * 60.0 * 60.0) + (start_date.min * 60.0) + start_date.sec) /
|
132
|
+
start_ts = (((start_date.yday * 60.0 * 60.0 * 24) + (start_date.hour * 60.0 * 60.0) + (start_date.min * 60.0) + start_date.sec) / \
|
126
133
|
(( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int
|
127
134
|
end_date = Time.parse(t.by_col["Datetime"][-1])
|
128
|
-
end_ts = (((end_date.yday * 60.0 * 60.0 * 24) + (end_date.hour * 60.0 * 60.0) + (end_date.min * 60.0) + end_date.sec) /
|
135
|
+
end_ts = (((end_date.yday * 60.0 * 60.0 * 24) + (end_date.hour * 60.0 * 60.0) + (end_date.min * 60.0) + end_date.sec) / \
|
129
136
|
(( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int
|
130
|
-
|
137
|
+
energy_timeseries_kw = [0.0]*(start_ts-1) + energy_timeseries_kw + [0.0]*((scenario_report.timesteps_per_hour * 8760) - end_ts)
|
131
138
|
end
|
132
|
-
reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] =
|
139
|
+
reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = energy_timeseries_kw.map { |e| e ? e : 0 }[0,(scenario_report.timesteps_per_hour * 8760)]
|
133
140
|
rescue StandardError
|
134
141
|
@@logger.error("Could not parse the annual electric load from the timeseries csv - #{scenario_report.timeseries_csv.path}")
|
135
142
|
raise "Could not parse the annual electric load from the timeseries csv - #{scenario_report.timeseries_csv.path}"
|
@@ -142,7 +149,7 @@ module URBANopt # :nodoc:
|
|
142
149
|
#
|
143
150
|
# [*parameters:*]
|
144
151
|
#
|
145
|
-
# * +scenario_report+ - _URBANopt::
|
152
|
+
# * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting FeatureReports and respecitive +reopt_assumptions_hashes+, if provided, to a \REopt Lite post. If no +reopt_assumptions_hashes+ are provided default posts will be updated from these FeatureReports and submitted to the \REopt Lite API.
|
146
153
|
# * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of hashes formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API. The order should match the list in ScenarioReport.feature_reports.
|
147
154
|
#
|
148
155
|
# [*return:*] _Array_ - Returns an array of hashes formatted for submittal to the \REopt Lite API in the order of the FeatureReports lited in ScenarioReport.feature_reports.
|
@@ -164,18 +171,18 @@ module URBANopt # :nodoc:
|
|
164
171
|
#
|
165
172
|
# [*parameters:*]
|
166
173
|
#
|
167
|
-
# * +scenario_report+ - _URBANopt::
|
174
|
+
# * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to update from a \REopt Lite response.
|
168
175
|
# * +reopt_output+ - _Hash_ - A hash response from the \REopt Lite API.
|
169
176
|
# * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which new timeseries data will be written. If not provided a file is created based on the run_uuid of the \REopt Lite optimization task.
|
170
177
|
#
|
171
|
-
# [*return:*] _URBANopt::
|
178
|
+
# [*return:*] _URBANopt::Reporting::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
|
172
179
|
##
|
173
|
-
def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path =
|
180
|
+
def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil)
|
174
181
|
if reopt_output['outputs']['Scenario']['status'] != 'optimal'
|
175
182
|
@@logger.info("Warning cannot Feature Report #{scenario_report.name} #{scenario_report.id} - REopt optimization was non-optimal")
|
176
183
|
return scenario_report
|
177
184
|
end
|
178
|
-
|
185
|
+
|
179
186
|
$ts_per_hour = scenario_report.timesteps_per_hour
|
180
187
|
def scale_timeseries(input, ts_per_hr=$ts_per_hour)
|
181
188
|
if input.nil?
|
@@ -188,7 +195,7 @@ module URBANopt # :nodoc:
|
|
188
195
|
return input
|
189
196
|
end
|
190
197
|
result = []
|
191
|
-
input.each do |val|
|
198
|
+
input.each do |val|
|
192
199
|
(1..ts_per_hr).each do |x|
|
193
200
|
result.push(val/ts_per_hr.to_f)
|
194
201
|
end
|
@@ -197,8 +204,8 @@ module URBANopt # :nodoc:
|
|
197
204
|
end
|
198
205
|
|
199
206
|
# Update location
|
200
|
-
scenario_report.location.
|
201
|
-
scenario_report.location.
|
207
|
+
scenario_report.location.latitude_deg = reopt_output['inputs']['Scenario']['Site']['latitude']
|
208
|
+
scenario_report.location.longitude_deg = reopt_output['inputs']['Scenario']['Site']['longitude']
|
202
209
|
|
203
210
|
# Update timeseries csv from \REopt Lite dispatch data
|
204
211
|
scenario_report.timesteps_per_hour = reopt_output['inputs']['Scenario']['time_steps_per_hour']
|
@@ -211,36 +218,50 @@ module URBANopt # :nodoc:
|
|
211
218
|
scenario_report.distributed_generation.year_one_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars'] || 0
|
212
219
|
scenario_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars'] || 0
|
213
220
|
scenario_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars'] || 0
|
214
|
-
|
221
|
+
scenario_report.distributed_generation.total_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_us_dollars'] || 0
|
222
|
+
scenario_report.distributed_generation.year_one_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_bau_us_dollars'] || 0
|
223
|
+
scenario_report.distributed_generation.year_one_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_bau_us_dollars'] || 0
|
224
|
+
scenario_report.distributed_generation.year_one_bill_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_bau_us_dollars'] || 0
|
225
|
+
scenario_report.distributed_generation.total_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_bau_us_dollars'] || 0
|
226
|
+
scenario_report.distributed_generation.total_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_bau_us_dollars'] || 0
|
227
|
+
if !resilience_stats.nil?
|
228
|
+
scenario_report.distributed_generation.resilience_hours_min = resilience_stats['resilience_hours_min']
|
229
|
+
scenario_report.distributed_generation.resilience_hours_max = resilience_stats['resilience_hours_max']
|
230
|
+
scenario_report.distributed_generation.resilience_hours_avg = resilience_stats['resilience_hours_avg']
|
231
|
+
scenario_report.distributed_generation.probs_of_surviving = resilience_stats['probs_of_surviving']
|
232
|
+
scenario_report.distributed_generation.probs_of_surviving_by_month = resilience_stats['probs_of_surviving_by_month']
|
233
|
+
scenario_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
|
234
|
+
end
|
235
|
+
|
215
236
|
if reopt_output['outputs']['Scenario']['Site']['PV'].class == Hash
|
216
237
|
reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
|
217
238
|
elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
|
218
239
|
reopt_output['outputs']['Scenario']['Site']['PV'] = []
|
219
240
|
end
|
220
|
-
|
221
|
-
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
222
|
-
scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::
|
241
|
+
|
242
|
+
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
243
|
+
scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
|
223
244
|
end
|
224
245
|
|
225
246
|
wind = reopt_output['outputs']['Scenario']['Site']['Wind']
|
226
247
|
if !wind['size_kw'].nil? and wind['size_kw'] != 0
|
227
|
-
scenario_report.distributed_generation.add_tech 'wind', URBANopt::
|
248
|
+
scenario_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
|
228
249
|
end
|
229
250
|
|
230
251
|
generator = reopt_output['outputs']['Scenario']['Site']['Generator']
|
231
252
|
if !generator['size_kw'].nil? and generator['size_kw'] != 0
|
232
|
-
scenario_report.distributed_generation.add_tech 'generator', URBANopt::
|
253
|
+
scenario_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
|
233
254
|
end
|
234
255
|
|
235
256
|
storage = reopt_output['outputs']['Scenario']['Site']['Storage']
|
236
257
|
if !storage['size_kw'].nil? and storage['size_kw'] != 0
|
237
|
-
scenario_report.distributed_generation.add_tech 'storage', URBANopt::
|
258
|
+
scenario_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
|
238
259
|
end
|
239
|
-
|
240
|
-
generation_timeseries_kwh = Matrix[[0] * 8760]
|
241
260
|
|
242
|
-
|
243
|
-
|
261
|
+
generation_timeseries_kwh = Matrix[[0] * (8760 * scenario_report.timesteps_per_hour)]
|
262
|
+
|
263
|
+
|
264
|
+
reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
|
244
265
|
if (pv['size_kw'] || 0) > 0
|
245
266
|
if !pv['year_one_power_production_series_kw'].nil?
|
246
267
|
generation_timeseries_kwh += Matrix[pv['year_one_power_production_series_kw']]
|
@@ -272,7 +293,7 @@ module URBANopt # :nodoc:
|
|
272
293
|
end
|
273
294
|
end
|
274
295
|
|
275
|
-
$generation_timeseries_kwh = generation_timeseries_kwh.to_a[0]
|
296
|
+
$generation_timeseries_kwh = generation_timeseries_kwh.to_a[0] || [0] * (8760 * scenario_report.timesteps_per_hour)
|
276
297
|
$generation_timeseries_kwh_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Total(kw)')
|
277
298
|
if $generation_timeseries_kwh_col.nil?
|
278
299
|
$generation_timeseries_kwh_col = scenario_report.timeseries_csv.column_names.length
|