urbanopt-reopt 0.5.6 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -79,18 +79,18 @@ module URBANopt # :nodoc:
79
79
  if !scenario_report.nil?
80
80
  @scenario_report = scenario_report
81
81
 
82
- if !Dir.exist?(File.join(@scenario_report.directory_name, "reopt"))
83
- Dir.mkdir(File.join(@scenario_report.directory_name, "reopt"))
84
- @@logger.info("Created directory: " + File.join(@scenario_report.directory_name, "reopt"))
82
+ if !Dir.exist?(File.join(@scenario_report.directory_name, 'reopt'))
83
+ Dir.mkdir(File.join(@scenario_report.directory_name, 'reopt'))
84
+ @@logger.info("Created directory: #{File.join(@scenario_report.directory_name, 'reopt')}")
85
85
  end
86
86
 
87
87
  @scenario_reopt_default_output_file = File.join(@scenario_report.directory_name, "reopt/scenario_report_#{@scenario_report.id}_reopt_run.json")
88
88
  @scenario_timeseries_default_output_file = File.join(@scenario_report.directory_name, "scenario_report_#{@scenario_report.id}_timeseries.csv")
89
89
 
90
90
  @scenario_report.feature_reports.each do |fr|
91
- if !Dir.exist?(File.join(fr.directory_name, "reopt"))
92
- Dir.mkdir(File.join(fr.directory_name, "reopt"))
93
- @@logger.info("Created directory: " + File.join(fr.directory_name, "reopt"))
91
+ if !Dir.exist?(File.join(fr.directory_name, 'reopt'))
92
+ Dir.mkdir(File.join(fr.directory_name, 'reopt'))
93
+ @@logger.info("Created directory: #{File.join(fr.directory_name, 'reopt')}")
94
94
  end
95
95
  @feature_reports_reopt_default_output_files << File.join(fr.directory_name, "reopt/feature_report_#{fr.id}_reopt_run.json")
96
96
  end
@@ -113,8 +113,7 @@ module URBANopt # :nodoc:
113
113
  end
114
114
  end
115
115
 
116
- attr_accessor :scenario_reopt_default_assumptions_hash, :scenario_reopt_default_output_file, :scenario_timeseries_default_output_file
117
- attr_accessor :feature_reports_reopt_default_assumption_hashes, :feature_reports_reopt_default_output_files, :feature_reports_timeseries_default_output_files
116
+ attr_accessor :scenario_reopt_default_assumptions_hash, :scenario_reopt_default_output_file, :scenario_timeseries_default_output_file, :feature_reports_reopt_default_assumption_hashes, :feature_reports_reopt_default_output_files, :feature_reports_timeseries_default_output_files
118
117
 
119
118
  ##
120
119
  # Updates a FeatureReport based on an optional set of \REopt Lite optimization assumptions.
@@ -129,7 +128,7 @@ module URBANopt # :nodoc:
129
128
  #
130
129
  # [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
131
130
  ##
132
- def run_feature_report(feature_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil, run_resilience:true)
131
+ def run_feature_report(feature_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: true)
133
132
  api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
134
133
  adapter = URBANopt::REopt::FeatureReportAdapter.new
135
134
 
@@ -143,15 +142,14 @@ module URBANopt # :nodoc:
143
142
  if File.directory? reopt_output_file
144
143
  resilience_stats = api.resilience_request(run_uuid, reopt_output_file)
145
144
  else
146
- resilience_stats = api.resilience_request(run_uuid, reopt_output_file.sub!('.json','_resilience.json'))
145
+ resilience_stats = api.resilience_request(run_uuid, reopt_output_file.sub!('.json', '_resilience.json'))
147
146
  end
148
147
  else
149
148
  resilience_stats = nil
150
149
  end
151
150
  result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path, resilience_stats)
152
151
  if !save_name.nil?
153
- #result.save_feature_report save_name
154
- result.save_json_report(save_name)
152
+ result.save save_name
155
153
  end
156
154
  return result
157
155
  end
@@ -168,7 +166,7 @@ module URBANopt # :nodoc:
168
166
  # * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
169
167
  #
170
168
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
171
- def run_scenario_report(scenario_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil, run_resilience:true)
169
+ def run_scenario_report(scenario_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: true, community_photovoltaic: nil)
172
170
  if !reopt_assumptions_hash.nil?
173
171
  @scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
174
172
  end
@@ -182,7 +180,7 @@ module URBANopt # :nodoc:
182
180
  api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
