urbanopt-reopt 0.1.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- end
139
+ end
121
140
 
122
141
  request = Net::HTTP::Post.new(@uri_submit, header)
123
- request.body = data.to_json
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.to_json
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.write(response.body)
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,8 +290,18 @@ module URBANopt # :nodoc:
195
290
 
196
291
  while status == 'Optimizing...'
197
292
  response = make_request(http, request)
198
- data = JSON.parse(response.body)
199
- sizes = (data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
293
+
294
+ data = JSON.parse(response.body, allow_nan:true)
295
+
296
+ if data['outputs']['Scenario']['Site']['PV'].kind_of?(Array)
297
+ pv_sizes = 0
298
+ data['outputs']['Scenario']['Site']['PV'].each do |x|
299
+ pv_sizes = pv_sizes + x['size_kw'].to_f
300
+ end
301
+ else
302
+ pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
303
+ end
304
+ sizes = pv_sizes + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
200
305
  status = data['outputs']['Scenario']['status']
201
306
 
202
307
  sleep 5
@@ -208,14 +313,28 @@ module URBANopt # :nodoc:
208
313
  while (_tries < _max_retry) && check_complete
209
314
  sleep 1
210
315
  response = make_request(http, request)
211
- data = JSON.parse(response.body)
212
- sizes = (data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
316
+ data = JSON.parse(response.body, allow_nan:true)
317
+ if data['outputs']['Scenario']['Site']['PV'].kind_of?(Array)
318
+ pv_sizes = 0
319
+ data['outputs']['Scenario']['Site']['PV'].each do |x|
320
+ pv_sizes = pv_sizes + x['size_kw'].to_f
321
+ end
322
+ else
323
+ pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
324
+ end
325
+ sizes = pv_sizes + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
213
326
  (check_complete = sizes == 0) && ((data['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0) > 0)
214
327
  _tries += 1
215
328
  end
216
329
 
217
- File.open(filename, 'w') do |f|
218
- f.write(data.to_json)
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}")
219
338
  end
220
339
 
221
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,
@@ -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,7 +29,7 @@
29
29
  # *********************************************************************************
30
30
 
31
31
  require 'bundler/setup'
32
- require 'urbanopt/scenario/default_reports'
32
+ require 'urbanopt/reporting/default_reports'
33
33
  require 'urbanopt/reopt/reopt_logger'
34
34
  require 'urbanopt/reopt'
35
35
  require 'csv'
@@ -43,7 +43,7 @@ module URBANopt # :nodoc:
43
43
  #
44
44
  # [*parameters:*]
45
45
  #
46
- # * +scenario_report+ - _ScenarioReport_ - Optional. A scenario report that has been returned from the URBANopt::Scenario::ScenarioDefaultPostProcessor - used in creating default output file names in \REopt Lite optimizations.
46
+ # * +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
47
  # * +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
48
  # * +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
49
  # * +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,11 +70,20 @@ module URBANopt # :nodoc:
70
70
  if !scenario_report.nil?
71
71
  @scenario_report = scenario_report
72
72
 
73
- @scenario_reopt_default_output_file = File.join(@scenario_report.directory_name, "scenario_report_#{@scenario_report.id}_reopt_run.json")
73
+ if !Dir.exist?(File.join(@scenario_report.directory_name, "reopt"))
74
+ Dir.mkdir(File.join(@scenario_report.directory_name, "reopt"))
75
+ @@logger.info("Created directory: " + File.join(@scenario_report.directory_name, "reopt"))
76
+ end
77
+
78
+ @scenario_reopt_default_output_file = File.join(@scenario_report.directory_name, "reopt/scenario_report_#{@scenario_report.id}_reopt_run.json")
74
79
  @scenario_timeseries_default_output_file = File.join(@scenario_report.directory_name, "scenario_report_#{@scenario_report.id}_timeseries.csv")
75
80
 
76
81
  @scenario_report.feature_reports.each do |fr|
77
- @feature_reports_reopt_default_output_files << File.join(fr.directory_name, "feature_report_#{fr.id}_reopt_run.json")
82
+ if !Dir.exist?(File.join(fr.directory_name, "reopt"))
83
+ Dir.mkdir(File.join(fr.directory_name, "reopt"))
84
+ @@logger.info("Created directory: " + File.join(fr.directory_name, "reopt"))
85
+ end
86
+ @feature_reports_reopt_default_output_files << File.join(fr.directory_name, "reopt/feature_report_#{fr.id}_reopt_run.json")
78
87
  end
79
88
 
80
89
  @scenario_report.feature_reports.each do |fr|
@@ -104,23 +113,37 @@ module URBANopt # :nodoc:
104
113
  #
105
114
  # [*parameters:*]
106
115
  #
107
- # * +feature_report+ - _URBANopt::Scenario::DefaultReports::FeatureReport_ - FeatureReport which will be used in creating and then updated by a \REopt Lite opimization response.
116
+ # * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport which will be used in creating and then updated by a \REopt Lite opimization response.
108
117
  # * +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)
109
118
  # * +reopt_output_file+ - _String_ - Optional. Path to a file at which REpopt Lite responses will be saved.
110
119
  # * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the FeatureReport will be saved.
111
120
  #
112
- # [*return:*] _URBANopt::Scenario::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
121
+ # [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
113
122
  ##
114
- def run_feature_report(feature_report, reopt_assumptions_hash = nil, reopt_output_file = nil, timeseries_csv_path = nil)
123
+ def run_feature_report(feature_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil, run_resilience:true)
115
124
  api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
116
125
  adapter = URBANopt::REopt::FeatureReportAdapter.new
117
126
 
118
127
  reopt_input = adapter.reopt_json_from_feature_report(feature_report, reopt_assumptions_hash)
119
128
  if reopt_output_file.nil?
120
- reopt_output_file = feature_report.directory_name
129
+ reopt_output_file = File.join(feature_report.directory_name, 'reopt')
121
130
  end
122
131
  reopt_output = api.reopt_request(reopt_input, reopt_output_file)
123
- return adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path)
132
+ if run_resilience
133
+ run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
134
+ if File.directory? reopt_output_file
135
+ resilience_stats = api.resilience_request(run_uuid, reopt_output_file)
136
+ else
137
+ resilience_stats = api.resilience_request(run_uuid, reopt_output_file.sub!('.json','_resilience.json'))
138
+ end
139
+ else
140
+ resilience_stats = nil
141
+ end
142
+ result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path, resilience_stats)
143
+ if !save_name.nil?
144
+ result.save_feature_report save_name
145
+ end
146
+ return result
124
147
  end
125
148
 
126
149
  ##
@@ -129,13 +152,13 @@ module URBANopt # :nodoc:
129
152
  #
130
153
  # [*parameters:*]
131
154
  #
132
- # * +feature_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport which will be used in creating and then updated by a \REopt Lite opimization response.
155
+ # * +feature_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport which will be used in creating and then updated by a \REopt Lite opimization response.
133
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)
134
157
  # * +reopt_output_file+ - _String_ - Optional. Path to a file at which REpopt Lite responses will be saved.
135
158
  # * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
136
159
  #
137
160
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
138
- def run_scenario_report(scenario_report, reopt_assumptions_hash = nil, reopt_output_file = nil, timeseries_csv_path = 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)
139
162
  if !reopt_assumptions_hash.nil?
140
163
  @scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
141
164
  end
@@ -152,8 +175,22 @@ module URBANopt # :nodoc:
152
175
  reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash)
