urbanopt-reopt 0.5.4 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +30 -0
- data/Gemfile +1 -1
- data/LICENSE.md +33 -21
- data/Rakefile +18 -8
- data/docs/package-lock.json +13843 -1118
- data/docs/package.json +10 -6
- data/lib/urbanopt-reopt.rb +16 -6
- data/lib/urbanopt/reopt.rb +16 -6
- data/lib/urbanopt/reopt/extension.rb +16 -6
- data/lib/urbanopt/reopt/feature_report_adapter.rb +82 -103
- data/lib/urbanopt/reopt/reopt_lite_api.rb +81 -57
- data/lib/urbanopt/reopt/reopt_logger.rb +17 -7
- data/lib/urbanopt/reopt/reopt_post_processor.rb +89 -56
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +23 -13
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +88 -110
- data/lib/urbanopt/reopt/utilities.rb +106 -102
- data/lib/urbanopt/reopt/version.rb +17 -7
- data/lib/urbanopt/reopt_scenario.rb +16 -6
- data/urbanopt-reopt.gemspec +4 -7
- metadata +15 -29
@@ -1,21 +1,31 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt
|
2
|
+
# URBANopt™, Copyright (c) 2019-2021, 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,
|
6
6
|
# are permitted provided that the following conditions are met:
|
7
|
-
|
7
|
+
|
8
8
|
# Redistributions of source code must retain the above copyright notice, this list
|
9
9
|
# of conditions and the following disclaimer.
|
10
|
-
|
10
|
+
|
11
11
|
# Redistributions in binary form must reproduce the above copyright notice, this
|
12
12
|
# list of conditions and the following disclaimer in the documentation and/or other
|
13
13
|
# materials provided with the distribution.
|
14
|
-
|
14
|
+
|
15
15
|
# Neither the name of the copyright holder nor the names of its contributors may be
|
16
16
|
# used to endorse or promote products derived from this software without specific
|
17
17
|
# prior written permission.
|
18
|
-
|
18
|
+
|
19
|
+
# Redistribution of this software, without modification, must refer to the software
|
20
|
+
# by the same designation. Redistribution of a modified version of this software
|
21
|
+
# (i) may not refer to the modified version by the same designation, or by any
|
22
|
+
# confusingly similar designation, and (ii) must refer to the underlying software
|
23
|
+
# originally provided by Alliance as “URBANopt”. Except to comply with the foregoing,
|
24
|
+
# the term “URBANopt”, or any confusingly similar designation may not be used to
|
25
|
+
# refer to any modified version of this software or any modified version of the
|
26
|
+
# underlying software originally provided by Alliance without the prior written
|
27
|
+
# consent of Alliance.
|
28
|
+
|
19
29
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
20
30
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
31
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
@@ -87,6 +97,7 @@ module URBANopt # :nodoc:
|
|
87
97
|
if @use_localhost
|
88
98
|
return URI.parse("http://127.0.0.1:8000/v1/job/#{run_uuid}/results")
|
89
99
|
end
|
100
|
+
|
90
101
|
return URI.parse("https://developer.nrel.gov/api/reopt/v1/job/#{run_uuid}/results?api_key=#{@nrel_developer_key}")
|
91
102
|
end
|
92
103
|
|
@@ -104,6 +115,7 @@ module URBANopt # :nodoc:
|
|
104
115
|
if @use_localhost
|
105
116
|
return URI.parse("http://127.0.0.1:8000/v1/job/#{run_uuid}/resilience_stats")
|
106
117
|
end
|
118
|
+
|
107
119
|
return URI.parse("https://developer.nrel.gov/api/reopt/v1/job/#{run_uuid}/resilience_stats?api_key=#{@nrel_developer_key}")
|
108
120
|
end
|
109
121
|
|
@@ -112,11 +124,20 @@ module URBANopt # :nodoc:
|
|
112
124
|
tries = 0
|
113
125
|
while tries < max_tries
|
114
126
|
begin
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
127
|
+
result = http.request(r)
|
128
|
+
# Result codes sourced from https://developer.nrel.gov/docs/errors/
|
129
|
+
if result.code == '429'
|
130
|
+
@@logger.fatal('Exceeded the REopt-Lite API limit of 300 requests per hour')
|
131
|
+
puts 'Using the URBANopt CLI to submit a Scenario optimization counts as one request per scenario'
|
132
|
+
puts 'Using the URBANopt CLI to submit a Feature optimization counts as one request per feature'
|
133
|
+
abort('Please wait and try again once the time period has elapsed')
|
134
|
+
elsif (result.code != '201') && (result.code != '200') # Anything in the 200s is success
|
135
|
+
@@logger.debug("REopt-Lite has returned a '#{result.code}' status code. Visit https://developer.nrel.gov/docs/errors/ for more status code information")
|
136
|
+
end
|
137
|
+
tries = 4
|
138
|
+
rescue StandardError
|
139
|
+
tries += 1
|
140
|
+
end
|
120
141
|
end
|
121
142
|
return result
|
122
143
|
end
|
@@ -138,11 +159,11 @@ module URBANopt # :nodoc:
|
|
138
159
|
http.use_ssl = true
|
139
160
|
end
|
140
161
|
|
141
|
-
|
142
|
-
|
162
|
+
post_request = Net::HTTP::Post.new(@uri_submit, header)
|
163
|
+
post_request.body = ::JSON.generate(data, allow_nan: true)
|
143
164
|
|
144
165
|
# Send the request
|
145
|
-
response = make_request(http,
|
166
|
+
response = make_request(http, post_request)
|
146
167
|
|
147
168
|
if !response.is_a?(Net::HTTPSuccess)
|
148
169
|
@@logger.error('Check_connection Failed')
|
@@ -165,7 +186,6 @@ module URBANopt # :nodoc:
|
|
165
186
|
# [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
|
166
187
|
##
|
167
188
|
def resilience_request(run_uuid, filename)
|
168
|
-
|
169
189
|
if File.directory? filename
|
170
190
|
if run_uuid.nil?
|
171
191
|
run_uuid = 'error'
|
@@ -176,55 +196,59 @@ module URBANopt # :nodoc:
|
|
176
196
|
filename = File.join(filename, "#{run_uuid}_resilience.json")
|
177
197
|
@@logger.info("REopt results saved to #{filename}")
|
178
198
|
end
|
179
|
-
|
180
|
-
#Submit Job
|
199
|
+
|
200
|
+
# Submit Job
|
181
201
|
@@logger.info("Submitting Resilience Statistics job for #{run_uuid}")
|
182
202
|
header = { 'Content-Type' => 'application/json' }
|
183
203
|
http = Net::HTTP.new(@uri_submit_outagesimjob.host, @uri_submit_outagesimjob.port)
|
184
204
|
if !@use_localhost
|
185
205
|
http.use_ssl = true
|
186
206
|
end
|
187
|
-
|
188
|
-
|
189
|
-
submit_response = make_request(http,
|
190
|
-
@@logger.
|
207
|
+
post_request = Net::HTTP::Post.new(@uri_submit_outagesimjob, header)
|
208
|
+
post_request.body = ::JSON.generate({ 'run_uuid' => run_uuid, 'bau' => false }, allow_nan: true)
|
209
|
+
submit_response = make_request(http, post_request)
|
210
|
+
@@logger.debug(submit_response.body)
|
191
211
|
|
192
|
-
#Fetch Results
|
212
|
+
# Fetch Results
|
193
213
|
uri = uri_resilience(run_uuid)
|
194
214
|
http = Net::HTTP.new(uri.host, uri.port)
|
195
215
|
if !@use_localhost
|
196
216
|
http.use_ssl = true
|
197
217
|
end
|
198
218
|
|
219
|
+
# Wait a few seconds for the REopt database to update before GETing results
|
220
|
+
sleep 5
|
221
|
+
get_request = Net::HTTP::Get.new(uri.request_uri)
|
222
|
+
response = make_request(http, get_request)
|
223
|
+
|
224
|
+
# Set a limit on retries when 404s are returned from REopt API
|
199
225
|
elapsed_time = 0
|
200
226
|
max_elapsed_time = 60 * 5
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
elapsed_time += 5
|
227
|
+
|
228
|
+
# If database still hasn't updated, wait a little longer and try again
|
229
|
+
while (elapsed_time < max_elapsed_time) & (response.code == '404')
|
230
|
+
response = make_request(http, get_request)
|
231
|
+
@@logger.warn('GET request was too fast for REOpt-Lite API. Retrying...')
|
232
|
+
elapsed_time += 5
|
208
233
|
sleep 5
|
209
234
|
end
|
210
|
-
|
235
|
+
|
211
236
|
data = JSON.parse(response.body)
|
212
237
|
text = ::JSON.generate(data, allow_nan: true)
|
213
238
|
begin
|
214
239
|
File.open(filename, 'w+') do |f|
|
215
240
|
f.puts(text)
|
216
241
|
end
|
217
|
-
rescue
|
218
|
-
@@logger.
|
242
|
+
rescue StandardError
|
243
|
+
@@logger.error("Cannot write - #{filename}")
|
219
244
|
end
|
220
245
|
|
221
|
-
if response.code ==
|
246
|
+
if response.code == '200'
|
222
247
|
return data
|
223
248
|
end
|
224
|
-
|
225
|
-
@@logger.
|
249
|
+
|
250
|
+
@@logger.error("Error from REopt API - #{data['Error']}")
|
226
251
|
return {}
|
227
|
-
|
228
252
|
end
|
229
253
|
|
230
254
|
##
|
@@ -251,14 +275,14 @@ module URBANopt # :nodoc:
|
|
251
275
|
if !@use_localhost
|
252
276
|
http.use_ssl = true
|
253
277
|
end
|
254
|
-
|
255
|
-
|
256
|
-
|
278
|
+
post_request = Net::HTTP::Post.new(@uri_submit, header)
|
279
|
+
post_request.body = ::JSON.generate(reopt_input, allow_nan: true)
|
280
|
+
|
257
281
|
# Send the request
|
258
|
-
response = make_request(http,
|
282
|
+
response = make_request(http, post_request)
|
259
283
|
|
260
284
|
# Get UUID
|
261
|
-
run_uuid = JSON.parse(response.body, allow_nan:true)['run_uuid']
|
285
|
+
run_uuid = JSON.parse(response.body, allow_nan: true)['run_uuid']
|
262
286
|
|
263
287
|
if File.directory? filename
|
264
288
|
if run_uuid.nil?
|
@@ -276,7 +300,7 @@ module URBANopt # :nodoc:
|
|
276
300
|
File.open(filename, 'w+') do |f|
|
277
301
|
f.puts(text)
|
278
302
|
end
|
279
|
-
@@logger.
|
303
|
+
@@logger.error("Cannot write - #{filename}")
|
280
304
|
raise "Error in REopt optimization post - see #{filename}"
|
281
305
|
end
|
282
306
|
|
@@ -288,18 +312,18 @@ module URBANopt # :nodoc:
|
|
288
312
|
http.use_ssl = true
|
289
313
|
end
|
290
314
|
|
291
|
-
|
315
|
+
get_request = Net::HTTP::Get.new(uri.request_uri)
|
292
316
|
|
293
317
|
while status == 'Optimizing...'
|
294
|
-
response = make_request(http,
|
295
|
-
|
296
|
-
data = JSON.parse(response.body, allow_nan:true)
|
318
|
+
response = make_request(http, get_request)
|
319
|
+
|
320
|
+
data = JSON.parse(response.body, allow_nan: true)
|
297
321
|
|
298
|
-
if data['outputs']['Scenario']['Site']['PV'].
|
322
|
+
if data['outputs']['Scenario']['Site']['PV'].is_a?(Array)
|
299
323
|
pv_sizes = 0
|
300
324
|
data['outputs']['Scenario']['Site']['PV'].each do |x|
|
301
|
-
pv_sizes
|
302
|
-
end
|
325
|
+
pv_sizes += x['size_kw'].to_f
|
326
|
+
end
|
303
327
|
else
|
304
328
|
pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
|
305
329
|
end
|
@@ -313,14 +337,14 @@ module URBANopt # :nodoc:
|
|
313
337
|
_tries = 0
|
314
338
|
(check_complete = sizes == 0) && ((data['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0) > 0)
|
315
339
|
while (_tries < _max_retry) && check_complete
|
316
|
-
sleep
|
317
|
-
response = make_request(http,
|
318
|
-
data = JSON.parse(response.body, allow_nan:true)
|
319
|
-
if data['outputs']['Scenario']['Site']['PV'].
|
340
|
+
sleep 3
|
341
|
+
response = make_request(http, get_request)
|
342
|
+
data = JSON.parse(response.body, allow_nan: true)
|
343
|
+
if data['outputs']['Scenario']['Site']['PV'].is_a?(Array)
|
320
344
|
pv_sizes = 0
|
321
345
|
data['outputs']['Scenario']['Site']['PV'].each do |x|
|
322
|
-
pv_sizes
|
323
|
-
end
|
346
|
+
pv_sizes += x['size_kw'].to_f
|
347
|
+
end
|
324
348
|
else
|
325
349
|
pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
|
326
350
|
end
|
@@ -335,8 +359,8 @@ module URBANopt # :nodoc:
|
|
335
359
|
File.open(filename, 'w+') do |f|
|
336
360
|
f.puts(text)
|
337
361
|
end
|
338
|
-
rescue
|
339
|
-
@@logger.
|
362
|
+
rescue StandardError
|
363
|
+
@@logger.error("Cannot write - #{filename}")
|
340
364
|
end
|
341
365
|
|
342
366
|
if status == 'optimal'
|
@@ -1,21 +1,31 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt
|
2
|
+
# URBANopt™, Copyright (c) 2019-2021, 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,
|
6
6
|
# are permitted provided that the following conditions are met:
|
7
|
-
|
7
|
+
|
8
8
|
# Redistributions of source code must retain the above copyright notice, this list
|
9
9
|
# of conditions and the following disclaimer.
|
10
|
-
|
10
|
+
|
11
11
|
# Redistributions in binary form must reproduce the above copyright notice, this
|
12
12
|
# list of conditions and the following disclaimer in the documentation and/or other
|
13
13
|
# materials provided with the distribution.
|
14
|
-
|
14
|
+
|
15
15
|
# Neither the name of the copyright holder nor the names of its contributors may be
|
16
16
|
# used to endorse or promote products derived from this software without specific
|
17
17
|
# prior written permission.
|
18
|
-
|
18
|
+
|
19
|
+
# Redistribution of this software, without modification, must refer to the software
|
20
|
+
# by the same designation. Redistribution of a modified version of this software
|
21
|
+
# (i) may not refer to the modified version by the same designation, or by any
|
22
|
+
# confusingly similar designation, and (ii) must refer to the underlying software
|
23
|
+
# originally provided by Alliance as “URBANopt”. Except to comply with the foregoing,
|
24
|
+
# the term “URBANopt”, or any confusingly similar designation may not be used to
|
25
|
+
# refer to any modified version of this software or any modified version of the
|
26
|
+
# underlying software originally provided by Alliance without the prior written
|
27
|
+
# consent of Alliance.
|
28
|
+
|
19
29
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
20
30
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
31
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
@@ -32,7 +42,7 @@ require 'logger'
|
|
32
42
|
|
33
43
|
module URBANopt
|
34
44
|
module REopt
|
35
|
-
@@reopt_logger = Logger.new(
|
45
|
+
@@reopt_logger = Logger.new($stdout)
|
36
46
|
##
|
37
47
|
# Definining class variable "@@logger" to log errors, info and warning messages.
|
38
48
|
def self.reopt_logger
|
@@ -1,21 +1,31 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt
|
2
|
+
# URBANopt™, Copyright (c) 2019-2021, 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,
|
6
6
|
# are permitted provided that the following conditions are met:
|
7
|
-
|
7
|
+
|
8
8
|
# Redistributions of source code must retain the above copyright notice, this list
|
9
9
|
# of conditions and the following disclaimer.
|
10
|
-
|
10
|
+
|
11
11
|
# Redistributions in binary form must reproduce the above copyright notice, this
|
12
12
|
# list of conditions and the following disclaimer in the documentation and/or other
|
13
13
|
# materials provided with the distribution.
|
14
|
-
|
14
|
+
|
15
15
|
# Neither the name of the copyright holder nor the names of its contributors may be
|
16
16
|
# used to endorse or promote products derived from this software without specific
|
17
17
|
# prior written permission.
|
18
|
-
|
18
|
+
|
19
|
+
# Redistribution of this software, without modification, must refer to the software
|
20
|
+
# by the same designation. Redistribution of a modified version of this software
|
21
|
+
# (i) may not refer to the modified version by the same designation, or by any
|
22
|
+
# confusingly similar designation, and (ii) must refer to the underlying software
|
23
|
+
# originally provided by Alliance as “URBANopt”. Except to comply with the foregoing,
|
24
|
+
# the term “URBANopt”, or any confusingly similar designation may not be used to
|
25
|
+
# refer to any modified version of this software or any modified version of the
|
26
|
+
# underlying software originally provided by Alliance without the prior written
|
27
|
+
# consent of Alliance.
|
28
|
+
|
19
29
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
20
30
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
31
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
@@ -69,18 +79,18 @@ module URBANopt # :nodoc:
|
|
69
79
|
if !scenario_report.nil?
|
70
80
|
@scenario_report = scenario_report
|
71
81
|
|
72
|
-
if !Dir.exist?(File.join(@scenario_report.directory_name,
|
73
|
-
Dir.mkdir(File.join(@scenario_report.directory_name,
|
74
|
-
@@logger.info("Created directory:
|
82
|
+
if !Dir.exist?(File.join(@scenario_report.directory_name, 'reopt'))
|
83
|
+
Dir.mkdir(File.join(@scenario_report.directory_name, 'reopt'))
|
84
|
+
@@logger.info("Created directory: #{File.join(@scenario_report.directory_name, 'reopt')}")
|
75
85
|
end
|
76
86
|
|
77
87
|
@scenario_reopt_default_output_file = File.join(@scenario_report.directory_name, "reopt/scenario_report_#{@scenario_report.id}_reopt_run.json")
|
78
88
|
@scenario_timeseries_default_output_file = File.join(@scenario_report.directory_name, "scenario_report_#{@scenario_report.id}_timeseries.csv")
|
79
89
|
|
80
90
|
@scenario_report.feature_reports.each do |fr|
|
81
|
-
if !Dir.exist?(File.join(fr.directory_name,
|
82
|
-
Dir.mkdir(File.join(fr.directory_name,
|
83
|
-
@@logger.info("Created directory:
|
91
|
+
if !Dir.exist?(File.join(fr.directory_name, 'reopt'))
|
92
|
+
Dir.mkdir(File.join(fr.directory_name, 'reopt'))
|
93
|
+
@@logger.info("Created directory: #{File.join(fr.directory_name, 'reopt')}")
|
84
94
|
end
|
85
95
|
@feature_reports_reopt_default_output_files << File.join(fr.directory_name, "reopt/feature_report_#{fr.id}_reopt_run.json")
|
86
96
|
end
|
@@ -103,8 +113,7 @@ module URBANopt # :nodoc:
|
|
103
113
|
end
|
104
114
|
end
|
105
115
|
|
106
|
-
attr_accessor :scenario_reopt_default_assumptions_hash, :scenario_reopt_default_output_file, :scenario_timeseries_default_output_file
|
107
|
-
attr_accessor :feature_reports_reopt_default_assumption_hashes, :feature_reports_reopt_default_output_files, :feature_reports_timeseries_default_output_files
|
116
|
+
attr_accessor :scenario_reopt_default_assumptions_hash, :scenario_reopt_default_output_file, :scenario_timeseries_default_output_file, :feature_reports_reopt_default_assumption_hashes, :feature_reports_reopt_default_output_files, :feature_reports_timeseries_default_output_files
|
108
117
|
|
109
118
|
##
|
110
119
|
# Updates a FeatureReport based on an optional set of \REopt Lite optimization assumptions.
|
@@ -119,7 +128,7 @@ module URBANopt # :nodoc:
|
|
119
128
|
#
|
120
129
|
# [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
|
121
130
|
##
|
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)
|
131
|
+
def run_feature_report(feature_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: true)
|
123
132
|
api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
|
124
133
|
adapter = URBANopt::REopt::FeatureReportAdapter.new
|
125
134
|
|
@@ -133,15 +142,14 @@ module URBANopt # :nodoc:
|
|
133
142
|
if File.directory? reopt_output_file
|
134
143
|
resilience_stats = api.resilience_request(run_uuid, reopt_output_file)
|
135
144
|
else
|
136
|
-
resilience_stats = api.resilience_request(run_uuid, reopt_output_file.sub!('.json','_resilience.json'))
|
145
|
+
resilience_stats = api.resilience_request(run_uuid, reopt_output_file.sub!('.json', '_resilience.json'))
|
137
146
|
end
|
138
147
|
else
|
139
148
|
resilience_stats = nil
|
140
149
|
end
|
141
150
|
result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path, resilience_stats)
|
142
151
|
if !save_name.nil?
|
143
|
-
|
144
|
-
result.save_json_report(save_name)
|
152
|
+
result.save save_name
|
145
153
|
end
|
146
154
|
return result
|
147
155
|
end
|
@@ -158,7 +166,7 @@ module URBANopt # :nodoc:
|
|
158
166
|
# * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
|
159
167
|
#
|
160
168
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
|
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)
|
169
|
+
def run_scenario_report(scenario_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: true)
|
162
170
|
if !reopt_assumptions_hash.nil?
|
163
171
|
@scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
|
164
172
|
end
|
@@ -180,7 +188,7 @@ module URBANopt # :nodoc:
|
|
180
188
|
if File.directory? @scenario_reopt_default_output_file
|
181
189
|
resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file)
|
182
190
|
else
|
183
|
-
resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file.sub!('.json','_resilience.json'))
|
191
|
+
resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file.sub!('.json', '_resilience.json'))
|
184
192
|
end
|
185
193
|
else
|
186
194
|
resilience_stats = nil
|
@@ -204,8 +212,7 @@ module URBANopt # :nodoc:
|
|
204
212
|
# * +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.
|
205
213
|
#
|
206
214
|
# [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
|
207
|
-
def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil, run_resilience:true)
|
208
|
-
|
215
|
+
def run_feature_reports(feature_reports:, reopt_assumptions_hashes: [], reopt_output_files: [], timeseries_csv_paths: [], save_names: nil, run_resilience: true, keep_existing_output: false)
|
209
216
|
if !reopt_assumptions_hashes.empty?
|
210
217
|
@feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
|
211
218
|
end
|
@@ -234,37 +241,59 @@ module URBANopt # :nodoc:
|
|
234
241
|
feature_adapter = URBANopt::REopt::FeatureReportAdapter.new
|
235
242
|
new_feature_reports = []
|
236
243
|
feature_reports.each_with_index do |feature_report, idx|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
if
|
243
|
-
|
244
|
+
# check if we should rerun
|
245
|
+
if !(keep_existing_output && output_exists(@feature_reports_reopt_default_output_files[idx]))
|
246
|
+
begin
|
247
|
+
reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx])
|
248
|
+
reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
|
249
|
+
if run_resilience
|
250
|
+
run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
|
251
|
+
if File.directory? @feature_reports_reopt_default_output_files[idx]
|
252
|
+
resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx])
|
253
|
+
else
|
254
|
+
resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx].sub!('.json', '_resilience.json'))
|
255
|
+
end
|
244
256
|
else
|
245
|
-
resilience_stats =
|
257
|
+
resilience_stats = nil
|
246
258
|
end
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
new_feature_report.save_json_report save_names[idx]
|
256
|
-
else
|
257
|
-
warn "Could not save feature reports - the number of save names provided did not match the number of feature reports"
|
259
|
+
new_feature_report = feature_adapter.update_feature_report(feature_report, reopt_output, @feature_reports_timeseries_default_output_files[idx], resilience_stats)
|
260
|
+
new_feature_reports.push(new_feature_report)
|
261
|
+
if !save_names.nil?
|
262
|
+
if save_names.length == feature_reports.length
|
263
|
+
new_feature_report.save save_names[idx]
|
264
|
+
else
|
265
|
+
warn 'Could not save feature reports - the number of save names provided did not match the number of feature reports'
|
266
|
+
end
|
258
267
|
end
|
268
|
+
rescue StandardError
|
269
|
+
@@logger.info("Could not optimize Feature Report #{feature_report.name} #{feature_report.id}")
|
259
270
|
end
|
260
|
-
|
261
|
-
|
271
|
+
else
|
272
|
+
puts('Output file already exists...skipping')
|
262
273
|
end
|
263
274
|
end
|
264
275
|
|
265
276
|
return new_feature_reports
|
266
277
|
end
|
267
278
|
|
279
|
+
# Checks whether a feature has already been run by determining if output files already exists (for rate limit issues and larger projects)
|
280
|
+
##
|
281
|
+
#
|
282
|
+
# [*parameters:*]
|
283
|
+
#
|
284
|
+
# * +output_file+ - _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.
|
285
|
+
# [*return:*] _Boolean_ - Returns true if file or nonempty directory exist
|
286
|
+
def output_exists(output_file)
|
287
|
+
res = false
|
288
|
+
if File.directory?(output_file) && !File.empty?(output_file)
|
289
|
+
res = true
|
290
|
+
elsif File.exist? output_file
|
291
|
+
res = true
|
292
|
+
end
|
293
|
+
|
294
|
+
return res
|
295
|
+
end
|
296
|
+
|
268
297
|
# Updates a ScenarioReport based on an optional set of \REopt Lite optimization assumptions.
|
269
298
|
##
|
270
299
|
#
|
@@ -276,22 +305,26 @@ module URBANopt # :nodoc:
|
|
276
305
|
# * +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.
|
277
306
|
#
|
278
307
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
|
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)
|
281
|
-
|
308
|
+
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, keep_existing_output: false)
|
309
|
+
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, keep_existing_output: keep_existing_output)
|
310
|
+
puts("KEEP EXISTING? #{keep_existing_output}")
|
311
|
+
# only do this if you have run feature reports
|
282
312
|
new_scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new
|
283
|
-
|
284
|
-
new_scenario_report.name = scenario_report.name
|
285
|
-
new_scenario_report.directory_name = scenario_report.directory_name
|
313
|
+
if !new_feature_reports.empty?
|
286
314
|
|
287
|
-
|
288
|
-
|
315
|
+
new_scenario_report.id = scenario_report.id
|
316
|
+
new_scenario_report.name = scenario_report.name
|
317
|
+
new_scenario_report.directory_name = scenario_report.directory_name
|
289
318
|
|
290
|
-
|
291
|
-
new_scenario_report.
|
292
|
-
|
293
|
-
|
294
|
-
|
319
|
+
timeseries_hash = { column_names: scenario_report.timeseries_csv.column_names }
|
320
|
+
new_scenario_report.timeseries_csv = URBANopt::Reporting::DefaultReports::TimeseriesCSV.new(timeseries_hash)
|
321
|
+
|
322
|
+
new_feature_reports.each do |feature_report|
|
323
|
+
new_scenario_report.add_feature_report(feature_report)
|
324
|
+
end
|
325
|
+
if !save_name_scenario_report.nil?
|
326
|
+
new_scenario_report.save save_name_scenario_report
|
327
|
+
end
|
295
328
|
end
|
296
329
|
return new_scenario_report
|
297
330
|
end
|