urbanopt-reopt 0.3.0 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
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,59 +120,63 @@ 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
-
143
- $ts_per_hour = feature_report.timesteps_per_hour
144
- def scale_timeseries(input, ts_per_hr=$ts_per_hour)
145
- if input.nil?
146
- return nil
147
- end
148
- if input.length ==0
149
- return nil
150
- end
151
- if input.length == (8760 * ts_per_hr)
152
- return input
153
- end
154
- result = []
155
- input.each do |val|
156
- (1..ts_per_hr).each do |x|
157
- result.push(val/ts_per_hr.to_f)
158
- end
159
- end
160
- return result
161
- end
162
-
176
+
163
177
  # 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']
178
+ feature_report.location.latitude_deg = reopt_output['inputs']['Scenario']['Site']['latitude']
179
+ feature_report.location.longitude_deg = reopt_output['inputs']['Scenario']['Site']['longitude']
169
180
 
170
181
  # Update distributed generation sizing and financials
171
182
  feature_report.distributed_generation.lcc_us_dollars = reopt_output['outputs']['Scenario']['Site']['Financial']['lcc_us_dollars'] || 0
@@ -174,56 +185,63 @@ module URBANopt # :nodoc:
174
185
  feature_report.distributed_generation.year_one_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars'] || 0
175
186
  feature_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars'] || 0
176
187
  feature_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars'] || 0
188
+ feature_report.distributed_generation.total_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_us_dollars'] || 0
189
+ 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
190
+ 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
191
+ feature_report.distributed_generation.year_one_bill_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_bau_us_dollars'] || 0
192
+ feature_report.distributed_generation.total_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_bau_us_dollars'] || 0
193
+ feature_report.distributed_generation.total_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_bau_us_dollars'] || 0
194
+ if !resilience_stats.nil?
195
+ feature_report.distributed_generation.resilience_hours_min = resilience_stats['resilience_hours_min']
196
+ feature_report.distributed_generation.resilience_hours_max = resilience_stats['resilience_hours_max']
197
+ feature_report.distributed_generation.resilience_hours_avg = resilience_stats['resilience_hours_avg']
198
+ feature_report.distributed_generation.probs_of_surviving = resilience_stats['probs_of_surviving']
199
+ feature_report.distributed_generation.probs_of_surviving_by_month = resilience_stats['probs_of_surviving_by_month']
200
+ feature_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
201
+ end
177
202
 
178
203
  if reopt_output['outputs']['Scenario']['Site']['PV'].class == Hash
179
204
  reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
180
205
  elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
181
206
  reopt_output['outputs']['Scenario']['Site']['PV'] = []
182
207
  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 })
208
+
209
+ reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
210
+ feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
186
211
  end
187
212
 
188
213
  wind = reopt_output['outputs']['Scenario']['Site']['Wind']
189
214
  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) })
215
+ feature_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
191
216
  end
192
217
 
193
218
  generator = reopt_output['outputs']['Scenario']['Site']['Generator']
194
219
  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) })
220
+ feature_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
196
221
  end
197
222
 
198
223
  storage = reopt_output['outputs']['Scenario']['Site']['Storage']
199
224
  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) })
225
+ 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
226
  end
202
-
227
+
203
228
  generation_timeseries_kwh = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
229
+ reopt_resolution = reopt_output['inputs']['Scenario']['time_steps_per_hour']
204
230
 
205
231
  unless reopt_output['outputs']['Scenario']['Site']['PV'].nil?
206
- reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
232
+ reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
207
233
  if (pv['size_kw'] || 0) > 0
208
234
  if !pv['year_one_power_production_series_kw'].nil?
209
- generation_timeseries_kwh += Matrix[pv['year_one_power_production_series_kw']]
235
+ generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
210
236
  end
211
237
  end
212
238
  end
213
239
  end
214
240
 
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
241
  unless reopt_output['outputs']['Scenario']['Site']['Wind'].nil?
224
242
  if (reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0
225
243
  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']]
244
+ 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
245
  end
228
246
  end
229
247
  end
@@ -231,7 +249,7 @@ module URBANopt # :nodoc:
231
249
  unless reopt_output['outputs']['Scenario']['Site']['Generator'].nil?
232
250
  if (reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0) > 0
233
251
  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']]
252
+ 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
253
  end
236
254
  end
237
255
  end
@@ -243,70 +261,70 @@ module URBANopt # :nodoc:
243
261
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Total(kw)')
244
262
  end
245
263
 
246
- $load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['LoadProfile']['year_one_electric_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
264
+ $load = 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
265
  $load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Load:Total(kw)')
248
266
  if $load_col.nil?
249
267
  $load_col = feature_report.timeseries_csv.column_names.length
250
268
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Load:Total(kw)')
251
269
  end
252
270
 
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)
271
+ $utility_to_load = 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
272
  $utility_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToLoad(kw)')
255
273
  if $utility_to_load_col.nil?
256
274
  $utility_to_load_col = feature_report.timeseries_csv.column_names.length
257
275
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToLoad(kw)')
258
276
  end
259
277
 
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)
278
+ $utility_to_battery = 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
279
  $utility_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToBattery(kw)')
