urbanopt-reopt 0.2.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -29,9 +29,8 @@
29
29
  # *********************************************************************************
30
30
 
31
31
  require 'bundler/setup'
32
- require 'urbanopt/scenario/default_reports'
32
+ require 'urbanopt/reporting/default_reports'
33
33
  require 'urbanopt/reopt/reopt_logger'
34
- require 'urbanopt/reopt'
35
34
  require 'csv'
36
35
 
37
36
  module URBANopt # :nodoc:
@@ -43,7 +42,7 @@ module URBANopt # :nodoc:
43
42
  #
44
43
  # [*parameters:*]
45
44
  #
46
- # * +scenario_report+ - _ScenarioReport_ - Optional. A scenario report that has been returned from the URBANopt::Scenario::ScenarioDefaultPostProcessor - used in creating default output file names in \REopt Lite optimizations.
45
+ # * +scenario_report+ - _ScenarioReport_ - Optional. A scenario report that has been returned from the URBANopt::Reporting::ScenarioDefaultPostProcessor - used in creating default output file names in \REopt Lite optimizations.
47
46
  # * +scenario_reopt_assumptions_file+ - _String_ - Optional. JSON file formatted for a \REopt Lite analysis containing custom input parameters for optimizations at the Scenario Report level
48
47
  # * +reopt_feature_assumptions+ - _Array_ - Optional. A list of JSON file formatted for a \REopt Lite analysis containing custom input parameters for optimizations at the Feature Report level. The order and number of files must match the Feature Reports in the scenario_report input.
49
48
  # * +use_localhost+ - _Bool_ - If this is true, requests will be sent to a version of the \REopt Lite API running on localhost. Default is false, such that the production version of \REopt Lite is accessed.
@@ -70,8 +69,9 @@ module URBANopt # :nodoc:
70
69
  if !scenario_report.nil?
71
70
  @scenario_report = scenario_report
72
71
 
73
- if !Dir.exist?(File.join(@scenario_report.directory_name, "reopt"))
72
+ if !Dir.exist?(File.join(@scenario_report.directory_name, "reopt"))
74
73
  Dir.mkdir(File.join(@scenario_report.directory_name, "reopt"))
74
+ @@logger.info("Created directory: " + File.join(@scenario_report.directory_name, "reopt"))
75
75
  end
76
76
 
77
77
  @scenario_reopt_default_output_file = File.join(@scenario_report.directory_name, "reopt/scenario_report_#{@scenario_report.id}_reopt_run.json")
@@ -80,6 +80,7 @@ module URBANopt # :nodoc:
80
80
  @scenario_report.feature_reports.each do |fr|
81
81
  if !Dir.exist?(File.join(fr.directory_name, "reopt"))
82
82
  Dir.mkdir(File.join(fr.directory_name, "reopt"))
83
+ @@logger.info("Created directory: " + File.join(fr.directory_name, "reopt"))
83
84
  end
84
85
  @feature_reports_reopt_default_output_files << File.join(fr.directory_name, "reopt/feature_report_#{fr.id}_reopt_run.json")
85
86
  end
@@ -111,25 +112,36 @@ module URBANopt # :nodoc:
111
112
  #
112
113
  # [*parameters:*]
113
114
  #
114
- # * +feature_report+ - _URBANopt::Scenario::DefaultReports::FeatureReport_ - FeatureReport which will be used in creating and then updated by a \REopt Lite opimization response.
115
+ # * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport which will be used in creating and then updated by a \REopt Lite opimization response.
115
116
  # * +reopt_assumptions_hash+ - _Hash_ - Optional. A \REopt Lite formatted hash containing default parameters (i.e. utility rate, escalation rate) which will be updated by the FeatureReport (i.e. location, roof availability)
116
117
  # * +reopt_output_file+ - _String_ - Optional. Path to a file at which REpopt Lite responses will be saved.
117
118
  # * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the FeatureReport will be saved.
118
119
  #
119
- # [*return:*] _URBANopt::Scenario::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
120
+ # [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
120
121
  ##
