urbanopt-reopt 0.3.0.pre1 → 0.5.4

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.
data/docs/package.json CHANGED
@@ -10,13 +10,17 @@
10
10
  },
11
11
  "author": "NREL",
12
12
  "dependencies": {
13
- "highlight.js": "^9.15.6",
13
+ "braces": "^3.0.2",
14
+ "highlight.js": "^10.4.1",
14
15
  "json-schema-ref-parser": "^6.1.0",
15
16
  "json-schema-view-js": "git+https://git@github.com/bgschiller/json-schema-view-js.git",
16
- "vuepress": "^0.14.10",
17
- "webpack-dev-middleware": "^3.6.0"
17
+ "serialize-javascript": "^5.0.1",
18
+ "vuepress": "^1.6.0",
19
+ "webpack-dev-middleware": "^3.6.0",
20
+ "yargs-parser": "^18.1.1"
18
21
  },
19
22
  "devDependencies": {
20
- "gh-pages": "^2.0.1"
23
+ "gh-pages": "^2.0.1",
24
+ "ini": "^2.0.0"
21
25
  }
22
26
  }
data/index.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # **URBANopt REopt Gem**
2
2
 
3
- The **URBANopt REopt Gem** extends **URBANopt::Scenario::DefaultReports::ScenarioReport** and **URBANopt::Scenario::DefaultReports::FeatureReport** with the ability to derive cost-optimal distributed energy resource (DER) technology sizes and annual dispatch strageties via the [REopt Lite](https://reopt.nrel.gov/tool) decision support platform.
4
- REopt Lite is a technoeconomic model which leverages mixed integer linear programming to identify the cost-optimal sizing of solar PV, Wind, Storage and/or diesel generation given an electric load profile, a utility rate tariff and other technoeconomic parameters. See [https://developer.nrel.gov/docs/energy-optimization/reopt-v1/](https://developer.nrel.gov/docs/energy-optimization/reopt-v1/) for more detailed information on input parameters and default assumptions.
3
+ The **URBANopt<sup>&trade;</sup> REopt Gem** extends **URBANopt::Reporting::DefaultReports::ScenarioReport** and **URBANopt::Reporting::DefaultReports::FeatureReport** with the ability to derive cost-optimal distributed energy resource (DER) technology sizes and annual dispatch strageties via the [REopt Lite](https://reopt.nrel.gov/tool) decision support platform.
4
+ REopt Lite is a technoeconomic model which leverages mixed integer linear programming to identify the cost-optimal sizing of solar PV, Wind, Storage and/or diesel generation given an electric load profile, a utility rate tariff and other technoeconomic parameters. See [https://developer.nrel.gov/docs/energy-optimization/reopt-v1/](https://developer.nrel.gov/docs/energy-optimization/reopt-v1/) for more detailed information on input parameters and default assumptions.
5
5
 
6
6
  See the [example project](https://github.com/urbanopt/urbanopt-example-reopt-project.git) for more infomation about usage of this gem.
7
7
 
@@ -9,7 +9,7 @@ See the [example project](https://github.com/urbanopt/urbanopt-example-reopt-pro
9
9
 
10
10
  ## Installation
11
11
 
12
- See [https://docs.urbanopt.net/installation/installation.html](https://docs.urbanopt.net/installation/installation.html) for instructions on prerequiste software, including:
12
+ See [https://docs.urbanopt.net/installation/installation.html](https://docs.urbanopt.net/installation/installation.html) for instructions on prerequiste software, including:
13
13
  - Ruby 2.2.6
14
14
  - Bundler 1.17.0
15
15
  - OpenStudio 2.8.1
@@ -31,7 +31,7 @@ Or install it yourself as:
31
31
 
32
32
  ## Functionality
33
33
 
34
- This gem is used to call the REopt Lite API on a Scenario Report or Feature Report to update the object's Distributed Generation attributes (including system financial and sizing metrics) as shown in an example below:
34
+ This gem is used to call the REopt Lite API on a Scenario Report or Feature Report to update the object's Distributed Generation attributes (including system financial and sizing metrics) as shown in an example below:
35
35
 
36
36
  ```
37
37
  "distributed_generation": {
@@ -83,7 +83,7 @@ Moreover, the following optimal dispatch fields are added to its timeseries CSV.
83
83
  | ElectricityProduced:Wind:ToGrid | kWh |
84
84
  ```
85
85
 
86
- The REopt Lite has default values for all non-required input parameters that are used unless the user specifies custom assumptions. See [https://developer.nrel.gov/docs/energy-optimization/reopt-v1/](https://developer.nrel.gov/docs/energy-optimization/reopt-v1/) for more detailed information on input parameters and default assumptions.
86
+ The REopt Lite has default values for all non-required input parameters that are used unless the user specifies custom assumptions. See [https://developer.nrel.gov/docs/energy-optimization/reopt-v1/](https://developer.nrel.gov/docs/energy-optimization/reopt-v1/) for more detailed information on input parameters and default assumptions.
87
87
 
88
88
  <b>Note:</b> Required attributes for a REopt run include latitude and longitude. If no utility rate is specified in your REopt Lite assumption settings, then a constant default rate of $0.13 is assumed without demand charges. Also, by default, only solar PV and storage are considered in the analysis (i.e. Wind and Generators are excluded from consideration).
89
89
 
@@ -99,7 +99,7 @@ require 'urbanopt/reopt'
99
99
  feature_reports_hash = {} # <insert a Feature Report hash here>
100
100
 
101
101
  #Create a Feature Report
102
- feature_report = URBANopt::Scenario::DefaultReports::FeatureReport.new(feature_reports_hash)
102
+ feature_report = URBANopt::Reporting::DefaultReports::FeatureReport.new(feature_reports_hash)
103
103
 
104
104
  #Specify a file name where REopt Lite results will be written in JSON format
105
105
  reopt_output_file = File.join(feature_report.directory_name, 'feature_report_reopt_run1.json')
@@ -115,7 +115,7 @@ reopt_post_processor = URBANopt::REopt::REoptPostProcessor.new(nil, nil, nil, DE
115
115
 
116
116
  #Call REopt Lite with the post processor to update the feature's distributed generation attributes and timeseries CSV.
117
117
  updated_feature_report = reopt_post_processor.run_feature_report(feature_report,reopt_assumptions_file,reopt_output_file,timeseries_output_file)
118
-
118
+
119
119
  ```
120
120
 
121
121
  More commonly, this gem can be used to run REopt a collection of features stored in a Scenario Report as show here:
@@ -123,9 +123,9 @@ More commonly, this gem can be used to run REopt a collection of features stored
123
123
  ```ruby
124
124
  require 'urbanopt/reopt'
125
125
  #Create a Scenario Report
126
- scenario_report = URBANopt::Scenario::DefaultReports::ScenarioReport.new({:directory_name => File.join(File.dirname(__FILE__), '../run/example_scenario'), :timeseries_csv => {:path => File.join(File.dirname(__FILE__), '../run/example_scenario/timeseries.csv') }})
126
+ scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new({:directory_name => File.join(File.dirname(__FILE__), '../run/example_scenario'), :timeseries_csv => {:path => File.join(File.dirname(__FILE__), '../run/example_scenario/timeseries.csv') }})
127
127
 
128
- #Load Feature Reports into the Scenario Report
128
+ #Load Feature Reports into the Scenario Report
129
129
  (1..2).each do |i|
130
130
  feature_reports_path = File.join(File.dirname(__FILE__), "../run/example_scenario/#{i}/010_default_feature_reports/default_feature_reports.json")
131
131
 
@@ -134,8 +134,8 @@ scenario_report = URBANopt::Scenario::DefaultReports::ScenarioReport.new({:direc
134
134
  feature_reports_hash = JSON.parse(file.read, symbolize_names: true)
135
135
  end
136
136
 
137
- feature_report = URBANopt::Scenario::DefaultReports::FeatureReport.new(feature_reports_hash)
138
-
137
+ feature_report = URBANopt::Reporting::DefaultReports::FeatureReport.new(feature_reports_hash)
138
+
139
139
  feature_report_dir = File.join(File.dirname(__FILE__), "../run/example_scenario/#{i}")
140
140
  feature_report.directory_name = feature_report_dir
141
141
 
@@ -150,7 +150,7 @@ reopt_post_processor = URBANopt::REopt::REoptPostProcessor.new(scenario_report,
150
150
 
151
151
  #Call REopt Lite with the post processor once on the sceanrio's aggregated load to update the scenario's distributed generation attributes and timeseries CSV.
152
152
  updated_scenario_report = reopt_post_processor.run_scenario_report(scenario_report)
153
-
153
+
154
154
  ```
155
155
 
156
156
  ## Testing
@@ -164,7 +164,7 @@ Next, obtain a developer.nrel.gov API key from the [NREL Developer Network](http
164
164
  Finally, execute:
165
165
 
166
166
  $ bundle install
167
- $ bundle update
167
+ $ bundle update
168
168
  $ bundle exec rake
169
169
 
170
170
 
@@ -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,
@@ -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,17 +28,18 @@
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 'csv'
34
34
  require 'matrix'
35
+ require_relative 'utilities'
35
36
  require 'time'
36
37
 
37
38
  module URBANopt # :nodoc:
38
39
  module REopt # :nodoc:
39
40
  class FeatureReportAdapter
40
41
  ##
41
- # FeatureReportAdapter can convert a URBANopt::Scenario::DefaultReports::FeatureReport into a \REopt Lite posts or update a URBANopt::Scenario::DefaultReports::FeatureReport from a \REopt Lite response.
42
+ # FeatureReportAdapter can convert a URBANopt::Reporting::DefaultReports::FeatureReport into a \REopt Lite posts or update a URBANopt::Reporting::DefaultReports::FeatureReport from a \REopt Lite response.
42
43
  ##
43
44
  # [*parameters:*]
44
45
  ##
@@ -52,7 +53,7 @@ module URBANopt # :nodoc:
52
53
  #
53
54
  # [*parameters:*]
54
55
  #
55
- # * +feature_report+ - _URBANopt::Scenario::DefaultReports::FeatureReport_ - FeatureReport to use in converting the optional +reopt_assumptions_hash+ to a \REopt Lite post. If a +reopt_assumptions_hash+ is not provided, a default post will be updated from this FeatureReport and submitted to the \REopt Lite API.
56
+ # * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to use in converting the optional +reopt_assumptions_hash+ to a \REopt Lite post. If a +reopt_assumptions_hash+ is not provided, a default post will be updated from this FeatureReport and submitted to the \REopt Lite API.
56
57
  # * +reopt_assumptions_hash+ - _Hash_ - Optional. A hash formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the FeatureReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API.
57
58
  #
58
59
  # [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
@@ -69,11 +70,11 @@ module URBANopt # :nodoc:
69
70
 
70
71
  # Check FeatureReport has required data
71
72
  requireds_names = ['latitude', 'longitude']
72
- requireds = [feature_report.location.latitude, feature_report.location.longitude]
73
+ requireds = [feature_report.location.latitude_deg, feature_report.location.longitude_deg]
73
74
 
74
75
  if requireds.include?(nil) || requireds.include?(0)
75
76
  requireds.each_with_index do |i, x|
76
- if [nil, 0].include? x
77
+ if [nil].include? x
77
78
  n = requireds_names[i]
78
79
  # @@logger.error("Missing value for #{n} - this is a required input")
79
80
  raise "Missing value for #{n} - this is a required input"
@@ -84,27 +85,33 @@ module URBANopt # :nodoc:
84
85
  reopt_inputs[:Scenario][:description] = description
85
86
 
86
87
  # Parse Location
87
- reopt_inputs[:Scenario][:Site][:latitude] = feature_report.location.latitude
88
- reopt_inputs[:Scenario][:Site][:longitude] = feature_report.location.longitude
88
+ reopt_inputs[:Scenario][:Site][:latitude] = feature_report.location.latitude_deg
89
+ reopt_inputs[:Scenario][:Site][:longitude] = feature_report.location.longitude_deg
89
90
 
90
- # Parse Optional FeatureReport metrics
91
- unless feature_report.program.roof_area.nil?
92
- reopt_inputs[:Scenario][:Site][:roof_squarefeet] = feature_report.program.roof_area[:available_roof_area]
91
+ # Parse Optional FeatureReport metrics - do not overwrite from assumptions file
92
+ if reopt_inputs[:Scenario][:Site][:roof_squarefeet].nil?
93
+ unless feature_report.program.roof_area_sqft.nil?
94
+ reopt_inputs[:Scenario][:Site][:roof_squarefeet] = feature_report.program.roof_area_sqft[:available_roof_area_sqft]
95
+ end
93
96
  end
94
97
 
95
- unless feature_report.program.site_area.nil?
96
- reopt_inputs[:Scenario][:Site][:land_acres] = feature_report.program.site_area * 1.0 / 43560 # acres/sqft
98
+ if reopt_inputs[:Scenario][:Site][:land_acres].nil?
99
+ unless feature_report.program.site_area_sqft.nil?
100
+ reopt_inputs[:Scenario][:Site][:land_acres] = feature_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
101
+ end
97
102
  end
98
103
 
99
- unless feature_report.timesteps_per_hour.nil?
100
- reopt_inputs[:Scenario][:time_steps_per_hour] = feature_report.timesteps_per_hour
104
+ if reopt_inputs[:Scenario][:time_steps_per_hour].nil?
105
+ reopt_inputs[:Scenario][:time_steps_per_hour] = 1
101
106
  end
102
107
 
103
108
  # Parse Load Profile
104
109
  begin
110
+ #Convert kWh values in the timeseries CSV to kW
105
111
  col_num = feature_report.timeseries_csv.column_names.index('Electricity:Facility(kWh)')
106
112
  t = CSV.read(feature_report.timeseries_csv.path, headers: true, converters: :numeric)
107
113
  energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * feature_report.timesteps_per_hour || 0) ) }
114
+ #Fill in missing timestep values with 0 if a full year is not provided
108
115
  if energy_timeseries_kw.length < (feature_report.timesteps_per_hour * 8760)
109
116
  start_date = Time.parse(t.by_col["Datetime"][0])
110
117
  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) /
@@ -113,33 +120,60 @@ module URBANopt # :nodoc:
113
120
  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) /
114
121
  (( 60 / feature_report.timesteps_per_hour ) * 60)).to_int
115
122
  energy_timeseries_kw = [0.0]*(start_ts-1) + energy_timeseries_kw + [0.0]*((feature_report.timesteps_per_hour * 8760) - end_ts)
116
- end
117
- reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = energy_timeseries_kw.map { |e| e ? e : 0 }[0,(feature_report.timesteps_per_hour * 8760)]
123
+ end
124
+ #Clip to one non-leap year's worth of data
125
+ energy_timeseries_kw = energy_timeseries_kw.map { |e| e ? e : 0 }[0,(feature_report.timesteps_per_hour * 8760)]
126
+ #Convert from the OpenDSS resolution to the REopt Lite resolution, if necessary
118
127
  rescue StandardError
119
128
  @@logger.error("Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}")
120
129
  raise "Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}"
121
130
  end
131
+
132
+ # Convert load to REopt Resolution
133
+ begin
134
+ reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = convert_powerflow_resolution(energy_timeseries_kw, feature_report.timesteps_per_hour, reopt_inputs[:Scenario][:time_steps_per_hour])
135
+
136
+ rescue
137
+ @@logger.error("Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:Scenario][:time_steps_per_hour]}")
138
+ raise "Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:Scenario][:time_steps_per_hour]}"
139
+ end
140
+
141
+ if reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps].nil?
142
+ n_top_values = 100
143
+ tmp1 = reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw]
144
+ tmp2 = tmp1.each_index.max_by(n_top_values*reopt_inputs[:Scenario][:time_steps_per_hour]){|i| tmp1[i]}
145
+ for i in (0...tmp2.count)
146
+ tmp2[i] += 1
147
+ end
148
+ reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_active_timesteps] = tmp2
149
+ end
150
+
151
+ if reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_charge_us_dollars_per_kw].nil?
152
+ reopt_inputs[:Scenario][:Site][:ElectricTariff][:coincident_peak_load_charge_us_dollars_per_kw] = 0
153
+ end
154
+
122
155
  return reopt_inputs
123
156
  end
124
157
 
158
+
125
159
  ##
126
160
  # Update a FeatureReport from a \REopt Lite response
127
161
  #
128
162
  # [*parameters:*]
129
163
  #
130
- # * +feature_report+ - _URBANopt::Scenario::DefaultReports::FeatureReport_ - FeatureReport to update from a \REopt Lite reponse hash.
164
+ # * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to update from a \REopt Lite reponse hash.
131
165
  # * +reopt_output+ - _Hash_ - A reponse hash from the \REopt Lite API to use in overwriting FeatureReport technology sizes, costs and dispatch strategies.
132
166
  # * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which a new timeseries CSV will be written. If not provided a file is created based on the run_uuid of the \REopt Lite optimization task.
133
167
  #
134
- # [*return:*] _URBANopt::Scenario::DefaultReports::FeatureReport_ - Returns an updated FeatureReport.
168
+ # [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport.
135
169
  ##
136
- def update_feature_report(feature_report, reopt_output, timeseries_csv_path = nil)
170
+ def update_feature_report(feature_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil)
137
171
  # Check if the \REopt Lite response is valid
138
172
  if reopt_output['outputs']['Scenario']['status'] != 'optimal'
139
173
  @@logger.info("Warning cannot Feature Report #{feature_report.name} #{feature_report.id} - REopt optimization was non-optimal")
140
174
  return feature_report
141
175
  end
142
-
176
+
143
177
  $ts_per_hour = feature_report.timesteps_per_hour
144
178
  def scale_timeseries(input, ts_per_hr=$ts_per_hour)
145
179
  if input.nil?
@@ -152,7 +186,7 @@ module URBANopt # :nodoc:
152
186
  return input
153
187
  end
154
188
  result = []
155
- input.each do |val|
189
+ input.each do |val|
156
190
  (1..ts_per_hr).each do |x|
157
191
  result.push(val/ts_per_hr.to_f)
158
192
  end
@@ -161,11 +195,8 @@ module URBANopt # :nodoc:
161
195
  end
162
196
 
163
197
  # Update location
164
- feature_report.location.latitude = reopt_output['inputs']['Scenario']['Site']['latitude']
165
- feature_report.location.longitude = reopt_output['inputs']['Scenario']['Site']['longitude']
166
-
167
- # Update timeseries csv from \REopt Lite dispatch data
168
- feature_report.timesteps_per_hour = reopt_output['inputs']['Scenario']['time_steps_per_hour']
198
+ feature_report.location.latitude_deg = reopt_output['inputs']['Scenario']['Site']['latitude']
199
+ feature_report.location.longitude_deg = reopt_output['inputs']['Scenario']['Site']['longitude']
169
200
 
170
201
  # Update distributed generation sizing and financials
171
202
  feature_report.distributed_generation.lcc_us_dollars = reopt_output['outputs']['Scenario']['Site']['Financial']['lcc_us_dollars'] || 0
@@ -174,56 +205,63 @@ module URBANopt # :nodoc:
174
205
  feature_report.distributed_generation.year_one_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars'] || 0
175
206
  feature_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars'] || 0
176
207
  feature_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars'] || 0
208
+ feature_report.distributed_generation.total_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_us_dollars'] || 0
209
+ feature_report.distributed_generation.year_one_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_bau_us_dollars'] || 0
210
+ feature_report.distributed_generation.year_one_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_bau_us_dollars'] || 0
211
+ feature_report.distributed_generation.year_one_bill_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_bau_us_dollars'] || 0
212
+ feature_report.distributed_generation.total_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_bau_us_dollars'] || 0
213
+ feature_report.distributed_generation.total_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_bau_us_dollars'] || 0
214
+ if !resilience_stats.nil?
215
+ feature_report.distributed_generation.resilience_hours_min = resilience_stats['resilience_hours_min']
216
+ feature_report.distributed_generation.resilience_hours_max = resilience_stats['resilience_hours_max']
217
+ feature_report.distributed_generation.resilience_hours_avg = resilience_stats['resilience_hours_avg']
218
+ feature_report.distributed_generation.probs_of_surviving = resilience_stats['probs_of_surviving']
219
+ feature_report.distributed_generation.probs_of_surviving_by_month = resilience_stats['probs_of_surviving_by_month']
220
+ feature_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
221
+ end
177
222
 
178
223
  if reopt_output['outputs']['Scenario']['Site']['PV'].class == Hash
179
224
  reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
180
225
  elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
181
226
  reopt_output['outputs']['Scenario']['Site']['PV'] = []
182
227
  end
183
-
184
- reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
185
- feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Scenario::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
228
+
229
+ reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
230
+ feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
186
231
  end
187
232
 
188
233
  wind = reopt_output['outputs']['Scenario']['Site']['Wind']
189
234
  if !wind['size_kw'].nil? and wind['size_kw'] != 0
190
- feature_report.distributed_generation.add_tech 'wind', URBANopt::Scenario::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
235
+ feature_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
191
236
  end
192
237
 
193
238
  generator = reopt_output['outputs']['Scenario']['Site']['Generator']
194
239
  if !generator['size_kw'].nil? and generator['size_kw'] != 0
195
- feature_report.distributed_generation.add_tech 'generator', URBANopt::Scenario::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
240
+ feature_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
196
241
  end
197
242
 
198
243
  storage = reopt_output['outputs']['Scenario']['Site']['Storage']
199
244
  if !storage['size_kw'].nil? and storage['size_kw'] != 0
200
- feature_report.distributed_generation.add_tech 'storage', URBANopt::Scenario::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
245
+ feature_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
201
246
  end
202
-
247
+
203
248
  generation_timeseries_kwh = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
249
+ reopt_resolution = reopt_output['inputs']['Scenario']['time_steps_per_hour']
204
250
 
205
251
  unless reopt_output['outputs']['Scenario']['Site']['PV'].nil?
206
- reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
252
+ reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
207
253
  if (pv['size_kw'] || 0) > 0
208
254
  if !pv['year_one_power_production_series_kw'].nil?
209
- generation_timeseries_kwh += Matrix[pv['year_one_power_production_series_kw']]
255
+ generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
210
256
  end
211
257
  end
212
258
  end
213
259
  end
214
260
 
215
- unless reopt_output['outputs']['Scenario']['Site']['Storage'].nil?
216
- if (reopt_output['outputs']['Scenario']['Site']['Storage']['size_kw'] or 0) > 0
217
- if !reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'].nil?
218
- generation_timeseries_kwh = generation_timeseries_kwh + Matrix[reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw']]
219
- end
220
- end
221
- end
222
-
223
261
  unless reopt_output['outputs']['Scenario']['Site']['Wind'].nil?
224
262
  if (reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0
225
263
  if !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
226
- generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw']]
264
+ generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
227
265
  end
228
266
  end
229
267
  end
@@ -231,7 +269,7 @@ module URBANopt # :nodoc:
231
269
  unless reopt_output['outputs']['Scenario']['Site']['Generator'].nil?
232
270
  if (reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0) > 0
233
271
  if !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
234
- generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw']]
272
+ generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
235
273
  end
236
274
  end
237
275
  end
@@ -243,70 +281,70 @@ module URBANopt # :nodoc:
243
281
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Total(kw)')
244
282
  end
245
283
 
246
- $load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['LoadProfile']['year_one_electric_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
284
+ $load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['LoadProfile']['year_one_electric_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
247
285
  $load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Load:Total(kw)')
248
286
  if $load_col.nil?
249
287
  $load_col = feature_report.timeseries_csv.column_names.length
250
288
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Load:Total(kw)')
251
289
  end
252
290
 
253
- $utility_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
291
+ $utility_to_load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
254
292
  $utility_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToLoad(kw)')
255
293
  if $utility_to_load_col.nil?
256
294
  $utility_to_load_col = feature_report.timeseries_csv.column_names.length
257
295
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToLoad(kw)')
258
296
  end
259
297
 
260
- $utility_to_battery = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_battery_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
298
+ $utility_to_battery = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_battery_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
261
299
  $utility_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToBattery(kw)')
262
300
  if $utility_to_battery_col.nil?
263
301
  $utility_to_battery_col = feature_report.timeseries_csv.column_names.length
264
302
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToBattery(kw)')
265
303
  end
266
304
 
267
- $storage_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
305
+ $storage_to_load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
268
306
  $storage_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToLoad(kw)')
269
307
  if $storage_to_load_col.nil?
270
308
  $storage_to_load_col = feature_report.timeseries_csv.column_names.length
271
309
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToLoad(kw)')
272
310
  end
273
311
 
274
- $storage_to_grid = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
312
+ $storage_to_grid = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
275
313
  $storage_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToGrid(kw)')
276
314
  if $storage_to_grid_col.nil?
277
315
  $storage_to_grid_col = feature_report.timeseries_csv.column_names.length
278
316
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToGrid(kw)')
279
317
  end
280
318
 
281
- $storage_soc = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct']) || [0] * (8760 * feature_report.timesteps_per_hour)
319
+ $storage_soc = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
282
320
  $storage_soc_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:StateOfCharge(pct)')
283
321
  if $storage_soc_col.nil?
284
322
  $storage_soc_col = feature_report.timeseries_csv.column_names.length
285
323
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:StateOfCharge(pct)')
286
324
  end
287
325
 
288
- $generator_total = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
326
+ $generator_total = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
289
327
  $generator_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:Total(kw)')
290
328
  if $generator_total_col.nil?
291
329
  $generator_total_col = feature_report.timeseries_csv.column_names.length
292
330
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:Total(kw)')
293
331
  end
294
332
 
295
- $generator_to_battery = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_battery_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
333
+ $generator_to_battery = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_battery_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
296
334
  $generator_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToBattery(kw)')
297
335
  if $generator_to_battery_col.nil?
298
336
  $generator_to_battery_col = feature_report.timeseries_csv.column_names.length
299
337
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToBattery(kw)')
300
338
  end
301
339
 
302
- $generator_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
340
+ $generator_to_load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
303
341
  $generator_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToLoad(kw)')
304
342
  if $generator_to_load_col.nil?
305
343
  $generator_to_load_col = feature_report.timeseries_csv.column_names.length
306
344
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToLoad(kw)')
307
345
  end
308
346
 
309
- $generator_to_grid = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_grid_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
347
+ $generator_to_grid = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_grid_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
310
348
  $generator_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToGrid(kw)')
311
349
  if $generator_to_grid_col.nil?
312
350
  $generator_to_grid_col = feature_report.timeseries_csv.column_names.length
@@ -345,10 +383,10 @@ module URBANopt # :nodoc:
345
383
 
346
384
  reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
347
385
  if (pv['size_kw'] || 0) > 0
348
- $pv_total += Matrix[scale_timeseries(pv['year_one_power_production_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)]
349
- $pv_to_battery += Matrix[scale_timeseries(pv['year_one_to_battery_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)]
350
- $pv_to_load += Matrix[scale_timeseries(pv['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)]
351
- $pv_to_grid += Matrix[scale_timeseries(pv['year_one_to_grid_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)]
386
+ $pv_total += Matrix[scale_timeseries(convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)]
387
+ $pv_to_battery += Matrix[scale_timeseries(convert_powerflow_resolution(pv['year_one_to_battery_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)]
388
+ $pv_to_load += Matrix[scale_timeseries(convert_powerflow_resolution(pv['year_one_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)]
389
+ $pv_to_grid += Matrix[scale_timeseries(convert_powerflow_resolution(pv['year_one_to_grid_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)]
352
390
  end
353
391
  end
354
392
 
@@ -357,28 +395,28 @@ module URBANopt # :nodoc:
357
395
  $pv_to_load = $pv_to_load.to_a[0]
358
396
  $pv_to_grid = $pv_to_grid.to_a[0]
359
397
 
360
- $wind_total = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
398
+ $wind_total = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
361
399
  $wind_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:Total(kw)')
362
400
  if $wind_total_col.nil?
363
401
  $wind_total_col = feature_report.timeseries_csv.column_names.length
364
402
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:Total(kw)')
365
403
  end
366
404
 
367
- $wind_to_battery = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_battery_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
405
+ $wind_to_battery = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_battery_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
368
406
  $wind_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToBattery(kw)')
369
407
  if $wind_to_battery_col.nil?
370
408
  $wind_to_battery_col = feature_report.timeseries_csv.column_names.length
371
409
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToBattery(kw)')
372
410
  end
373
411
 
374
- $wind_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
412
+ $wind_to_load = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
375
413
  $wind_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToLoad(kw)')
376
414
  if $wind_to_load_col.nil?
377
415
  $wind_to_load_col = feature_report.timeseries_csv.column_names.length
378
416
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToLoad(kw)')
379
417
  end
380
418
 
381
- $wind_to_grid = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_grid_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
419
+ $wind_to_grid = scale_timeseries(convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_grid_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)) || [0] * (8760 * feature_report.timesteps_per_hour)
382
420
  $wind_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToGrid(kw)')
383
421
  if $wind_to_grid_col.nil?
384
422
  $wind_to_grid_col = feature_report.timeseries_csv.column_names.length