urbanopt-reopt 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/nightly_ci_build.yml +1 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +1 -1
- data/RDOC_MAIN.md +6 -6
- data/README.md +6 -6
- data/docs/README.md +6 -6
- data/docs/schemas/reopt-input-schema.md +25 -23
- data/docs/schemas/reopt-output-schema.md +7 -7
- data/index.md +9 -9
- data/lib/urbanopt/reopt/feature_report_adapter.rb +229 -196
- data/lib/urbanopt/reopt/reopt_lite_api.rb +76 -37
- data/lib/urbanopt/reopt/reopt_post_processor.rb +11 -10
- data/lib/urbanopt/reopt/reopt_schema/reopt_input_schema.json +1125 -1105
- data/lib/urbanopt/reopt/reopt_schema/reopt_output_schema.json +448 -523
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +260 -225
- data/lib/urbanopt/reopt/version.rb +1 -1
- data/urbanopt-reopt.gemspec +1 -1
- metadata +4 -4
@@ -29,8 +29,8 @@ module URBANopt # :nodoc:
|
|
29
29
|
def initialize(nrel_developer_key = nil, use_localhost = false)
|
30
30
|
@use_localhost = use_localhost
|
31
31
|
if @use_localhost
|
32
|
-
@uri_submit = URI.parse('http//:127.0.0.1:8000/
|
33
|
-
@uri_submit_outagesimjob = URI.parse('http//:127.0.0.1:8000/
|
32
|
+
@uri_submit = URI.parse('http//:127.0.0.1:8000/v3/job/')
|
33
|
+
@uri_submit_outagesimjob = URI.parse('http//:127.0.0.1:8000/v3/outagesimjob/')
|
34
34
|
else
|
35
35
|
if [nil, '', '<insert your key here>'].include? nrel_developer_key
|
36
36
|
if [nil, '', '<insert your key here>'].include? DEVELOPER_NREL_KEY
|
@@ -40,8 +40,8 @@ module URBANopt # :nodoc:
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
@nrel_developer_key = nrel_developer_key
|
43
|
-
@uri_submit = URI.parse("https://developer.nrel.gov/api/reopt/
|
44
|
-
@uri_submit_outagesimjob = URI.parse("https://developer.nrel.gov/api/reopt/
|
43
|
+
@uri_submit = URI.parse("https://developer.nrel.gov/api/reopt/v3/job?api_key=#{@nrel_developer_key}")
|
44
|
+
@uri_submit_outagesimjob = URI.parse("https://developer.nrel.gov/api/reopt/v3/outagesimjob?api_key=#{@nrel_developer_key}")
|
45
45
|
# initialize @@logger
|
46
46
|
@@logger ||= URBANopt::REopt.reopt_logger
|
47
47
|
end
|
@@ -59,10 +59,10 @@ module URBANopt # :nodoc:
|
|
59
59
|
##
|
60
60
|
def uri_results(run_uuid) # :nodoc:
|
61
61
|
if @use_localhost
|
62
|
-
return URI.parse("http://127.0.0.1:8000/
|
62
|
+
return URI.parse("http://127.0.0.1:8000/v3/job/#{run_uuid}/results")
|
63
63
|
end
|
64
64
|
|
65
|
-
return URI.parse("https://developer.nrel.gov/api/reopt/
|
65
|
+
return URI.parse("https://developer.nrel.gov/api/reopt/v3/job/#{run_uuid}/results?api_key=#{@nrel_developer_key}")
|
66
66
|
end
|
67
67
|
|
68
68
|
##
|
@@ -77,10 +77,10 @@ module URBANopt # :nodoc:
|
|
77
77
|
##
|
78
78
|
def uri_resilience(run_uuid) # :nodoc:
|
79
79
|
if @use_localhost
|
80
|
-
return URI.parse("http://127.0.0.1:8000/
|
80
|
+
return URI.parse("http://127.0.0.1:8000/v3/job/#{run_uuid}/resilience_stats")
|
81
81
|
end
|
82
82
|
|
83
|
-
return URI.parse("https://developer.nrel.gov/api/reopt/
|
83
|
+
return URI.parse("https://developer.nrel.gov/api/reopt/v3/job/#{run_uuid}/resilience_stats?api_key=#{@nrel_developer_key}")
|
84
84
|
end
|
85
85
|
|
86
86
|
def make_request(http, req, max_tries = 3)
|
@@ -199,21 +199,21 @@ module URBANopt # :nodoc:
|
|
199
199
|
http.use_ssl = true
|
200
200
|
end
|
201
201
|
|
202
|
-
# Wait
|
203
|
-
sleep
|
202
|
+
# Wait for the REopt API before attempting to GET results
|
203
|
+
sleep 30
|
204
204
|
get_request = Net::HTTP::Get.new(uri.request_uri)
|
205
205
|
response = make_request(http, get_request, 8)
|
206
206
|
|
207
207
|
# Set a limit on retries when 404s are returned from REopt API
|
208
208
|
elapsed_time = 0
|
209
|
-
max_elapsed_time = 60 *
|
209
|
+
max_elapsed_time = 60 * 15
|
210
210
|
|
211
|
-
# If database still hasn't updated, wait
|
211
|
+
# If database still hasn't updated, wait longer and try again
|
212
212
|
while (elapsed_time < max_elapsed_time) && (response && response.code == '404')
|
213
213
|
response = make_request(http, get_request)
|
214
214
|
@@logger.warn('GET request was too fast for REOpt-API. Retrying...')
|
215
|
-
elapsed_time +=
|
216
|
-
sleep
|
215
|
+
elapsed_time += 15
|
216
|
+
sleep 15
|
217
217
|
end
|
218
218
|
|
219
219
|
data = JSON.parse(response.body, allow_nan: true)
|
@@ -249,7 +249,7 @@ module URBANopt # :nodoc:
|
|
249
249
|
# [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
|
250
250
|
##
|
251
251
|
def reopt_request(reopt_input, filename)
|
252
|
-
description = reopt_input[:
|
252
|
+
description = reopt_input[:description]
|
253
253
|
|
254
254
|
@@logger.info("Submitting #{description} to REopt API")
|
255
255
|
|
@@ -301,45 +301,84 @@ module URBANopt # :nodoc:
|
|
301
301
|
|
302
302
|
get_request = Net::HTTP::Get.new(uri.request_uri)
|
303
303
|
|
304
|
+
counter = 0
|
304
305
|
while status == 'Optimizing...'
|
305
306
|
response = make_request(http, get_request)
|
306
307
|
|
307
308
|
data = JSON.parse(response.body, allow_nan: true)
|
308
309
|
|
309
|
-
if data['outputs']['
|
310
|
+
if !data['outputs']['PV']
|
310
311
|
pv_sizes = 0
|
311
|
-
|
312
|
-
pv_sizes += x['size_kw'].to_f
|
313
|
-
end
|
312
|
+
sizes = 0
|
314
313
|
else
|
315
|
-
|
314
|
+
# there should be results in there now
|
315
|
+
if data['outputs']['PV'].is_a?(Array)
|
316
|
+
pv_sizes = 0
|
317
|
+
data['outputs']['PV'].each do |x|
|
318
|
+
pv_sizes += x['size_kw'].to_f
|
319
|
+
end
|
320
|
+
else
|
321
|
+
data['outputs'].each do |energy_source, data|
|
322
|
+
if data.is_a?(Hash) && data.key?('size_kw')
|
323
|
+
@@logger.debug("#{energy_source}: size_kw = #{data['size_kw'].to_f}")
|
324
|
+
sizes += data['size_kw'].to_f
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
status = data['status']
|
331
|
+
@@logger.debug("STATUS: #{status}")
|
332
|
+
|
333
|
+
if status == 'error'
|
334
|
+
puts "response.code: #{response.code}"
|
335
|
+
puts "message: #{response.message}"
|
336
|
+
error_message = data['messages']['errors']
|
337
|
+
raise "Error from REopt API - #{error_message}"
|
316
338
|
end
|
317
|
-
sizes = pv_sizes + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
|
318
|
-
status = data['outputs']['Scenario']['status']
|
319
339
|
|
320
|
-
sleep
|
340
|
+
sleep 15
|
321
341
|
end
|
322
342
|
|
323
343
|
max_retry = 5
|
324
344
|
tries = 0
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
345
|
+
|
346
|
+
check_complete = sizes == 0
|
347
|
+
# I don't know what this line does:
|
348
|
+
# (((data['outputs'] && data['outputs'].key?('Financial') && data['outputs']['Financial']['npv']) || 0) > 0)
|
349
|
+
|
350
|
+
if check_complete
|
351
|
+
@@logger.info('sizes are 0...checking optimization complete')
|
352
|
+
|
353
|
+
while (tries < max_retry) && check_complete
|
354
|
+
sleep 3
|
355
|
+
response = make_request(http, get_request)
|
356
|
+
data = JSON.parse(response.body, allow_nan: true)
|
357
|
+
|
358
|
+
if data['outputs'].key?('PV') && data['outputs']['PV'].is_a?(Array)
|
359
|
+
pv_sizes = 0
|
360
|
+
data['outputs']['PV'].each do |x|
|
361
|
+
pv_sizes += x['size_kw'].to_f
|
362
|
+
end
|
363
|
+
else
|
364
|
+
data['outputs'].each do |energy_source, data|
|
365
|
+
if data.is_a?(Hash) && data.key?('size_kw')
|
366
|
+
@@logger.debug("#{energy_source}: size_kw = #{data['size_kw'].to_f}")
|
367
|
+
sizes += data['size_kw'].to_f
|
368
|
+
end
|
369
|
+
end
|
334
370
|
end
|
335
|
-
|
336
|
-
|
371
|
+
|
372
|
+
# I don't understand this line fully:
|
373
|
+
#(check_complete = sizes == 0) && ((data['outputs']['Financial']['npv'] || 0) > 0)
|
374
|
+
|
375
|
+
check_complete = sizes == 0
|
376
|
+
tries += 1
|
337
377
|
end
|
338
|
-
sizes = pv_sizes + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
|
339
|
-
(check_complete = sizes == 0) && ((data['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0) > 0)
|
340
|
-
tries += 1
|
341
378
|
end
|
342
379
|
|
380
|
+
@@logger.info('REopt optimization complete and processed')
|
381
|
+
|
343
382
|
data = JSON.parse(response.body, allow_nan: true)
|
344
383
|
text = JSON.pretty_generate(data)
|
345
384
|
begin
|
@@ -7,6 +7,7 @@ require 'bundler/setup'
|
|
7
7
|
require 'urbanopt/reporting/default_reports'
|
8
8
|
require 'urbanopt/reopt/reopt_logger'
|
9
9
|
require 'csv'
|
10
|
+
require 'json'
|
10
11
|
|
11
12
|
module URBANopt # :nodoc:
|
12
13
|
module REopt # :nodoc:
|
@@ -32,7 +33,7 @@ module URBANopt # :nodoc:
|
|
32
33
|
end
|
33
34
|
@nrel_developer_key = nrel_developer_key
|
34
35
|
@localhost = localhost
|
35
|
-
@reopt_base_post = {
|
36
|
+
@reopt_base_post = { ElectricTariff: {}, ElectricLoad: {}, Wind: { max_kw: 0 } }
|
36
37
|
|
37
38
|
@scenario_reopt_default_output_file = nil
|
38
39
|
@scenario_timeseries_default_output_file = nil
|
@@ -92,7 +93,7 @@ module URBANopt # :nodoc:
|
|
92
93
|
#
|
93
94
|
# [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
|
94
95
|
##
|
95
|
-
def run_feature_report(feature_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience:
|
96
|
+
def run_feature_report(feature_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: false)
|
96
97
|
api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
|
97
98
|
adapter = URBANopt::REopt::FeatureReportAdapter.new
|
98
99
|
|
@@ -103,7 +104,7 @@ module URBANopt # :nodoc:
|
|
103
104
|
reopt_output = api.reopt_request(reopt_input, reopt_output_file)
|
104
105
|
@@logger.debug("REOpt output file: #{reopt_output_file}")
|
105
106
|
if run_resilience
|
106
|
-
run_uuid = reopt_output['outputs']['
|
107
|
+
run_uuid = reopt_output['outputs']['run_uuid']
|
107
108
|
if File.directory? reopt_output_file
|
108
109
|
resilience_stats = api.resilience_request(run_uuid, reopt_output_file)
|
109
110
|
else
|
@@ -112,6 +113,7 @@ module URBANopt # :nodoc:
|
|
112
113
|
else
|
113
114
|
resilience_stats = nil
|
114
115
|
end
|
116
|
+
|
115
117
|
result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path, resilience_stats)
|
116
118
|
if !save_name.nil?
|
117
119
|
result.save save_name
|
@@ -131,8 +133,7 @@ module URBANopt # :nodoc:
|
|
131
133
|
# * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
|
132
134
|
#
|
133
135
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
|
134
|
-
def run_scenario_report(scenario_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience:
|
135
|
-
puts 'run scenario report'
|
136
|
+
def run_scenario_report(scenario_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: false, community_photovoltaic: nil)
|
136
137
|
@save_assumptions_filepath = false
|
137
138
|
if !reopt_assumptions_hash.nil?
|
138
139
|
@scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
|
@@ -150,10 +151,10 @@ module URBANopt # :nodoc:
|
|
150
151
|
adapter = URBANopt::REopt::ScenarioReportAdapter.new
|
151
152
|
|
152
153
|
reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash, community_photovoltaic)
|
153
|
-
|
154
154
|
reopt_output = api.reopt_request(reopt_input, @scenario_reopt_default_output_file)
|
155
|
+
|
155
156
|
if run_resilience
|
156
|
-
run_uuid = reopt_output['outputs']['
|
157
|
+
run_uuid = reopt_output['outputs']['run_uuid']
|
157
158
|
if File.directory? @scenario_reopt_default_output_file
|
158
159
|
resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file)
|
159
160
|
else
|
@@ -187,7 +188,7 @@ module URBANopt # :nodoc:
|
|
187
188
|
# * +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.
|
188
189
|
#
|
189
190
|
# [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
|
190
|
-
def run_feature_reports(feature_reports:, reopt_assumptions_hashes: [], reopt_output_files: [], timeseries_csv_paths: [], save_names: nil, run_resilience:
|
191
|
+
def run_feature_reports(feature_reports:, reopt_assumptions_hashes: [], reopt_output_files: [], timeseries_csv_paths: [], save_names: nil, run_resilience: false, keep_existing_output: false, groundmount_photovoltaic: nil)
|
191
192
|
if !reopt_assumptions_hashes.empty?
|
192
193
|
@feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
|
193
194
|
end
|
@@ -222,7 +223,7 @@ module URBANopt # :nodoc:
|
|
222
223
|
reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx], groundmount_photovoltaic)
|
223
224
|
reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
|
224
225
|
if run_resilience
|
225
|
-
run_uuid = reopt_output['outputs']['
|
226
|
+
run_uuid = reopt_output['outputs']['run_uuid']
|
226
227
|
if File.directory? @feature_reports_reopt_default_output_files[idx]
|
227
228
|
resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx])
|
228
229
|
else
|
@@ -281,7 +282,7 @@ module URBANopt # :nodoc:
|
|
281
282
|
# * +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.
|
282
283
|
#
|
283
284
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
|
284
|
-
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:
|
285
|
+
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: false, keep_existing_output: false, groundmount_photovoltaic: nil)
|
285
286
|
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, groundmount_photovoltaic: groundmount_photovoltaic)
|
286
287
|
|
287
288
|
# only do this if you have run feature reports
|