121
- def run_feature_report(feature_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil)
122
+ def run_feature_report(feature_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil, run_resilience:true)
122
123
  api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
123
124
  adapter = URBANopt::REopt::FeatureReportAdapter.new
124
125
 
125
126
  reopt_input = adapter.reopt_json_from_feature_report(feature_report, reopt_assumptions_hash)
126
127
  if reopt_output_file.nil?
127
- reopt_output_file = feature_report.directory_name
128
+ reopt_output_file = File.join(feature_report.directory_name, 'reopt')
128
129
  end
129
130
  reopt_output = api.reopt_request(reopt_input, reopt_output_file)
130
- result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path)
131
+ if run_resilience
132
+ run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
133
+ if File.directory? reopt_output_file
134
+ resilience_stats = api.resilience_request(run_uuid, reopt_output_file)
135
+ else
136
+ resilience_stats = api.resilience_request(run_uuid, reopt_output_file.sub!('.json','_resilience.json'))
137
+ end
138
+ else
139
+ resilience_stats = nil
140
+ end
141
+ result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path, resilience_stats)
131
142
  if !save_name.nil?
132
- result.save_feature_report save_name
143
+ #result.save_feature_report save_name
144
+ result.save_json_report(save_name)
133
145
  end
134
146
  return result
135
147
  end
@@ -140,13 +152,13 @@ module URBANopt # :nodoc:
140
152
  #
141
153
  # [*parameters:*]
142
154
  #
143
- # * +feature_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport which will be used in creating and then updated by a \REopt Lite opimization response.
155
+ # * +feature_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport which will be used in creating and then updated by a \REopt Lite opimization response.
144
156
  # * +reopt_assumptions_hash+ - _Hash_ - Optional. A \REopt Lite formatted hash containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability)
145
157
  # * +reopt_output_file+ - _String_ - Optional. Path to a file at which REpopt Lite responses will be saved.
146
158
  # * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
147
159
  #
148
160
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
149
- def run_scenario_report(scenario_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil)
161
+ def run_scenario_report(scenario_report:, reopt_assumptions_hash:nil, reopt_output_file:nil, timeseries_csv_path:nil, save_name:nil, run_resilience:true)
150
162
  if !reopt_assumptions_hash.nil?
151
163
  @scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
152
164
  end
@@ -163,8 +175,18 @@ module URBANopt # :nodoc:
163
175
  reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash)
164
176
 
165
177
  reopt_output = api.reopt_request(reopt_input, @scenario_reopt_default_output_file)
178
+ if run_resilience
179
+ run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
180
+ if File.directory? @scenario_reopt_default_output_file
181
+ resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file)
182
+ else
183
+ resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file.sub!('.json','_resilience.json'))
184
+ end
185
+ else
186
+ resilience_stats = nil
187
+ end
166
188
 
167
- result = adapter.update_scenario_report(scenario_report, reopt_output, @scenario_timeseries_default_output_file)
189
+ result = adapter.update_scenario_report(scenario_report, reopt_output, @scenario_timeseries_default_output_file, resilience_stats)
168
190
  if !save_name.nil?
169
191
  result.save save_name
170
192
  end
@@ -176,13 +198,13 @@ module URBANopt # :nodoc:
176
198
  #
177
199
  # [*parameters:*]
178
200
  #
179
- # * +feature_reports+ - _Array_ - An array of _URBANopt::Scenario::DefaultReports::FeatureReport_ objetcs which will each be used to create (and are subsquently updated by) a \REopt Lite opimization response.
201
+ # * +feature_reports+ - _Array_ - An array of _URBANopt::Reporting::DefaultReports::FeatureReport_ objetcs which will each be used to create (and are subsquently updated by) a \REopt Lite opimization response.
180
202
  # * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of \REopt Lite formatted hashes containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability). The number and order of the hashes should match the feature_reports array.
181
203
  # * +reopt_output_files+ - _Array_ - Optional. A array of paths to files at which REpopt Lite responses will be saved. The number and order of the paths should match the feature_reports array.
