urbanopt-reopt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.rakeTasks +7 -0
  4. data/.rdoc_options +37 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +9 -0
  7. data/.travis.yml +22 -0
  8. data/CHANGELOG.md +5 -0
  9. data/Gemfile +67 -0
  10. data/Jenkinsfile +10 -0
  11. data/LICENSE.md +27 -0
  12. data/RDOC_MAIN.md +176 -0
  13. data/README.md +177 -0
  14. data/Rakefile +30 -0
  15. data/deploy_docs.sh +5 -0
  16. data/developer_nrel_key.rb +31 -0
  17. data/doc_templates/LICENSE.md +27 -0
  18. data/doc_templates/README.md.erb +42 -0
  19. data/doc_templates/copyright_erb.txt +31 -0
  20. data/doc_templates/copyright_js.txt +4 -0
  21. data/doc_templates/copyright_ruby.txt +29 -0
  22. data/docs/.gitignore +3 -0
  23. data/docs/.vuepress/components/InnerJsonSchema.vue +78 -0
  24. data/docs/.vuepress/components/JsonSchema.vue +12 -0
  25. data/docs/.vuepress/components/ReoptInputSchema.vue +12 -0
  26. data/docs/.vuepress/components/ReoptOutputSchema.vue +12 -0
  27. data/docs/.vuepress/components/StaticLink.vue +8 -0
  28. data/docs/.vuepress/config.js +16 -0
  29. data/docs/.vuepress/highlight.js +8 -0
  30. data/docs/.vuepress/public/custom_rdoc_styles.css +58 -0
  31. data/docs/.vuepress/utils.js +17 -0
  32. data/docs/README.md +196 -0
  33. data/docs/package-lock.json +254 -0
  34. data/docs/package.json +22 -0
  35. data/docs/schemas/reopt-input-schema.md +57 -0
  36. data/docs/schemas/reopt-output-schema.md +66 -0
  37. data/index.html +1 -0
  38. data/index.md +176 -0
  39. data/lib/files/.gitkeep +0 -0
  40. data/lib/urbanopt/reopt/extension.rb +44 -0
  41. data/lib/urbanopt/reopt/feature_report_adapter.rb +364 -0
  42. data/lib/urbanopt/reopt/reopt_lite_api.rb +230 -0
  43. data/lib/urbanopt/reopt/reopt_logger.rb +42 -0
  44. data/lib/urbanopt/reopt/reopt_post_processor.rb +245 -0
  45. data/lib/urbanopt/reopt/reopt_schema/reopt_input_schema.json +1111 -0
  46. data/lib/urbanopt/reopt/reopt_schema/reopt_output_schema.json +538 -0
  47. data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +115 -0
  48. data/lib/urbanopt/reopt/scenario_report_adapter.rb +404 -0
  49. data/lib/urbanopt/reopt/version.rb +35 -0
  50. data/lib/urbanopt/reopt.rb +36 -0
  51. data/lib/urbanopt/reopt_scenario.rb +31 -0
  52. data/lib/urbanopt-reopt.rb +31 -0
  53. data/urbanopt-reopt.gemspec +33 -0
  54. 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