urbanopt-reopt 0.1.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|