182
204
  # * +timeseries_csv_path+ - _Array_ - Optional. A array of paths to files at which the new timeseries CSV for the FeatureReports will be saved. The number and order of the paths should match the feature_reports array.
183
205
  #
184
206
  # [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
185
- def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil)
207
+ def run_feature_reports(feature_reports:, reopt_assumptions_hashes:[], reopt_output_files:[], timeseries_csv_paths:[], save_names:nil, run_resilience:true)
186
208
 
187
209
  if !reopt_assumptions_hashes.empty?
188
210
  @feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
@@ -215,20 +237,31 @@ module URBANopt # :nodoc:
215
237
  begin
216
238
  reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx])
217
239
  reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
218
- new_feature_report = feature_adapter.update_feature_report(feature_report, reopt_output, @feature_reports_timeseries_default_output_files[idx])
240
+ if run_resilience
241
+ run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
242
+ if File.directory? @feature_reports_reopt_default_output_files[idx]
243
+ resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx])
244
+ else
245
+ resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx].sub!('.json','_resilience.json'))
246
+ end
247
+ else
248
+ resilience_stats = nil
249
+ end
250
+ new_feature_report = feature_adapter.update_feature_report(feature_report, reopt_output, @feature_reports_timeseries_default_output_files[idx], resilience_stats)
219
251
  new_feature_reports.push(new_feature_report)
220
252
  if !save_names.nil?
221
253
  if save_names.length == feature_reports.length
222
- new_feature_report.save_feature_report save_names[idx]
254
+ #new_feature_report.save_feature_report save_names[idx]
255
+ new_feature_report.save_json_report save_names[idx]
223
256
  else
224
257
  warn "Could not save feature reports - the number of save names provided did not match the number of feature reports"
225
258
  end
226
- end
259
+ end
227
260
  rescue StandardError
228
261
  @@logger.info("Could not optimize Feature Report #{feature_report.name} #{feature_report.id}")
229
262
  end
230
263
  end
231
-
264
+
232
265
  return new_feature_reports
233
266
  end
234
267
 
@@ -237,22 +270,22 @@ module URBANopt # :nodoc:
237
270
  #
238
271
  # [*parameters:*]
239
272
  #
240
- # * +scenario_report+ - _Array_ - A _URBANopt::Scenario::DefaultReports::ScenarioReport_ which will each be used to create (and is subsquently updated by) \REopt Lite opimization responses for each of its FeatureReports.
273
+ # * +scenario_report+ - _Array_ - A _URBANopt::Reporting::DefaultReports::ScenarioReport_ which will each be used to create (and is subsquently updated by) \REopt Lite opimization responses for each of its FeatureReports.
241
274
  # * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of \REopt Lite formatted hashes containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability). The number and order of the hashes should match the array in ScenarioReport.feature_reports.
242
275
  # * +reopt_output_files+ - _Array_ - Optional. An array of paths to files at which REpopt Lite responses will be saved. The number and order of the paths should match the array in ScenarioReport.feature_reports.
243
276
  # * +feature_report_timeseries_csv_paths+ - _Array_ - Optional. An array of paths to files at which the new timeseries CSV for the FeatureReports will be saved. The number and order of the paths should match the array in ScenarioReport.feature_reports.
244
277
  #
245
278
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
246
- def run_scenario_report_features(scenario_report:, reopt_assumptions_hashes:[], reopt_output_files:[], feature_report_timeseries_csv_paths:[], save_names_feature_reports:nil, save_name_scenario_report:nil)
247
- new_feature_reports = run_feature_reports(feature_reports:scenario_report.feature_reports, reopt_assumptions_hashes:reopt_assumptions_hashes, reopt_output_files:reopt_output_files, timeseries_csv_paths:feature_report_timeseries_csv_paths,save_names:save_names_feature_reports)
279
+ def run_scenario_report_features(scenario_report:, reopt_assumptions_hashes:[], reopt_output_files:[], feature_report_timeseries_csv_paths:[], save_names_feature_reports:nil, save_name_scenario_report:nil, run_resilience:true)
280
+ new_feature_reports = run_feature_reports(feature_reports:scenario_report.feature_reports, reopt_assumptions_hashes:reopt_assumptions_hashes, reopt_output_files:reopt_output_files, timeseries_csv_paths:feature_report_timeseries_csv_paths,save_names:save_names_feature_reports, run_resilience:run_resilience)
248
281
 
