urbanopt-reopt 0.5.6 → 0.6.2
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/.rubocop.yml +2 -2
- data/CHANGELOG.md +28 -0
- data/Gemfile +1 -1
- data/Rakefile +2 -2
- data/docs/package-lock.json +19524 -1605
- data/docs/package.json +13 -6
- data/lib/urbanopt/reopt/feature_report_adapter.rb +58 -54
- data/lib/urbanopt/reopt/reopt_lite_api.rb +80 -48
- data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
- data/lib/urbanopt/reopt/reopt_post_processor.rb +75 -51
- data/lib/urbanopt/reopt/reopt_schema/reopt_input_schema.json +5 -0
- data/lib/urbanopt/reopt/reopt_schema/reopt_output_schema.json +5 -0
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +7 -7
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +60 -63
- data/lib/urbanopt/reopt/utilities.rb +106 -106
- data/lib/urbanopt/reopt/version.rb +1 -1
- data/urbanopt-reopt.gemspec +4 -7
- metadata +15 -29
data/docs/package.json
CHANGED
@@ -10,17 +10,24 @@
|
|
10
10
|
},
|
11
11
|
"author": "NREL",
|
12
12
|
"dependencies": {
|
13
|
-
"braces": "^3.0.2",
|
14
13
|
"highlight.js": "^10.4.1",
|
15
14
|
"json-schema-ref-parser": "^6.1.0",
|
16
15
|
"json-schema-view-js": "git+https://git@github.com/bgschiller/json-schema-view-js.git",
|
17
|
-
"
|
18
|
-
"vuepress": "^1.6.0",
|
19
|
-
"webpack-dev-middleware": "^3.6.0",
|
20
|
-
"yargs-parser": "^18.1.1"
|
16
|
+
"webpack-dev-middleware": "^3.6.0"
|
21
17
|
},
|
22
18
|
"devDependencies": {
|
19
|
+
"braces": "^3.0.2",
|
20
|
+
"chokidar": ">=3.5.1",
|
21
|
+
"fsevents": ">=2.3.2",
|
23
22
|
"gh-pages": "^2.0.1",
|
24
|
-
"ini": "^2.0.0"
|
23
|
+
"ini": "^2.0.0",
|
24
|
+
"is-svg": "4.3.1",
|
25
|
+
"lodash": "^4.17.21",
|
26
|
+
"postcss": "^8.2.15",
|
27
|
+
"serialize-javascript": "^5.0.1",
|
28
|
+
"ssri": "8.0.1",
|
29
|
+
"url-parse": "^1.5.1",
|
30
|
+
"vuepress": "^1.8.2",
|
31
|
+
"yargs-parser": "^18.1.1"
|
25
32
|
}
|
26
33
|
}
|
@@ -68,7 +68,7 @@ module URBANopt # :nodoc:
|
|
68
68
|
#
|
69
69
|
# [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
|
70
70
|
##
|
71
|
-
def reopt_json_from_feature_report(feature_report, reopt_assumptions_hash = nil)
|
71
|
+
def reopt_json_from_feature_report(feature_report, reopt_assumptions_hash = nil, groundmount_photovoltaic = nil)
|
72
72
|
name = feature_report.name.delete ' '
|
73
73
|
description = "feature_report_#{name}_#{feature_report.id}"
|
74
74
|
reopt_inputs = { Scenario: { Site: { ElectricTariff: { blended_monthly_demand_charges_us_dollars_per_kw: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], blended_monthly_rates_us_dollars_per_kwh: [0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13] }, LoadProfile: {}, Wind: { max_kw: 0 } } } }
|
@@ -99,15 +99,21 @@ module URBANopt # :nodoc:
|
|
99
99
|
reopt_inputs[:Scenario][:Site][:longitude] = feature_report.location.longitude_deg
|
100
100
|
|
101
101
|
# Parse Optional FeatureReport metrics - do not overwrite from assumptions file
|
102
|
-
if reopt_inputs[:Scenario][:Site][:roof_squarefeet].nil?
|
103
|
-
|
104
|
-
reopt_inputs[:Scenario][:Site][:roof_squarefeet] = feature_report.program.roof_area_sqft[:available_roof_area_sqft]
|
105
|
-
end
|
102
|
+
if reopt_inputs[:Scenario][:Site][:roof_squarefeet].nil? && !feature_report.program.roof_area_sqft.nil?
|
103
|
+
reopt_inputs[:Scenario][:Site][:roof_squarefeet] = feature_report.program.roof_area_sqft[:available_roof_area_sqft]
|
106
104
|
end
|
107
105
|
|
108
106
|
if reopt_inputs[:Scenario][:Site][:land_acres].nil?
|
109
|
-
|
110
|
-
|
107
|
+
# Check if ground-mount PV is specified with the Feature ID and take footprint area of PV
|
108
|
+
# constrain for REopt optimization
|
109
|
+
begin
|
110
|
+
if !groundmount_photovoltaic[feature_report.id].nil?
|
111
|
+
reopt_inputs[:Scenario][:Site][:land_acres] = groundmount_photovoltaic[feature_report.id] * 1.0 / 43560 # acres/sqft
|
112
|
+
# If no ground-mount PV associated with feature use site area as constrain for REopt optimization
|
113
|
+
elsif !feature_report.program.site_area_sqft.nil?
|
114
|
+
reopt_inputs[:Scenario][:Site][:land_acres] = feature_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
|
115
|
+
end
|
116
|
+
rescue StandardError
|
111
117
|
end
|
112
118
|
end
|
113
119
|
|
@@ -117,23 +123,23 @@ module URBANopt # :nodoc:
|
|
117
123
|
|
118
124
|
# Parse Load Profile
|
119
125
|
begin
|
120
|
-
#Convert kWh values in the timeseries CSV to kW
|
126
|
+
# Convert kWh values in the timeseries CSV to kW
|
121
127
|
col_num = feature_report.timeseries_csv.column_names.index('Electricity:Facility(kWh)')
|
122
128
|
t = CSV.read(feature_report.timeseries_csv.path, headers: true, converters: :numeric)
|
123
|
-
energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * feature_report.timesteps_per_hour || 0)
|
124
|
-
#Fill in missing timestep values with 0 if a full year is not provided
|
129
|
+
energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * feature_report.timesteps_per_hour || 0)) }
|
130
|
+
# Fill in missing timestep values with 0 if a full year is not provided
|
125
131
|
if energy_timeseries_kw.length < (feature_report.timesteps_per_hour * 8760)
|
126
|
-
start_date = Time.parse(t.by_col[
|
132
|
+
start_date = Time.parse(t.by_col['Datetime'][0])
|
127
133
|
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) /
|
128
|
-
((
|
129
|
-
end_date = Time.parse(t.by_col[
|
134
|
+
((60 / feature_report.timesteps_per_hour) * 60)).to_int
|
135
|
+
end_date = Time.parse(t.by_col['Datetime'][-1])
|
130
136
|
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) /
|
131
|
-
((
|
132
|
-
energy_timeseries_kw = [0.0]*(start_ts-1) + energy_timeseries_kw + [0.0]*((feature_report.timesteps_per_hour * 8760) - end_ts)
|
137
|
+
((60 / feature_report.timesteps_per_hour) * 60)).to_int
|
138
|
+
energy_timeseries_kw = [0.0] * (start_ts - 1) + energy_timeseries_kw + [0.0] * ((feature_report.timesteps_per_hour * 8760) - end_ts)
|
133
139
|
end
|
134
|
-
#Clip to one non-leap year's worth of data
|
135
|
-
energy_timeseries_kw = energy_timeseries_kw.map { |e| e
|
136
|
-
#Convert from the OpenDSS resolution to the REopt Lite resolution, if necessary
|
140
|
+
# Clip to one non-leap year's worth of data
|
141
|
+
energy_timeseries_kw = energy_timeseries_kw.map { |e| e || 0 }[0, (feature_report.timesteps_per_hour * 8760)]
|
142
|
+
# Convert from the OpenDSS resolution to the REopt Lite resolution, if necessary
|
137
143
|
rescue StandardError
|
138
144
|
@@logger.error("Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}")
|
139
145
|
raise "Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}"
|
@@ -142,8 +148,7 @@ module URBANopt # :nodoc:
|
|
142
148
|
# Convert load to REopt Resolution
|
143
149
|
begin
|
144
150
|
reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = convert_powerflow_resolution(energy_timeseries_kw, feature_report.timesteps_per_hour, reopt_inputs[:Scenario][:time_steps_per_hour])
|
145
|
-
|
146
|
-
rescue
|
151
|
+
rescue StandardError
|
147
152
|
@@logger.error("Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:Scenario][:time_steps_per_hour]}")
|
148
153
|
raise "Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:Scenario][:time_steps_per_hour]}"
|
149
154
|
end
|
@@ -151,9 +156,9 @@ module URBANopt # :nodoc:
|
|
151
156
|
if reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps].nil?
|
152
157
|
n_top_values = 100
|
153
158
|
tmp1 = reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw]
|
154
|
-
tmp2 = tmp1.each_index.max_by(n_top_values*reopt_inputs[:Scenario][:time_steps_per_hour]){|i| tmp1[i]}
|
159
|
+
tmp2 = tmp1.each_index.max_by(n_top_values * reopt_inputs[:Scenario][:time_steps_per_hour]) { |i| tmp1[i] }
|
155
160
|
for i in (0...tmp2.count)
|
156
|
-
|
161
|
+
tmp2[i] += 1
|
157
162
|
end
|
158
163
|
reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps] = tmp2
|
159
164
|
end
|
@@ -165,7 +170,6 @@ module URBANopt # :nodoc:
|
|
165
170
|
return reopt_inputs
|
166
171
|
end
|
167
172
|
|
168
|
-
|
169
173
|
##
|
170
174
|
# Update a FeatureReport from a \REopt Lite response
|
171
175
|
#
|
@@ -177,7 +181,7 @@ module URBANopt # :nodoc:
|
|
177
181
|
#
|
178
182
|
# [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport.
|
179
183
|
##
|
180
|
-
def update_feature_report(feature_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil)
|
184
|
+
def update_feature_report(feature_report, reopt_output, timeseries_csv_path = nil, resilience_stats = nil)
|
181
185
|
# Check if the \REopt Lite response is valid
|
182
186
|
if reopt_output['outputs']['Scenario']['status'] != 'optimal'
|
183
187
|
@@logger.info("Warning cannot Feature Report #{feature_report.name} #{feature_report.id} - REopt optimization was non-optimal")
|
@@ -210,29 +214,40 @@ module URBANopt # :nodoc:
|
|
210
214
|
feature_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
|
211
215
|
end
|
212
216
|
|
213
|
-
if reopt_output['outputs']['Scenario']['Site']['PV'].
|
217
|
+
if reopt_output['outputs']['Scenario']['Site']['PV'].instance_of?(Hash)
|
214
218
|
reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
|
215
219
|
elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
|
216
220
|
reopt_output['outputs']['Scenario']['Site']['PV'] = []
|
217
221
|
end
|
218
222
|
|
223
|
+
# Store the PV name and location in a hash
|
224
|
+
location = {}
|
225
|
+
# Check whether multi PV assumption input file is used or single PV
|
226
|
+
if reopt_output['inputs']['Scenario']['Site']['PV'].is_a?(Array)
|
227
|
+
reopt_output['inputs']['Scenario']['Site']['PV'].each do |pv|
|
228
|
+
location[pv['pv_name']] = pv['location']
|
229
|
+
end
|
230
|
+
else
|
231
|
+
location[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['location']
|
232
|
+
end
|
233
|
+
|
219
234
|
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
220
|
-
feature_report.distributed_generation.add_tech 'solar_pv',
|
235
|
+
feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i, location: location[pv['pv_name']] })
|
221
236
|
end
|
222
237
|
|
223
238
|
wind = reopt_output['outputs']['Scenario']['Site']['Wind']
|
224
|
-
if !wind['size_kw'].nil?
|
225
|
-
feature_report.distributed_generation.add_tech 'wind',
|
239
|
+
if !wind['size_kw'].nil? && (wind['size_kw'] != 0)
|
240
|
+
feature_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new({ size_kw: (wind['size_kw'] || 0) })
|
226
241
|
end
|
227
242
|
|
228
243
|
generator = reopt_output['outputs']['Scenario']['Site']['Generator']
|
229
|
-
if !generator['size_kw'].nil?
|
230
|
-
feature_report.distributed_generation.add_tech 'generator',
|
244
|
+
if !generator['size_kw'].nil? && (generator['size_kw'] != 0)
|
245
|
+
feature_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new({ size_kw: (generator['size_kw'] || 0) })
|
231
246
|
end
|
232
247
|
|
233
248
|
storage = reopt_output['outputs']['Scenario']['Site']['Storage']
|
234
|
-
if !storage['size_kw'].nil?
|
235
|
-
feature_report.distributed_generation.add_tech 'storage',
|
249
|
+
if !storage['size_kw'].nil? && (storage['size_kw'] != 0)
|
250
|
+
feature_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new({ size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
|
236
251
|
end
|
237
252
|
|
238
253
|
generation_timeseries_kwh = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
@@ -240,28 +255,18 @@ module URBANopt # :nodoc:
|
|
240
255
|
|
241
256
|
unless reopt_output['outputs']['Scenario']['Site']['PV'].nil?
|
242
257
|
reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
|
243
|
-
if (pv['size_kw'] || 0) > 0
|
244
|
-
|
245
|
-
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
|
246
|
-
end
|
258
|
+
if (pv['size_kw'] || 0) > 0 && !pv['year_one_power_production_series_kw'].nil?
|
259
|
+
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
|
247
260
|
end
|
248
|
-
|
261
|
+
end
|
249
262
|
end
|
250
263
|
|
251
|
-
|
252
|
-
|
253
|
-
if !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
|
254
|
-
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
|
255
|
-
end
|
256
|
-
end
|
264
|
+
if !reopt_output['outputs']['Scenario']['Site']['Wind'].nil? && ((reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0) && !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
|
265
|
+
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
|
257
266
|
end
|
258
267
|
|
259
|
-
|
260
|
-
|
261
|
-
if !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
|
262
|
-
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
|
263
|
-
end
|
264
|
-
end
|
268
|
+
if !reopt_output['outputs']['Scenario']['Site']['Generator'].nil? && ((reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0) > 0) && !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
|
269
|
+
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
|
265
270
|
end
|
266
271
|
|
267
272
|
$generation_timeseries_kwh = generation_timeseries_kwh.to_a[0] || [0] * (8760 * feature_report.timesteps_per_hour)
|
@@ -359,7 +364,6 @@ module URBANopt # :nodoc:
|
|
359
364
|
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:ToLoad(kw)')
|
360
365
|
end
|
361
366
|
|
362
|
-
|
363
367
|
$pv_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:ToGrid(kw)')
|
364
368
|
if $pv_to_grid_col.nil?
|
365
369
|
$pv_to_grid_col = feature_report.timeseries_csv.column_names.length
|
@@ -441,14 +445,14 @@ module URBANopt # :nodoc:
|
|
441
445
|
start_ts = (
|
442
446
|
(
|
443
447
|
((start_date.yday - 1) * 60.0 * 60.0 * 24) +
|
444
|
-
((
|
445
|
-
(start_date.min * 60.0) + start_date.sec
|
446
|
-
((
|
448
|
+
((start_date.hour - 1) * 60.0 * 60.0) +
|
449
|
+
(start_date.min * 60.0) + start_date.sec) /
|
450
|
+
((60 / feature_report.timesteps_per_hour) * 60)
|
447
451
|
).to_int
|
448
452
|
|
449
453
|
mod_data = old_data.map.with_index do |x, i|
|
450
454
|
if i > 0
|
451
|
-
modrow(x, start_ts + i -1)
|
455
|
+
modrow(x, start_ts + i - 1)
|
452
456
|
else
|
453
457
|
x
|
454
458
|
end
|
@@ -97,6 +97,7 @@ module URBANopt # :nodoc:
|
|
97
97
|
if @use_localhost
|
98
98
|
return URI.parse("http://127.0.0.1:8000/v1/job/#{run_uuid}/results")
|
99
99
|
end
|
100
|
+
|
100
101
|
return URI.parse("https://developer.nrel.gov/api/reopt/v1/job/#{run_uuid}/results?api_key=#{@nrel_developer_key}")
|
101
102
|
end
|
102
103
|
|
@@ -114,6 +115,7 @@ module URBANopt # :nodoc:
|
|
114
115
|
if @use_localhost
|
115
116
|
return URI.parse("http://127.0.0.1:8000/v1/job/#{run_uuid}/resilience_stats")
|
116
117
|
end
|
118
|
+
|
117
119
|
return URI.parse("https://developer.nrel.gov/api/reopt/v1/job/#{run_uuid}/resilience_stats?api_key=#{@nrel_developer_key}")
|
118
120
|
end
|
119
121
|
|
@@ -122,11 +124,34 @@ module URBANopt # :nodoc:
|
|
122
124
|
tries = 0
|
123
125
|
while tries < max_tries
|
124
126
|
begin
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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. The URBANopt CLI flag --reopt-keep-existing can be used to resume the optimization')
|
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
|
+
# display error messages
|
137
|
+
json_res = JSON.parse(result.body, allow_nan: true)
|
138
|
+
json_res['messages'].delete('warnings') if json_res['messages']['warnings']
|
139
|
+
json_res['messages'].delete('Deprecations') if json_res['messages']['Deprecations']
|
140
|
+
if json_res['messages']
|
141
|
+
@@logger.error("MESSAGES: #{json_res['messages']}")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
tries = max_tries
|
145
|
+
rescue StandardError => e
|
146
|
+
@@logger.debug("error from REopt lite API: #{e}")
|
147
|
+
if tries + 1 < max_tries
|
148
|
+
@@logger.debug('trying again...')
|
149
|
+
else
|
150
|
+
@@logger.debug('max tries reached!')
|
151
|
+
return
|
152
|
+
end
|
153
|
+
tries += 1
|
154
|
+
end
|
130
155
|
end
|
131
156
|
return result
|
132
157
|
end
|
@@ -148,11 +173,11 @@ module URBANopt # :nodoc:
|
|
148
173
|
http.use_ssl = true
|
149
174
|
end
|
150
175
|
|
151
|
-
|
152
|
-
|
176
|
+
post_request = Net::HTTP::Post.new(@uri_submit, header)
|
177
|
+
post_request.body = ::JSON.generate(data, allow_nan: true)
|
153
178
|
|
154
179
|
# Send the request
|
155
|
-
response = make_request(http,
|
180
|
+
response = make_request(http, post_request)
|
156
181
|
|
157
182
|
if !response.is_a?(Net::HTTPSuccess)
|
158
183
|
@@logger.error('Check_connection Failed')
|
@@ -175,7 +200,6 @@ module URBANopt # :nodoc:
|
|
175
200
|
# [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
|
176
201
|
##
|
177
202
|
def resilience_request(run_uuid, filename)
|
178
|
-
|
179
203
|
if File.directory? filename
|
180
204
|
if run_uuid.nil?
|
181
205
|
run_uuid = 'error'
|
@@ -187,54 +211,59 @@ module URBANopt # :nodoc:
|
|
187
211
|
@@logger.info("REopt results saved to #{filename}")
|
188
212
|
end
|
189
213
|
|
190
|
-
#Submit Job
|
214
|
+
# Submit Job
|
191
215
|
@@logger.info("Submitting Resilience Statistics job for #{run_uuid}")
|
192
216
|
header = { 'Content-Type' => 'application/json' }
|
193
217
|
http = Net::HTTP.new(@uri_submit_outagesimjob.host, @uri_submit_outagesimjob.port)
|
194
218
|
if !@use_localhost
|
195
219
|
http.use_ssl = true
|
196
220
|
end
|
197
|
-
|
198
|
-
|
199
|
-
submit_response = make_request(http,
|
200
|
-
@@logger.
|
221
|
+
post_request = Net::HTTP::Post.new(@uri_submit_outagesimjob, header)
|
222
|
+
post_request.body = ::JSON.generate({ 'run_uuid' => run_uuid, 'bau' => false }, allow_nan: true)
|
223
|
+
submit_response = make_request(http, post_request)
|
224
|
+
@@logger.debug(submit_response.body)
|
201
225
|
|
202
|
-
#Fetch Results
|
226
|
+
# Fetch Results
|
203
227
|
uri = uri_resilience(run_uuid)
|
204
228
|
http = Net::HTTP.new(uri.host, uri.port)
|
205
229
|
if !@use_localhost
|
206
230
|
http.use_ssl = true
|
207
231
|
end
|
208
232
|
|
233
|
+
# Wait a few seconds for the REopt database to update before GETing results
|
234
|
+
sleep 5
|
235
|
+
get_request = Net::HTTP::Get.new(uri.request_uri)
|
236
|
+
response = make_request(http, get_request, 8)
|
237
|
+
|
238
|
+
# Set a limit on retries when 404s are returned from REopt API
|
209
239
|
elapsed_time = 0
|
210
240
|
max_elapsed_time = 60 * 5
|
211
241
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
response = make_request(http, request)
|
242
|
+
# If database still hasn't updated, wait a little longer and try again
|
243
|
+
while (elapsed_time < max_elapsed_time) & (response.code == '404')
|
244
|
+
response = make_request(http, get_request)
|
245
|
+
@@logger.warn('GET request was too fast for REOpt-Lite API. Retrying...')
|
217
246
|
elapsed_time += 5
|
218
247
|
sleep 5
|
219
248
|
end
|
220
249
|
|
221
|
-
data = JSON.parse(response.body)
|
222
|
-
text =
|
250
|
+
data = JSON.parse(response.body, allow_nan: true)
|
251
|
+
text = JSON.pretty_generate(data)
|
223
252
|
begin
|
224
253
|
File.open(filename, 'w+') do |f|
|
225
254
|
f.puts(text)
|
226
255
|
end
|
227
|
-
rescue
|
228
|
-
@@logger.
|
256
|
+
rescue StandardError => e
|
257
|
+
@@logger.error("Cannot write - #{filename}")
|
258
|
+
@@logger.error("ERROR: #{e}")
|
229
259
|
end
|
230
260
|
|
231
|
-
if response.code ==
|
261
|
+
if response.code == '200'
|
232
262
|
return data
|
233
263
|
end
|
234
264
|
|
235
|
-
@@logger.
|
265
|
+
@@logger.error("Error from REopt API - #{data['Error']}")
|
236
266
|
return {}
|
237
|
-
|
238
267
|
end
|
239
268
|
|
240
269
|
##
|
@@ -261,14 +290,18 @@ module URBANopt # :nodoc:
|
|
261
290
|
if !@use_localhost
|
262
291
|
http.use_ssl = true
|
263
292
|
end
|
264
|
-
|
265
|
-
|
293
|
+
post_request = Net::HTTP::Post.new(@uri_submit, header)
|
294
|
+
post_request.body = ::JSON.generate(reopt_input, allow_nan: true)
|
266
295
|
|
267
296
|
# Send the request
|
268
|
-
response = make_request(http,
|
297
|
+
response = make_request(http, post_request)
|
298
|
+
if !response.is_a?(Net::HTTPSuccess)
|
299
|
+
@@logger.error('make_request Failed')
|
300
|
+
raise 'Check_connection Failed'
|
301
|
+
end
|
269
302
|
|
270
303
|
# Get UUID
|
271
|
-
run_uuid = JSON.parse(response.body, allow_nan:true)['run_uuid']
|
304
|
+
run_uuid = JSON.parse(response.body, allow_nan: true)['run_uuid']
|
272
305
|
|
273
306
|
if File.directory? filename
|
274
307
|
if run_uuid.nil?
|
@@ -281,12 +314,11 @@ module URBANopt # :nodoc:
|
|
281
314
|
@@logger.info("REopt results saved to #{filename}")
|
282
315
|
end
|
283
316
|
|
284
|
-
text =
|
317
|
+
text = JSON.parse(response.body, allow_nan: true)
|
285
318
|
if response.code != '201'
|
286
319
|
File.open(filename, 'w+') do |f|
|
287
|
-
f.puts(text)
|
320
|
+
f.puts(JSON.pretty_generate(text))
|
288
321
|
end
|
289
|
-
@@logger.info("Cannot write - #{filename}")
|
290
322
|
raise "Error in REopt optimization post - see #{filename}"
|
291
323
|
end
|
292
324
|
|
@@ -298,17 +330,17 @@ module URBANopt # :nodoc:
|
|
298
330
|
http.use_ssl = true
|
299
331
|
end
|
300
332
|
|
301
|
-
|
333
|
+
get_request = Net::HTTP::Get.new(uri.request_uri)
|
302
334
|
|
303
335
|
while status == 'Optimizing...'
|
304
|
-
response = make_request(http,
|
336
|
+
response = make_request(http, get_request)
|
305
337
|
|
306
|
-
data = JSON.parse(response.body, allow_nan:true)
|
338
|
+
data = JSON.parse(response.body, allow_nan: true)
|
307
339
|
|
308
|
-
if data['outputs']['Scenario']['Site']['PV'].
|
340
|
+
if data['outputs']['Scenario']['Site']['PV'].is_a?(Array)
|
309
341
|
pv_sizes = 0
|
310
342
|
data['outputs']['Scenario']['Site']['PV'].each do |x|
|
311
|
-
pv_sizes
|
343
|
+
pv_sizes += x['size_kw'].to_f
|
312
344
|
end
|
313
345
|
else
|
314
346
|
pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
|
@@ -323,13 +355,13 @@ module URBANopt # :nodoc:
|
|
323
355
|
_tries = 0
|
324
356
|
(check_complete = sizes == 0) && ((data['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0) > 0)
|
325
357
|
while (_tries < _max_retry) && check_complete
|
326
|
-
sleep
|
327
|
-
response = make_request(http,
|
328
|
-
data = JSON.parse(response.body, allow_nan:true)
|
329
|
-
if data['outputs']['Scenario']['Site']['PV'].
|
358
|
+
sleep 3
|
359
|
+
response = make_request(http, get_request)
|
360
|
+
data = JSON.parse(response.body, allow_nan: true)
|
361
|
+
if data['outputs']['Scenario']['Site']['PV'].is_a?(Array)
|
330
362
|
pv_sizes = 0
|
331
363
|
data['outputs']['Scenario']['Site']['PV'].each do |x|
|
332
|
-
pv_sizes
|
364
|
+
pv_sizes += x['size_kw'].to_f
|
333
365
|
end
|
334
366
|
else
|
335
367
|
pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
|
@@ -339,14 +371,14 @@ module URBANopt # :nodoc:
|
|
339
371
|
_tries += 1
|
340
372
|
end
|
341
373
|
|
342
|
-
data = JSON.parse(response.body)
|
343
|
-
text =
|
374
|
+
data = JSON.parse(response.body, allow_nan: true)
|
375
|
+
text = JSON.pretty_generate(data)
|
344
376
|
begin
|
345
377
|
File.open(filename, 'w+') do |f|
|
346
378
|
f.puts(text)
|
347
379
|
end
|
348
|
-
rescue
|
349
|
-
@@logger.
|
380
|
+
rescue StandardError
|
381
|
+
@@logger.error("Cannot write - #{filename}")
|
350
382
|
end
|
351
383
|
|
352
384
|
if status == 'optimal'
|