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.
@@ -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,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.to_json
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.write(response.body)
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
- data = JSON.parse(response.body)
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
- File.open(filename, 'w') do |f|
234
- f.write(data.to_json)
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/scenario/default_reports'
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::Scenario::ScenarioDefaultPostProcessor - used in creating default output file names in \REopt Lite optimizations.
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 !Dir.exist?(File.join(@scenario_report.directory_name, "reopt"))
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::Scenario::DefaultReports::FeatureReport_ - FeatureReport which will be used in creating and then updated by a \REopt Lite opimization response.
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::Scenario::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
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
- result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path)
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::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.
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::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.
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
- 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)
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::Scenario::DefaultReports::ScenarioReport_ which will each be used to create (and is subsquently updated by) \REopt Lite opimization responses for each of its FeatureReports.
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::Scenario::DefaultReports::ScenarioReport.new
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::Scenario::DefaultReports::TimeseriesCSV.new(timeseries_hash)
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/scenario/default_reports'
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::Scenario::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
+ # * +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.latitude.nil? || scenario_report.location.longitude.nil? || (scenario_report.location.latitude == 0) || (scenario_report.location.longitude == 0)
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
- if ![nil, 0].include?(x[:location][:latitude]) && ![nil, 0].include?(x[:location][:longitude])
79
- lats.push(x[:location][:latitude])
80
- longs.push(x[:location][:longitude])
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.latitude = lats.reduce(:+) / lats.size.to_f
86
- scenario_report.location.longitude = longs.reduce(:+) / longs.size.to_f
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.latitude, scenario_report.location.longitude]
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 |i, x|
97
- if [nil, 0].include? x
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.latitude
107
- reopt_inputs[:Scenario][:Site][:longitude] = scenario_report.location.longitude
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
- if !scenario_report.program.roof_area.nil?
111
- reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area[:available_roof_area]
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 !scenario_report.program.site_area.nil?
115
- reopt_inputs[:Scenario][:Site][:land_acres] = scenario_report.program.site_area * 1.0 / 43560 # acres/sqft
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
- unless scenario_report.timesteps_per_hour.nil?
119
- reopt_inputs[:Scenario][:time_steps_per_hour] = scenario_report.timesteps_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
- reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = energy_timeseries_kw.map { |e| e ? e : 0 }[0,(scenario_report.timesteps_per_hour * 8760)]
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::Scenario::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.
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::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to update from a \REopt Lite response.
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::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
206
+ # [*return:*] _URBANopt::Reporting::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
176
207
  ##
177
- def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path = nil)
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.latitude = reopt_output['inputs']['Scenario']['Site']['latitude']
205
- scenario_report.location.longitude = reopt_output['inputs']['Scenario']['Site']['longitude']
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::Scenario::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
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::Scenario::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
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::Scenario::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
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::Scenario::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
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