249
- new_scenario_report = URBANopt::Scenario::DefaultReports::ScenarioReport.new
282
+ new_scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new
250
283
  new_scenario_report.id = scenario_report.id
251
284
  new_scenario_report.name = scenario_report.name
252
285
  new_scenario_report.directory_name = scenario_report.directory_name
253
286
 
254
287
  timeseries_hash = { column_names: scenario_report.timeseries_csv.column_names }
255
- new_scenario_report.timeseries_csv = URBANopt::Scenario::DefaultReports::TimeseriesCSV.new(timeseries_hash)
288
+ new_scenario_report.timeseries_csv = URBANopt::Reporting::DefaultReports::TimeseriesCSV.new(timeseries_hash)
256
289
 
257
290
  new_feature_reports.each do |feature_report|
258
291
  new_scenario_report.add_feature_report(feature_report)
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -1,5 +1,5 @@
1
1
  # *********************************************************************************
2
- # URBANopt, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
2
+ # URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
3
3
  # contributors. All rights reserved.
4
4
  #
5
5
  # Redistribution and use in source and binary forms, with or without modification,
@@ -28,7 +28,7 @@
28
28
  # OF THE POSSIBILITY OF SUCH DAMAGE.
29
29
  # *********************************************************************************
30
30
 
31
- require 'urbanopt/scenario/default_reports'
31
+ require 'urbanopt/reporting/default_reports'
32
32
  require 'urbanopt/reopt/reopt_logger'
33
33
  require 'matrix'
34
34
  require 'csv'
@@ -51,7 +51,7 @@ module URBANopt # :nodoc:
51
51
  #
52
52
  # [*parameters:*]
53
53
  #
54
- # * +scenario_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting the +reopt_assumptions_hash+, if provided, to a \REopt Lite post. Otherwise, if the +reopt_assumptions_hash+ is nil a default post will be updated from this ScenarioReport and submitted to the \REopt Lite API.
54
+ # * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting the +reopt_assumptions_hash+, if provided, to a \REopt Lite post. Otherwise, if the +reopt_assumptions_hash+ is nil a default post will be updated from this ScenarioReport and submitted to the \REopt Lite API.
55
55
  # * +reopt_assumptions_hash+ - _Hash_ - Optional. A hash formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API.
56
56
  #
57
57
  # [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
@@ -70,31 +70,33 @@ module URBANopt # :nodoc:
70
70
  end
71
71
 
72
72
  # Update required info
73
- if scenario_report.location.latitude.nil? || scenario_report.location.longitude.nil? || (scenario_report.location.latitude == 0) || (scenario_report.location.longitude == 0)
73
+ if scenario_report.location.latitude_deg.nil? || scenario_report.location.longitude_deg.nil? || (scenario_report.location.latitude_deg == 0) || (scenario_report.location.longitude_deg == 0)
74
74
  if !scenario_report.feature_reports.nil? && (scenario_report.feature_reports != [])
75
75
  lats = []
76
76
  longs = []
77
77
  scenario_report.feature_reports.each do |x|
78
- if ![nil, 0].include?(x[:location][:latitude]) && ![nil, 0].include?(x[:location][:longitude])
79
- lats.push(x[:location][:latitude])
80
- longs.push(x[:location][:longitude])
78
+ puts " ERROR: #{x.location.latitude_deg}"
79
+ if ![nil].include?(x.location.latitude_deg) && ![nil].include?(x.location.longitude_deg)
80
+ lats.push(x.location.latitude_deg)
81
+ longs.push(x.location.longitude_deg)
81
82
  end
