urbanopt-reopt 0.3.0 → 0.4.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/.gitignore +3 -0
- data/CHANGELOG.md +22 -0
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +3 -0
- data/LICENSE.md +1 -1
- data/RDOC_MAIN.md +13 -13
- data/README.md +15 -15
- data/Rakefile +30 -0
- data/a.txt +1 -0
- data/doc_templates/LICENSE.md +1 -1
- data/doc_templates/copyright_erb.txt +1 -1
- data/doc_templates/copyright_js.txt +1 -1
- data/doc_templates/copyright_ruby.txt +1 -1
- data/docs/README.md +16 -16
- data/index.md +13 -13
- data/lib/urbanopt-reopt.rb +1 -1
- data/lib/urbanopt/reopt.rb +1 -1
- data/lib/urbanopt/reopt/extension.rb +1 -1
- data/lib/urbanopt/reopt/feature_report_adapter.rb +31 -17
- data/lib/urbanopt/reopt/reopt_lite_api.rb +114 -11
- data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
- data/lib/urbanopt/reopt/reopt_post_processor.rb +53 -21
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +1 -1
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +35 -21
- data/lib/urbanopt/reopt/version.rb +2 -2
- data/lib/urbanopt/reopt_scenario.rb +1 -1
- data/urbanopt-reopt.gemspec +7 -6
- metadata +19 -17
data/index.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# **URBANopt REopt Gem**
|
2
2
|
|
3
|
-
The **URBANopt REopt Gem** extends **URBANopt::
|
4
|
-
REopt Lite is a technoeconomic model which leverages mixed integer linear programming to identify the cost-optimal sizing of solar PV, Wind, Storage and/or diesel generation given an electric load profile, a utility rate tariff and other technoeconomic parameters. See [https://developer.nrel.gov/docs/energy-optimization/reopt-v1/](https://developer.nrel.gov/docs/energy-optimization/reopt-v1/) for more detailed information on input parameters and default assumptions.
|
3
|
+
The **URBANopt<sup>™</sup> REopt Gem** extends **URBANopt::Reporting::DefaultReports::ScenarioReport** and **URBANopt::Reporting::DefaultReports::FeatureReport** with the ability to derive cost-optimal distributed energy resource (DER) technology sizes and annual dispatch strageties via the [REopt Lite](https://reopt.nrel.gov/tool) decision support platform.
|
4
|
+
REopt Lite is a technoeconomic model which leverages mixed integer linear programming to identify the cost-optimal sizing of solar PV, Wind, Storage and/or diesel generation given an electric load profile, a utility rate tariff and other technoeconomic parameters. See [https://developer.nrel.gov/docs/energy-optimization/reopt-v1/](https://developer.nrel.gov/docs/energy-optimization/reopt-v1/) for more detailed information on input parameters and default assumptions.
|
5
5
|
|
6
6
|
See the [example project](https://github.com/urbanopt/urbanopt-example-reopt-project.git) for more infomation about usage of this gem.
|
7
7
|
|
@@ -9,7 +9,7 @@ See the [example project](https://github.com/urbanopt/urbanopt-example-reopt-pro
|
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
|
12
|
-
See [https://docs.urbanopt.net/installation/installation.html](https://docs.urbanopt.net/installation/installation.html) for instructions on prerequiste software, including:
|
12
|
+
See [https://docs.urbanopt.net/installation/installation.html](https://docs.urbanopt.net/installation/installation.html) for instructions on prerequiste software, including:
|
13
13
|
- Ruby 2.2.6
|
14
14
|
- Bundler 1.17.0
|
15
15
|
- OpenStudio 2.8.1
|
@@ -31,7 +31,7 @@ Or install it yourself as:
|
|
31
31
|
|
32
32
|
## Functionality
|
33
33
|
|
34
|
-
This gem is used to call the REopt Lite API on a Scenario Report or Feature Report to update the object's Distributed Generation attributes (including system financial and sizing metrics) as shown in an example below:
|
34
|
+
This gem is used to call the REopt Lite API on a Scenario Report or Feature Report to update the object's Distributed Generation attributes (including system financial and sizing metrics) as shown in an example below:
|
35
35
|
|
36
36
|
```
|
37
37
|
"distributed_generation": {
|
@@ -83,7 +83,7 @@ Moreover, the following optimal dispatch fields are added to its timeseries CSV.
|
|
83
83
|
| ElectricityProduced:Wind:ToGrid | kWh |
|
84
84
|
```
|
85
85
|
|
86
|
-
The REopt Lite has default values for all non-required input parameters that are used unless the user specifies custom assumptions. See [https://developer.nrel.gov/docs/energy-optimization/reopt-v1/](https://developer.nrel.gov/docs/energy-optimization/reopt-v1/) for more detailed information on input parameters and default assumptions.
|
86
|
+
The REopt Lite has default values for all non-required input parameters that are used unless the user specifies custom assumptions. See [https://developer.nrel.gov/docs/energy-optimization/reopt-v1/](https://developer.nrel.gov/docs/energy-optimization/reopt-v1/) for more detailed information on input parameters and default assumptions.
|
87
87
|
|
88
88
|
<b>Note:</b> Required attributes for a REopt run include latitude and longitude. If no utility rate is specified in your REopt Lite assumption settings, then a constant default rate of $0.13 is assumed without demand charges. Also, by default, only solar PV and storage are considered in the analysis (i.e. Wind and Generators are excluded from consideration).
|
89
89
|
|
@@ -99,7 +99,7 @@ require 'urbanopt/reopt'
|
|
99
99
|
feature_reports_hash = {} # <insert a Feature Report hash here>
|
100
100
|
|
101
101
|
#Create a Feature Report
|
102
|
-
feature_report = URBANopt::
|
102
|
+
feature_report = URBANopt::Reporting::DefaultReports::FeatureReport.new(feature_reports_hash)
|
103
103
|
|
104
104
|
#Specify a file name where REopt Lite results will be written in JSON format
|
105
105
|
reopt_output_file = File.join(feature_report.directory_name, 'feature_report_reopt_run1.json')
|
@@ -115,7 +115,7 @@ reopt_post_processor = URBANopt::REopt::REoptPostProcessor.new(nil, nil, nil, DE
|
|
115
115
|
|
116
116
|
#Call REopt Lite with the post processor to update the feature's distributed generation attributes and timeseries CSV.
|
117
117
|
updated_feature_report = reopt_post_processor.run_feature_report(feature_report,reopt_assumptions_file,reopt_output_file,timeseries_output_file)
|
118
|
-
|
118
|
+
|
119
119
|
```
|
120
120
|
|
121
121
|
More commonly, this gem can be used to run REopt a collection of features stored in a Scenario Report as show here:
|
@@ -123,9 +123,9 @@ More commonly, this gem can be used to run REopt a collection of features stored
|
|
123
123
|
```ruby
|
124
124
|
require 'urbanopt/reopt'
|
125
125
|
#Create a Scenario Report
|
126
|
-
scenario_report = URBANopt::
|
126
|
+
scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new({:directory_name => File.join(File.dirname(__FILE__), '../run/example_scenario'), :timeseries_csv => {:path => File.join(File.dirname(__FILE__), '../run/example_scenario/timeseries.csv') }})
|
127
127
|
|
128
|
-
#Load Feature Reports into the Scenario Report
|
128
|
+
#Load Feature Reports into the Scenario Report
|
129
129
|
(1..2).each do |i|
|
130
130
|
feature_reports_path = File.join(File.dirname(__FILE__), "../run/example_scenario/#{i}/010_default_feature_reports/default_feature_reports.json")
|
131
131
|
|
@@ -134,8 +134,8 @@ scenario_report = URBANopt::Scenario::DefaultReports::ScenarioReport.new({:direc
|
|
134
134
|
feature_reports_hash = JSON.parse(file.read, symbolize_names: true)
|
135
135
|
end
|
136
136
|
|
137
|
-
feature_report = URBANopt::
|
138
|
-
|
137
|
+
feature_report = URBANopt::Reporting::DefaultReports::FeatureReport.new(feature_reports_hash)
|
138
|
+
|
139
139
|
feature_report_dir = File.join(File.dirname(__FILE__), "../run/example_scenario/#{i}")
|
140
140
|
feature_report.directory_name = feature_report_dir
|
141
141
|
|
@@ -150,7 +150,7 @@ reopt_post_processor = URBANopt::REopt::REoptPostProcessor.new(scenario_report,
|
|
150
150
|
|
151
151
|
#Call REopt Lite with the post processor once on the sceanrio's aggregated load to update the scenario's distributed generation attributes and timeseries CSV.
|
152
152
|
updated_scenario_report = reopt_post_processor.run_scenario_report(scenario_report)
|
153
|
-
|
153
|
+
|
154
154
|
```
|
155
155
|
|
156
156
|
## Testing
|
@@ -164,7 +164,7 @@ Next, obtain a developer.nrel.gov API key from the [NREL Developer Network](http
|
|
164
164
|
Finally, execute:
|
165
165
|
|
166
166
|
$ bundle install
|
167
|
-
$ bundle update
|
167
|
+
$ bundle update
|
168
168
|
$ bundle exec rake
|
169
169
|
|
170
170
|
|
data/lib/urbanopt-reopt.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, 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,
|
data/lib/urbanopt/reopt.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, 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,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, 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,
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, 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,
|
@@ -28,7 +28,7 @@
|
|
28
28
|
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
29
|
# *********************************************************************************
|
30
30
|
|
31
|
-
require 'urbanopt/
|
31
|
+
require 'urbanopt/reporting/default_reports'
|
32
32
|
require 'urbanopt/reopt/reopt_logger'
|
33
33
|
require 'csv'
|
34
34
|
require 'matrix'
|
@@ -38,7 +38,7 @@ module URBANopt # :nodoc:
|
|
38
38
|
module REopt # :nodoc:
|
39
39
|
class FeatureReportAdapter
|
40
40
|
##
|
41
|
-
# FeatureReportAdapter can convert a URBANopt::
|
41
|
+
# FeatureReportAdapter can convert a URBANopt::Reporting::DefaultReports::FeatureReport into a \REopt Lite posts or update a URBANopt::Reporting::DefaultReports::FeatureReport from a \REopt Lite response.
|
42
42
|
##
|
43
43
|
# [*parameters:*]
|
44
44
|
##
|
@@ -52,7 +52,7 @@ module URBANopt # :nodoc:
|
|
52
52
|
#
|
53
53
|
# [*parameters:*]
|
54
54
|
#
|
55
|
-
# * +feature_report+ - _URBANopt::
|
55
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to use in converting the optional +reopt_assumptions_hash+ to a \REopt Lite post. If a +reopt_assumptions_hash+ is not provided, a default post will be updated from this FeatureReport and submitted to the \REopt Lite API.
|
56
56
|
# * +reopt_assumptions_hash+ - _Hash_ - Optional. A hash formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the FeatureReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API.
|
57
57
|
#
|
58
58
|
# [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
|
@@ -127,19 +127,19 @@ module URBANopt # :nodoc:
|
|
127
127
|
#
|
128
128
|
# [*parameters:*]
|
129
129
|
#
|
130
|
-
# * +feature_report+ - _URBANopt::
|
130
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to update from a \REopt Lite reponse hash.
|
131
131
|
# * +reopt_output+ - _Hash_ - A reponse hash from the \REopt Lite API to use in overwriting FeatureReport technology sizes, costs and dispatch strategies.
|
132
132
|
# * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which a new timeseries CSV will be written. If not provided a file is created based on the run_uuid of the \REopt Lite optimization task.
|
133
133
|
#
|
134
|
-
# [*return:*] _URBANopt::
|
134
|
+
# [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport.
|
135
135
|
##
|
136
|
-
def update_feature_report(feature_report, reopt_output, timeseries_csv_path =
|
136
|
+
def update_feature_report(feature_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil)
|
137
137
|
# Check if the \REopt Lite response is valid
|
138
138
|
if reopt_output['outputs']['Scenario']['status'] != 'optimal'
|
139
139
|
@@logger.info("Warning cannot Feature Report #{feature_report.name} #{feature_report.id} - REopt optimization was non-optimal")
|
140
140
|
return feature_report
|
141
141
|
end
|
142
|
-
|
142
|
+
|
143
143
|
$ts_per_hour = feature_report.timesteps_per_hour
|
144
144
|
def scale_timeseries(input, ts_per_hr=$ts_per_hour)
|
145
145
|
if input.nil?
|
@@ -152,7 +152,7 @@ module URBANopt # :nodoc:
|
|
152
152
|
return input
|
153
153
|
end
|
154
154
|
result = []
|
155
|
-
input.each do |val|
|
155
|
+
input.each do |val|
|
156
156
|
(1..ts_per_hr).each do |x|
|
157
157
|
result.push(val/ts_per_hr.to_f)
|
158
158
|
end
|
@@ -174,36 +174,50 @@ module URBANopt # :nodoc:
|
|
174
174
|
feature_report.distributed_generation.year_one_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars'] || 0
|
175
175
|
feature_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars'] || 0
|
176
176
|
feature_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars'] || 0
|
177
|
+
feature_report.distributed_generation.total_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_us_dollars'] || 0
|
178
|
+
feature_report.distributed_generation.year_one_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_bau_us_dollars'] || 0
|
179
|
+
feature_report.distributed_generation.year_one_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_bau_us_dollars'] || 0
|
180
|
+
feature_report.distributed_generation.year_one_bill_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_bau_us_dollars'] || 0
|
181
|
+
feature_report.distributed_generation.total_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_bau_us_dollars'] || 0
|
182
|
+
feature_report.distributed_generation.total_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_bau_us_dollars'] || 0
|
183
|
+
if !resilience_stats.nil?
|
184
|
+
feature_report.distributed_generation.resilience_hours_min = resilience_stats['resilience_hours_min']
|
185
|
+
feature_report.distributed_generation.resilience_hours_max = resilience_stats['resilience_hours_max']
|
186
|
+
feature_report.distributed_generation.resilience_hours_avg = resilience_stats['resilience_hours_avg']
|
187
|
+
feature_report.distributed_generation.probs_of_surviving = resilience_stats['probs_of_surviving']
|
188
|
+
feature_report.distributed_generation.probs_of_surviving_by_month = resilience_stats['probs_of_surviving_by_month']
|
189
|
+
feature_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
|
190
|
+
end
|
177
191
|
|
178
192
|
if reopt_output['outputs']['Scenario']['Site']['PV'].class == Hash
|
179
193
|
reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
|
180
194
|
elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
|
181
195
|
reopt_output['outputs']['Scenario']['Site']['PV'] = []
|
182
196
|
end
|
183
|
-
|
184
|
-
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
185
|
-
feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::
|
197
|
+
|
198
|
+
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
199
|
+
feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
|
186
200
|
end
|
187
201
|
|
188
202
|
wind = reopt_output['outputs']['Scenario']['Site']['Wind']
|
189
203
|
if !wind['size_kw'].nil? and wind['size_kw'] != 0
|
190
|
-
feature_report.distributed_generation.add_tech 'wind', URBANopt::
|
204
|
+
feature_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
|
191
205
|
end
|
192
206
|
|
193
207
|
generator = reopt_output['outputs']['Scenario']['Site']['Generator']
|
194
208
|
if !generator['size_kw'].nil? and generator['size_kw'] != 0
|
195
|
-
feature_report.distributed_generation.add_tech 'generator', URBANopt::
|
209
|
+
feature_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
|
196
210
|
end
|
197
211
|
|
198
212
|
storage = reopt_output['outputs']['Scenario']['Site']['Storage']
|
199
213
|
if !storage['size_kw'].nil? and storage['size_kw'] != 0
|
200
|
-
feature_report.distributed_generation.add_tech 'storage', URBANopt::
|
214
|
+
feature_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
|
201
215
|
end
|
202
|
-
|
216
|
+
|
203
217
|
generation_timeseries_kwh = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
204
218
|
|
205
219
|
unless reopt_output['outputs']['Scenario']['Site']['PV'].nil?
|
206
|
-
reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
|
220
|
+
reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
|
207
221
|
if (pv['size_kw'] || 0) > 0
|
208
222
|
if !pv['year_one_power_production_series_kw'].nil?
|
209
223
|
generation_timeseries_kwh += Matrix[pv['year_one_power_production_series_kw']]
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, 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,
|
@@ -56,6 +56,7 @@ module URBANopt # :nodoc:
|
|
56
56
|
@use_localhost = use_localhost
|
57
57
|
if @use_localhost
|
58
58
|
@uri_submit = URI.parse('http//:127.0.0.1:8000/v1/job/')
|
59
|
+
@uri_submit_outagesimjob = URI.parse('http//:127.0.0.1:8000/v1/outagesimjob/')
|
59
60
|
else
|
60
61
|
if [nil, '', '<insert your key here>'].include? nrel_developer_key
|
61
62
|
if [nil, '', '<insert your key here>'].include? DEVELOPER_NREL_KEY
|
@@ -66,6 +67,7 @@ module URBANopt # :nodoc:
|
|
66
67
|
end
|
67
68
|
@nrel_developer_key = nrel_developer_key
|
68
69
|
@uri_submit = URI.parse("https://developer.nrel.gov/api/reopt/v1/job/?api_key=#{@nrel_developer_key}")
|
70
|
+
@uri_submit_outagesimjob = URI.parse("https://developer.nrel.gov/api/reopt/v1/outagesimjob/?api_key=#{@nrel_developer_key}")
|
69
71
|
# initialize @@logger
|
70
72
|
@@logger ||= URBANopt::REopt.reopt_logger
|
71
73
|
end
|
@@ -88,6 +90,23 @@ module URBANopt # :nodoc:
|
|
88
90
|
return URI.parse("https://developer.nrel.gov/api/reopt/v1/job/#{run_uuid}/results?api_key=#{@nrel_developer_key}")
|
89
91
|
end
|
90
92
|
|
93
|
+
##
|
94
|
+
# URL of the resilience statistics end point for a specific optimization task
|
95
|
+
##
|
96
|
+
#
|
97
|
+
# [*parameters:*]
|
98
|
+
#
|
99
|
+
# * +run_uuid+ - _String_ - Resilience statistics for a unique run_uuid obtained from the \REopt Lite job submittal URL for a specific optimization task.
|
100
|
+
#
|
101
|
+
# [*return:*] _URI_ - Returns URI object for use in calling the \REopt Lite resilience statistics endpoint for a specifc optimization task.
|
102
|
+
##
|
103
|
+
def uri_resilience(run_uuid) # :nodoc:
|
104
|
+
if @use_localhost
|
105
|
+
return URI.parse("http://127.0.0.1:8000/v1/job/#{run_uuid}/resilience_stats")
|
106
|
+
end
|
107
|
+
return URI.parse("https://developer.nrel.gov/api/reopt/v1/job/#{run_uuid}/resilience_stats?api_key=#{@nrel_developer_key}")
|
108
|
+
end
|
109
|
+
|
91
110
|
def make_request(http, r, max_tries = 3)
|
92
111
|
result = nil
|
93
112
|
tries = 0
|
@@ -117,10 +136,10 @@ module URBANopt # :nodoc:
|
|
117
136
|
http = Net::HTTP.new(@uri_submit.host, @uri_submit.port)
|
118
137
|
if !@use_localhost
|
119
138
|
http.use_ssl = true
|
120
|
-
|
139
|
+
end
|
121
140
|
|
122
141
|
request = Net::HTTP::Post.new(@uri_submit, header)
|
123
|
-
request.body = data
|
142
|
+
request.body = ::JSON.generate(data, allow_nan: true)
|
124
143
|
|
125
144
|
# Send the request
|
126
145
|
response = make_request(http, request)
|
@@ -132,6 +151,80 @@ module URBANopt # :nodoc:
|
|
132
151
|
return true
|
133
152
|
end
|
134
153
|
|
154
|
+
##
|
155
|
+
# Completes a \REopt Lite optimization. From a formatted hash, an optimization task is submitted to the API.
|
156
|
+
# Results are polled at 5 second interval until they are ready or an error is returned from the API. Results
|
157
|
+
# are written to disk.
|
158
|
+
##
|
159
|
+
#
|
160
|
+
# [*parameters:*]
|
161
|
+
#
|
162
|
+
# * +reopt_input+ - _Hash_ - \REopt Lite formatted post containing at least required parameters.
|
163
|
+
# * +filename+ - _String_ - Path to file that will be created containing the full \REopt Lite response.
|
164
|
+
#
|
165
|
+
# [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
|
166
|
+
##
|
167
|
+
def resilience_request(run_uuid, filename)
|
168
|
+
|
169
|
+
if File.directory? filename
|
170
|
+
if run_uuid.nil?
|
171
|
+
run_uuid = 'error'
|
172
|
+
end
|
173
|
+
if run_uuid.downcase.include? 'error'
|
174
|
+
run_uuid = "error#{SecureRandom.uuid}"
|
175
|
+
end
|
176
|
+
filename = File.join(filename, "#{run_uuid}_resilience.json")
|
177
|
+
@@logger.info("REopt results saved to #{filename}")
|
178
|
+
end
|
179
|
+
|
180
|
+
#Submit Job
|
181
|
+
@@logger.info("Submitting Resilience Statistics job for #{run_uuid}")
|
182
|
+
header = { 'Content-Type' => 'application/json' }
|
183
|
+
http = Net::HTTP.new(@uri_submit_outagesimjob.host, @uri_submit_outagesimjob.port)
|
184
|
+
if !@use_localhost
|
185
|
+
http.use_ssl = true
|
186
|
+
end
|
187
|
+
request = Net::HTTP::Post.new(@uri_submit_outagesimjob, header)
|
188
|
+
request.body = ::JSON.generate({"run_uuid" => run_uuid, "bau" => false }, allow_nan: true)
|
189
|
+
submit_response = make_request(http, request)
|
190
|
+
@@logger.info(submit_response.body)
|
191
|
+
|
192
|
+
#Fetch Results
|
193
|
+
uri = uri_resilience(run_uuid)
|
194
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
195
|
+
if !@use_localhost
|
196
|
+
http.use_ssl = true
|
197
|
+
end
|
198
|
+
|
199
|
+
elapsed_time = 0
|
200
|
+
max_elapsed_time = 60
|
201
|
+
|
202
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
203
|
+
response = make_request(http, request)
|
204
|
+
|
205
|
+
while (elapsed_time < max_elapsed_time) & (response.code == "404")
|
206
|
+
response = make_request(http, request)
|
207
|
+
elapsed_time += 5
|
208
|
+
sleep 5
|
209
|
+
end
|
210
|
+
|
211
|
+
data = JSON.parse(response.body)
|
212
|
+
text = ::JSON.generate(data, allow_nan: true)
|
213
|
+
begin
|
214
|
+
File.open(filename, 'w+') do |f|
|
215
|
+
f.puts(text)
|
216
|
+
end
|
217
|
+
rescue
|
218
|
+
@@logger.info("Cannot write - #{filename}")
|
219
|
+
end
|
220
|
+
|
221
|
+
if response.code == "200"
|
222
|
+
return data
|
223
|
+
end
|
224
|
+
|
225
|
+
raise "Error from REopt API - #{data['Error']}"
|
226
|
+
end
|
227
|
+
|
135
228
|
##
|
136
229
|
# Completes a \REopt Lite optimization. From a formatted hash, an optimization task is submitted to the API.
|
137
230
|
# Results are polled at 5 second interval until they are ready or an error is returned from the API. Results
|
@@ -157,13 +250,13 @@ module URBANopt # :nodoc:
|
|
157
250
|
http.use_ssl = true
|
158
251
|
end
|
159
252
|
request = Net::HTTP::Post.new(@uri_submit, header)
|
160
|
-
request.body = reopt_input
|
253
|
+
request.body = ::JSON.generate(reopt_input, allow_nan: true)
|
161
254
|
|
162
255
|
# Send the request
|
163
256
|
response = make_request(http, request)
|
164
257
|
|
165
258
|
# Get UUID
|
166
|
-
run_uuid = JSON.parse(response.body)['run_uuid']
|
259
|
+
run_uuid = JSON.parse(response.body, allow_nan:true)['run_uuid']
|
167
260
|
|
168
261
|
if File.directory? filename
|
169
262
|
if run_uuid.nil?
|
@@ -176,10 +269,12 @@ module URBANopt # :nodoc:
|
|
176
269
|
@@logger.info("REopt results saved to #{filename}")
|
177
270
|
end
|
178
271
|
|
272
|
+
text = ::JSON.generate(response.body, allow_nan: true)
|
179
273
|
if response.code != '201'
|
180
|
-
File.open(filename, 'w') do |f|
|
181
|
-
f.
|
274
|
+
File.open(filename, 'w+') do |f|
|
275
|
+
f.puts(text)
|
182
276
|
end
|
277
|
+
@@logger.info("Cannot write - #{filename}")
|
183
278
|
raise "Error in REopt optimization post - see #{filename}"
|
184
279
|
end
|
185
280
|
|
@@ -195,7 +290,9 @@ module URBANopt # :nodoc:
|
|
195
290
|
|
196
291
|
while status == 'Optimizing...'
|
197
292
|
response = make_request(http, request)
|
198
|
-
|
293
|
+
|
294
|
+
data = JSON.parse(response.body, allow_nan:true)
|
295
|
+
|
199
296
|
if data['outputs']['Scenario']['Site']['PV'].kind_of?(Array)
|
200
297
|
pv_sizes = 0
|
201
298
|
data['outputs']['Scenario']['Site']['PV'].each do |x|
|
@@ -216,7 +313,7 @@ module URBANopt # :nodoc:
|
|
216
313
|
while (_tries < _max_retry) && check_complete
|
217
314
|
sleep 1
|
218
315
|
response = make_request(http, request)
|
219
|
-
data = JSON.parse(response.body)
|
316
|
+
data = JSON.parse(response.body, allow_nan:true)
|
220
317
|
if data['outputs']['Scenario']['Site']['PV'].kind_of?(Array)
|
221
318
|
pv_sizes = 0
|
222
319
|
data['outputs']['Scenario']['Site']['PV'].each do |x|
|
@@ -230,8 +327,14 @@ module URBANopt # :nodoc:
|
|
230
327
|
_tries += 1
|
231
328
|
end
|
232
329
|
|
233
|
-
|
234
|
-
|
330
|
+
data = JSON.parse(response.body)
|
331
|
+
text = ::JSON.generate(data, allow_nan: true)
|
332
|
+
begin
|
333
|
+
File.open(filename, 'w+') do |f|
|
334
|
+
f.puts(text)
|
335
|
+
end
|
336
|
+
rescue
|
337
|
+
@@logger.info("Cannot write - #{filename}")
|
235
338
|
end
|
236
339
|
|
237
340
|
if status == 'optimal'
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, 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,
|