urbanopt-reopt 0.3.0.pre1 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/pull_request_template.md +2 -2
- data/.gitignore +3 -0
- data/CHANGELOG.md +69 -3
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +2 -5
- data/LICENSE.md +1 -1
- data/RDOC_MAIN.md +13 -13
- data/README.md +15 -15
- data/Rakefile +30 -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/docs/package-lock.json +10084 -151
- data/docs/package.json +8 -4
- 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 +102 -64
- data/lib/urbanopt/reopt/reopt_lite_api.rb +117 -12
- data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
- data/lib/urbanopt/reopt/reopt_post_processor.rb +57 -24
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +1 -1
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +106 -72
- data/lib/urbanopt/reopt/utilities.rb +107 -0
- data/lib/urbanopt/reopt/version.rb +2 -2
- data/lib/urbanopt/reopt_scenario.rb +1 -1
- data/urbanopt-reopt.gemspec +8 -5
- metadata +35 -20
- data/.travis.yml +0 -22
@@ -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,82 @@ 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 * 5
|
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
|
+
@@logger.info("Error from REopt API - #{data['Error']}")
|
226
|
+
return {}
|
227
|
+
|
228
|
+
end
|
229
|
+
|
135
230
|
##
|
136
231
|
# Completes a \REopt Lite optimization. From a formatted hash, an optimization task is submitted to the API.
|
137
232
|
# Results are polled at 5 second interval until they are ready or an error is returned from the API. Results
|
@@ -157,13 +252,13 @@ module URBANopt # :nodoc:
|
|
157
252
|
http.use_ssl = true
|
158
253
|
end
|
159
254
|
request = Net::HTTP::Post.new(@uri_submit, header)
|
160
|
-
request.body = reopt_input
|
161
|
-
|
255
|
+
request.body = ::JSON.generate(reopt_input, allow_nan: true)
|
256
|
+
|
162
257
|
# Send the request
|
163
258
|
response = make_request(http, request)
|
164
259
|
|
165
260
|
# Get UUID
|
166
|
-
run_uuid = JSON.parse(response.body)['run_uuid']
|
261
|
+
run_uuid = JSON.parse(response.body, allow_nan:true)['run_uuid']
|
167
262
|
|
168
263
|
if File.directory? filename
|
169
264
|
if run_uuid.nil?
|
@@ -176,10 +271,12 @@ module URBANopt # :nodoc:
|
|
176
271
|
@@logger.info("REopt results saved to #{filename}")
|
177
272
|
end
|
178
273
|
|
274
|
+
text = ::JSON.generate(response.body, allow_nan: true)
|
179
275
|
if response.code != '201'
|
180
|
-
File.open(filename, 'w') do |f|
|
181
|
-
f.
|
276
|
+
File.open(filename, 'w+') do |f|
|
277
|
+
f.puts(text)
|
182
278
|
end
|
279
|
+
@@logger.info("Cannot write - #{filename}")
|
183
280
|
raise "Error in REopt optimization post - see #{filename}"
|
184
281
|
end
|
185
282
|
|
@@ -195,7 +292,9 @@ module URBANopt # :nodoc:
|
|
195
292
|
|
196
293
|
while status == 'Optimizing...'
|
197
294
|
response = make_request(http, request)
|
198
|
-
|
295
|
+
|
296
|
+
data = JSON.parse(response.body, allow_nan:true)
|
297
|
+
|
199
298
|
if data['outputs']['Scenario']['Site']['PV'].kind_of?(Array)
|
200
299
|
pv_sizes = 0
|
201
300
|
data['outputs']['Scenario']['Site']['PV'].each do |x|
|
@@ -216,7 +315,7 @@ module URBANopt # :nodoc:
|
|
216
315
|
while (_tries < _max_retry) && check_complete
|
217
316
|
sleep 1
|
218
317
|
response = make_request(http, request)
|
219
|
-
data = JSON.parse(response.body)
|
318
|
+
data = JSON.parse(response.body, allow_nan:true)
|
220
319
|
if data['outputs']['Scenario']['Site']['PV'].kind_of?(Array)
|
221
320
|
pv_sizes = 0
|
222
321
|
data['outputs']['Scenario']['Site']['PV'].each do |x|
|
@@ -230,8 +329,14 @@ module URBANopt # :nodoc:
|
|
230
329
|
_tries += 1
|
231
330
|
end
|
232
331
|
|
233
|
-
|
234
|
-
|
332
|
+
data = JSON.parse(response.body)
|
333
|
+
text = ::JSON.generate(data, allow_nan: true)
|
334
|
+
begin
|
335
|
+
File.open(filename, 'w+') do |f|
|
336
|
+
f.puts(text)
|
337
|
+
end
|
338
|
+
rescue
|
339
|
+
@@logger.info("Cannot write - #{filename}")
|
235
340
|
end
|
236
341
|
|
237
342
|
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,
|
@@ -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,
|
@@ -29,9 +29,8 @@
|
|
29
29
|
# *********************************************************************************
|
30
30
|
|
31
31
|
require 'bundler/setup'
|
32
|
-
require 'urbanopt/
|
32
|
+
require 'urbanopt/reporting/default_reports'
|
33
33
|
require 'urbanopt/reopt/reopt_logger'
|
34
|
-
require 'urbanopt/reopt'
|
35
34
|
require 'csv'
|
36
35
|
|
37
36
|
module URBANopt # :nodoc:
|
@@ -43,7 +42,7 @@ module URBANopt # :nodoc:
|
|
43
42
|
#
|
44
43
|
# [*parameters:*]
|
45
44
|
#
|
46
|
-
# * +scenario_report+ - _ScenarioReport_ - Optional. A scenario report that has been returned from the URBANopt::
|
45
|
+
# * +scenario_report+ - _ScenarioReport_ - Optional. A scenario report that has been returned from the URBANopt::Reporting::ScenarioDefaultPostProcessor - used in creating default output file names in \REopt Lite optimizations.
|
47
46
|
# * +scenario_reopt_assumptions_file+ - _String_ - Optional. JSON file formatted for a \REopt Lite analysis containing custom input parameters for optimizations at the Scenario Report level
|
48
47
|
# * +reopt_feature_assumptions+ - _Array_ - Optional. A list of JSON file formatted for a \REopt Lite analysis containing custom input parameters for optimizations at the Feature Report level. The order and number of files must match the Feature Reports in the scenario_report input.
|
49
48
|
# * +use_localhost+ - _Bool_ - If this is true, requests will be sent to a version of the \REopt Lite API running on localhost. Default is false, such that the production version of \REopt Lite is accessed.
|
@@ -70,8 +69,9 @@ module URBANopt # :nodoc:
|
|
70
69
|
if !scenario_report.nil?
|
71
70
|
@scenario_report = scenario_report
|
72
71
|
|
73
|
-
if
|
72
|
+
if !Dir.exist?(File.join(@scenario_report.directory_name, "reopt"))
|
74
73
|
Dir.mkdir(File.join(@scenario_report.directory_name, "reopt"))
|
74
|
+
@@logger.info("Created directory: " + File.join(@scenario_report.directory_name, "reopt"))
|
75
75
|
end
|
76
76
|
|
77
77
|
@scenario_reopt_default_output_file = File.join(@scenario_report.directory_name, "reopt/scenario_report_#{@scenario_report.id}_reopt_run.json")
|
@@ -80,6 +80,7 @@ module URBANopt # :nodoc:
|
|
80
80
|
@scenario_report.feature_reports.each do |fr|
|
81
81
|
if !Dir.exist?(File.join(fr.directory_name, "reopt"))
|
82
82
|
Dir.mkdir(File.join(fr.directory_name, "reopt"))
|
83
|
+
@@logger.info("Created directory: " + File.join(fr.directory_name, "reopt"))
|
83
84
|
end
|
84
85
|
@feature_reports_reopt_default_output_files << File.join(fr.directory_name, "reopt/feature_report_#{fr.id}_reopt_run.json")
|
85
86
|
end
|
@@ -111,25 +112,36 @@ module URBANopt # :nodoc:
|
|
111
112
|
#
|
112
113
|
# [*parameters:*]
|
113
114
|
#
|
114
|
-
# * +feature_report+ - _URBANopt::
|
115
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport which will be used in creating and then updated by a \REopt Lite opimization response.
|
115
116
|
# * +reopt_assumptions_hash+ - _Hash_ - Optional. A \REopt Lite formatted hash containing default parameters (i.e. utility rate, escalation rate) which will be updated by the FeatureReport (i.e. location, roof availability)
|
116
117
|
# * +reopt_output_file+ - _String_ - Optional. Path to a file at which REpopt Lite responses will be saved.
|
117
118
|
# * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the FeatureReport will be saved.
|
118
119
|
#
|
119
|
-
# [*return:*] _URBANopt::
|
120
|
+
# [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
|
120
121
|
##
|
121
|
-
def run_feature_report(feature_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil)
|
122
|
+
def run_feature_report(feature_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil, run_resilience:true)
|
122
123
|
api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
|
123
124
|
adapter = URBANopt::REopt::FeatureReportAdapter.new
|
124
125
|
|
125
126
|
reopt_input = adapter.reopt_json_from_feature_report(feature_report, reopt_assumptions_hash)
|
126
127
|
if reopt_output_file.nil?
|
127
|
-
reopt_output_file = feature_report.directory_name
|
128
|
+
reopt_output_file = File.join(feature_report.directory_name, 'reopt')
|
128
129
|
end
|
129
130
|
reopt_output = api.reopt_request(reopt_input, reopt_output_file)
|
130
|
-
|
131
|
+
if run_resilience
|
132
|
+
run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
|
133
|
+
if File.directory? reopt_output_file
|
134
|
+
resilience_stats = api.resilience_request(run_uuid, reopt_output_file)
|
135
|
+
else
|
136
|
+
resilience_stats = api.resilience_request(run_uuid, reopt_output_file.sub!('.json','_resilience.json'))
|
137
|
+
end
|
138
|
+
else
|
139
|
+
resilience_stats = nil
|
140
|
+
end
|
141
|
+
result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path, resilience_stats)
|
131
142
|
if !save_name.nil?
|
132
|
-
result.save_feature_report save_name
|
143
|
+
#result.save_feature_report save_name
|
144
|
+
result.save_json_report(save_name)
|
133
145
|
end
|
134
146
|
return result
|
135
147
|
end
|
@@ -140,13 +152,13 @@ module URBANopt # :nodoc:
|
|
140
152
|
#
|
141
153
|
# [*parameters:*]
|
142
154
|
#
|
143
|
-
# * +feature_report+ - _URBANopt::
|
155
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport which will be used in creating and then updated by a \REopt Lite opimization response.
|
144
156
|
# * +reopt_assumptions_hash+ - _Hash_ - Optional. A \REopt Lite formatted hash containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability)
|
145
157
|
# * +reopt_output_file+ - _String_ - Optional. Path to a file at which REpopt Lite responses will be saved.
|
146
158
|
# * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
|
147
159
|
#
|
148
160
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
|
149
|
-
def run_scenario_report(scenario_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil)
|
161
|
+
def run_scenario_report(scenario_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil, run_resilience:true)
|
150
162
|
if !reopt_assumptions_hash.nil?
|
151
163
|
@scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
|
152
164
|
end
|
@@ -163,8 +175,18 @@ module URBANopt # :nodoc:
|
|
163
175
|
reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash)
|
164
176
|
|
165
177
|
reopt_output = api.reopt_request(reopt_input, @scenario_reopt_default_output_file)
|
178
|
+
if run_resilience
|
179
|
+
run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
|
180
|
+
if File.directory? @scenario_reopt_default_output_file
|
181
|
+
resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file)
|
182
|
+
else
|
183
|
+
resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file.sub!('.json','_resilience.json'))
|
184
|
+
end
|
185
|
+
else
|
186
|
+
resilience_stats = nil
|
187
|
+
end
|
166
188
|
|
167
|
-
result = adapter.update_scenario_report(scenario_report, reopt_output, @scenario_timeseries_default_output_file)
|
189
|
+
result = adapter.update_scenario_report(scenario_report, reopt_output, @scenario_timeseries_default_output_file, resilience_stats)
|
168
190
|
if !save_name.nil?
|
169
191
|
result.save save_name
|
170
192
|
end
|
@@ -176,13 +198,13 @@ module URBANopt # :nodoc:
|
|
176
198
|
#
|
177
199
|
# [*parameters:*]
|
178
200
|
#
|
179
|
-
# * +feature_reports+ - _Array_ - An array of _URBANopt::
|
201
|
+
# * +feature_reports+ - _Array_ - An array of _URBANopt::Reporting::DefaultReports::FeatureReport_ objetcs which will each be used to create (and are subsquently updated by) a \REopt Lite opimization response.
|
180
202
|
# * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of \REopt Lite formatted hashes containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability). The number and order of the hashes should match the feature_reports array.
|
181
203
|
# * +reopt_output_files+ - _Array_ - Optional. A array of paths to files at which REpopt Lite responses will be saved. The number and order of the paths should match the feature_reports array.
|
182
204
|
# * +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.
|
183
205
|
#
|
184
206
|
# [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
|
185
|
-
def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil)
|
207
|
+
def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil, run_resilience:true)
|
186
208
|
|
187
209
|
if !reopt_assumptions_hashes.empty?
|
188
210
|
@feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
|
@@ -215,11 +237,22 @@ module URBANopt # :nodoc:
|
|
215
237
|
begin
|
216
238
|
reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx])
|
217
239
|
reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
|
218
|
-
|
240
|
+
if run_resilience
|
241
|
+
run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
|
242
|
+
if File.directory? @feature_reports_reopt_default_output_files[idx]
|
243
|
+
resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx])
|
244
|
+
else
|
245
|
+
resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx].sub!('.json','_resilience.json'))
|
246
|
+
end
|
247
|
+
else
|
248
|
+
resilience_stats = nil
|
249
|
+
end
|
250
|
+
new_feature_report = feature_adapter.update_feature_report(feature_report, reopt_output, @feature_reports_timeseries_default_output_files[idx], resilience_stats)
|
219
251
|
new_feature_reports.push(new_feature_report)
|
220
252
|
if !save_names.nil?
|
221
253
|
if save_names.length == feature_reports.length
|
222
|
-
new_feature_report.save_feature_report save_names[idx]
|
254
|
+
#new_feature_report.save_feature_report save_names[idx]
|
255
|
+
new_feature_report.save_json_report save_names[idx]
|
223
256
|
else
|
224
257
|
warn "Could not save feature reports - the number of save names provided did not match the number of feature reports"
|
225
258
|
end
|
@@ -228,7 +261,7 @@ module URBANopt # :nodoc:
|
|
228
261
|
@@logger.info("Could not optimize Feature Report #{feature_report.name} #{feature_report.id}")
|
229
262
|
end
|
230
263
|
end
|
231
|
-
|
264
|
+
|
232
265
|
return new_feature_reports
|
233
266
|
end
|
234
267
|
|
@@ -237,22 +270,22 @@ module URBANopt # :nodoc:
|
|
237
270
|
#
|
238
271
|
# [*parameters:*]
|
239
272
|
#
|
240
|
-
# * +scenario_report+ - _Array_ - A _URBANopt::
|
273
|
+
# * +scenario_report+ - _Array_ - A _URBANopt::Reporting::DefaultReports::ScenarioReport_ which will each be used to create (and is subsquently updated by) \REopt Lite opimization responses for each of its FeatureReports.
|
241
274
|
# * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of \REopt Lite formatted hashes containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability). The number and order of the hashes should match the array in ScenarioReport.feature_reports.
|
242
275
|
# * +reopt_output_files+ - _Array_ - Optional. An array of paths to files at which REpopt Lite responses will be saved. The number and order of the paths should match the array in ScenarioReport.feature_reports.
|
243
276
|
# * +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.
|
244
277
|
#
|
245
278
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
|
246
|
-
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)
|
247
|
-
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)
|
279
|
+
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)
|
280
|
+
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)
|
248
281
|
|
249
|
-
new_scenario_report = URBANopt::
|
282
|
+
new_scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new
|
250
283
|
new_scenario_report.id = scenario_report.id
|
251
284
|
new_scenario_report.name = scenario_report.name
|
252
285
|
new_scenario_report.directory_name = scenario_report.directory_name
|
253
286
|
|
254
287
|
timeseries_hash = { column_names: scenario_report.timeseries_csv.column_names }
|
255
|
-
new_scenario_report.timeseries_csv = URBANopt::
|
288
|
+
new_scenario_report.timeseries_csv = URBANopt::Reporting::DefaultReports::TimeseriesCSV.new(timeseries_hash)
|
256
289
|
|
257
290
|
new_feature_reports.each do |feature_report|
|
258
291
|
new_scenario_report.add_feature_report(feature_report)
|
@@ -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,11 +28,12 @@
|
|
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 'matrix'
|
34
34
|
require 'csv'
|
35
35
|
require 'time'
|
36
|
+
require_relative 'utilities'
|
36
37
|
|
37
38
|
module URBANopt # :nodoc:
|
38
39
|
module REopt # :nodoc:
|
@@ -51,7 +52,7 @@ module URBANopt # :nodoc:
|
|
51
52
|
#
|
52
53
|
# [*parameters:*]
|
53
54
|
#
|
54
|
-
# * +scenario_report+ - _URBANopt::
|
55
|
+
# * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting the +reopt_assumptions_hash+, if provided, to a \REopt Lite post. Otherwise, if the +reopt_assumptions_hash+ is nil a default post will be updated from this ScenarioReport and submitted to the \REopt Lite API.
|
55
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 ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API.
|
56
57
|
#
|
57
58
|
# [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
|
@@ -70,31 +71,32 @@ module URBANopt # :nodoc:
|
|
70
71
|
end
|
71
72
|
|
72
73
|
# Update required info
|
73
|
-
if scenario_report.location.
|
74
|
+
if scenario_report.location.latitude_deg.nil? || scenario_report.location.longitude_deg.nil? || (scenario_report.location.latitude_deg == 0) || (scenario_report.location.longitude_deg == 0)
|
74
75
|
if !scenario_report.feature_reports.nil? && (scenario_report.feature_reports != [])
|
75
76
|
lats = []
|
76
77
|
longs = []
|
77
78
|
scenario_report.feature_reports.each do |x|
|
78
|
-
|
79
|
-
|
80
|
-
|
79
|
+
puts " ERROR: #{x.location.latitude_deg}"
|
80
|
+
if ![nil].include?(x.location.latitude_deg) && ![nil].include?(x.location.longitude_deg)
|
81
|
+
lats.push(x.location.latitude_deg)
|
82
|
+
longs.push(x.location.longitude_deg)
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
84
86
|
if !lats.empty? && !longs.empty?
|
85
|
-
scenario_report.location.
|
86
|
-
scenario_report.location.
|
87
|
+
scenario_report.location.latitude_deg = lats.reduce(:+) / lats.size.to_f
|
88
|
+
scenario_report.location.longitude_deg = longs.reduce(:+) / longs.size.to_f
|
87
89
|
end
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
91
93
|
# Update required info
|
92
94
|
requireds_names = ['latitude', 'longitude']
|
93
|
-
requireds = [scenario_report.location.
|
95
|
+
requireds = [scenario_report.location.latitude_deg, scenario_report.location.longitude_deg]
|
94
96
|
|
95
97
|
if requireds.include?(nil) || requireds.include?(0)
|
96
|
-
requireds.each_with_index do |
|
97
|
-
if [nil
|
98
|
+
requireds.each_with_index do |x, i|
|
99
|
+
if [nil].include? x
|
98
100
|
n = requireds_names[i]
|
99
101
|
raise "Missing value for #{n} - this is a required input"
|
100
102
|
end
|
@@ -103,20 +105,25 @@ module URBANopt # :nodoc:
|
|
103
105
|
|
104
106
|
reopt_inputs[:Scenario][:description] = description
|
105
107
|
|
106
|
-
reopt_inputs[:Scenario][:Site][:latitude] = scenario_report.location.
|
107
|
-
reopt_inputs[:Scenario][:Site][:longitude] = scenario_report.location.
|
108
|
+
reopt_inputs[:Scenario][:Site][:latitude] = scenario_report.location.latitude_deg
|
109
|
+
reopt_inputs[:Scenario][:Site][:longitude] = scenario_report.location.longitude_deg
|
108
110
|
|
109
111
|
# Update optional info
|
110
|
-
|
111
|
-
|
112
|
+
# REK: attribute names should be updated
|
113
|
+
if reopt_inputs[:Scenario][:Site][:roof_squarefeet].nil?
|
114
|
+
if !scenario_report.program.roof_area_sqft.nil?
|
115
|
+
reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area_sqft[:available_roof_area_sqft]
|
116
|
+
end
|
112
117
|
end
|
113
118
|
|
114
|
-
if
|
115
|
-
|
119
|
+
if reopt_inputs[:Scenario][:Site][:land_acres].nil?
|
120
|
+
if !scenario_report.program.site_area_sqft.nil?
|
121
|
+
reopt_inputs[:Scenario][:Site][:land_acres] = scenario_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
|
122
|
+
end
|
116
123
|
end
|
117
124
|
|
118
|
-
|
119
|
-
reopt_inputs[:Scenario][:time_steps_per_hour] =
|
125
|
+
if reopt_inputs[:Scenario][:time_steps_per_hour].nil?
|
126
|
+
reopt_inputs[:Scenario][:time_steps_per_hour] = 1
|
120
127
|
end
|
121
128
|
|
122
129
|
# Update load profile info
|
@@ -126,27 +133,51 @@ module URBANopt # :nodoc:
|
|
126
133
|
energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * scenario_report.timesteps_per_hour || 0) ) }
|
127
134
|
if energy_timeseries_kw.length < (scenario_report.timesteps_per_hour * 8760)
|
128
135
|
start_date = Time.parse(t.by_col["Datetime"][0])
|
129
|
-
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) /
|
136
|
+
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) / \
|
130
137
|
(( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int
|
131
138
|
end_date = Time.parse(t.by_col["Datetime"][-1])
|
132
|
-
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) /
|
139
|
+
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) / \
|
133
140
|
(( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int
|
134
141
|
energy_timeseries_kw = [0.0]*(start_ts-1) + energy_timeseries_kw + [0.0]*((scenario_report.timesteps_per_hour * 8760) - end_ts)
|
135
142
|
end
|
136
|
-
|
143
|
+
energy_timeseries_kw = energy_timeseries_kw.map { |e| e ? e : 0 }[0,(scenario_report.timesteps_per_hour * 8760)]
|
137
144
|
rescue StandardError
|
138
145
|
@@logger.error("Could not parse the annual electric load from the timeseries csv - #{scenario_report.timeseries_csv.path}")
|
139
146
|
raise "Could not parse the annual electric load from the timeseries csv - #{scenario_report.timeseries_csv.path}"
|
140
147
|
end
|
148
|
+
|
149
|
+
# Convert load to REopt Resolution
|
150
|
+
begin
|
151
|
+
reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = convert_powerflow_resolution(energy_timeseries_kw, scenario_report.timesteps_per_hour, reopt_inputs[:Scenario][:time_steps_per_hour])
|
152
|
+
rescue
|
153
|
+
@@logger.error("Could not convert the annual electric load from a resolution of #{scenario_report.timesteps_per_hour} to #{reopt_inputs[:Scenario][:time_steps_per_hour]}")
|
154
|
+
raise "Could not convert the annual electric load from a resolution of #{scenario_report.timesteps_per_hour} to #{reopt_inputs[:Scenario][:time_steps_per_hour]}"
|
155
|
+
end
|
156
|
+
|
157
|
+
if reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps].nil?
|
158
|
+
n_top_values = 100
|
159
|
+
tmp1 = reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw]
|
160
|
+
tmp2 = tmp1.each_index.max_by(n_top_values*reopt_inputs[:Scenario][:time_steps_per_hour]){|i| tmp1[i]}
|
161
|
+
for i in (0...tmp2.count)
|
162
|
+
tmp2[i] += 1
|
163
|
+
end
|
164
|
+
reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps] = tmp2
|
165
|
+
end
|
166
|
+
|
167
|
+
if reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_charge_us_dollars_per_kw].nil?
|
168
|
+
reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_charge_us_dollars_per_kw] = 0
|
169
|
+
end
|
170
|
+
|
141
171
|
return reopt_inputs
|
142
172
|
end
|
143
173
|
|
174
|
+
|
144
175
|
##
|
145
176
|
# Converts a FeatureReport list from a ScenarioReport into an array of \REopt Lite posts
|
146
177
|
#
|
147
178
|
# [*parameters:*]
|
148
179
|
#
|
149
|
-
# * +scenario_report+ - _URBANopt::
|
180
|
+
# * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting FeatureReports and respecitive +reopt_assumptions_hashes+, if provided, to a \REopt Lite post. If no +reopt_assumptions_hashes+ are provided default posts will be updated from these FeatureReports and submitted to the \REopt Lite API.
|
150
181
|
# * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of hashes formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API. The order should match the list in ScenarioReport.feature_reports.
|
151
182
|
#
|
152
183
|
# [*return:*] _Array_ - Returns an array of hashes formatted for submittal to the \REopt Lite API in the order of the FeatureReports lited in ScenarioReport.feature_reports.
|
@@ -168,18 +199,18 @@ module URBANopt # :nodoc:
|
|
168
199
|
#
|
169
200
|
# [*parameters:*]
|
170
201
|
#
|
171
|
-
# * +scenario_report+ - _URBANopt::
|
202
|
+
# * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to update from a \REopt Lite response.
|
172
203
|
# * +reopt_output+ - _Hash_ - A hash response from the \REopt Lite API.
|
173
204
|
# * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which new timeseries data will be written. If not provided a file is created based on the run_uuid of the \REopt Lite optimization task.
|
174
205
|
#
|
175
|
-
# [*return:*] _URBANopt::
|
206
|
+
# [*return:*] _URBANopt::Reporting::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
|
176
207
|
##
|
177
|
-
def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path =
|
208
|
+
def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil)
|
178
209
|
if reopt_output['outputs']['Scenario']['status'] != 'optimal'
|
179
210
|
@@logger.info("Warning cannot Feature Report #{scenario_report.name} #{scenario_report.id} - REopt optimization was non-optimal")
|
180
211
|
return scenario_report
|
181
212
|
end
|
182
|
-
|
213
|
+
|
183
214
|
$ts_per_hour = scenario_report.timesteps_per_hour
|
184
215
|
def scale_timeseries(input, ts_per_hr=$ts_per_hour)
|
185
216
|
if input.nil?
|
@@ -192,7 +223,7 @@ module URBANopt # :nodoc:
|
|
192
223
|
return input
|
193
224
|
end
|
194
225
|
result = []
|
195
|
-
input.each do |val|
|
226
|
+
input.each do |val|
|
196
227
|
(1..ts_per_hr).each do |x|
|
197
228
|
result.push(val/ts_per_hr.to_f)
|
198
229
|
end
|
@@ -201,11 +232,8 @@ module URBANopt # :nodoc:
|
|
201
232
|
end
|
202
233
|
|
203
234
|
# Update location
|
204
|
-
scenario_report.location.
|
205
|
-
scenario_report.location.
|
206
|
-
|
207
|
-
# Update timeseries csv from \REopt Lite dispatch data
|
208
|
-
scenario_report.timesteps_per_hour = reopt_output['inputs']['Scenario']['time_steps_per_hour']
|
235
|
+
scenario_report.location.latitude_deg = reopt_output['inputs']['Scenario']['Site']['latitude']
|
236
|
+
scenario_report.location.longitude_deg = reopt_output['inputs']['Scenario']['Site']['longitude']
|
209
237
|
|
210
238
|
# Update distributed generation sizing and financials
|
211
239
|
|
@@ -215,55 +243,61 @@ module URBANopt # :nodoc:
|
|
215
243
|
scenario_report.distributed_generation.year_one_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars'] || 0
|
216
244
|
scenario_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars'] || 0
|
217
245
|
scenario_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars'] || 0
|
218
|
-
|
246
|
+
scenario_report.distributed_generation.total_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_us_dollars'] || 0
|
247
|
+
scenario_report.distributed_generation.year_one_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_bau_us_dollars'] || 0
|
248
|
+
scenario_report.distributed_generation.year_one_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_bau_us_dollars'] || 0
|
249
|
+
scenario_report.distributed_generation.year_one_bill_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_bau_us_dollars'] || 0
|
250
|
+
scenario_report.distributed_generation.total_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_bau_us_dollars'] || 0
|
251
|
+
scenario_report.distributed_generation.total_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_bau_us_dollars'] || 0
|
252
|
+
if !resilience_stats.nil?
|
253
|
+
scenario_report.distributed_generation.resilience_hours_min = resilience_stats['resilience_hours_min']
|
254
|
+
scenario_report.distributed_generation.resilience_hours_max = resilience_stats['resilience_hours_max']
|
255
|
+
scenario_report.distributed_generation.resilience_hours_avg = resilience_stats['resilience_hours_avg']
|
256
|
+
scenario_report.distributed_generation.probs_of_surviving = resilience_stats['probs_of_surviving']
|
257
|
+
scenario_report.distributed_generation.probs_of_surviving_by_month = resilience_stats['probs_of_surviving_by_month']
|
258
|
+
scenario_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
|
259
|
+
end
|
260
|
+
|
219
261
|
if reopt_output['outputs']['Scenario']['Site']['PV'].class == Hash
|
220
262
|
reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
|
221
263
|
elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
|
222
264
|
reopt_output['outputs']['Scenario']['Site']['PV'] = []
|
223
265
|
end
|
224
|
-
|
225
|
-
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
226
|
-
scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::
|
266
|
+
|
267
|
+
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
268
|
+
scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
|
227
269
|
end
|
228
270
|
|
229
271
|
wind = reopt_output['outputs']['Scenario']['Site']['Wind']
|
230
272
|
if !wind['size_kw'].nil? and wind['size_kw'] != 0
|
231
|
-
scenario_report.distributed_generation.add_tech 'wind', URBANopt::
|
273
|
+
scenario_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
|
232
274
|
end
|
233
275
|
|
234
276
|
generator = reopt_output['outputs']['Scenario']['Site']['Generator']
|
235
277
|
if !generator['size_kw'].nil? and generator['size_kw'] != 0
|
236
|
-
scenario_report.distributed_generation.add_tech 'generator', URBANopt::
|
278
|
+
scenario_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
|
237
279
|
end
|
238
280
|
|
239
281
|
storage = reopt_output['outputs']['Scenario']['Site']['Storage']
|
240
282
|
if !storage['size_kw'].nil? and storage['size_kw'] != 0
|
241
|
-
scenario_report.distributed_generation.add_tech 'storage', URBANopt::
|
283
|
+
scenario_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
|
242
284
|
end
|
243
285
|
|
286
|
+
reopt_resolution = reopt_output['inputs']['Scenario']['time_steps_per_hour']
|
244
287
|
generation_timeseries_kwh = Matrix[[0] * (8760 * scenario_report.timesteps_per_hour)]
|
245
|
-
|
246
288
|
|
247
|
-
reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
|
289
|
+
reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
|
248
290
|
if (pv['size_kw'] || 0) > 0
|
249
291
|
if !pv['year_one_power_production_series_kw'].nil?
|
250
|
-
generation_timeseries_kwh += Matrix[pv['year_one_power_production_series_kw']]
|
292
|
+
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)]
|
251
293
|
end
|
252
294
|
end
|
253
295
|
end
|
254
296
|
|
255
|
-
unless reopt_output['outputs']['Scenario']['Site']['Storage'].nil?
|
256
|
-
if (reopt_output['outputs']['Scenario']['Site']['Storage']['size_kw'] or 0) > 0
|
257
|
-
if !reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'].nil?
|
258
|
-
generation_timeseries_kwh = generation_timeseries_kwh + Matrix[reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw']]
|
259
|
-
end
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
297
|
unless reopt_output['outputs']['Scenario']['Site']['Wind'].nil?
|
264
298
|
if (reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0
|
265
299
|
if !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
|
266
|
-
generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw']]
|
300
|
+
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)]
|
267
301
|
end
|
268
302
|
end
|
269
303
|
end
|
@@ -271,7 +305,7 @@ module URBANopt # :nodoc:
|
|
271
305
|
unless reopt_output['outputs']['Scenario']['Site']['Generator'].nil?
|
272
306
|
if (reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0) > 0
|
273
307
|
if !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
|
274
|
-
generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw']]
|
308
|
+
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)]
|
275
309
|
end
|
276
310
|
end
|
277
311
|
end
|
@@ -283,70 +317,70 @@ module URBANopt # :nodoc:
|
|
283
317
|
scenario_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Total(kw)')
|
284
318
|
end
|
285
319
|
|
286
|
-
$load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['LoadProfile']['year_one_electric_load_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
320
|
+
$load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['LoadProfile']['year_one_electric_load_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
287
321
|
$load_col = scenario_report.timeseries_csv.column_names.index('REopt:Electricity:Load:Total(kw)')
|
288
322
|
if $load_col.nil?
|
289
323
|
$load_col = scenario_report.timeseries_csv.column_names.length
|
290
324
|
scenario_report.timeseries_csv.column_names.push('REopt:Electricity:Load:Total(kw)')
|
291
325
|
end
|
292
326
|
|
293
|
-
$utility_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_load_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
327
|
+
$utility_to_load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_load_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
294
328
|
$utility_to_load_col = scenario_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToLoad(kw)')
|
295
329
|
if $utility_to_load_col.nil?
|
296
330
|
$utility_to_load_col = scenario_report.timeseries_csv.column_names.length
|
297
331
|
scenario_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToLoad(kw)')
|
298
332
|
end
|
299
333
|
|
300
|
-
$utility_to_battery = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_battery_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
334
|
+
$utility_to_battery = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_battery_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
301
335
|
$utility_to_battery_col = scenario_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToBattery(kw)')
|
302
336
|
if $utility_to_battery_col.nil?
|
303
337
|
$utility_to_battery_col = scenario_report.timeseries_csv.column_names.length
|
304
338
|
scenario_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToBattery(kw)')
|
305
339
|
end
|
306
340
|
|
307
|
-
$storage_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_load_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
341
|
+
$storage_to_load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_load_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
308
342
|
$storage_to_load_col = scenario_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToLoad(kw)')
|
309
343
|
if $storage_to_load_col.nil?
|
310
344
|
$storage_to_load_col = scenario_report.timeseries_csv.column_names.length
|
311
345
|
scenario_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToLoad(kw)')
|
312
346
|
end
|
313
347
|
|
314
|
-
$storage_to_grid = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
348
|
+
$storage_to_grid = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
315
349
|
$storage_to_grid_col = scenario_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToGrid(kw)')
|
316
350
|
if $storage_to_grid_col.nil?
|
317
351
|
$storage_to_grid_col = scenario_report.timeseries_csv.column_names.length
|
318
352
|
scenario_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToGrid(kw)')
|
319
353
|
end
|
320
354
|
|
321
|
-
$storage_soc = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
355
|
+
$storage_soc = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
322
356
|
$storage_soc_col = scenario_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:StateOfCharge(pct)')
|
323
357
|
if $storage_soc_col.nil?
|
324
358
|
$storage_soc_col = scenario_report.timeseries_csv.column_names.length
|
325
359
|
scenario_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:StateOfCharge(pct)')
|
326
360
|
end
|
327
361
|
|
328
|
-
$generator_total = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
362
|
+
$generator_total = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
329
363
|
$generator_total_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:Total(kw)')
|
330
364
|
if $generator_total_col.nil?
|
331
365
|
$generator_total_col = scenario_report.timeseries_csv.column_names.length
|
332
366
|
scenario_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:Total(kw)')
|
333
367
|
end
|
334
368
|
|
335
|
-
$generator_to_battery = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_battery_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
369
|
+
$generator_to_battery = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_battery_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
336
370
|
$generator_to_battery_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToBattery(kw)')
|
337
371
|
if $generator_to_battery_col.nil?
|
338
372
|
$generator_to_battery_col = scenario_report.timeseries_csv.column_names.length
|
339
373
|
scenario_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToBattery(kw)')
|
340
374
|
end
|
341
375
|
|
342
|
-
$generator_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_load_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
376
|
+
$generator_to_load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_load_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
343
377
|
$generator_to_load_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToLoad(kw)')
|
344
378
|
if $generator_to_load_col.nil?
|
345
379
|
$generator_to_load_col = scenario_report.timeseries_csv.column_names.length
|
346
380
|
scenario_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToLoad(kw)')
|
347
381
|
end
|
348
382
|
|
349
|
-
$generator_to_grid = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_grid_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
383
|
+
$generator_to_grid = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_grid_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
350
384
|
$generator_to_grid_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToGrid(kw)')
|
351
385
|
if $generator_to_grid_col.nil?
|
352
386
|
$generator_to_grid_col = scenario_report.timeseries_csv.column_names.length
|
@@ -384,10 +418,10 @@ module URBANopt # :nodoc:
|
|
384
418
|
|
385
419
|
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
386
420
|
if (pv['size_kw'] || 0) > 0
|
387
|
-
$pv_total += Matrix[scale_timeseries(pv['year_one_power_production_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)]
|
388
|
-
$pv_to_battery += Matrix[scale_timeseries(pv['year_one_to_battery_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)]
|
389
|
-
$pv_to_load += Matrix[scale_timeseries(pv['year_one_to_load_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)]
|
390
|
-
$pv_to_grid += Matrix[scale_timeseries(pv['year_one_to_grid_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)]
|
421
|
+
$pv_total += Matrix[scale_timeseries(convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)]
|
422
|
+
$pv_to_battery += Matrix[scale_timeseries(convert_powerflow_resolution(pv['year_one_to_battery_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)]
|
423
|
+
$pv_to_load += Matrix[scale_timeseries(convert_powerflow_resolution(pv['year_one_to_load_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)]
|
424
|
+
$pv_to_grid += Matrix[scale_timeseries(convert_powerflow_resolution(pv['year_one_to_grid_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)]
|
391
425
|
end
|
392
426
|
end
|
393
427
|
|
@@ -396,28 +430,28 @@ module URBANopt # :nodoc:
|
|
396
430
|
$pv_to_load = $pv_to_load.to_a[0]
|
397
431
|
$pv_to_grid = $pv_to_grid.to_a[0]
|
398
432
|
|
399
|
-
$wind_total = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
433
|
+
$wind_total = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
400
434
|
$wind_total_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:Total(kw)')
|
401
435
|
if $wind_total_col.nil?
|
402
436
|
$wind_total_col = scenario_report.timeseries_csv.column_names.length
|
403
437
|
scenario_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:Total(kw)')
|
404
438
|
end
|
405
439
|
|
406
|
-
$wind_to_battery = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_battery_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
440
|
+
$wind_to_battery = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_battery_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
407
441
|
$wind_to_battery_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToBattery(kw)')
|
408
442
|
if $wind_to_battery_col.nil?
|
409
443
|
$wind_to_battery_col = scenario_report.timeseries_csv.column_names.length
|
410
444
|
scenario_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToBattery(kw)')
|
411
445
|
end
|
412
446
|
|
413
|
-
$wind_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_load_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
447
|
+
$wind_to_load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_load_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
414
448
|
$wind_to_load_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToLoad(kw)')
|
415
449
|
if $wind_to_load_col.nil?
|
416
450
|
$wind_to_load_col = scenario_report.timeseries_csv.column_names.length
|
417
451
|
scenario_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToLoad(kw)')
|
418
452
|
end
|
419
453
|
|
420
|
-
$wind_to_grid = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_grid_series_kw']) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
454
|
+
$wind_to_grid = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_grid_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)) || [0] * (8760 * scenario_report.timesteps_per_hour)
|
421
455
|
$wind_to_grid_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToGrid(kw)')
|
422
456
|
if $wind_to_grid_col.nil?
|
423
457
|
$wind_to_grid_col = scenario_report.timeseries_csv.column_names.length
|