82
83
  end
83
84
 
84
85
  if !lats.empty? && !longs.empty?
85
- scenario_report.location.latitude = lats.reduce(:+) / lats.size.to_f
86
- scenario_report.location.longitude = longs.reduce(:+) / longs.size.to_f
86
+ scenario_report.location.latitude_deg = lats.reduce(:+) / lats.size.to_f
87
+ scenario_report.location.longitude_deg = longs.reduce(:+) / longs.size.to_f
87
88
  end
88
89
  end
89
90
  end
90
91
 
91
92
  # Update required info
92
93
  requireds_names = ['latitude', 'longitude']
93
- requireds = [scenario_report.location.latitude, scenario_report.location.longitude]
94
+ requireds = [scenario_report.location.latitude_deg, scenario_report.location.longitude_deg]
95
+
94
96
 
95
97
  if requireds.include?(nil) || requireds.include?(0)
96
- requireds.each_with_index do |i, x|
97
- if [nil, 0].include? x
98
+ requireds.each_with_index do |x, i|
99
+ if [nil].include? x
98
100
  n = requireds_names[i]
99
101
  raise "Missing value for #{n} - this is a required input"
100
102
  end
@@ -103,33 +105,38 @@ module URBANopt # :nodoc:
103
105
 
104
106
  reopt_inputs[:Scenario][:description] = description
105
107
 
106
- reopt_inputs[:Scenario][:Site][:latitude] = scenario_report.location.latitude
107
- reopt_inputs[:Scenario][:Site][:longitude] = scenario_report.location.longitude
108
+ reopt_inputs[:Scenario][:Site][:latitude] = scenario_report.location.latitude_deg
109
+ reopt_inputs[:Scenario][:Site][:longitude] = scenario_report.location.longitude_deg
108
110
 
109
111
  # Update optional info
110
- if !scenario_report.program.roof_area.nil?
111
- reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area[:available_roof_area]
112
+ # REK: attribute names should be updated
113
+ if !scenario_report.program.roof_area_sqft.nil?
114
+ reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area_sqft[:available_roof_area]
115
+ end
116
+
117
+ if !scenario_report.program.site_area_sqft.nil?
118
+ reopt_inputs[:Scenario][:Site][:land_acres] = scenario_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
112
119
  end
113
120
 
114
- if !scenario_report.program.site_area.nil?
115
- reopt_inputs[:Scenario][:Site][:land_acres] = scenario_report.program.site_area * 1.0 / 43560 # acres/sqft
121
+ unless scenario_report.timesteps_per_hour.nil?
122
+ reopt_inputs[:Scenario][:time_steps_per_hour] = scenario_report.timesteps_per_hour
116
123
  end
117
124
 
118
125
  # Update load profile info
119
126
  begin
120
127
  col_num = scenario_report.timeseries_csv.column_names.index('Electricity:Facility(kWh)')
121
128
  t = CSV.read(scenario_report.timeseries_csv.path, headers: true, converters: :numeric)
122
- energy_timeseries_kwh = t.by_col[col_num].map { |e| ((e || 0) ) }
123
- if energy_timeseries_kwh.length < (scenario_report.timesteps_per_hour * 8760)
129
+ energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * scenario_report.timesteps_per_hour || 0) ) }
130
+ if energy_timeseries_kw.length < (scenario_report.timesteps_per_hour * 8760)
124
131
  start_date = Time.parse(t.by_col["Datetime"][0])
125
- 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) /
132
+ 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) / \
126
133
  (( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int
127
134
  end_date = Time.parse(t.by_col["Datetime"][-1])
128
- 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) /
135
+ 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) / \
129
136
  (( 60 / scenario_report.timesteps_per_hour ) * 60)).to_int
130
- energy_timeseries_kwh = [0.0]*(start_ts-1) + energy_timeseries_kwh + [0.0]*((scenario_report.timesteps_per_hour * 8760) - end_ts)
137
+ energy_timeseries_kw = [0.0]*(start_ts-1) + energy_timeseries_kw + [0.0]*((scenario_report.timesteps_per_hour * 8760) - end_ts)
131
138
  end