183
181
  adapter = URBANopt::REopt::ScenarioReportAdapter.new
184
182
 
185
- reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash)
183
+ reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash, community_photovoltaic)
186
184
 
187
185
  reopt_output = api.reopt_request(reopt_input, @scenario_reopt_default_output_file)
188
186
  if run_resilience
@@ -190,7 +188,7 @@ module URBANopt # :nodoc:
190
188
  if File.directory? @scenario_reopt_default_output_file
191
189
  resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file)
192
190
  else
193
- resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file.sub!('.json','_resilience.json'))
191
+ resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file.sub!('.json', '_resilience.json'))
194
192
  end
195
193
  else
196
194
  resilience_stats = nil
@@ -214,8 +212,7 @@ module URBANopt # :nodoc:
214
212
  # * +timeseries_csv_path+ - _Array_ - Optional. A array of paths to files at which the new timeseries CSV for the FeatureReports will be saved. The number and order of the paths should match the feature_reports array.
215
213
  #
216
214
  # [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
217
- def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil, run_resilience:true)
218
-
215
+ def run_feature_reports(feature_reports:, reopt_assumptions_hashes: [], reopt_output_files: [], timeseries_csv_paths: [], save_names: nil, run_resilience: true, keep_existing_output: false, groundmount_photovoltaic: nil)
219
216
  if !reopt_assumptions_hashes.empty?
220
217
  @feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
221
218
  end
@@ -244,37 +241,60 @@ module URBANopt # :nodoc:
244
241
  feature_adapter = URBANopt::REopt::FeatureReportAdapter.new
245
242
  new_feature_reports = []
246
243
  feature_reports.each_with_index do |feature_report, idx|
247
- begin
248
- reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx])
249
- reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
250
- if run_resilience
251
- run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
252
- if File.directory? @feature_reports_reopt_default_output_files[idx]
253
- resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx])
244
+ # check if we should rerun
245
+ if !(keep_existing_output && output_exists(@feature_reports_reopt_default_output_files[idx]))
246
+ begin
247
+ reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx], groundmount_photovoltaic)
248
+ reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
249
+ if run_resilience
250
+ run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
251
+ if File.directory? @feature_reports_reopt_default_output_files[idx]
252
+ resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx])
253
+ else
254
+ resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx].sub!('.json', '_resilience.json'))
255
+ end
254
256
  else
255
- resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx].sub!('.json','_resilience.json'))
257
+ resilience_stats = nil
256
258
  end
257
- else
258
- resilience_stats = nil
259
- end
260
- new_feature_report = feature_adapter.update_feature_report(feature_report, reopt_output, @feature_reports_timeseries_default_output_files[idx], resilience_stats)
261
- new_feature_reports.push(new_feature_report)
262
- if !save_names.nil?
263
- if save_names.length == feature_reports.length
264
- #new_feature_report.save_feature_report save_names[idx]
265
- new_feature_report.save_json_report save_names[idx]
266
- else
267
- warn "Could not save feature reports - the number of save names provided did not match the number of feature reports"
259
+ new_feature_report = feature_adapter.update_feature_report(feature_report, reopt_output, @feature_reports_timeseries_default_output_files[idx], resilience_stats)
260
+ new_feature_reports.push(new_feature_report)
261
+ if !save_names.nil?
262
+ if save_names.length == feature_reports.length
263
+ new_feature_report.save save_names[idx]
264
+ else
265
+ warn 'Could not save feature reports - the number of save names provided did not match the number of feature reports'
266
+ end
268
267
  end
268
+ rescue StandardError => e
269
+ @@logger.info("Could not optimize Feature Report #{feature_report.name} #{feature_report.id}")
270
+ @@logger.error("ERROR: #{e}")
269
271
  end
270
- rescue StandardError
271
- @@logger.info("Could not optimize Feature Report #{feature_report.name} #{feature_report.id}")
272
+ else
273
+ puts('Output file already exists...skipping')
272
274
  end
273
275
  end
274
276
 
275
277
  return new_feature_reports
276
278
  end
277
279
 
280
+ # Checks whether a feature has already been run by determining if output files already exists (for rate limit issues and larger projects)
281
+ ##
282
+ #
283
+ # [*parameters:*]
284
+ #
285
+ # * +output_file+ - _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.
286
+ # [*return:*] _Boolean_ - Returns true if file or nonempty directory exist
287
+ def output_exists(output_file)
288
+ res = false
289
+ if File.directory?(output_file) && !File.empty?(output_file)
290
+ res = true
291
+ elsif File.exist? output_file
292
+ res = true
293
+ end
294
+
295
+ return res
296
+ end
297
+
278
298
  # Updates a ScenarioReport based on an optional set of \REopt Lite optimization assumptions.
