urbanopt-reopt 0.5.6 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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