132
- reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = energy_timeseries_kwh.map { |e| e ? e : 0 }[0,(scenario_report.timesteps_per_hour * 8760)]
139
+ reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = energy_timeseries_kw.map { |e| e ? e : 0 }[0,(scenario_report.timesteps_per_hour * 8760)]
133
140
  rescue StandardError
134
141
  @@logger.error("Could not parse the annual electric load from the timeseries csv - #{scenario_report.timeseries_csv.path}")
135
142
  raise "Could not parse the annual electric load from the timeseries csv - #{scenario_report.timeseries_csv.path}"
@@ -142,7 +149,7 @@ module URBANopt # :nodoc:
142
149
  #
143
150
  # [*parameters:*]
144
151
  #
145
- # * +scenario_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting FeatureReports and respecitive +reopt_assumptions_hashes+, if provided, to a \REopt Lite post. If no +reopt_assumptions_hashes+ are provided default posts will be updated from these FeatureReports and submitted to the \REopt Lite API.
152
+ # * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting FeatureReports and respecitive +reopt_assumptions_hashes+, if provided, to a \REopt Lite post. If no +reopt_assumptions_hashes+ are provided default posts will be updated from these FeatureReports and submitted to the \REopt Lite API.
146
153
  # * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of hashes formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API. The order should match the list in ScenarioReport.feature_reports.
147
154
  #
148
155
  # [*return:*] _Array_ - Returns an array of hashes formatted for submittal to the \REopt Lite API in the order of the FeatureReports lited in ScenarioReport.feature_reports.
@@ -164,18 +171,18 @@ module URBANopt # :nodoc:
164
171
  #
165
172
  # [*parameters:*]
166
173
  #
167
- # * +scenario_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to update from a \REopt Lite response.
174
+ # * +scenario_report+ - _URBANopt::Reporting::DefaultReports::ScenarioReport_ - ScenarioReport to update from a \REopt Lite response.
168
175
  # * +reopt_output+ - _Hash_ - A hash response from the \REopt Lite API.
169
176
  # * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which new timeseries data will be written. If not provided a file is created based on the run_uuid of the \REopt Lite optimization task.
170
177
  #
171
- # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
178
+ # [*return:*] _URBANopt::Reporting::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
172
179
  ##
173
- def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path = nil)
180
+ def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil)
174
181
  if reopt_output['outputs']['Scenario']['status'] != 'optimal'
175
182
  @@logger.info("Warning cannot Feature Report #{scenario_report.name} #{scenario_report.id} - REopt optimization was non-optimal")
176
183
  return scenario_report
177
184
  end
178
-
185
+
179
186
  $ts_per_hour = scenario_report.timesteps_per_hour
180
187
  def scale_timeseries(input, ts_per_hr=$ts_per_hour)
181
188
  if input.nil?
@@ -188,7 +195,7 @@ module URBANopt # :nodoc:
188
195
  return input
189
196
  end
190
197
  result = []
191
- input.each do |val|
198
+ input.each do |val|
192
199
  (1..ts_per_hr).each do |x|
193
200
  result.push(val/ts_per_hr.to_f)
194
201
  end
@@ -197,8 +204,8 @@ module URBANopt # :nodoc:
197
204
  end
198
205
 
199
206
  # Update location
200
- scenario_report.location.latitude = reopt_output['inputs']['Scenario']['Site']['latitude']
201
- scenario_report.location.longitude = reopt_output['inputs']['Scenario']['Site']['longitude']
207
+ scenario_report.location.latitude_deg = reopt_output['inputs']['Scenario']['Site']['latitude']
208
+ scenario_report.location.longitude_deg = reopt_output['inputs']['Scenario']['Site']['longitude']
202
209
 
203
210
  # Update timeseries csv from \REopt Lite dispatch data