279
299
  ##
280
300
  #
@@ -286,22 +306,26 @@ module URBANopt # :nodoc:
286
306
  # * +feature_report_timeseries_csv_paths+ - _Array_ - Optional. An array of paths to files at which the new timeseries CSV for the FeatureReports will be saved. The number and order of the paths should match the array in ScenarioReport.feature_reports.
287
307
  #
288
308
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
289
- 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)
290
- 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)
291
-
309
+ def run_scenario_report_features(scenario_report:, reopt_assumptions_hashes: [], reopt_output_files: [], feature_report_timeseries_csv_paths: [], save_names_feature_reports: nil, save_name_scenario_report: nil, run_resilience: true, keep_existing_output: false, groundmount_photovoltaic: nil)
310
+ new_feature_reports = run_feature_reports(feature_reports: scenario_report.feature_reports, reopt_assumptions_hashes: reopt_assumptions_hashes, reopt_output_files: reopt_output_files, timeseries_csv_paths: feature_report_timeseries_csv_paths, save_names: save_names_feature_reports, run_resilience: run_resilience, keep_existing_output: keep_existing_output, groundmount_photovoltaic: groundmount_photovoltaic)
311
+ puts("KEEP EXISTING? #{keep_existing_output}")
312
+ # only do this if you have run feature reports
292
313
  new_scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new
293
- new_scenario_report.id = scenario_report.id
294
- new_scenario_report.name = scenario_report.name
295
- new_scenario_report.directory_name = scenario_report.directory_name
314
+ if !new_feature_reports.empty?
296
315
 
297
- timeseries_hash = { column_names: scenario_report.timeseries_csv.column_names }
298
- new_scenario_report.timeseries_csv = URBANopt::Reporting::DefaultReports::TimeseriesCSV.new(timeseries_hash)
316
+ new_scenario_report.id = scenario_report.id
317
+ new_scenario_report.name = scenario_report.name
318
+ new_scenario_report.directory_name = scenario_report.directory_name
299
319
 
300
- new_feature_reports.each do |feature_report|
301
- new_scenario_report.add_feature_report(feature_report)
302
- end
303
- if !save_name_scenario_report.nil?
304
- new_scenario_report.save save_name_scenario_report
320
+ timeseries_hash = { column_names: scenario_report.timeseries_csv.column_names }
321
+ new_scenario_report.timeseries_csv = URBANopt::Reporting::DefaultReports::TimeseriesCSV.new(timeseries_hash)
322
+
323
+ new_feature_reports.each do |feature_report|
324
+ new_scenario_report.add_feature_report(feature_report)
325
+ end
326
+ if !save_name_scenario_report.nil?
327
+ new_scenario_report.save save_name_scenario_report
328
+ end
305
329
  end
306
330
  return new_scenario_report
307
331
  end
@@ -371,6 +371,11 @@
371
371
  "description": "State rebate based on installed capacity",
372
372
  "min": 0
373
373
  },
