urbanopt-reopt 0.6.1 → 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/CHANGELOG.md +9 -1
- data/docs/package-lock.json +6084 -890
- data/docs/package.json +4 -1
- data/lib/urbanopt/reopt/feature_report_adapter.rb +25 -4
- data/lib/urbanopt/reopt/reopt_lite_api.rb +30 -12
- data/lib/urbanopt/reopt/reopt_post_processor.rb +8 -7
- 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_report_adapter.rb +18 -4
- data/lib/urbanopt/reopt/version.rb +1 -1
- data/urbanopt-reopt.gemspec +1 -1
- metadata +4 -4
data/docs/package.json
CHANGED
@@ -13,7 +13,6 @@
|
|
13
13
|
"highlight.js": "^10.4.1",
|
14
14
|
"json-schema-ref-parser": "^6.1.0",
|
15
15
|
"json-schema-view-js": "git+https://git@github.com/bgschiller/json-schema-view-js.git",
|
16
|
-
"vuepress": "^1.8.2",
|
17
16
|
"webpack-dev-middleware": "^3.6.0"
|
18
17
|
},
|
19
18
|
"devDependencies": {
|
@@ -23,8 +22,12 @@
|
|
23
22
|
"gh-pages": "^2.0.1",
|
24
23
|
"ini": "^2.0.0",
|
25
24
|
"is-svg": "4.3.1",
|
25
|
+
"lodash": "^4.17.21",
|
26
|
+
"postcss": "^8.2.15",
|
26
27
|
"serialize-javascript": "^5.0.1",
|
27
28
|
"ssri": "8.0.1",
|
29
|
+
"url-parse": "^1.5.1",
|
30
|
+
"vuepress": "^1.8.2",
|
28
31
|
"yargs-parser": "^18.1.1"
|
29
32
|
}
|
30
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 } } } }
|
@@ -103,8 +103,18 @@ module URBANopt # :nodoc:
|
|
103
103
|
reopt_inputs[:Scenario][:Site][:roof_squarefeet] = feature_report.program.roof_area_sqft[:available_roof_area_sqft]
|
104
104
|
end
|
105
105
|
|
106
|
-
if reopt_inputs[:Scenario][:Site][:land_acres].nil?
|
107
|
-
|
106
|
+
if reopt_inputs[:Scenario][:Site][:land_acres].nil?
|
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
|
117
|
+
end
|
108
118
|
end
|
109
119
|
|
110
120
|
if reopt_inputs[:Scenario][:time_steps_per_hour].nil?
|
@@ -210,8 +220,19 @@ module URBANopt # :nodoc:
|
|
210
220
|
reopt_output['outputs']['Scenario']['Site']['PV'] = []
|
211
221
|
end
|
212
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
|
+
|
213
234
|
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
214
|
-
feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i })
|
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']] })
|
215
236
|
end
|
216
237
|
|
217
238
|
wind = reopt_output['outputs']['Scenario']['Site']['Wind']
|
@@ -130,12 +130,26 @@ module URBANopt # :nodoc:
|
|
130
130
|
@@logger.fatal('Exceeded the REopt-Lite API limit of 300 requests per hour')
|
131
131
|
puts 'Using the URBANopt CLI to submit a Scenario optimization counts as one request per scenario'
|
132
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')
|
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
134
|
elsif (result.code != '201') && (result.code != '200') # Anything in the 200s is success
|
135
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
|
136
152
|
end
|
137
|
-
tries = 4
|
138
|
-
rescue StandardError
|
139
153
|
tries += 1
|
140
154
|
end
|
141
155
|
end
|
@@ -219,7 +233,7 @@ module URBANopt # :nodoc:
|
|
219
233
|
# Wait a few seconds for the REopt database to update before GETing results
|
220
234
|
sleep 5
|
221
235
|
get_request = Net::HTTP::Get.new(uri.request_uri)
|
222
|
-
response = make_request(http, get_request)
|
236
|
+
response = make_request(http, get_request, 8)
|
223
237
|
|
224
238
|
# Set a limit on retries when 404s are returned from REopt API
|
225
239
|
elapsed_time = 0
|
@@ -233,14 +247,15 @@ module URBANopt # :nodoc:
|
|
233
247
|
sleep 5
|
234
248
|
end
|
235
249
|
|
236
|
-
data = JSON.parse(response.body)
|
237
|
-
text =
|
250
|
+
data = JSON.parse(response.body, allow_nan: true)
|
251
|
+
text = JSON.pretty_generate(data)
|
238
252
|
begin
|
239
253
|
File.open(filename, 'w+') do |f|
|
240
254
|
f.puts(text)
|
241
255
|
end
|
242
|
-
rescue StandardError
|
256
|
+
rescue StandardError => e
|
243
257
|
@@logger.error("Cannot write - #{filename}")
|
258
|
+
@@logger.error("ERROR: #{e}")
|
244
259
|
end
|
245
260
|
|
246
261
|
if response.code == '200'
|
@@ -280,6 +295,10 @@ module URBANopt # :nodoc:
|
|
280
295
|
|
281
296
|
# Send the request
|
282
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
|
283
302
|
|
284
303
|
# Get UUID
|
285
304
|
run_uuid = JSON.parse(response.body, allow_nan: true)['run_uuid']
|
@@ -295,12 +314,11 @@ module URBANopt # :nodoc:
|
|
295
314
|
@@logger.info("REopt results saved to #{filename}")
|
296
315
|
end
|
297
316
|
|
298
|
-
text =
|
317
|
+
text = JSON.parse(response.body, allow_nan: true)
|
299
318
|
if response.code != '201'
|
300
319
|
File.open(filename, 'w+') do |f|
|
301
|
-
f.puts(text)
|
320
|
+
f.puts(JSON.pretty_generate(text))
|
302
321
|
end
|
303
|
-
@@logger.error("Cannot write - #{filename}")
|
304
322
|
raise "Error in REopt optimization post - see #{filename}"
|
305
323
|
end
|
306
324
|
|
@@ -353,8 +371,8 @@ module URBANopt # :nodoc:
|
|
353
371
|
_tries += 1
|
354
372
|
end
|
355
373
|
|
356
|
-
data = JSON.parse(response.body)
|
357
|
-
text =
|
374
|
+
data = JSON.parse(response.body, allow_nan: true)
|
375
|
+
text = JSON.pretty_generate(data)
|
358
376
|
begin
|
359
377
|
File.open(filename, 'w+') do |f|
|
360
378
|
f.puts(text)
|
@@ -166,7 +166,7 @@ module URBANopt # :nodoc:
|
|
166
166
|
# * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
|
167
167
|
#
|
168
168
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
|
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)
|
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, community_photovoltaic: nil)
|
170
170
|
if !reopt_assumptions_hash.nil?
|
171
171
|
@scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
|
172
172
|
end
|
@@ -180,7 +180,7 @@ module URBANopt # :nodoc:
|
|
180
180
|
api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
|
181
181
|
adapter = URBANopt::REopt::ScenarioReportAdapter.new
|
182
182
|
|
183
|
-
reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash)
|
183
|
+
reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash, community_photovoltaic)
|
184
184
|
|
185
185
|
reopt_output = api.reopt_request(reopt_input, @scenario_reopt_default_output_file)
|
186
186
|
if run_resilience
|
@@ -212,7 +212,7 @@ module URBANopt # :nodoc:
|
|
212
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.
|
213
213
|
#
|
214
214
|
# [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
|
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)
|
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, groundmount_photovoltaic: nil)
|
216
216
|
if !reopt_assumptions_hashes.empty?
|
217
217
|
@feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
|
218
218
|
end
|
@@ -244,7 +244,7 @@ module URBANopt # :nodoc:
|
|
244
244
|
# check if we should rerun
|
245
245
|
if !(keep_existing_output && output_exists(@feature_reports_reopt_default_output_files[idx]))
|
246
246
|
begin
|
247
|
-
reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx])
|
247
|
+
reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx], groundmount_photovoltaic)
|
248
248
|
reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
|
249
249
|
if run_resilience
|
250
250
|
run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
|
@@ -265,8 +265,9 @@ module URBANopt # :nodoc:
|
|
265
265
|
warn 'Could not save feature reports - the number of save names provided did not match the number of feature reports'
|
266
266
|
end
|
267
267
|
end
|
268
|
-
rescue StandardError
|
268
|
+
rescue StandardError => e
|
269
269
|
@@logger.info("Could not optimize Feature Report #{feature_report.name} #{feature_report.id}")
|
270
|
+
@@logger.error("ERROR: #{e}")
|
270
271
|
end
|
271
272
|
else
|
272
273
|
puts('Output file already exists...skipping')
|
@@ -305,8 +306,8 @@ module URBANopt # :nodoc:
|
|
305
306
|
# * +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.
|
306
307
|
#
|
307
308
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
|
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)
|
309
|
+
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, groundmount_photovoltaic: nil)
|
310
|
+
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)
|
310
311
|
puts("KEEP EXISTING? #{keep_existing_output}")
|
311
312
|
# only do this if you have run feature reports
|
312
313
|
new_scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new
|
@@ -371,6 +371,11 @@
|
|
371
371
|
"description": "State rebate based on installed capacity",
|
372
372
|
"min": 0
|
373
373
|
},
|
374
|
+
"location": {
|
375
|
+
"default": "roof",
|
376
|
+
"type": "string",
|
377
|
+
"description": "Indicates location of PV. Available options are roof and ground."
|
378
|
+
},
|
374
379
|
"max_kw": {
|
375
380
|
"default": 1000000000.0,
|
376
381
|
"max": 1000000000.0,
|
@@ -142,6 +142,11 @@
|
|
142
142
|
"PV": {
|
143
143
|
"type": "object",
|
144
144
|
"properties": {
|
145
|
+
"location": {
|
146
|
+
"default": "roof",
|
147
|
+
"type": "string",
|
148
|
+
"description": "Indicates location of PV. Available options are roof and ground."
|
149
|
+
},
|
145
150
|
"size_kw": {
|
146
151
|
"type": "float",
|
147
152
|
"description": "Optimal PV system size",
|
@@ -67,7 +67,7 @@ module URBANopt # :nodoc:
|
|
67
67
|
#
|
68
68
|
# [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
|
69
69
|
##
|
70
|
-
def reopt_json_from_scenario_report(scenario_report, reopt_assumptions_json = nil)
|
70
|
+
def reopt_json_from_scenario_report(scenario_report, reopt_assumptions_json = nil, community_photovoltaic = nil)
|
71
71
|
name = scenario_report.name.delete ' '
|
72
72
|
scenario_id = scenario_report.id.delete ' '
|
73
73
|
description = "scenario_report_#{name}_#{scenario_id}"
|
@@ -122,8 +122,11 @@ module URBANopt # :nodoc:
|
|
122
122
|
reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area_sqft[:available_roof_area_sqft]
|
123
123
|
end
|
124
124
|
|
125
|
-
|
126
|
-
reopt_inputs[:Scenario][:Site][:land_acres]
|
125
|
+
begin
|
126
|
+
if reopt_inputs[:Scenario][:Site][:land_acres].nil? && !community_photovoltaic[0][:properties][:footprint_area].nil?
|
127
|
+
reopt_inputs[:Scenario][:Site][:land_acres] = community_photovoltaic[0][:properties][:footprint_area] * 1.0 / 43560 # acres/sqft
|
128
|
+
end
|
129
|
+
rescue StandardError
|
127
130
|
end
|
128
131
|
|
129
132
|
if reopt_inputs[:Scenario][:time_steps_per_hour].nil?
|
@@ -247,8 +250,19 @@ module URBANopt # :nodoc:
|
|
247
250
|
reopt_output['outputs']['Scenario']['Site']['PV'] = []
|
248
251
|
end
|
249
252
|
|
253
|
+
# Store the PV name and location in a hash
|
254
|
+
location = {}
|
255
|
+
# Check whether multi PV assumption input file is used or single PV
|
256
|
+
if reopt_output['inputs']['Scenario']['Site']['PV'].is_a?(Array)
|
257
|
+
reopt_output['inputs']['Scenario']['Site']['PV'].each do |pv|
|
258
|
+
location[pv['pv_name']] = pv['location']
|
259
|
+
end
|
260
|
+
else
|
261
|
+
location[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['location']
|
262
|
+
end
|
263
|
+
|
250
264
|
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
251
|
-
scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i })
|
265
|
+
scenario_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']] })
|
252
266
|
end
|
253
267
|
|
254
268
|
wind = reopt_output['outputs']['Scenario']['Site']['Wind']
|
data/urbanopt-reopt.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: urbanopt-reopt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ''
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.6.
|
103
|
+
version: 0.6.3
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.6.
|
110
|
+
version: 0.6.3
|
111
111
|
description: Classes and measures for utilizing the REopt Lite API within OpenStudio
|
112
112
|
workflows.
|
113
113
|
email:
|