urbanopt-reopt 0.1.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 +7 -0
- data/.gitignore +27 -0
- data/.rakeTasks +7 -0
- data/.rdoc_options +37 -0
- data/.rspec +3 -0
- data/.rubocop.yml +9 -0
- data/.travis.yml +22 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +67 -0
- data/Jenkinsfile +10 -0
- data/LICENSE.md +27 -0
- data/RDOC_MAIN.md +176 -0
- data/README.md +177 -0
- data/Rakefile +30 -0
- data/deploy_docs.sh +5 -0
- data/developer_nrel_key.rb +31 -0
- data/doc_templates/LICENSE.md +27 -0
- data/doc_templates/README.md.erb +42 -0
- data/doc_templates/copyright_erb.txt +31 -0
- data/doc_templates/copyright_js.txt +4 -0
- data/doc_templates/copyright_ruby.txt +29 -0
- data/docs/.gitignore +3 -0
- data/docs/.vuepress/components/InnerJsonSchema.vue +78 -0
- data/docs/.vuepress/components/JsonSchema.vue +12 -0
- data/docs/.vuepress/components/ReoptInputSchema.vue +12 -0
- data/docs/.vuepress/components/ReoptOutputSchema.vue +12 -0
- data/docs/.vuepress/components/StaticLink.vue +8 -0
- data/docs/.vuepress/config.js +16 -0
- data/docs/.vuepress/highlight.js +8 -0
- data/docs/.vuepress/public/custom_rdoc_styles.css +58 -0
- data/docs/.vuepress/utils.js +17 -0
- data/docs/README.md +196 -0
- data/docs/package-lock.json +254 -0
- data/docs/package.json +22 -0
- data/docs/schemas/reopt-input-schema.md +57 -0
- data/docs/schemas/reopt-output-schema.md +66 -0
- data/index.html +1 -0
- data/index.md +176 -0
- data/lib/files/.gitkeep +0 -0
- data/lib/urbanopt/reopt/extension.rb +44 -0
- data/lib/urbanopt/reopt/feature_report_adapter.rb +364 -0
- data/lib/urbanopt/reopt/reopt_lite_api.rb +230 -0
- data/lib/urbanopt/reopt/reopt_logger.rb +42 -0
- data/lib/urbanopt/reopt/reopt_post_processor.rb +245 -0
- data/lib/urbanopt/reopt/reopt_schema/reopt_input_schema.json +1111 -0
- data/lib/urbanopt/reopt/reopt_schema/reopt_output_schema.json +538 -0
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +115 -0
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +404 -0
- data/lib/urbanopt/reopt/version.rb +35 -0
- data/lib/urbanopt/reopt.rb +36 -0
- data/lib/urbanopt/reopt_scenario.rb +31 -0
- data/lib/urbanopt-reopt.rb +31 -0
- data/urbanopt-reopt.gemspec +33 -0
- metadata +194 -0
@@ -0,0 +1,404 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt, 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
|
+
|
31
|
+
require 'urbanopt/scenario/default_reports'
|
32
|
+
require 'urbanopt/reopt/reopt_logger'
|
33
|
+
|
34
|
+
require 'csv'
|
35
|
+
|
36
|
+
module URBANopt # :nodoc:
|
37
|
+
module REopt # :nodoc:
|
38
|
+
class ScenarioReportAdapter
|
39
|
+
##
|
40
|
+
# ScenarioReportAdapter can convert a ScenarioReport into a \REopt Lite posts or updates a ScenarioReport and its FeatureReports from \REopt Lite response(s)
|
41
|
+
##
|
42
|
+
# [*parameters:*]
|
43
|
+
def initialize
|
44
|
+
# initialize @@logger
|
45
|
+
@@logger ||= URBANopt::REopt.reopt_logger
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Convert a ScenarioReport into a \REopt Lite post
|
50
|
+
#
|
51
|
+
# [*parameters:*]
|
52
|
+
#
|
53
|
+
# * +scenario_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting the +reopt_assumptions_hash+, if provided, to a \REopt Lite post. Otherwise, if the +reopt_assumptions_hash+ is nil a default post will be updated from this ScenarioReport and submitted to the \REopt Lite API.
|
54
|
+
# * +reopt_assumptions_hash+ - _Hash_ - Optional. A hash formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API.
|
55
|
+
#
|
56
|
+
# [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
|
57
|
+
##
|
58
|
+
def reopt_json_from_scenario_report(scenario_report, reopt_assumptions_json = nil)
|
59
|
+
name = scenario_report.name.delete ' '
|
60
|
+
scenario_id = scenario_report.id.delete ' '
|
61
|
+
description = "scenario_report_#{name}_#{scenario_id}"
|
62
|
+
|
63
|
+
# Create base REpopt Lite post
|
64
|
+
reopt_inputs = { Scenario: { Site: { ElectricTariff: { blended_monthly_demand_charges_us_dollars_per_kw: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], blended_monthly_rates_us_dollars_per_kwh: [0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13] }, LoadProfile: {}, Wind: { max_kw: 0 } } } }
|
65
|
+
if !reopt_assumptions_json.nil?
|
66
|
+
reopt_inputs = reopt_assumptions_json
|
67
|
+
else
|
68
|
+
@@logger.info('Using default REopt Lite assumptions')
|
69
|
+
end
|
70
|
+
|
71
|
+
# Update required info
|
72
|
+
if scenario_report.location.latitude.nil? || scenario_report.location.longitude.nil? || (scenario_report.location.latitude == 0) || (scenario_report.location.longitude == 0)
|
73
|
+
if !scenario_report.feature_reports.nil? && (scenario_report.feature_reports != [])
|
74
|
+
lats = []
|
75
|
+
longs = []
|
76
|
+
scenario_report.feature_reports.each do |x|
|
77
|
+
if ![nil, 0].include?(x[:location][:latitude]) && ![nil, 0].include?(x[:location][:longitude])
|
78
|
+
lats.push(x[:location][:latitude])
|
79
|
+
longs.push(x[:location][:longitude])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if !lats.empty? && !longs.empty?
|
84
|
+
scenario_report.location.latitude = lats.reduce(:+) / lats.size.to_f
|
85
|
+
scenario_report.location.longitude = longs.reduce(:+) / longs.size.to_f
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Update required info
|
91
|
+
requireds_names = ['latitude', 'longitude']
|
92
|
+
requireds = [scenario_report.location.latitude, scenario_report.location.longitude]
|
93
|
+
|
94
|
+
if requireds.include?(nil) || requireds.include?(0)
|
95
|
+
requireds.each_with_index do |i, x|
|
96
|
+
if [nil, 0].include? x
|
97
|
+
n = requireds_names[i]
|
98
|
+
raise "Missing value for #{n} - this is a required input"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
reopt_inputs[:Scenario][:description] = description
|
104
|
+
|
105
|
+
reopt_inputs[:Scenario][:Site][:latitude] = scenario_report.location.latitude
|
106
|
+
reopt_inputs[:Scenario][:Site][:longitude] = scenario_report.location.longitude
|
107
|
+
|
108
|
+
# Update optional info
|
109
|
+
if !scenario_report.program.roof_area.nil?
|
110
|
+
reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area[:available_roof_area]
|
111
|
+
end
|
112
|
+
|
113
|
+
if !scenario_report.program.site_area.nil?
|
114
|
+
reopt_inputs[:Scenario][:Site][:land_acres] = scenario_report.program.site_area * 1.0 / 43560 # acres/sqft
|
115
|
+
end
|
116
|
+
|
117
|
+
# Update load profile info
|
118
|
+
begin
|
119
|
+
col_num = scenario_report.timeseries_csv.column_names.index('Electricity:Facility')
|
120
|
+
t = CSV.read(scenario_report.timeseries_csv.path, headers: true, converters: :numeric)
|
121
|
+
energy_timeseries_kwh = t.by_col[col_num].map { |e| ((e || 0) * 0.293071) } # convert kBTU to KWH
|
122
|
+
|
123
|
+
if (scenario_report.timesteps_per_hour || 1) > 1
|
124
|
+
energy_timeseries_kwh = energy_timeseries_kwh.each_slice(scenario_report.timesteps_per_hour).to_a.map { |x| x.inject(0, :+) / x.length.to_f }
|
125
|
+
end
|
126
|
+
|
127
|
+
if energy_timeseries_kwh.length < scenario_report.timesteps_per_hour * 8760
|
128
|
+
energy_timeseries_kwh += [0] * ((scenario_report.timesteps_per_hour * 8760) - energy_timeseries_kwh.length)
|
129
|
+
@@logger.info("Assuming load profile for Scenario Report #{scenario_report.name} #{scenario_report.id} starts January 1 - filling in rest with zeros")
|
130
|
+
end
|
131
|
+
reopt_inputs[:Scenario][:Site][:LoadProfile][:loads_kw] = energy_timeseries_kwh
|
132
|
+
rescue StandardError
|
133
|
+
raise "Could not parse the annual electric load from the timeseries csv - #{scenario_report.timeseries_csv.path}"
|
134
|
+
end
|
135
|
+
|
136
|
+
return reopt_inputs
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Converts a FeatureReport list from a ScenarioReport into an array of \REopt Lite posts
|
141
|
+
#
|
142
|
+
# [*parameters:*]
|
143
|
+
#
|
144
|
+
# * +scenario_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to use in converting FeatureReports and respecitive +reopt_assumptions_hashes+, if provided, to a \REopt Lite post. If no +reopt_assumptions_hashes+ are provided default posts will be updated from these FeatureReports and submitted to the \REopt Lite API.
|
145
|
+
# * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of hashes formatted for submittal to the \REopt Lite API containing default values. Values will be overwritten from the ScenarioReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API. The order should match the list in ScenarioReport.feature_reports.
|
146
|
+
#
|
147
|
+
# [*return:*] _Array_ - Returns an array of hashes formatted for submittal to the \REopt Lite API in the order of the FeatureReports lited in ScenarioReport.feature_reports.
|
148
|
+
##
|
149
|
+
def reopt_jsons_from_scenario_feature_reports(scenario_report, reopt_assumptions_hashes = [])
|
150
|
+
results = []
|
151
|
+
adapter = URBANopt::REopt::FeatureReportAdapter.new
|
152
|
+
|
153
|
+
scenario_report.feature_reports.each_with_index do |feature_report, idx|
|
154
|
+
fr = adapter.reopt_json_from_feature_report(feature_report, reopt_assumptions_hashes[idx])
|
155
|
+
results << fr
|
156
|
+
end
|
157
|
+
|
158
|
+
return results
|
159
|
+
end
|
160
|
+
|
161
|
+
##
|
162
|
+
# Updates a ScenarioReport from a \REopt Lite response
|
163
|
+
#
|
164
|
+
# [*parameters:*]
|
165
|
+
#
|
166
|
+
# * +scenario_report+ - _URBANopt::Scenario::DefaultReports::ScenarioReport_ - ScenarioReport to update from a \REopt Lite response.
|
167
|
+
# * +reopt_output+ - _Hash_ - A hash response from the \REopt Lite API.
|
168
|
+
# * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which new timeseries data will be written. If not provided a file is created based on the run_uuid of the \REopt Lite optimization task.
|
169
|
+
#
|
170
|
+
# [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
|
171
|
+
##
|
172
|
+
def update_scenario_report(scenario_report, reopt_output, timeseries_csv_path = nil)
|
173
|
+
if reopt_output['outputs']['Scenario']['status'] != 'optimal'
|
174
|
+
@@logger.info("Warning cannot Feature Report #{scenario_report.name} #{scenario_report.id} - REopt optimization was non-optimal")
|
175
|
+
return scenario_report
|
176
|
+
end
|
177
|
+
|
178
|
+
# Update location
|
179
|
+
scenario_report.location.latitude = reopt_output['inputs']['Scenario']['Site']['latitude']
|
180
|
+
scenario_report.location.longitude = reopt_output['inputs']['Scenario']['Site']['longitude']
|
181
|
+
|
182
|
+
# Update timeseries csv from \REopt Lite dispatch data
|
183
|
+
scenario_report.timesteps_per_hour = reopt_output['inputs']['Scenario']['time_steps_per_hour']
|
184
|
+
|
185
|
+
# Update distributed generation sizing and financials
|
186
|
+
(scenario_report.distributed_generation.lcc_us_dollars = reopt_output['outputs']['Scenario']['Site']['Financial']['lcc_us_dollars']) || 0
|
187
|
+
(scenario_report.distributed_generation.npv_us_dollars = reopt_output['outputs']['Scenario']['Site']['Financial']['npv_us_dollars']) || 0
|
188
|
+
(scenario_report.distributed_generation.year_one_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_energy_cost_us_dollars']) || 0
|
189
|
+
(scenario_report.distributed_generation.year_one_demand_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_demand_cost_us_dollars']) || 0
|
190
|
+
(scenario_report.distributed_generation.year_one_bill_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_bill_us_dollars']) || 0
|
191
|
+
(scenario_report.distributed_generation.total_energy_cost_us_dollars = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['total_energy_cost_us_dollars']) || 0
|
192
|
+
|
193
|
+
(scenario_report.distributed_generation.solar_pv.size_kw = reopt_output['outputs']['Scenario']['Site']['PV']['size_kw']) || 0
|
194
|
+
(scenario_report.distributed_generation.wind.size_kw = reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw']) || 0
|
195
|
+
(scenario_report.distributed_generation.generator.size_kw = reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw']) || 0
|
196
|
+
(scenario_report.distributed_generation.storage.size_kw = reopt_output['outputs']['Scenario']['Site']['Storage']['size_kw']) || 0
|
197
|
+
(scenario_report.distributed_generation.storage.size_kwh = reopt_output['outputs']['Scenario']['Site']['Storage']['size_kwh']) || 0
|
198
|
+
|
199
|
+
# Update dispatch
|
200
|
+
generation_timeseries_kwh = Matrix[[0] * 8760]
|
201
|
+
unless reopt_output['outputs']['Scenario']['Site']['PV'].nil?
|
202
|
+
if (reopt_output['outputs']['Scenario']['Site']['PV']['size_kw'] || 0) > 0
|
203
|
+
if !reopt_output['outputs']['Scenario']['Site']['PV']['year_one_power_production_series_kw'].nil?
|
204
|
+
generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['PV']['year_one_power_production_series_kw']]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# unless reopt_output['outputs']['Scenario']['Site']['Storage'].nil?
|
210
|
+
# if (reopt_output['outputs']['Scenario']['Site']['Storage']['size_kw'] or 0) > 0
|
211
|
+
# if !reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'].nil?
|
212
|
+
# generation_timeseries_kwh = generation_timeseries_kwh + Matrix[reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw']]
|
213
|
+
# end
|
214
|
+
# end
|
215
|
+
# end
|
216
|
+
|
217
|
+
unless reopt_output['outputs']['Scenario']['Site']['Wind'].nil?
|
218
|
+
if (reopt_output['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) > 0
|
219
|
+
if !reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'].nil?
|
220
|
+
generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw']]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
unless reopt_output['outputs']['Scenario']['Site']['Generator'].nil?
|
226
|
+
if (reopt_output['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0) > 0
|
227
|
+
if !reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'].nil?
|
228
|
+
generation_timeseries_kwh += Matrix[reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw']]
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
$generation_timeseries_kwh = generation_timeseries_kwh.to_a[0]
|
234
|
+
$generation_timeseries_kwh_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:Total')
|
235
|
+
if $generation_timeseries_kwh_col.nil?
|
236
|
+
$generation_timeseries_kwh_col = scenario_report.timeseries_csv.column_names.length
|
237
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:Total')
|
238
|
+
end
|
239
|
+
|
240
|
+
$load = reopt_output['outputs']['Scenario']['Site']['LoadProfile']['year_one_electric_load_series_kw'] || [0] * 8760
|
241
|
+
$load_col = scenario_report.timeseries_csv.column_names.index('Electricity:Load:Total')
|
242
|
+
if $load_col.nil?
|
243
|
+
$load_col = scenario_report.timeseries_csv.column_names.length
|
244
|
+
scenario_report.timeseries_csv.column_names.push('Electricity:Load:Total')
|
245
|
+
end
|
246
|
+
|
247
|
+
$utility_to_load = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_load_series_kw'] || [0] * 8760
|
248
|
+
$utility_to_load_col = scenario_report.timeseries_csv.column_names.index('Electricity:Grid:ToLoad')
|
249
|
+
if $utility_to_load_col.nil?
|
250
|
+
$utility_to_load_col = scenario_report.timeseries_csv.column_names.length
|
251
|
+
scenario_report.timeseries_csv.column_names.push('Electricity:Grid:ToLoad')
|
252
|
+
end
|
253
|
+
|
254
|
+
$utility_to_battery = reopt_output['outputs']['Scenario']['Site']['ElectricTariff']['year_one_to_battery_series_kw'] || [0] * 8760
|
255
|
+
$utility_to_battery_col = scenario_report.timeseries_csv.column_names.index('Electricity:Grid:ToBattery')
|
256
|
+
if $utility_to_battery_col.nil?
|
257
|
+
$utility_to_battery_col = scenario_report.timeseries_csv.column_names.length
|
258
|
+
scenario_report.timeseries_csv.column_names.push('Electricity:Grid:ToBattery')
|
259
|
+
end
|
260
|
+
|
261
|
+
$storage_to_load = reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_load_series_kw'] || [0] * 8760
|
262
|
+
$storage_to_load_col = scenario_report.timeseries_csv.column_names.index('Electricity:Storage:ToLoad')
|
263
|
+
if $storage_to_load_col.nil?
|
264
|
+
$storage_to_load_col = scenario_report.timeseries_csv.column_names.length
|
265
|
+
scenario_report.timeseries_csv.column_names.push('Electricity:Storage:ToLoad')
|
266
|
+
end
|
267
|
+
|
268
|
+
$storage_to_grid = reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_to_grid_series_kw'] || [0] * 8760
|
269
|
+
$storage_to_grid_col = scenario_report.timeseries_csv.column_names.index('Electricity:Storage:ToGrid')
|
270
|
+
if $storage_to_grid_col.nil?
|
271
|
+
$storage_to_grid_col = scenario_report.timeseries_csv.column_names.length
|
272
|
+
scenario_report.timeseries_csv.column_names.push('Electricity:Storage:ToGrid')
|
273
|
+
end
|
274
|
+
|
275
|
+
$storage_soc = reopt_output['outputs']['Scenario']['Site']['Storage']['year_one_soc_series_pct'] || [0] * 8760
|
276
|
+
$storage_soc_col = scenario_report.timeseries_csv.column_names.index('Electricity:Storage:StateOfCharge')
|
277
|
+
if $storage_soc_col.nil?
|
278
|
+
$storage_soc_col = scenario_report.timeseries_csv.column_names.length
|
279
|
+
scenario_report.timeseries_csv.column_names.push('Electricity:Storage:StateOfCharge')
|
280
|
+
end
|
281
|
+
|
282
|
+
$generator_total = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'] || [0] * 8760
|
283
|
+
$generator_total_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:Total')
|
284
|
+
if $generator_total_col.nil?
|
285
|
+
$generator_total_col = scenario_report.timeseries_csv.column_names.length
|
286
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:Total')
|
287
|
+
end
|
288
|
+
|
289
|
+
$generator_to_battery = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_battery_series_kw'] || [0] * 8760
|
290
|
+
$generator_to_battery_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:ToBattery')
|
291
|
+
if $generator_to_battery_col.nil?
|
292
|
+
$generator_to_battery_col = scenario_report.timeseries_csv.column_names.length
|
293
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:ToBattery')
|
294
|
+
end
|
295
|
+
|
296
|
+
$generator_to_load = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_load_series_kw'] || [0] * 8760
|
297
|
+
$generator_to_load_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:ToLoad')
|
298
|
+
if $generator_to_load_col.nil?
|
299
|
+
$generator_to_load_col = scenario_report.timeseries_csv.column_names.length
|
300
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:ToLoad')
|
301
|
+
end
|
302
|
+
|
303
|
+
$generator_to_grid = reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_to_grid_series_kw'] || [0] * 8760
|
304
|
+
$generator_to_grid_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:Generator:ToGrid')
|
305
|
+
if $generator_to_grid_col.nil?
|
306
|
+
$generator_to_grid_col = scenario_report.timeseries_csv.column_names.length
|
307
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:Generator:ToGrid')
|
308
|
+
end
|
309
|
+
|
310
|
+
$pv_total = reopt_output['outputs']['Scenario']['Site']['PV']['year_one_power_production_series_kw'] || [0] * 8760
|
311
|
+
$pv_total_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:PV:Total')
|
312
|
+
if $pv_total_col.nil?
|
313
|
+
$pv_total_col = scenario_report.timeseries_csv.column_names.length
|
314
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:PV:Total')
|
315
|
+
end
|
316
|
+
|
317
|
+
$pv_to_battery = reopt_output['outputs']['Scenario']['Site']['PV']['year_one_to_battery_series_kw'] || [0] * 8760
|
318
|
+
$pv_to_battery_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:PV:ToBattery')
|
319
|
+
if $pv_to_battery_col.nil?
|
320
|
+
$pv_to_battery_col = scenario_report.timeseries_csv.column_names.length
|
321
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:PV:ToBattery')
|
322
|
+
end
|
323
|
+
|
324
|
+
$pv_to_load = reopt_output['outputs']['Scenario']['Site']['PV']['year_one_to_load_series_kw'] || [0] * 8760
|
325
|
+
$pv_to_load_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:PV:ToLoad')
|
326
|
+
if $pv_to_load_col.nil?
|
327
|
+
$pv_to_load_col = scenario_report.timeseries_csv.column_names.length
|
328
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:PV:ToLoad')
|
329
|
+
end
|
330
|
+
|
331
|
+
$pv_to_grid = reopt_output['outputs']['Scenario']['Site']['PV']['year_one_to_grid_series_kw'] || [0] * 8760
|
332
|
+
$pv_to_grid_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:PV:ToGrid')
|
333
|
+
if $pv_to_grid_col.nil?
|
334
|
+
$pv_to_grid_col = scenario_report.timeseries_csv.column_names.length
|
335
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:PV:ToGrid')
|
336
|
+
end
|
337
|
+
|
338
|
+
$wind_total = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'] || [0] * 8760
|
339
|
+
$wind_total_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:Total')
|
340
|
+
if $wind_total_col.nil?
|
341
|
+
$wind_total_col = scenario_report.timeseries_csv.column_names.length
|
342
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:Total')
|
343
|
+
end
|
344
|
+
|
345
|
+
$wind_to_battery = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_battery_series_kw'] || [0] * 8760
|
346
|
+
$wind_to_battery_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:ToBattery')
|
347
|
+
if $wind_to_battery_col.nil?
|
348
|
+
$wind_to_battery_col = scenario_report.timeseries_csv.column_names.length
|
349
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:ToBattery')
|
350
|
+
end
|
351
|
+
|
352
|
+
$wind_to_load = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_load_series_kw'] || [0] * 8760
|
353
|
+
$wind_to_load_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:ToLoad')
|
354
|
+
if $wind_to_load_col.nil?
|
355
|
+
$wind_to_load_col = scenario_report.timeseries_csv.column_names.length
|
356
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:ToLoad')
|
357
|
+
end
|
358
|
+
|
359
|
+
$wind_to_grid = reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_to_grid_series_kw'] || [0] * 8760
|
360
|
+
$wind_to_grid_col = scenario_report.timeseries_csv.column_names.index('ElectricityProduced:Wind:ToGrid')
|
361
|
+
if $wind_to_grid_col.nil?
|
362
|
+
$wind_to_grid_col = scenario_report.timeseries_csv.column_names.length
|
363
|
+
scenario_report.timeseries_csv.column_names.push('ElectricityProduced:Wind:ToGrid')
|
364
|
+
end
|
365
|
+
|
366
|
+
def modrow(x, i) # :nodoc:
|
367
|
+
x[$generation_timeseries_kwh_col] = $generation_timeseries_kwh[i] || 0
|
368
|
+
x[$load_col] = $load[i] || 0
|
369
|
+
x[$utility_to_load_col] = $utility_to_load[i] || 0
|
370
|
+
x[$utility_to_battery_col] = $utility_to_battery[i] || 0
|
371
|
+
x[$storage_to_load_col] = $storage_to_load[i] || 0
|
372
|
+
x[$storage_to_grid_col] = $storage_to_grid[i] || 0
|
373
|
+
x[$storage_soc_col] = $storage_soc[i] || 0
|
374
|
+
x[$generator_total_col] = $generator_total[i] || 0
|
375
|
+
x[$generator_to_battery_col] = $generator_to_battery[i] || 0
|
376
|
+
x[$generator_to_load_col] = $generator_to_load[i] || 0
|
377
|
+
x[$generator_to_grid_col] = $generator_to_grid[i] || 0
|
378
|
+
x[$pv_total_col] = $pv_total[i] || 0
|
379
|
+
x[$pv_to_battery_col] = $pv_to_battery[i] || 0
|
380
|
+
x[$pv_to_load_col] = $pv_to_load[i] || 0
|
381
|
+
x[$pv_to_grid_col] = $pv_to_grid[i] || 0
|
382
|
+
x[$wind_total_col] = $wind_total[i] || 0
|
383
|
+
x[$wind_to_battery_col] = $wind_to_battery[i] || 0
|
384
|
+
x[$wind_to_load_col] = $wind_to_load[i] || 0
|
385
|
+
x[$wind_to_grid_col] = $wind_to_grid[i] || 0
|
386
|
+
return x
|
387
|
+
end
|
388
|
+
|
389
|
+
old_data = CSV.open(scenario_report.timeseries_csv.path).read
|
390
|
+
mod_data = old_data.map.with_index do |x, i|
|
391
|
+
if i > 0
|
392
|
+
modrow(x, i)
|
393
|
+
else
|
394
|
+
x
|
395
|
+
end
|
396
|
+
end
|
397
|
+
mod_data[0] = scenario_report.timeseries_csv.column_names
|
398
|
+
|
399
|
+
scenario_report.timeseries_csv.reload_data(mod_data)
|
400
|
+
return scenario_report
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt, 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
|
+
|
31
|
+
module URBANopt # :nodoc:
|
32
|
+
module REopt # :nodoc:
|
33
|
+
VERSION = '0.1.0'.freeze
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt, 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
|
+
|
31
|
+
require 'urbanopt/reopt/extension'
|
32
|
+
require 'urbanopt/reopt/reopt_lite_api'
|
33
|
+
require 'urbanopt/reopt/feature_report_adapter'
|
34
|
+
require 'urbanopt/reopt/scenario_report_adapter'
|
35
|
+
require 'urbanopt/reopt/reopt_post_processor'
|
36
|
+
require 'urbanopt/reopt/version'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt, 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
|
+
|
31
|
+
require 'urbanopt/reopt/scenario/reopt_scenario_csv'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt, 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
|
+
|
31
|
+
require_relative 'urbanopt/reopt'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path('lib', __dir__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'urbanopt/reopt/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'urbanopt-reopt'
|
8
|
+
spec.version = URBANopt::REopt::VERSION
|
9
|
+
spec.authors = ['']
|
10
|
+
spec.email = ['']
|
11
|
+
|
12
|
+
spec.summary = 'Classes and measures for utilizing the REopt Lite API within OpenStudio workflows.'
|
13
|
+
spec.description = 'Classes and measures for utilizing the REopt Lite API within OpenStudio workflows.'
|
14
|
+
spec.homepage = 'https://github.com/urbanopt/urbanopt-reopt-gem'
|
15
|
+
|
16
|
+
# Specify which files should be added to the gem when it is released.
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
+
end
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.14'
|
26
|
+
spec.add_development_dependency 'rake', '12.3.1'
|
27
|
+
spec.add_development_dependency 'rspec', '3.7.0'
|
28
|
+
spec.add_development_dependency 'rubocop', '~> 0.54.0'
|
29
|
+
|
30
|
+
spec.add_dependency 'certified'
|
31
|
+
spec.add_dependency 'json_pure'
|
32
|
+
spec.add_dependency 'urbanopt-scenario', '0.1.1'
|
33
|
+
end
|