urbanopt-reopt 0.5.7 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +26 -0
- data/Gemfile +1 -1
- data/Rakefile +2 -2
- data/docs/package-lock.json +6084 -890
- data/docs/package.json +4 -1
- data/lib/urbanopt/reopt/feature_report_adapter.rb +58 -54
- data/lib/urbanopt/reopt/reopt_lite_api.rb +80 -48
- data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
- data/lib/urbanopt/reopt/reopt_post_processor.rb +74 -48
- data/lib/urbanopt/reopt/reopt_schema/reopt_input_schema.json +5 -0
- data/lib/urbanopt/reopt/reopt_schema/reopt_output_schema.json +5 -0
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +7 -7
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +60 -63
- data/lib/urbanopt/reopt/utilities.rb +106 -106
- data/lib/urbanopt/reopt/version.rb +1 -1
- data/urbanopt-reopt.gemspec +4 -7
- metadata +18 -32
@@ -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,
|
83
|
-
Dir.mkdir(File.join(@scenario_report.directory_name,
|
84
|
-
@@logger.info("Created directory:
|
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,
|
92
|
-
Dir.mkdir(File.join(fr.directory_name,
|
93
|
-
@@logger.info("Created directory:
|
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,7 +142,7 @@ 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
|
@@ -167,7 +166,7 @@ module URBANopt # :nodoc:
|
|
167
166
|
# * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
|
168
167
|
#
|
169
168
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
|
170
|
-
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)
|
171
170
|
if !reopt_assumptions_hash.nil?
|
172
171
|
@scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
|
173
172
|
end
|
@@ -181,7 +180,7 @@ module URBANopt # :nodoc:
|
|
181
180
|
api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
|
182
181
|
adapter = URBANopt::REopt::ScenarioReportAdapter.new
|
183
182
|
|
184
|
-
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)
|
185
184
|
|
186
185
|
reopt_output = api.reopt_request(reopt_input, @scenario_reopt_default_output_file)
|
187
186
|
if run_resilience
|
@@ -189,7 +188,7 @@ module URBANopt # :nodoc:
|
|
189
188
|
if File.directory? @scenario_reopt_default_output_file
|
190
189
|
resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file)
|
191
190
|
else
|
192
|
-
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'))
|
193
192
|
end
|
194
193
|
else
|
195
194
|
resilience_stats = nil
|
@@ -213,8 +212,7 @@ module URBANopt # :nodoc:
|
|
213
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.
|
214
213
|
#
|
215
214
|
# [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
|
216
|
-
def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil, run_resilience:true)
|
217
|
-
|
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)
|
218
216
|
if !reopt_assumptions_hashes.empty?
|
219
217
|
@feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
|
220
218
|
end
|
@@ -243,36 +241,60 @@ module URBANopt # :nodoc:
|
|
243
241
|
feature_adapter = URBANopt::REopt::FeatureReportAdapter.new
|
244
242
|
new_feature_reports = []
|
245
243
|
feature_reports.each_with_index do |feature_report, idx|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
if
|
252
|
-
|
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
|
253
256
|
else
|
254
|
-
resilience_stats =
|
257
|
+
resilience_stats = nil
|
255
258
|
end
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
else
|
265
|
-
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
|
266
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}")
|
267
271
|
end
|
268
|
-
|
269
|
-
|
272
|
+
else
|
273
|
+
puts('Output file already exists...skipping')
|
270
274
|
end
|
271
275
|
end
|
272
276
|
|
273
277
|
return new_feature_reports
|
274
278
|
end
|
275
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
|
+
|
276
298
|
# Updates a ScenarioReport based on an optional set of \REopt Lite optimization assumptions.
|
277
299
|
##
|
278
300
|
#
|
@@ -284,22 +306,26 @@ module URBANopt # :nodoc:
|
|
284
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.
|
285
307
|
#
|
286
308
|
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
|
287
|
-
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)
|
288
|
-
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)
|
289
|
-
|
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
|
290
313
|
new_scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new
|
291
|
-
|
292
|
-
new_scenario_report.name = scenario_report.name
|
293
|
-
new_scenario_report.directory_name = scenario_report.directory_name
|
314
|
+
if !new_feature_reports.empty?
|
294
315
|
|
295
|
-
|
296
|
-
|
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
|
297
319
|
|
298
|
-
|
299
|
-
new_scenario_report.
|
300
|
-
|
301
|
-
|
302
|
-
|
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
|
303
329
|
end
|
304
330
|
return new_scenario_report
|
305
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
|
-
|
48
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
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
|
-
|
130
|
-
if !
|
131
|
-
reopt_inputs[:Scenario][:Site][:land_acres] =
|
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[
|
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
|
-
((
|
148
|
-
end_date = Time.parse(t.by_col[
|
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
|
-
((
|
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
|
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
|
-
|
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'].
|
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',
|
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?
|
263
|
-
scenario_report.distributed_generation.add_tech 'wind',
|
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?
|
268
|
-
scenario_report.distributed_generation.add_tech 'generator',
|
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?
|
273
|
-
scenario_report.distributed_generation.add_tech 'storage',
|
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
|
-
|
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
|
-
|
290
|
+
end
|
286
291
|
|
287
|
-
|
288
|
-
|
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
|
-
|
296
|
-
|
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
|
-
((
|
480
|
-
(start_date.min * 60.0) + start_date.sec
|
481
|
-
((
|
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
|