153
176
 
154
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
155
188
 
156
- return 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)
190
+ if !save_name.nil?
191
+ result.save save_name
192
+ end
193
+ return result
157
194
  end
158
195
 
159
196
  # Updates a set of FeatureReports based on an optional set of \REopt Lite optimization assumptions.
@@ -161,13 +198,14 @@ module URBANopt # :nodoc:
161
198
  #
162
199
  # [*parameters:*]
163
200
  #
164
- # * +feature_reports+ - _Array_ - An array of _URBANopt::Scenario::DefaultReports::FeatureReport_ objetcs which will each be used to create (and are subsquently updated by) a \REopt Lite opimization response.
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.
165
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.
166
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.
167
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.
168
205
  #
169
206
  # [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
170
- def run_feature_reports(feature_reports, reopt_assumptions_hashes = [], reopt_output_files = [], timeseries_csv_paths = [])
207
+ def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil, run_resilience:true)
208
+
171
209
  if !reopt_assumptions_hashes.empty?
172
210
  @feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
173
211
  end
@@ -182,7 +220,7 @@ module URBANopt # :nodoc:
182
220
 
183
221
  if @feature_reports_reopt_default_output_files.empty?
184
222
  feature_reports.each do |fr|
185
- @feature_reports_reopt_default_output_files << File.join(fr.directory_name, "feature_report_#{fr.id}_reopt_run.json")
223
+ @feature_reports_reopt_default_output_files << File.join(fr.directory_name, "reopt/feature_report_#{fr.id}_reopt_run.json")
186
224
  end
