urbanopt-reopt 0.1.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE/bug_report.md +24 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- data/.github/pull_request_template.md +13 -0
- data/.gitignore +6 -1
- data/.rdoc_options +1 -1
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +39 -0
- data/CONTRIBUTING.md +58 -0
- data/Gemfile +14 -19
- data/LICENSE.md +1 -1
- data/RDOC_MAIN.md +13 -13
- data/README.md +15 -15
- data/Rakefile +31 -1
- data/a.txt +1 -0
- data/doc_templates/LICENSE.md +1 -1
- data/doc_templates/copyright_erb.txt +1 -1
- data/doc_templates/copyright_js.txt +1 -1
- data/doc_templates/copyright_ruby.txt +1 -1
- data/docs/README.md +16 -16
- data/index.md +13 -13
- data/lib/urbanopt-reopt.rb +1 -1
- data/lib/urbanopt/reopt.rb +1 -1
- data/lib/urbanopt/reopt/extension.rb +1 -1
- data/lib/urbanopt/reopt/feature_report_adapter.rb +179 -99
- data/lib/urbanopt/reopt/reopt_lite_api.rb +132 -13
- data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
- data/lib/urbanopt/reopt/reopt_post_processor.rb +80 -26
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +1 -1
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +182 -105
- data/lib/urbanopt/reopt/version.rb +2 -2
- data/lib/urbanopt/reopt_scenario.rb +1 -1
- data/urbanopt-reopt.gemspec +10 -7
- metadata +35 -30
data/Rakefile
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt (tm), Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
3
|
+
# contributors. All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# Redistributions of source code must retain the above copyright notice, this list
|
9
|
+
# of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# Redistributions in binary form must reproduce the above copyright notice, this
|
12
|
+
# list of conditions and the following disclaimer in the documentation and/or other
|
13
|
+
# materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# Neither the name of the copyright holder nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without specific
|
17
|
+
# prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
20
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
21
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
22
|
+
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
23
|
+
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
24
|
+
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
25
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
26
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
27
|
+
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
28
|
+
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
|
+
# *********************************************************************************
|
30
|
+
|
1
31
|
require 'bundler/gem_tasks'
|
2
32
|
require 'rspec/core/rake_task'
|
3
33
|
|
@@ -10,7 +40,7 @@ RuboCop::RakeTask.new
|
|
10
40
|
require 'openstudio/extension/rake_task'
|
11
41
|
require 'urbanopt/reopt/extension'
|
12
42
|
os_extension = OpenStudio::Extension::RakeTask.new
|
13
|
-
os_extension.set_extension_class(URBANopt::REopt::Extension)
|
43
|
+
os_extension.set_extension_class(URBANopt::REopt::Extension, 'urbanopt/urbanopt-reopt-gem')
|
14
44
|
|
15
45
|
desc 'CLI OpenSSL test'
|
16
46
|
task :cli_openssl_test do
|
data/a.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
123
|
data/doc_templates/LICENSE.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
URBANopt
|
1
|
+
URBANopt™, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
2
2
|
contributors. All rights reserved.
|
3
3
|
|
4
4
|
Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%
|
2
2
|
# *********************************************************************************
|
3
|
-
# URBANopt
|
3
|
+
# URBANopt™, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other
|
4
4
|
# contributors. All rights reserved.
|
5
5
|
#
|
6
6
|
# Redistribution and use in source and binary forms, with or without modification,
|
@@ -1,4 +1,4 @@
|
|
1
1
|
/* @preserve
|
2
|
-
* URBANopt
|
2
|
+
* URBANopt™, Copyright (c) 2019-2020, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved.
|
3
3
|
* Use of this source code is governed by the BSD 3-Clause license.
|
4
4
|
*/
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# *********************************************************************************
|
2
|
-
# URBANopt
|
2
|
+
# URBANopt™, 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,
|
data/docs/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
### <StaticLink target="\_blank" href="rdoc/">Rdocs</StaticLink>
|
4
4
|
|
5
|
-
The **URBANopt REopt Gem** extends a **URBANopt::
|
6
|
-
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 <StaticLink target="\_blank" href="https://developer.nrel.gov/docs/energy-optimization/reopt-v1/">https://developer.nrel.gov/docs/energy-optimization/reopt-v1/</StaticLink> for more detailed information on input parameters and default assumptions.
|
5
|
+
The **URBANopt<sup>™</sup> REopt Gem** extends a **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 <StaticLink target="\_blank" href="https://reopt.nrel.gov/tool">REopt Lite</StaticLink> decision support platform.
|
6
|
+
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 <StaticLink target="\_blank" href="https://developer.nrel.gov/docs/energy-optimization/reopt-v1/">https://developer.nrel.gov/docs/energy-optimization/reopt-v1/</StaticLink> for more detailed information on input parameters and default assumptions.
|
7
7
|
|
8
8
|
The REopt Gem accomplishes three basic functions (described more below in the _Functionality_ section):
|
9
9
|
|
@@ -13,8 +13,8 @@ The REopt Gem accomplishes three basic functions (described more below in the _F
|
|
13
13
|
|
14
14
|
Moreover, the REopt Gem can be run in several modes, either on:
|
15
15
|
|
16
|
-
* A Feature Report,
|
17
|
-
* A collection of Feature Reports
|
16
|
+
* A Feature Report,
|
17
|
+
* A collection of Feature Reports
|
18
18
|
* All the features in a Scenario Report before aggregating results at the scenario level
|
19
19
|
* The collection of features in aggregate as summarized in a Scenario Report
|
20
20
|
|
@@ -27,7 +27,7 @@ See the <StaticLink target="\_blank" href="https://github.com/urbanopt/urbanopt-
|
|
27
27
|
|
28
28
|
## Installation
|
29
29
|
|
30
|
-
See [https://docs.urbanopt.net/installation/installation.html](https://docs.urbanopt.net/installation/installation.html) for instructions on prerequiste software, including:
|
30
|
+
See [https://docs.urbanopt.net/installation/installation.html](https://docs.urbanopt.net/installation/installation.html) for instructions on prerequiste software, including:
|
31
31
|
- Ruby 2.2.6
|
32
32
|
- Bundler 1.17.0
|
33
33
|
- OpenStudio 2.8.1
|
@@ -49,7 +49,7 @@ Or install it yourself as:
|
|
49
49
|
|
50
50
|
## Functionality
|
51
51
|
|
52
|
-
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:
|
52
|
+
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:
|
53
53
|
```
|
54
54
|
"distributed_generation": {
|
55
55
|
"lcc_us_dollars": 100000000.0,
|
@@ -97,9 +97,9 @@ Moreover, the following optimal dispatch fields are added to its timeseries CSV.
|
|
97
97
|
| ElectricityProduced:Wind:ToBattery | kWh |
|
98
98
|
| ElectricityProduced:Wind:ToLoad | kWh |
|
99
99
|
| ElectricityProduced:Wind:ToGrid | kWh |
|
100
|
-
|
101
100
|
|
102
|
-
|
101
|
+
|
102
|
+
The REopt Lite has default values for all non-required input parameters that are used unless the user specifies custom assumptions. See <StaticLink target="\_blank" href="https://developer.nrel.gov/docs/energy-optimization/reopt-v1/">https://developer.nrel.gov/docs/energy-optimization/reopt-v1/</StaticLink> for more detailed information on input parameters and default assumptions.
|
103
103
|
|
104
104
|
<b>Note:</b> Required attributes for a REopt run include latitude and longitude, parsed from the Feature or Scenario Report attributes. If no utility rate is specified in your assumptions, then a constant 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).
|
105
105
|
|
@@ -118,7 +118,7 @@ DEVELOPER_NREL_KEY = "" # <insert a valid API key from https://developer.nrel.go
|
|
118
118
|
feature_reports_hash = {} # <insert a valid Feature Report hash here with latitude and longitude filled in>
|
119
119
|
|
120
120
|
#Create a Feature Report
|
121
|
-
feature_report = URBANopt::
|
121
|
+
feature_report = URBANopt::Reporting::DefaultReports::FeatureReport.new(feature_reports_hash)
|
122
122
|
|
123
123
|
#Specify a file name where REopt Lite results will be written in JSON format
|
124
124
|
reopt_output_file = File.join(feature_report.directory_name, 'feature_report_reopt_run.json')
|
@@ -134,7 +134,7 @@ reopt_post_processor = URBANopt::REopt::REoptPostProcessor.new(nil, nil, nil, DE
|
|
134
134
|
|
135
135
|
#Call REopt Lite with the post processor to update the feature's distributed generation attributes and timeseries CSV.
|
136
136
|
updated_feature_report = reopt_post_processor.run_feature_report(feature_report,reopt_assumptions_file,reopt_output_file,timeseries_output_file)
|
137
|
-
|
137
|
+
|
138
138
|
```
|
139
139
|
|
140
140
|
More commonly, this gem can be used to run REopt a collection of features stored in a Scenario Report as show here:
|
@@ -143,9 +143,9 @@ require 'urbanopt/reopt'
|
|
143
143
|
DEVELOPER_NREL_KEY = "" # <insert a valid API key from https://developer.nrel.gov/signup >
|
144
144
|
|
145
145
|
#Create a Scenario Report
|
146
|
-
scenario_report = URBANopt::
|
146
|
+
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') }})
|
147
147
|
|
148
|
-
#Load Feature Reports into the Scenario Report
|
148
|
+
#Load Feature Reports into the Scenario Report
|
149
149
|
(1..2).each do |i|
|
150
150
|
feature_reports_path = File.join(File.dirname(__FILE__), "run/example_scenario/#{i}/010_default_feature_reports/default_feature_reports.json")
|
151
151
|
|
@@ -154,8 +154,8 @@ scenario_report = URBANopt::Scenario::DefaultReports::ScenarioReport.new({:direc
|
|
154
154
|
feature_reports_hash = JSON.parse(file.read, symbolize_names: true)
|
155
155
|
end
|
156
156
|
|
157
|
-
feature_report = URBANopt::
|
158
|
-
|
157
|
+
feature_report = URBANopt::Reporting::DefaultReports::FeatureReport.new(feature_reports_hash)
|
158
|
+
|
159
159
|
feature_report_dir = File.join(File.dirname(__FILE__), "run/example_scenario/#{i}")
|
160
160
|
feature_report.directory_name = feature_report_dir
|
161
161
|
|
@@ -170,7 +170,7 @@ reopt_post_processor = URBANopt::REopt::REoptPostProcessor.new(scenario_report,
|
|
170
170
|
|
171
171
|
#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.
|
172
172
|
updated_scenario_report = reopt_post_processor.run_scenario_report(scenario_report)
|
173
|
-
|
173
|
+
|
174
174
|
```
|
175
175
|
|
176
176
|
## Testing
|
@@ -184,7 +184,7 @@ Next, obtain a developer.nrel.gov API key from the [NREL Developer Network](http
|
|
184
184
|
Finally, execute:
|
185
185
|
|
186
186
|
$ bundle install
|
187
|
-
$ bundle update
|
187
|
+
$ bundle update
|
188
188
|
$ bundle exec rake
|
189
189
|
|
190
190
|
|
data/index.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# **URBANopt REopt Gem**
|
2
2
|
|
3
|
-
The **URBANopt REopt Gem** extends **URBANopt::
|
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>™</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::
|
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::
|
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::
|
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
|
|
data/lib/urbanopt-reopt.rb
CHANGED
@@ -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,
|
data/lib/urbanopt/reopt.rb
CHANGED
@@ -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,16 +28,17 @@
|
|
28
28
|
# OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
29
|
# *********************************************************************************
|
30
30
|
|
31
|
-
require 'urbanopt/
|
31
|
+
require 'urbanopt/reporting/default_reports'
|
32
32
|
require 'urbanopt/reopt/reopt_logger'
|
33
33
|
require 'csv'
|
34
34
|
require 'matrix'
|
35
|
+
require 'time'
|
35
36
|
|
36
37
|
module URBANopt # :nodoc:
|
37
38
|
module REopt # :nodoc:
|
38
39
|
class FeatureReportAdapter
|
39
40
|
##
|
40
|
-
# FeatureReportAdapter can convert a URBANopt::
|
41
|
+
# 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.
|
41
42
|
##
|
42
43
|
# [*parameters:*]
|
43
44
|
##
|
@@ -51,7 +52,7 @@ module URBANopt # :nodoc:
|
|
51
52
|
#
|
52
53
|
# [*parameters:*]
|
53
54
|
#
|
54
|
-
# * +feature_report+ - _URBANopt::
|
55
|
+
# * +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.
|
55
56
|
# * +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.
|
56
57
|
#
|
57
58
|
# [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
|
@@ -74,7 +75,7 @@ module URBANopt # :nodoc:
|
|
74
75
|
requireds.each_with_index do |i, x|
|
75
76
|
if [nil, 0].include? x
|
76
77
|
n = requireds_names[i]
|
77
|
-
|
78
|
+
# @@logger.error("Missing value for #{n} - this is a required input")
|
78
79
|
raise "Missing value for #{n} - this is a required input"
|
79
80
|
end
|
80
81
|
end
|
@@ -95,20 +96,25 @@ module URBANopt # :nodoc:
|
|
95
96
|
reopt_inputs[:Scenario][:Site][:land_acres] = feature_report.program.site_area * 1.0 / 43560 # acres/sqft
|
96
97
|
end
|
97
98
|
|
99
|
+
unless feature_report.timesteps_per_hour.nil?
|
100
|
+
reopt_inputs[:Scenario][:time_steps_per_hour] = feature_report.timesteps_per_hour
|
101
|
+
end
|
102
|
+
|
98
103
|
# Parse Load Profile
|
99
104
|
begin
|
100
|
-
col_num = feature_report.timeseries_csv.column_names.index('Electricity:Facility')
|
105
|
+
col_num = feature_report.timeseries_csv.column_names.index('Electricity:Facility(kWh)')
|
101
106
|
t = CSV.read(feature_report.timeseries_csv.path, headers: true, converters: :numeric)
|
102
|
-
|
103
|
-
if (feature_report.timesteps_per_hour
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
107
|
+
energy_timeseries_kw = t.by_col[col_num].map { |e| ((e * feature_report.timesteps_per_hour || 0) ) }
|
108
|
+
if energy_timeseries_kw.length < (feature_report.timesteps_per_hour * 8760)
|
109
|
+
start_date = Time.parse(t.by_col["Datetime"][0])
|
110
|
+
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) /
|
111
|
+
(( 60 / feature_report.timesteps_per_hour ) * 60)).to_int
|
112
|
+
end_date = Time.parse(t.by_col["Datetime"][-1])
|
113
|
+
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
|
+
(( 60 / feature_report.timesteps_per_hour ) * 60)).to_int
|
115
|
+
energy_timeseries_kw = [0.0]*(start_ts-1) + energy_timeseries_kw + [0.0]*((feature_report.timesteps_per_hour * 8760) - end_ts)
|
110
116
|
end
|
111
|
-
reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] =
|
117
|
+
reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = energy_timeseries_kw.map { |e| e ? e : 0 }[0,(feature_report.timesteps_per_hour * 8760)]
|
112
118
|
rescue StandardError
|
113
119
|
@@logger.error("Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}")
|
114
120
|
raise "Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}"
|
@@ -121,19 +127,39 @@ module URBANopt # :nodoc:
|
|
121
127
|
#
|
122
128
|
# [*parameters:*]
|
123
129
|
#
|
124
|
-
# * +feature_report+ - _URBANopt::
|
130
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to update from a \REopt Lite reponse hash.
|
125
131
|
# * +reopt_output+ - _Hash_ - A reponse hash from the \REopt Lite API to use in overwriting FeatureReport technology sizes, costs and dispatch strategies.
|
126
132
|
# * +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.
|
127
133
|
#
|
128
|
-
# [*return:*] _URBANopt::
|
134
|
+
# [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport.
|
129
135
|
##
|
130
|
-
def update_feature_report(feature_report, reopt_output, timeseries_csv_path =
|
136
|
+
def update_feature_report(feature_report, reopt_output, timeseries_csv_path=nil, resilience_stats=nil)
|
131
137
|
# Check if the \REopt Lite response is valid
|
132
138
|
if reopt_output['outputs']['Scenario']['status'] != 'optimal'
|
133
139
|
@@logger.info("Warning cannot Feature Report #{feature_report.name} #{feature_report.id} - REopt optimization was non-optimal")
|
134
140
|
return feature_report
|
135
141
|
end
|
136
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
|
+
|
137
163
|
# Update location
|
138
164
|
feature_report.location.latitude = reopt_output['inputs']['Scenario']['Site']['latitude']
|
139
165
|
feature_report.location.longitude = reopt_output['inputs']['Scenario']['Site']['longitude']
|
@@ -142,36 +168,71 @@ module URBANopt # :nodoc:
|
|
142
168
|
feature_report.timesteps_per_hour = reopt_output['inputs']['Scenario']['time_steps_per_hour']
|
143
169
|
|
144
170
|
# Update distributed generation sizing and financials
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
171
|
+
feature_report.distributed_generation.lcc_us_dollars = reopt_output['outputs']['Scenario']['Site']['Financial']['lcc_us_dollars'] || 0
|
172
|
+
feature_report.distributed_generation.npv_us_dollars = reopt_output['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0
|
173
|
+
feature_report.distributed_generation.year_one_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_us_dollars'] || 0
|
174
|
+
feature_report.distributed_generation.year_one_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars'] || 0
|
175
|
+
feature_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars'] || 0
|
176
|
+
feature_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars'] || 0
|
177
|
+
feature_report.distributed_generation.total_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_us_dollars'] || 0
|
178
|
+
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
|
179
|
+
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
|
180
|
+
feature_report.distributed_generation.year_one_bill_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_bau_us_dollars'] || 0
|
181
|
+
feature_report.distributed_generation.total_demand_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_demand_cost_bau_us_dollars'] || 0
|
182
|
+
feature_report.distributed_generation.total_energy_cost_bau_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_bau_us_dollars'] || 0
|
183
|
+
if !resilience_stats.nil?
|
184
|
+
feature_report.distributed_generation.resilience_hours_min = resilience_stats['resilience_hours_min']
|
185
|
+
feature_report.distributed_generation.resilience_hours_max = resilience_stats['resilience_hours_max']
|
186
|
+
feature_report.distributed_generation.resilience_hours_avg = resilience_stats['resilience_hours_avg']
|
187
|
+
feature_report.distributed_generation.probs_of_surviving = resilience_stats['probs_of_surviving']
|
188
|
+
feature_report.distributed_generation.probs_of_surviving_by_month = resilience_stats['probs_of_surviving_by_month']
|
189
|
+
feature_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
|
190
|
+
end
|
191
|
+
|
192
|
+
if reopt_output['outputs']['Scenario']['Site']['PV'].class == Hash
|
193
|
+
reopt_output['outputs']['Scenario']['Site']['PV'] = [reopt_output['outputs']['Scenario']['Site']['PV']]
|
194
|
+
elsif reopt_output['outputs']['Scenario']['Site']['PV'].nil?
|
195
|
+
reopt_output['outputs']['Scenario']['Site']['PV'] = []
|
196
|
+
end
|
197
|
+
|
198
|
+
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
199
|
+
feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new( {size_kw: (pv['size_kw'] || 0), id: i })
|
200
|
+
end
|
151
201
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
202
|
+
wind = reopt_output['outputs']['Scenario']['Site']['Wind']
|
203
|
+
if !wind['size_kw'].nil? and wind['size_kw'] != 0
|
204
|
+
feature_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new( {size_kw: (wind['size_kw'] || 0) })
|
205
|
+
end
|
206
|
+
|
207
|
+
generator = reopt_output['outputs']['Scenario']['Site']['Generator']
|
208
|
+
if !generator['size_kw'].nil? and generator['size_kw'] != 0
|
209
|
+
feature_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new( {size_kw: (generator['size_kw'] || 0) })
|
210
|
+
end
|
211
|
+
|
212
|
+
storage = reopt_output['outputs']['Scenario']['Site']['Storage']
|
213
|
+
if !storage['size_kw'].nil? and storage['size_kw'] != 0
|
214
|
+
feature_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new( {size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
|
215
|
+
end
|
157
216
|
|
158
|
-
generation_timeseries_kwh = Matrix[[0] * 8760]
|
217
|
+
generation_timeseries_kwh = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
159
218
|
|
160
219
|
unless reopt_output['outputs']['Scenario']['Site']['PV'].nil?
|
161
|
-
|
162
|
-
if
|
163
|
-
|
220
|
+
reopt_output['outputs']['Scenario']['Site']['PV'].each do |pv|
|
221
|
+
if (pv['size_kw'] || 0) > 0
|
222
|
+
if !pv['year_one_power_production_series_kw'].nil?
|
223
|
+
generation_timeseries_kwh += Matrix[pv['year_one_power_production_series_kw']]
|
224
|
+
end
|
164
225
|
end
|
165
|
-
|
226
|
+
end
|
166
227
|
end
|
167
228
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
229
|
+
unless reopt_output['outputs']['Scenario']['Site']['Storage'].nil?
|
230
|
+
if (reopt_output['outputs']['Scenario']['Site']['Storage']['size_kw'] or 0) > 0
|
231
|
+
if !reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'].nil?
|
232
|
+
generation_timeseries_kwh = generation_timeseries_kwh + Matrix[reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw']]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
175
236
|
|
176
237
|
unless reopt_output['outputs']['Scenario']['Site']['Wind'].nil?
|
177
238
|
if (reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0
|
@@ -189,137 +250,153 @@ module URBANopt # :nodoc:
|
|
189
250
|
end
|
190
251
|
end
|
191
252
|
|
192
|
-
$generation_timeseries_kwh = generation_timeseries_kwh.to_a[0] || [0] * 8760
|
193
|
-
$generation_timeseries_kwh_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Total')
|
253
|
+
$generation_timeseries_kwh = generation_timeseries_kwh.to_a[0] || [0] * (8760 * feature_report.timesteps_per_hour)
|
254
|
+
$generation_timeseries_kwh_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Total(kw)')
|
194
255
|
if $generation_timeseries_kwh_col.nil?
|
195
256
|
$generation_timeseries_kwh_col = feature_report.timeseries_csv.column_names.length
|
196
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:Total')
|
257
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Total(kw)')
|
197
258
|
end
|
198
259
|
|
199
|
-
$load = reopt_output['outputs']['Scenario']['Site']['LoadProfile']['year_one_electric_load_series_kw'] || [0] * 8760
|
200
|
-
$load_col = feature_report.timeseries_csv.column_names.index('Electricity:Load:Total')
|
260
|
+
$load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['LoadProfile']['year_one_electric_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
261
|
+
$load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Load:Total(kw)')
|
201
262
|
if $load_col.nil?
|
202
263
|
$load_col = feature_report.timeseries_csv.column_names.length
|
203
|
-
feature_report.timeseries_csv.column_names.push('Electricity:Load:Total')
|
264
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Load:Total(kw)')
|
204
265
|
end
|
205
266
|
|
206
|
-
$utility_to_load = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_load_series_kw'] || [0] * 8760
|
207
|
-
$utility_to_load_col = feature_report.timeseries_csv.column_names.index('Electricity:Grid:ToLoad')
|
267
|
+
$utility_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
268
|
+
$utility_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToLoad(kw)')
|
208
269
|
if $utility_to_load_col.nil?
|
209
270
|
$utility_to_load_col = feature_report.timeseries_csv.column_names.length
|
210
|
-
feature_report.timeseries_csv.column_names.push('Electricity:Grid:ToLoad')
|
271
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToLoad(kw)')
|
211
272
|
end
|
212
273
|
|
213
|
-
$utility_to_battery = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_battery_series_kw'] || [0] * 8760
|
214
|
-
$utility_to_battery_col = feature_report.timeseries_csv.column_names.index('Electricity:Grid:ToBattery')
|
274
|
+
$utility_to_battery = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_battery_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
275
|
+
$utility_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToBattery(kw)')
|
215
276
|
if $utility_to_battery_col.nil?
|
216
277
|
$utility_to_battery_col = feature_report.timeseries_csv.column_names.length
|
217
|
-
feature_report.timeseries_csv.column_names.push('Electricity:Grid:ToBattery')
|
278
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToBattery(kw)')
|
218
279
|
end
|
219
280
|
|
220
|
-
$storage_to_load = reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_load_series_kw'] || [0] * 8760
|
221
|
-
$storage_to_load_col = feature_report.timeseries_csv.column_names.index('Electricity:Storage:ToLoad')
|
281
|
+
$storage_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
282
|
+
$storage_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToLoad(kw)')
|
222
283
|
if $storage_to_load_col.nil?
|
223
284
|
$storage_to_load_col = feature_report.timeseries_csv.column_names.length
|
224
|
-
feature_report.timeseries_csv.column_names.push('Electricity:Storage:ToLoad')
|
285
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToLoad(kw)')
|
225
286
|
end
|
226
287
|
|
227
|
-
$storage_to_grid = reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'] || [0] * 8760
|
228
|
-
$storage_to_grid_col = feature_report.timeseries_csv.column_names.index('Electricity:Storage:ToGrid')
|
288
|
+
$storage_to_grid = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
289
|
+
$storage_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToGrid(kw)')
|
229
290
|
if $storage_to_grid_col.nil?
|
230
291
|
$storage_to_grid_col = feature_report.timeseries_csv.column_names.length
|
231
|
-
feature_report.timeseries_csv.column_names.push('Electricity:Storage:ToGrid')
|
292
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToGrid(kw)')
|
232
293
|
end
|
233
294
|
|
234
|
-
$storage_soc = reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct'] || [0] * 8760
|
235
|
-
$storage_soc_col = feature_report.timeseries_csv.column_names.index('Electricity:Storage:StateOfCharge')
|
295
|
+
$storage_soc = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
296
|
+
$storage_soc_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:StateOfCharge(pct)')
|
236
297
|
if $storage_soc_col.nil?
|
237
298
|
$storage_soc_col = feature_report.timeseries_csv.column_names.length
|
238
|
-
feature_report.timeseries_csv.column_names.push('Electricity:Storage:StateOfCharge')
|
299
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:StateOfCharge(pct)')
|
239
300
|
end
|
240
301
|
|
241
|
-
$generator_total = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'] || [0] * 8760
|
242
|
-
$generator_total_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:Total')
|
302
|
+
$generator_total = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
303
|
+
$generator_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:Total(kw)')
|
243
304
|
if $generator_total_col.nil?
|
244
305
|
$generator_total_col = feature_report.timeseries_csv.column_names.length
|
245
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:Total')
|
306
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:Total(kw)')
|
246
307
|
end
|
247
308
|
|
248
|
-
$generator_to_battery = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_battery_series_kw'] || [0] * 8760
|
249
|
-
$generator_to_battery_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:ToBattery')
|
309
|
+
$generator_to_battery = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_battery_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
310
|
+
$generator_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToBattery(kw)')
|
250
311
|
if $generator_to_battery_col.nil?
|
251
312
|
$generator_to_battery_col = feature_report.timeseries_csv.column_names.length
|
252
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:ToBattery')
|
313
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToBattery(kw)')
|
253
314
|
end
|
254
315
|
|
255
|
-
$generator_to_load = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_load_series_kw'] || [0] * 8760
|
256
|
-
$generator_to_load_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:ToLoad')
|
316
|
+
$generator_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
317
|
+
$generator_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToLoad(kw)')
|
257
318
|
if $generator_to_load_col.nil?
|
258
319
|
$generator_to_load_col = feature_report.timeseries_csv.column_names.length
|
259
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:ToLoad')
|
320
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToLoad(kw)')
|
260
321
|
end
|
261
322
|
|
262
|
-
$generator_to_grid = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_grid_series_kw'] || [0] * 8760
|
263
|
-
$generator_to_grid_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:ToGrid')
|
323
|
+
$generator_to_grid = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_grid_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
324
|
+
$generator_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToGrid(kw)')
|
264
325
|
if $generator_to_grid_col.nil?
|
265
326
|
$generator_to_grid_col = feature_report.timeseries_csv.column_names.length
|
266
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:ToGrid')
|
327
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToGrid(kw)')
|
267
328
|
end
|
268
329
|
|
269
|
-
$
|
270
|
-
$pv_total_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:PV:Total')
|
330
|
+
$pv_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:Total(kw)')
|
271
331
|
if $pv_total_col.nil?
|
272
332
|
$pv_total_col = feature_report.timeseries_csv.column_names.length
|
273
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:PV:Total')
|
333
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:Total(kw)')
|
274
334
|
end
|
275
335
|
|
276
|
-
$
|
277
|
-
$pv_to_battery_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:PV:ToBattery')
|
336
|
+
$pv_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:ToBattery(kw)')
|
278
337
|
if $pv_to_battery_col.nil?
|
279
338
|
$pv_to_battery_col = feature_report.timeseries_csv.column_names.length
|
280
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:PV:ToBattery')
|
339
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:ToBattery(kw)')
|
281
340
|
end
|
282
341
|
|
283
|
-
$
|
284
|
-
$pv_to_load_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:PV:ToLoad')
|
342
|
+
$pv_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:ToLoad(kw)')
|
285
343
|
if $pv_to_load_col.nil?
|
286
344
|
$pv_to_load_col = feature_report.timeseries_csv.column_names.length
|
287
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:PV:ToLoad')
|
345
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:ToLoad(kw)')
|
288
346
|
end
|
289
347
|
|
290
|
-
|
291
|
-
$pv_to_grid_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:PV:ToGrid')
|
348
|
+
|
349
|
+
$pv_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:ToGrid(kw)')
|
292
350
|
if $pv_to_grid_col.nil?
|
293
351
|
$pv_to_grid_col = feature_report.timeseries_csv.column_names.length
|
294
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:PV:ToGrid')
|
352
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:ToGrid(kw)')
|
295
353
|
end
|
296
354
|
|
297
|
-
$
|
298
|
-
$
|
355
|
+
$pv_total = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
356
|
+
$pv_to_battery = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
357
|
+
$pv_to_load = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
358
|
+
$pv_to_grid = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
359
|
+
|
360
|
+
reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
|
361
|
+
if (pv['size_kw'] || 0) > 0
|
362
|
+
$pv_total += Matrix[scale_timeseries(pv['year_one_power_production_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)]
|
363
|
+
$pv_to_battery += Matrix[scale_timeseries(pv['year_one_to_battery_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)]
|
364
|
+
$pv_to_load += Matrix[scale_timeseries(pv['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)]
|
365
|
+
$pv_to_grid += Matrix[scale_timeseries(pv['year_one_to_grid_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)]
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
$pv_total = $pv_total.to_a[0]
|
370
|
+
$pv_to_battery = $pv_to_battery.to_a[0]
|
371
|
+
$pv_to_load = $pv_to_load.to_a[0]
|
372
|
+
$pv_to_grid = $pv_to_grid.to_a[0]
|
373
|
+
|
374
|
+
$wind_total = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
375
|
+
$wind_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:Total(kw)')
|
299
376
|
if $wind_total_col.nil?
|
300
377
|
$wind_total_col = feature_report.timeseries_csv.column_names.length
|
301
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:Total')
|
378
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:Total(kw)')
|
302
379
|
end
|
303
380
|
|
304
|
-
$wind_to_battery = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_battery_series_kw'] || [0] * 8760
|
305
|
-
$wind_to_battery_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:ToBattery')
|
381
|
+
$wind_to_battery = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_battery_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
382
|
+
$wind_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToBattery(kw)')
|
306
383
|
if $wind_to_battery_col.nil?
|
307
384
|
$wind_to_battery_col = feature_report.timeseries_csv.column_names.length
|
308
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:ToBattery')
|
385
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToBattery(kw)')
|
309
386
|
end
|
310
387
|
|
311
|
-
$wind_to_load = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_load_series_kw'] || [0] * 8760
|
312
|
-
$wind_to_load_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:ToLoad')
|
388
|
+
$wind_to_load = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_load_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
389
|
+
$wind_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToLoad(kw)')
|
313
390
|
if $wind_to_load_col.nil?
|
314
391
|
$wind_to_load_col = feature_report.timeseries_csv.column_names.length
|
315
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:ToLoad')
|
392
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToLoad(kw)')
|
316
393
|
end
|
317
394
|
|
318
|
-
$wind_to_grid = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_grid_series_kw'] || [0] * 8760
|
319
|
-
$wind_to_grid_col = feature_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:ToGrid')
|
395
|
+
$wind_to_grid = scale_timeseries(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_grid_series_kw']) || [0] * (8760 * feature_report.timesteps_per_hour)
|
396
|
+
$wind_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToGrid(kw)')
|
320
397
|
if $wind_to_grid_col.nil?
|
321
398
|
$wind_to_grid_col = feature_report.timeseries_csv.column_names.length
|
322
|
-
feature_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:ToGrid')
|
399
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToGrid(kw)')
|
323
400
|
end
|
324
401
|
|
325
402
|
def modrow(x, i) # :nodoc:
|
@@ -346,9 +423,12 @@ module URBANopt # :nodoc:
|
|
346
423
|
end
|
347
424
|
|
348
425
|
old_data = CSV.open(feature_report.timeseries_csv.path).read
|
426
|
+
start_date = Time.parse(old_data[1][0])
|
427
|
+
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
|
428
|
+
|
349
429
|
mod_data = old_data.map.with_index do |x, i|
|
350
430
|
if i > 0
|
351
|
-
modrow(x, i)
|
431
|
+
modrow(x, start_ts + i -2)
|
352
432
|
else
|
353
433
|
x
|
354
434
|
end
|