urbanopt-reopt 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|