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.
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