374
+ "location": {
375
+ "default": "roof",
376
+ "type": "string",
377
+ "description": "Indicates location of PV. Available options are roof and ground."
378
+ },
374
379
  "max_kw": {
375
380
  "default": 1000000000.0,
376
381
  "max": 1000000000.0,
@@ -142,6 +142,11 @@
142
142
  "PV": {
143
143
  "type": "object",
144
144
  "properties": {
145
+ "location": {
146
+ "default": "roof",
147
+ "type": "string",
148
+ "description": "Indicates location of PV. Available options are roof and ground."
149
+ },
145
150
  "size_kw": {
146
151
  "type": "float",
147
152
  "description": "Optimal PV system size",
@@ -44,8 +44,10 @@ require 'urbanopt/scenario/simulation_dir_osw'
44
44
  require 'csv'
45
45
  require 'fileutils'
46
46
 
47
- module URBANopt # nodoc:
48
- module Scenario # nodoc:
47
+ # nodoc:
48
+ module URBANopt
49
+ # nodoc:
50
+ module Scenario
49
51
  class REoptScenarioCSV < ScenarioCSV
50
52
  ##
51
53
  # REoptScenarioCSV is an extension of ScenarioCSV which assigns a Simulation Mapper to each Feature in a FeatureFile using a simple CSV format.
@@ -101,10 +103,8 @@ module URBANopt # nodoc:
101
103
  mapper_class = row[2].chomp
102
104
  # Assume fourth columns, if exists, contains the name of the JSON file in the reopt_files_dir to use when running \REopt Lite for the feature report
103
105
 
104
- if row.length > 3
105
- if !@reopt_files_dir.nil?
106
- @reopt_feature_assumptions[idx - 1] = File.join(@reopt_files_dir, row[3].chomp)
107
- end
106
+ if row.length > 3 && !@reopt_files_dir.nil?
107
+ @reopt_feature_assumptions[idx - 1] = File.join(@reopt_files_dir, row[3].chomp)
108
108
  end
109
109
 
110
110
  # gets +features+ from the feature_file.
@@ -120,6 +120,6 @@ module URBANopt # nodoc:
120
120
  end
121
121
  return result
122
122
  end
123
- end
123
+ end
124
124
  end
125
125
  end
@@ -67,7 +67,7 @@ module URBANopt # :nodoc:
67
67
  #
68
68
  # [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
69
69
  ##
70
- def reopt_json_from_scenario_report(scenario_report, reopt_assumptions_json = nil)
70
+ def reopt_json_from_scenario_report(scenario_report, reopt_assumptions_json = nil, community_photovoltaic = nil)
71
71
  name = scenario_report.name.delete ' '
72
72
  scenario_id = scenario_report.id.delete ' '
73
73
  description = "scenario_report_#{name}_#{scenario_id}"
@@ -81,22 +81,20 @@ module URBANopt # :nodoc:
81
81
  end
82
82
 
83
83
  # Update required info
84
- 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)
85
- if !scenario_report.feature_reports.nil? && (scenario_report.feature_reports != [])
86
- lats = []
87
- longs = []
88
- scenario_report.feature_reports.each do |x|
89
- puts " ERROR: #{x.location.latitude_deg}"
90
- if ![nil].include?(x.location.latitude_deg) && ![nil].include?(x.location.longitude_deg)
91
- lats.push(x.location.latitude_deg)
92
- longs.push(x.location.longitude_deg)
93
- end
84
+ 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)) && (!scenario_report.feature_reports.nil? && (scenario_report.feature_reports != []))
85
+ lats = []
86
+ longs = []
87
+ scenario_report.feature_reports.each do |x|
88
+ puts " ERROR: #{x.location.latitude_deg}"
89
+ if ![nil].include?(x.location.latitude_deg) && ![nil].include?(x.location.longitude_deg)
90
+ lats.push(x.location.latitude_deg)
91
+ longs.push(x.location.longitude_deg)
94
92
  end
93
+ end
95
94
 
96
- if !lats.empty? && !longs.empty?
97
- scenario_report.location.latitude_deg = lats.reduce(:+) / lats.size.to_f
98
- scenario_report.location.longitude_deg = longs.reduce(:+) / longs.size.to_f
99
- end
95
+ if !lats.empty? && !longs.empty?
96
+ scenario_report.location.latitude_deg = lats.reduce(:+) / lats.size.to_f
97
+ scenario_report.location.longitude_deg = longs.reduce(:+) / longs.size.to_f
100
98
  end
101
99
  end
102
100
 
@@ -120,16 +118,15 @@ module URBANopt # :nodoc:
120
118
 
121
119
  # Update optional info
122
120
  # REK: attribute names should be updated
123
- if reopt_inputs[:Scenario][:Site][:roof_squarefeet].nil?
124
- if !scenario_report.program.roof_area_sqft.nil?
125
- reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area_sqft[:available_roof_area_sqft]
126
- end
121
+ if reopt_inputs[:Scenario][:Site][:roof_squarefeet].nil? && !scenario_report.program.roof_area_sqft.nil?
122
+ reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area_sqft[:available_roof_area_sqft]
127
123
  end
128
124
 
129
- if reopt_inputs[:Scenario][:Site][:land_acres].nil?
130
- if !scenario_report.program.site_area_sqft.nil?
131
- reopt_inputs[:Scenario][:Site][:land_acres] = scenario_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
125
+ begin
126
+ if reopt_inputs[:Scenario][:Site][:land_acres].nil? && !community_photovoltaic[0][:properties][:footprint_area].nil?
127
+ reopt_inputs[:Scenario][:Site][:land_acres] = community_photovoltaic[0][:properties][:footprint_area] * 1.0 / 43560 # acres/sqft
132
128
  end
129
+ rescue StandardError
133
130
  end
134
131
 
135
132
  if reopt_inputs[:Scenario][:time_steps_per_hour].nil?
@@ -140,17 +137,17 @@ module URBANopt # :nodoc:
140
137
  begin
141
138
  col_num = scenario_report.timeseries_csv.column_names.index('Electricity:Facility(kWh)')
142
139
  t = CSV.read(scenario_report.timeseries_csv.path, headers: true, converters: :numeric)
143
- energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * scenario_report.timesteps_per_hour || 0) ) }
140
+ energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * scenario_report.timesteps_per_hour || 0)) }
144
141
  if energy_timeseries_kw.length < (scenario_report.timesteps_per_hour * 8760)