187
225
  end
188
226
 
@@ -198,12 +236,26 @@ module URBANopt # :nodoc:
198
236
  feature_reports.each_with_index do |feature_report, idx|
199
237
  begin
200
238
  reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx])
201
- if reopt_output_files[idx].nil?
202
- reopt_output_files[idx] = feature_report.directory_name
203
- end
204
239
  reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
205
- new_feature_report = feature_adapter.update_feature_report(feature_report, reopt_output, @feature_reports_timeseries_default_output_files[idx])
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)
206
251
  new_feature_reports.push(new_feature_report)
252
+ if !save_names.nil?
253
+ if save_names.length == feature_reports.length
254
+ new_feature_report.save_feature_report save_names[idx]
255
+ else
256
+ warn "Could not save feature reports - the number of save names provided did not match the number of feature reports"
257
+ end
258
+ end
207
259
  rescue StandardError
208
260
  @@logger.info("Could not optimize Feature Report #{feature_report.name} #{feature_report.id}")
209
261
  end
@@ -217,27 +269,29 @@ module URBANopt # :nodoc:
217
269
  #
218
270
  # [*parameters:*]
219
271
  #
220
- # * +scenario_report+ - _Array_ - A _URBANopt::Scenario::DefaultReports::ScenarioReport_ which will each be used to create (and is subsquently updated by) \REopt Lite opimization responses for each of its FeatureReports.
272
+ # * +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.
221
273
  # * +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.
222
274
  # * +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.
223
275
  # * +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.
224
276
  #
225
277
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
226
- def run_scenario_report_features(scenario_report, reopt_assumptions_hashes = [], reopt_output_files = [], feature_report_timeseries_csv_paths = [])
227
- new_feature_reports = run_feature_reports(scenario_report.feature_reports, reopt_assumptions_hashes, reopt_output_files, feature_report_timeseries_csv_paths)
278
+ 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)
279
+ 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)
228
280
 
229
- new_scenario_report = URBANopt::Scenario::DefaultReports::ScenarioReport.new
281
+ new_scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new
230
282
  new_scenario_report.id = scenario_report.id
231
283
  new_scenario_report.name = scenario_report.name
232
284
  new_scenario_report.directory_name = scenario_report.directory_name
233
285
 
234
286
  timeseries_hash = { column_names: scenario_report.timeseries_csv.column_names }
235
- new_scenario_report.timeseries_csv = URBANopt::Scenario::DefaultReports::TimeseriesCSV.new(timeseries_hash)
287
+ new_scenario_report.timeseries_csv = URBANopt::Reporting::DefaultReports::TimeseriesCSV.new(timeseries_hash)
236
288
 
237
289
  new_feature_reports.each do |feature_report|
238
290
  new_scenario_report.add_feature_report(feature_report)
239
291
  end
240
-
292
+ if !save_name_scenario_report.nil?
293
+ new_scenario_report.save save_name_scenario_report
294
+ end
241
295
  return new_scenario_report
242
296
  end
243
297
  end
@@ -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,