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.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +28 -0
- data/Gemfile +1 -1
- data/Rakefile +2 -2
- data/docs/package-lock.json +19524 -1605
- data/docs/package.json +13 -6
- 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 +75 -51
- 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 +15 -29
@@ -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,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
|
-
|
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
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
if
|
253
|
-
|
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 =
|
257
|
+
resilience_stats = nil
|
256
258
|
end
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
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
|
-
|
271
|
-
|
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
|
-
|
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
|
-
|
298
|
-
|
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
|
-
|
301
|
-
new_scenario_report.
|
302
|
-
|
303
|
-
|
304
|
-
|
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
|
-
|
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
|