145
- start_date = Time.parse(t.by_col["Datetime"][0])
142
+ start_date = Time.parse(t.by_col['Datetime'][0])
146
143
  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) / \
147
- (( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int
148
- end_date = Time.parse(t.by_col["Datetime"][-1])
144
+ ((60 / scenario_report.timesteps_per_hour) * 60)).to_int
145
+ end_date = Time.parse(t.by_col['Datetime'][-1])
149
146
  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) / \
150
- (( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int
151
- energy_timeseries_kw = [0.0]*(start_ts-1) + energy_timeseries_kw + [0.0]*((scenario_report.timesteps_per_hour * 8760) - end_ts)
147
+ ((60 / scenario_report.timesteps_per_hour) * 60)).to_int
148
+ energy_timeseries_kw = [0.0] * (start_ts - 1) + energy_timeseries_kw + [0.0] * ((scenario_report.timesteps_per_hour * 8760) - end_ts)
152
149
  end
153
- energy_timeseries_kw = energy_timeseries_kw.map { |e| e ? e : 0 }[0,(scenario_report.timesteps_per_hour * 8760)]
150
+ energy_timeseries_kw = energy_timeseries_kw.map { |e| e || 0 }[0, (scenario_report.timesteps_per_hour * 8760)]
154
151
  rescue StandardError
155
152
  @@logger.error("Could not parse the annual electric load from the timeseries csv - #{scenario_report.timeseries_csv.path}")
156
153
  raise "Could not parse the annual electric load from the timeseries csv - #{scenario_report.timeseries_csv.path}"
@@ -159,7 +156,7 @@ module URBANopt # :nodoc:
159
156
  # Convert load to REopt Resolution
160
157
  begin
161
158
  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])
162
- rescue
159
+ rescue StandardError
163
160
  @@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]}")
164
161
  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]}"
165
162
  end
@@ -167,9 +164,9 @@ module URBANopt # :nodoc:
167
164
  if reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps].nil?
168
165
  n_top_values = 100
169
166
  tmp1 = reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw]
170
- tmp2 = tmp1.each_index.max_by(n_top_values*reopt_inputs[:Scenario][:time_steps_per_hour]){|i| tmp1[i]}
167
+ tmp2 = tmp1.each_index.max_by(n_top_values * reopt_inputs[:Scenario][:time_steps_per_hour]) { |i| tmp1[i] }
171
168
  for i in (0...tmp2.count)
172
- tmp2[i] += 1
169
+ tmp2[i] += 1
173
170
  end
174
171
  reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps] = tmp2
175
172
  end
@@ -181,7 +178,6 @@ module URBANopt # :nodoc:
181
178
  return reopt_inputs
182
179
  end
183
180
 
184
-
185
181
  ##
186
182
  # Converts a FeatureReport list from a ScenarioReport into an array of \REopt Lite posts
187
183
  #
@@ -215,7 +211,7 @@ module URBANopt # :nodoc:
215
211
  #
216
212
  # [*return:*] _URBANopt::Reporting::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
217
213
  ##
218
- def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil)
214
+ def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path = nil, resilience_stats = nil)
219
215
  if reopt_output['outputs']['Scenario']['status'] != 'optimal'
220
216
  @@logger.info("Warning cannot Feature Report #{scenario_report.name} #{scenario_report.id} - REopt optimization was non-optimal")
221
217
  return scenario_report
@@ -248,56 +244,57 @@ module URBANopt # :nodoc:
248
244
  scenario_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
249
245
  end
250
246
 