204
211
  scenario_report.timesteps_per_hour = reopt_output['inputs']['Scenario']['time_steps_per_hour']
@@ -211,36 +218,50 @@ module URBANopt # :nodoc:
211
218
  scenario_report.distributed_generation.year_one_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars'] || 0
212
219
  scenario_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars'] || 0
213
220
  scenario_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars'] || 0
214
-
221
+ scenario_report.distributed_generation.total_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_us_dollars'] || 0
222
+ scenario_report.distributed_generation.year_one_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_bau_us_dollars'] || 0
223
+ scenario_report.distributed_generation.year_one_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_bau_us_dollars'] || 0
224
+ scenario_report.distributed_generation.year_one_bill_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_bau_us_dollars'] || 0
225
+ scenario_report.distributed_generation.total_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_bau_us_dollars'] || 0
226
+ scenario_report.distributed_generation.total_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_bau_us_dollars'] || 0
227
+ if !resilience_stats.nil?
228
+ scenario_report.distributed_generation.resilience_hours_min = resilience_stats['resilience_hours_min']
229
+ scenario_report.distributed_generation.resilience_hours_max = resilience_stats['resilience_hours_max']
230
+ scenario_report.distributed_generation.resilience_hours_avg = resilience_stats['resilience_hours_avg']
231
+ scenario_report.distributed_generation.probs_of_surviving = resilience_stats['probs_of_surviving']
232
+ scenario_report.distributed_generation.probs_of_surviving_by_month = resilience_stats['probs_of_surviving_by_month']
233
+ scenario_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
234
+ end
235
+
215
236
  if reopt_output['outputs']['Scenario']['Site']['PV'].class == Hash
216
237
  reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
217
238
  elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
218
239
  reopt_output['outputs']['Scenario']['Site']['PV'] = []
219
240
  end
220
-
221
- reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
222
- scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Scenario::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
241
+
242
+ reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
243
+ scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
223
244
  end
224
245
 
225
246
  wind = reopt_output['outputs']['Scenario']['Site']['Wind']
226
247
  if !wind['size_kw'].nil? and wind['size_kw'] != 0
227
- scenario_report.distributed_generation.add_tech 'wind', URBANopt::Scenario::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
248
+ scenario_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
228
249
  end
229
250
 
230
251
  generator = reopt_output['outputs']['Scenario']['Site']['Generator']
231
252
  if !generator['size_kw'].nil? and generator['size_kw'] != 0
232
- scenario_report.distributed_generation.add_tech 'generator', URBANopt::Scenario::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
253
+ scenario_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
233
254
  end
234
255
 
235
256
  storage = reopt_output['outputs']['Scenario']['Site']['Storage']
236
257
  if !storage['size_kw'].nil? and storage['size_kw'] != 0
237
- scenario_report.distributed_generation.add_tech 'storage', URBANopt::Scenario::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
258
+ scenario_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
238
259
  end
239
-
240
- generation_timeseries_kwh = Matrix[[0] * 8760]
241
260
 
242
-
243
- reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
261
+ generation_timeseries_kwh = Matrix[[0] * (8760 * scenario_report.timesteps_per_hour)]
262
+
263
+
264
+ reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
244
265
  if (pv['size_kw'] || 0) > 0
245
266
  if !pv['year_one_power_production_series_kw'].nil?
246
267
  generation_timeseries_kwh += Matrix[pv['year_one_power_production_series_kw']]
@@ -272,7 +293,7 @@ module URBANopt # :nodoc:
272
293
  end
273
294
  end
274
295
 
275
- $generation_timeseries_kwh = generation_timeseries_kwh.to_a[0]
296
+ $generation_timeseries_kwh = generation_timeseries_kwh.to_a[0] || [0] * (8760 * scenario_report.timesteps_per_hour)
276
297
  $generation_timeseries_kwh_col = scenario_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Total(kw)')
277
298
  if $generation_timeseries_kwh_col.nil?
278
299
  $generation_timeseries_kwh_col = scenario_report.timeseries_csv.column_names.length