262
280
  if $utility_to_battery_col.nil?
263
281
  $utility_to_battery_col = feature_report.timeseries_csv.column_names.length
264
282
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToBattery(kw)')
265
283
  end
266
284
 
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)
285
+ $storage_to_load = 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
286
  $storage_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToLoad(kw)')
269
287
  if $storage_to_load_col.nil?
270
288
  $storage_to_load_col = feature_report.timeseries_csv.column_names.length
271
289
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToLoad(kw)')
272
290
  end
273
291
 
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)
292
+ $storage_to_grid = 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
293
  $storage_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToGrid(kw)')
276
294
  if $storage_to_grid_col.nil?
277
295
  $storage_to_grid_col = feature_report.timeseries_csv.column_names.length
278
296
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToGrid(kw)')
279
297
  end
280
298
 
281
- $storage_soc = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct']) || [0] * (8760 * feature_report.timesteps_per_hour)
299
+ $storage_soc = 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
300
  $storage_soc_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:StateOfCharge(pct)')
283
301
  if $storage_soc_col.nil?
284
302
  $storage_soc_col = feature_report.timeseries_csv.column_names.length
285
303
  feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:StateOfCharge(pct)')
286
304
  end
287
305
 
288
- $generator_total = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
306
+ $generator_total = 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
307
  $generator_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:Total(kw)')
290
308
  if $generator_total_col.nil?
291
309
  $generator_total_col = feature_report.timeseries_csv.column_names.length
292
310
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:Total(kw)')
293
311
  end
294
312
 
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)
313
+ $generator_to_battery = 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
314
  $generator_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToBattery(kw)')
297
315
  if $generator_to_battery_col.nil?
298
316
  $generator_to_battery_col = feature_report.timeseries_csv.column_names.length
299
317
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToBattery(kw)')
300
318
  end
301
319
 
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)
320
+ $generator_to_load = 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
321
  $generator_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToLoad(kw)')
304
322
  if $generator_to_load_col.nil?
305
323
  $generator_to_load_col = feature_report.timeseries_csv.column_names.length
306
324
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToLoad(kw)')
307
325
  end
308
326
 
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)
327
+ $generator_to_grid = 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
328
  $generator_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToGrid(kw)')
311
329
  if $generator_to_grid_col.nil?
312
330
  $generator_to_grid_col = feature_report.timeseries_csv.column_names.length
@@ -345,10 +363,10 @@ module URBANopt # :nodoc:
345
363
 
346
364
  reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
347
365
  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)]
366
+ $pv_total += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)]
367
+ $pv_to_battery += Matrix[convert_powerflow_resolution(pv['year_one_to_battery_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)]
368
+ $pv_to_load += Matrix[convert_powerflow_resolution(pv['year_one_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)]
369
+ $pv_to_grid += Matrix[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
370
  end
353
371
  end
354
372
 
@@ -357,28 +375,28 @@ module URBANopt # :nodoc:
357
375
  $pv_to_load = $pv_to_load.to_a[0]
358
376
  $pv_to_grid = $pv_to_grid.to_a[0]
359
377
 
360
- $wind_total = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
378
+ $wind_total = 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
379
  $wind_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:Total(kw)')
362
380
  if $wind_total_col.nil?
363
381
  $wind_total_col = feature_report.timeseries_csv.column_names.length
364
382
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:Total(kw)')
365
383
  end
366
384
 
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)
385
+ $wind_to_battery = 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
386
  $wind_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToBattery(kw)')
369
387
  if $wind_to_battery_col.nil?
370
388
  $wind_to_battery_col = feature_report.timeseries_csv.column_names.length
371
389
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToBattery(kw)')
372
390
  end
373
391
 
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)
392
+ $wind_to_load = 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
393
  $wind_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToLoad(kw)')
376
394
  if $wind_to_load_col.nil?
377
395
  $wind_to_load_col = feature_report.timeseries_csv.column_names.length
378
396
  feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToLoad(kw)')
379
397
  end
380
398
 
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)
399
+ $wind_to_grid = 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
400
  $wind_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToGrid(kw)')
383
401
  if $wind_to_grid_col.nil?
384
402
  $wind_to_grid_col = feature_report.timeseries_csv.column_names.length
@@ -410,8 +428,14 @@ module URBANopt # :nodoc:
410
428
 
411
429
  old_data = CSV.open(feature_report.timeseries_csv.path).read
412
430
  start_date = Time.parse(old_data[1][0])
413
- 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) / (( 60 / feature_report.timesteps_per_hour ) * 60)).to_int
414
-
431
+ start_ts = (
432
+ (
433
+ ((start_date.yday - 1) * 60.0 * 60.0 * 24) +
434
+ (((start_date.hour) - 1) * 60.0 * 60.0) +
435
+ (start_date.min * 60.0) + start_date.sec ) /
436
+ (( 60 / feature_report.timesteps_per_hour ) * 60)
437
+ ).to_int
438
+
415
439
  mod_data = old_data.map.with_index do |x, i|
416
440
  if i > 0
417
441
  modrow(x, start_ts + i -2)