251
- if reopt_output['outputs']['Scenario']['Site']['PV'].class == Hash
247
+ if reopt_output['outputs']['Scenario']['Site']['PV'].instance_of?(Hash)
252
248
  reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
253
249
  elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
254
250
  reopt_output['outputs']['Scenario']['Site']['PV'] = []
255
251
  end
256
252
 
253
+ # Store the PV name and location in a hash
254
+ location = {}
255
+ # Check whether multi PV assumption input file is used or single PV
256
+ if reopt_output['inputs']['Scenario']['Site']['PV'].is_a?(Array)
257
+ reopt_output['inputs']['Scenario']['Site']['PV'].each do |pv|
258
+ location[pv['pv_name']] = pv['location']
259
+ end
260
+ else
261
+ location[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['location']
262
+ end
263
+
257
264
  reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
258
- scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
265
+ scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i, location: location[pv['pv_name']] })
259
266
  end
260
267
 
261
268
  wind = reopt_output['outputs']['Scenario']['Site']['Wind']
262
- if !wind['size_kw'].nil? and wind['size_kw'] != 0
263
- scenario_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
269
+ if !wind['size_kw'].nil? && (wind['size_kw'] != 0)
270
+ scenario_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new({ size_kw: (wind['size_kw'] || 0) })
264
271
  end
265
272
 
266
273
  generator = reopt_output['outputs']['Scenario']['Site']['Generator']
267
- if !generator['size_kw'].nil? and generator['size_kw'] != 0
268
- scenario_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
274
+ if !generator['size_kw'].nil? && (generator['size_kw'] != 0)
275
+ scenario_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new({ size_kw: (generator['size_kw'] || 0) })
269
276
  end
270
277
 
271
278
  storage = reopt_output['outputs']['Scenario']['Site']['Storage']
272
- if !storage['size_kw'].nil? and storage['size_kw'] != 0
273
- scenario_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
279
+ if !storage['size_kw'].nil? && (storage['size_kw'] != 0)
280
+ scenario_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new({ size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
274
281
  end
275
282
 
276
283
  reopt_resolution = reopt_output['inputs']['Scenario']['time_steps_per_hour']
277
284
  generation_timeseries_kwh = Matrix[[0] * (8760 * scenario_report.timesteps_per_hour)]
278
285
 
279
286
  reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
280
- if (pv['size_kw'] || 0) > 0
281
- if !pv['year_one_power_production_series_kw'].nil?
282
- generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)]
283
- end
287
+ if (pv['size_kw'] || 0) > 0 && !pv['year_one_power_production_series_kw'].nil?
288
+ generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, scenario_report.timesteps_per_hour)]
284
289
  end
285
- end
290
+ end
286
291
 
287
- unless reopt_output['outputs']['Scenario']['Site']['Wind'].nil?
288
- if (reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0
289
- if !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
290
- 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)]
291
- end
292
- end
292
+ if !reopt_output['outputs']['Scenario']['Site']['Wind'].nil? && ((reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0) && !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
293
+ 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)]
293
294
  end
294
295
 
295
- unless reopt_output['outputs']['Scenario']['Site']['Generator'].nil?
296
- if (reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0) > 0
297
- if !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
298
- 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)]
299
- end
300
- end
296
+ if !reopt_output['outputs']['Scenario']['Site']['Generator'].nil? && ((reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0) > 0) && !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
297
+ 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)]
301
298
  end
302
299
 
303
300
  $generation_timeseries_kwh = generation_timeseries_kwh.to_a[0] || [0] * (8760 * scenario_report.timesteps_per_hour)
@@ -476,13 +473,13 @@ module URBANopt # :nodoc:
476
473
  start_ts = (
477
474
  (
478
475
  ((start_date.yday - 1) * 60.0 * 60.0 * 24) +
479
- (((start_date.hour) - 1) * 60.0 * 60.0) +
480
- (start_date.min * 60.0) + start_date.sec ) /
481
- (( 60 / scenario_report.timesteps_per_hour ) * 60)
476
+ ((start_date.hour - 1) * 60.0 * 60.0) +
477
+ (start_date.min * 60.0) + start_date.sec) /
478
+ ((60 / scenario_report.timesteps_per_hour) * 60)
482
479
  ).to_int
483
480
  mod_data = old_data.map.with_index do |x, i|
484
481
  if i > 0
485
- modrow(x, start_ts + i -1)
482
+ modrow(x, start_ts + i - 1)
486
483
  else
487
484
  x
488
485
  end