urbanopt-reopt 0.10.0 → 0.12.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 +4 -4
- data/.github/release.yml +25 -0
- data/.github/workflows/nightly_ci_build.yml +10 -3
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -1
- data/LICENSE.md +1 -1
- data/RDOC_MAIN.md +19 -19
- data/README.md +20 -20
- data/doc_templates/LICENSE.md +1 -1
- data/doc_templates/copyright_erb.txt +1 -1
- data/docs/README.md +18 -18
- data/docs/package-lock.json +4806 -4836
- data/docs/schemas/reopt-input-schema.md +25 -23
- data/docs/schemas/reopt-output-schema.md +7 -7
- data/index.md +22 -22
- data/lib/urbanopt/reopt/feature_report_adapter.rb +241 -208
- data/lib/urbanopt/reopt/reopt_lite_api.rb +98 -59
- data/lib/urbanopt/reopt/reopt_post_processor.rb +34 -33
- data/lib/urbanopt/reopt/reopt_schema/reopt_input_schema.json +1126 -1106
- data/lib/urbanopt/reopt/reopt_schema/reopt_output_schema.json +449 -524
- data/lib/urbanopt/reopt/scenario/reopt_scenario_csv.rb +7 -7
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +275 -240
- data/lib/urbanopt/reopt/version.rb +1 -1
- data/urbanopt-reopt.gemspec +5 -3
- metadata +35 -8
- data/Jenkinsfile +0 -10
@@ -14,7 +14,7 @@ module URBANopt # :nodoc:
|
|
14
14
|
module REopt # :nodoc:
|
15
15
|
class FeatureReportAdapter
|
16
16
|
##
|
17
|
-
# FeatureReportAdapter can convert a URBANopt::Reporting::DefaultReports::FeatureReport into a \REopt
|
17
|
+
# FeatureReportAdapter can convert a URBANopt::Reporting::DefaultReports::FeatureReport into a \REopt posts or update a URBANopt::Reporting::DefaultReports::FeatureReport from a \REopt response.
|
18
18
|
##
|
19
19
|
# [*parameters:*]
|
20
20
|
##
|
@@ -24,23 +24,32 @@ module URBANopt # :nodoc:
|
|
24
24
|
end
|
25
25
|
|
26
26
|
##
|
27
|
-
# Convert a FeatureReport into a \REopt
|
27
|
+
# Convert a FeatureReport into a \REopt post
|
28
28
|
#
|
29
29
|
# [*parameters:*]
|
30
30
|
#
|
31
|
-
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to use in converting the optional +reopt_assumptions_hash+ to a \REopt
|
32
|
-
# * +reopt_assumptions_hash+ - _Hash_ - Optional. A hash formatted for submittal to the \REopt
|
31
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to use in converting the optional +reopt_assumptions_hash+ to a \REopt post. If a +reopt_assumptions_hash+ is not provided, a default post will be updated from this FeatureReport and submitted to the \REopt API.
|
32
|
+
# * +reopt_assumptions_hash+ - _Hash_ - Optional. A hash formatted for submittal to the \REopt API containing default values. Values will be overwritten from the FeatureReport where available (i.e. latitude, roof_squarefeet). Missing optional parameters will be filled in with default values by the API.
|
33
33
|
#
|
34
|
-
# [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt
|
34
|
+
# [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt API
|
35
35
|
##
|
36
36
|
def reopt_json_from_feature_report(feature_report, reopt_assumptions_hash = nil, groundmount_photovoltaic = nil)
|
37
37
|
name = feature_report.name.delete ' '
|
38
38
|
description = "feature_report_#{name}_#{feature_report.id}"
|
39
|
-
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 } } } }
|
40
39
|
if !reopt_assumptions_hash.nil?
|
41
40
|
reopt_inputs = reopt_assumptions_hash
|
42
41
|
else
|
43
|
-
@@logger.info('Using default REopt
|
42
|
+
@@logger.info('Using default REopt assumptions')
|
43
|
+
reopt_inputs = {
|
44
|
+
Settings:{},
|
45
|
+
Site: {},
|
46
|
+
Financial:{},
|
47
|
+
ElectricTariff: {
|
48
|
+
monthly_demand_rates: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
49
|
+
monthly_energy_rates: [0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13]
|
50
|
+
},
|
51
|
+
ElectricLoad: {}
|
52
|
+
}
|
44
53
|
end
|
45
54
|
|
46
55
|
# Check FeatureReport has required data
|
@@ -57,33 +66,33 @@ module URBANopt # :nodoc:
|
|
57
66
|
end
|
58
67
|
end
|
59
68
|
|
60
|
-
reopt_inputs[:
|
69
|
+
reopt_inputs[:description] = description
|
61
70
|
|
62
71
|
# Parse Location
|
63
|
-
reopt_inputs[:
|
64
|
-
reopt_inputs[:
|
72
|
+
reopt_inputs[:Site][:latitude] = feature_report.location.latitude_deg
|
73
|
+
reopt_inputs[:Site][:longitude] = feature_report.location.longitude_deg
|
65
74
|
|
66
75
|
# Parse Optional FeatureReport metrics - do not overwrite from assumptions file
|
67
|
-
if reopt_inputs[:
|
68
|
-
reopt_inputs[:
|
76
|
+
if reopt_inputs[:Site][:roof_squarefeet].nil? && !feature_report.program.roof_area_sqft.nil?
|
77
|
+
reopt_inputs[:Site][:roof_squarefeet] = feature_report.program.roof_area_sqft[:available_roof_area_sqft]
|
69
78
|
end
|
70
79
|
|
71
|
-
if reopt_inputs[:
|
80
|
+
if reopt_inputs[:Site][:land_acres].nil?
|
72
81
|
# Check if ground-mount PV is specified with the Feature ID and take footprint area of PV
|
73
82
|
# constrain for REopt optimization
|
74
83
|
begin
|
75
84
|
if !groundmount_photovoltaic[feature_report.id].nil?
|
76
|
-
reopt_inputs[:
|
85
|
+
reopt_inputs[:Site][:land_acres] = groundmount_photovoltaic[feature_report.id] * 1.0 / 43560 # acres/sqft
|
77
86
|
# If no ground-mount PV associated with feature use site area as constrain for REopt optimization
|
78
87
|
elsif !feature_report.program.site_area_sqft.nil?
|
79
|
-
reopt_inputs[:
|
88
|
+
reopt_inputs[:Site][:land_acres] = feature_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
|
80
89
|
end
|
81
90
|
rescue StandardError
|
82
91
|
end
|
83
92
|
end
|
84
93
|
|
85
|
-
if reopt_inputs[:
|
86
|
-
reopt_inputs[:
|
94
|
+
if reopt_inputs[:Settings][:time_steps_per_hour].nil?
|
95
|
+
reopt_inputs[:Settings][:time_steps_per_hour] = 1
|
87
96
|
end
|
88
97
|
|
89
98
|
# Parse Load Profile
|
@@ -104,7 +113,7 @@ module URBANopt # :nodoc:
|
|
104
113
|
end
|
105
114
|
# Clip to one non-leap year's worth of data
|
106
115
|
energy_timeseries_kw = energy_timeseries_kw.map { |e| e || 0 }[0, (feature_report.timesteps_per_hour * 8760)]
|
107
|
-
# Convert from the OpenDSS resolution to the REopt
|
116
|
+
# Convert from the OpenDSS resolution to the REopt resolution, if necessary
|
108
117
|
rescue StandardError
|
109
118
|
@@logger.error("Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}")
|
110
119
|
raise "Could not parse the annual electric load from the timeseries csv - #{feature_report.timeseries_csv.path}"
|
@@ -112,65 +121,68 @@ module URBANopt # :nodoc:
|
|
112
121
|
|
113
122
|
# Convert load to REopt Resolution
|
114
123
|
begin
|
115
|
-
reopt_inputs[:
|
124
|
+
reopt_inputs[:ElectricLoad][:loads_kw] = convert_powerflow_resolution(energy_timeseries_kw, feature_report.timesteps_per_hour, reopt_inputs[:Settings][:time_steps_per_hour])
|
116
125
|
rescue StandardError
|
117
|
-
@@logger.error("Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:
|
118
|
-
raise "Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:
|
119
|
-
end
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
126
|
+
@@logger.error("Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:Settings][:time_steps_per_hour]}")
|
127
|
+
raise "Could not convert the annual electric load from a resolution of #{feature_report.timesteps_per_hour} to #{reopt_inputs[:Settings][:time_steps_per_hour]}"
|
128
|
+
end
|
129
|
+
|
130
|
+
# TODO: Restore coincident_peak stuff here and in Scenario Report adapter
|
131
|
+
# if reopt_inputs[:ElectricTariff][:coincident_peak_load_active_time_steps].nil?
|
132
|
+
# n_top_values = 100
|
133
|
+
# tmp1 = reopt_inputs[:ElectricLoad][:loads_kw]
|
134
|
+
# tmp2 = tmp1.each_index.max_by(n_top_values * reopt_inputs[:Settings][:time_steps_per_hour]) { |i| tmp1[i] }
|
135
|
+
# for i in (0...tmp2.count)
|
136
|
+
# tmp2[i] += 1
|
137
|
+
# end
|
138
|
+
# # this needs to be a 2D array
|
139
|
+
# reopt_inputs[:ElectricTariff][:coincident_peak_load_active_time_steps] = [tmp2]
|
140
|
+
# end
|
141
|
+
|
142
|
+
# if reopt_inputs[:ElectricTariff][:coincident_peak_load_charge_per_kw].nil?
|
143
|
+
# reopt_inputs[:ElectricTariff][:coincident_peak_load_charge_per_kw] = 0
|
144
|
+
# end
|
134
145
|
|
135
146
|
return reopt_inputs
|
136
147
|
end
|
137
148
|
|
138
149
|
##
|
139
|
-
# Update a FeatureReport from a \REopt
|
150
|
+
# Update a FeatureReport from a \REopt response
|
140
151
|
#
|
141
152
|
# [*parameters:*]
|
142
153
|
#
|
143
|
-
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to update from a \REopt
|
144
|
-
# * +reopt_output+ - _Hash_ - A reponse hash from the \REopt
|
145
|
-
# * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which a new timeseries CSV will be written. If not provided a file is created based on the run_uuid of the \REopt
|
154
|
+
# * +feature_report+ - _URBANopt::Reporting::DefaultReports::FeatureReport_ - FeatureReport to update from a \REopt reponse hash.
|
155
|
+
# * +reopt_output+ - _Hash_ - A reponse hash from the \REopt API to use in overwriting FeatureReport technology sizes, costs and dispatch strategies.
|
156
|
+
# * +timeseries_csv_path+ - _String_ - Optional. The path to a file at which a new timeseries CSV will be written. If not provided a file is created based on the run_uuid of the \REopt optimization task.
|
146
157
|
#
|
147
158
|
# [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport.
|
148
159
|
##
|
149
160
|
def update_feature_report(feature_report, reopt_output, timeseries_csv_path = nil, resilience_stats = nil)
|
150
|
-
|
151
|
-
if
|
152
|
-
|
161
|
+
|
162
|
+
# Check if the \REopt response is valid
|
163
|
+
if reopt_output['status'] != 'optimal'
|
164
|
+
@@logger.error("ERROR cannot update Feature Report #{feature_report.name} #{feature_report.id} - REopt optimization was non-optimal")
|
153
165
|
return feature_report
|
154
166
|
end
|
155
167
|
|
156
168
|
# Update location
|
157
|
-
feature_report.location.latitude_deg = reopt_output['inputs']['
|
158
|
-
feature_report.location.longitude_deg = reopt_output['inputs']['
|
169
|
+
feature_report.location.latitude_deg = reopt_output['inputs']['Site']['latitude']
|
170
|
+
feature_report.location.longitude_deg = reopt_output['inputs']['Site']['longitude']
|
159
171
|
|
160
172
|
# Update distributed generation sizing and financials
|
161
|
-
feature_report.distributed_generation.
|
162
|
-
feature_report.distributed_generation.
|
163
|
-
feature_report.distributed_generation.
|
164
|
-
feature_report.distributed_generation.
|
165
|
-
feature_report.distributed_generation.
|
166
|
-
feature_report.distributed_generation.
|
167
|
-
feature_report.distributed_generation.
|
168
|
-
feature_report.distributed_generation.
|
169
|
-
feature_report.distributed_generation.
|
170
|
-
feature_report.distributed_generation.
|
171
|
-
feature_report.distributed_generation.
|
172
|
-
feature_report.distributed_generation.
|
173
|
-
feature_report.distributed_generation.
|
173
|
+
feature_report.distributed_generation.renewable_electricity_fraction = reopt_output['outputs']['Site']['renewable_electricity_fraction'] || 0
|
174
|
+
feature_report.distributed_generation.lcc = reopt_output['outputs']['Financial']['lcc'] || 0
|
175
|
+
feature_report.distributed_generation.npv = reopt_output['outputs']['Financial']['npv'] || 0
|
176
|
+
feature_report.distributed_generation.year_one_energy_cost_before_tax = reopt_output['outputs']['ElectricTariff']['year_one_energy_cost_before_tax'] || 0
|
177
|
+
feature_report.distributed_generation.year_one_demand_cost_before_tax = reopt_output['outputs']['ElectricTariff']['year_one_demand_cost_before_tax'] || 0
|
178
|
+
feature_report.distributed_generation.year_one_bill_before_tax = reopt_output['outputs']['ElectricTariff']['year_one_bill_before_tax'] || 0
|
179
|
+
feature_report.distributed_generation.lifecycle_energy_cost_after_tax = reopt_output['outputs']['ElectricTariff']['lifecycle_energy_cost_after_tax'] || 0
|
180
|
+
feature_report.distributed_generation.lifecycle_demand_cost_after_tax = reopt_output['outputs']['ElectricTariff']['lifecycle_demand_cost_after_tax'] || 0
|
181
|
+
feature_report.distributed_generation.year_one_energy_cost_before_tax_bau = reopt_output['outputs']['ElectricTariff']['year_one_energy_cost_before_tax_bau'] || 0
|
182
|
+
feature_report.distributed_generation.year_one_demand_cost_before_tax_bau = reopt_output['outputs']['ElectricTariff']['year_one_demand_cost_before_tax_bau'] || 0
|
183
|
+
feature_report.distributed_generation.year_one_bill_before_tax_bau = reopt_output['outputs']['ElectricTariff']['year_one_bill_before_tax_bau'] || 0
|
184
|
+
feature_report.distributed_generation.lifecycle_demand_cost_after_tax_bau = reopt_output['outputs']['ElectricTariff']['lifecycle_demand_cost_after_tax_bau'] || 0
|
185
|
+
feature_report.distributed_generation.lifecycle_energy_cost_after_tax_bau = reopt_output['outputs']['ElectricTariff']['lifecycle_energy_cost_after_tax_bau'] || 0
|
174
186
|
if !resilience_stats.nil?
|
175
187
|
feature_report.distributed_generation.resilience_hours_min = resilience_stats['resilience_hours_min']
|
176
188
|
feature_report.distributed_generation.resilience_hours_max = resilience_stats['resilience_hours_max']
|
@@ -180,10 +192,10 @@ module URBANopt # :nodoc:
|
|
180
192
|
feature_report.distributed_generation.probs_of_surviving_by_hour_of_the_day = resilience_stats['probs_of_surviving_by_hour_of_the_day']
|
181
193
|
end
|
182
194
|
|
183
|
-
if reopt_output['outputs']['
|
184
|
-
reopt_output['outputs']['
|
185
|
-
elsif reopt_output['outputs']['
|
186
|
-
reopt_output['outputs']['
|
195
|
+
if reopt_output['outputs']['PV'].is_a?(Hash)
|
196
|
+
reopt_output['outputs']['PV'] = [reopt_output['outputs']['PV']]
|
197
|
+
elsif reopt_output['outputs']['PV'].nil?
|
198
|
+
reopt_output['outputs']['PV'] = []
|
187
199
|
end
|
188
200
|
|
189
201
|
# Store the PV name and location in a hash
|
@@ -194,58 +206,60 @@ module URBANopt # :nodoc:
|
|
194
206
|
gcr = {}
|
195
207
|
|
196
208
|
# Check whether multi PV assumption input file is used or single PV
|
197
|
-
if reopt_output['inputs']
|
198
|
-
reopt_output['inputs']['
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
209
|
+
if reopt_output['inputs'].key?('PV')
|
210
|
+
if reopt_output['inputs']['PV'].is_a?(Array)
|
211
|
+
reopt_output['inputs']['PV'].each do |pv|
|
212
|
+
location[pv['name']] = pv['location']
|
213
|
+
azimuth[pv['name']] = pv['azimuth']
|
214
|
+
tilt[pv['name']] = pv['tilt']
|
215
|
+
module_type[pv['name']] = pv['module_type']
|
216
|
+
gcr[pv['name']] = pv['gcr']
|
217
|
+
end
|
218
|
+
else
|
219
|
+
location[reopt_output['inputs']['PV']['name']] = reopt_output['inputs']['PV']['location']
|
220
|
+
azimuth[reopt_output['inputs']['PV']['name']] = reopt_output['inputs']['PV']['azimuth']
|
221
|
+
tilt[reopt_output['inputs']['PV']['name']] = reopt_output['inputs']['PV']['tilt']
|
222
|
+
module_type[reopt_output['inputs']['PV']['name']] = reopt_output['inputs']['PV']['module_type']
|
223
|
+
gcr[reopt_output['inputs']['PV']['name']] = reopt_output['inputs']['PV']['gcr']
|
204
224
|
end
|
205
|
-
else
|
206
|
-
location[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['location']
|
207
|
-
azimuth[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['azimuth']
|
208
|
-
tilt[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['tilt']
|
209
|
-
module_type[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['module_type']
|
210
|
-
gcr[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['gcr']
|
211
|
-
end
|
212
225
|
|
213
|
-
|
214
|
-
|
226
|
+
reopt_output['outputs']['PV'].each_with_index do |pv, i|
|
227
|
+
feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i, location: location[pv['name']], average_yearly_energy_produced_kwh: pv['annual_energy_produced_kwh'], azimuth: azimuth[pv['name']], tilt: tilt[pv['name']], module_type: module_type[pv['name']], gcr: gcr[pv['name']] })
|
228
|
+
end
|
215
229
|
end
|
216
230
|
|
217
|
-
|
218
|
-
|
231
|
+
if reopt_output['outputs'].key?('Wind')
|
232
|
+
wind = reopt_output['outputs']['Wind']
|
219
233
|
feature_report.distributed_generation.add_tech 'wind', URBANopt::Reporting::DefaultReports::Wind.new({ size_kw: (wind['size_kw'] || 0) })
|
220
234
|
end
|
221
235
|
|
222
|
-
|
223
|
-
|
236
|
+
if reopt_output['outputs'].key?('Generator')
|
237
|
+
generator = reopt_output['outputs']['Generator']
|
224
238
|
feature_report.distributed_generation.add_tech 'generator', URBANopt::Reporting::DefaultReports::Generator.new({ size_kw: (generator['size_kw'] || 0) })
|
225
239
|
end
|
226
240
|
|
227
|
-
|
228
|
-
|
241
|
+
if reopt_output['outputs'].key?('ElectricStorage')
|
242
|
+
storage = reopt_output['outputs']['ElectricStorage']
|
229
243
|
feature_report.distributed_generation.add_tech 'storage', URBANopt::Reporting::DefaultReports::Storage.new({ size_kwh: (storage['size_kwh'] || 0), size_kw: (storage['size_kw'] || 0) })
|
230
244
|
end
|
231
245
|
|
232
246
|
generation_timeseries_kwh = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
233
|
-
reopt_resolution = reopt_output['inputs']['
|
247
|
+
reopt_resolution = reopt_output['inputs']['Settings']['time_steps_per_hour']
|
234
248
|
|
235
|
-
|
236
|
-
reopt_output['outputs']['
|
249
|
+
if reopt_output['outputs'].key?('PV') && !reopt_output['outputs']['PV'].nil?
|
250
|
+
reopt_output['outputs']['PV'].each do |pv|
|
237
251
|
if (pv['size_kw'] || 0) > 0 && !pv['year_one_power_production_series_kw'].nil?
|
238
252
|
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
|
239
253
|
end
|
240
254
|
end
|
241
255
|
end
|
242
256
|
|
243
|
-
if
|
244
|
-
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['
|
257
|
+
if reopt_output['outputs'].key?('Wind') && !reopt_output['outputs']['Wind'].nil? && ((reopt_output['outputs']['Wind']['size_kw'] || 0) > 0) && !reopt_output['outputs']['Wind']['year_one_power_production_series_kw'].nil?
|
258
|
+
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Wind']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
|
245
259
|
end
|
246
260
|
|
247
|
-
if
|
248
|
-
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['
|
261
|
+
if reopt_output['outputs'].key?('Generator') && !reopt_output['outputs']['Generator'].nil? && ((reopt_output['outputs']['Generator']['size_kw'] || 0) > 0) && !reopt_output['outputs']['Generator']['year_one_power_production_series_kw'].nil?
|
262
|
+
generation_timeseries_kwh += Matrix[convert_powerflow_resolution(reopt_output['outputs']['Generator']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour)]
|
249
263
|
end
|
250
264
|
|
251
265
|
$generation_timeseries_kwh = generation_timeseries_kwh.to_a[0] || [0] * (8760 * feature_report.timesteps_per_hour)
|
@@ -255,167 +269,187 @@ module URBANopt # :nodoc:
|
|
255
269
|
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Total(kw)')
|
256
270
|
end
|
257
271
|
|
258
|
-
$load = convert_powerflow_resolution(reopt_output['outputs']['
|
272
|
+
$load = convert_powerflow_resolution(reopt_output['outputs']['ElectricLoad']['load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
259
273
|
$load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Load:Total(kw)')
|
260
274
|
if $load_col.nil?
|
261
275
|
$load_col = feature_report.timeseries_csv.column_names.length
|
262
276
|
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Load:Total(kw)')
|
263
277
|
end
|
264
278
|
|
265
|
-
$utility_to_load = convert_powerflow_resolution(reopt_output['outputs']['
|
279
|
+
$utility_to_load = convert_powerflow_resolution(reopt_output['outputs']['ElectricUtility']['electric_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
266
280
|
$utility_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToLoad(kw)')
|
267
281
|
if $utility_to_load_col.nil?
|
268
282
|
$utility_to_load_col = feature_report.timeseries_csv.column_names.length
|
269
283
|
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToLoad(kw)')
|
270
284
|
end
|
271
285
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
$utility_to_battery_col
|
276
|
-
|
277
|
-
|
286
|
+
if !storage.nil?
|
287
|
+
$utility_to_battery = convert_powerflow_resolution(reopt_output['outputs']['ElectricUtility']['electric_to_storage_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
288
|
+
$utility_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Grid:ToBattery(kw)')
|
289
|
+
if $utility_to_battery_col.nil?
|
290
|
+
$utility_to_battery_col = feature_report.timeseries_csv.column_names.length
|
291
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Grid:ToBattery(kw)')
|
292
|
+
end
|
278
293
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
294
|
+
$storage_to_load = convert_powerflow_resolution(reopt_output['outputs']['ElectricStorage']['storage_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
295
|
+
$storage_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToLoad(kw)')
|
296
|
+
if $storage_to_load_col.nil?
|
297
|
+
$storage_to_load_col = feature_report.timeseries_csv.column_names.length
|
298
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToLoad(kw)')
|
299
|
+
end
|
285
300
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
301
|
+
$storage_to_grid = convert_powerflow_resolution(reopt_output['outputs']['ElectricStorage']['electric_to_grid_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
302
|
+
$storage_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:ToGrid(kw)')
|
303
|
+
if $storage_to_grid_col.nil?
|
304
|
+
$storage_to_grid_col = feature_report.timeseries_csv.column_names.length
|
305
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:ToGrid(kw)')
|
306
|
+
end
|
292
307
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
308
|
+
$storage_soc = convert_powerflow_resolution(reopt_output['outputs']['ElectricStorage']['soc_series_fraction'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
309
|
+
$storage_soc_col = feature_report.timeseries_csv.column_names.index('REopt:Electricity:Storage:StateOfCharge(pct)')
|
310
|
+
if $storage_soc_col.nil?
|
311
|
+
$storage_soc_col = feature_report.timeseries_csv.column_names.length
|
312
|
+
feature_report.timeseries_csv.column_names.push('REopt:Electricity:Storage:StateOfCharge(pct)')
|
313
|
+
end
|
298
314
|
end
|
299
315
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
$generator_total_col
|
304
|
-
|
305
|
-
|
316
|
+
if !generator.nil?
|
317
|
+
# $generator_total = convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Generator']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
318
|
+
$generator_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:Total(kw)')
|
319
|
+
if $generator_total_col.nil?
|
320
|
+
$generator_total_col = feature_report.timeseries_csv.column_names.length
|
321
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:Total(kw)')
|
322
|
+
end
|
306
323
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
324
|
+
if !storage.nil?
|
325
|
+
$generator_to_battery = convert_powerflow_resolution(reopt_output['outputs']['Generator']['electric_to_storage_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
326
|
+
$generator_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToBattery(kw)')
|
327
|
+
if $generator_to_battery_col.nil?
|
328
|
+
$generator_to_battery_col = feature_report.timeseries_csv.column_names.length
|
329
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToBattery(kw)')
|
330
|
+
end
|
331
|
+
end
|
313
332
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
333
|
+
$generator_to_load = convert_powerflow_resolution(reopt_output['outputs']['Generator']['electric_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
334
|
+
$generator_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToLoad(kw)')
|
335
|
+
if $generator_to_load_col.nil?
|
336
|
+
$generator_to_load_col = feature_report.timeseries_csv.column_names.length
|
337
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToLoad(kw)')
|
338
|
+
end
|
320
339
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
340
|
+
$generator_to_grid = convert_powerflow_resolution(reopt_output['outputs']['Generator']['electric_to_grid_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
341
|
+
$generator_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Generator:ToGrid(kw)')
|
342
|
+
if $generator_to_grid_col.nil?
|
343
|
+
$generator_to_grid_col = feature_report.timeseries_csv.column_names.length
|
344
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Generator:ToGrid(kw)')
|
345
|
+
end
|
326
346
|
end
|
327
347
|
|
328
|
-
|
329
|
-
|
330
|
-
$pv_total_col
|
331
|
-
|
332
|
-
|
348
|
+
if reopt_output['outputs'].key?('PV') && !reopt_output['outputs']['PV'].nil?
|
349
|
+
$pv_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:Total(kw)')
|
350
|
+
if $pv_total_col.nil?
|
351
|
+
$pv_total_col = feature_report.timeseries_csv.column_names.length
|
352
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:Total(kw)')
|
353
|
+
end
|
333
354
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
355
|
+
if !storage.nil?
|
356
|
+
$pv_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:ToBattery(kw)')
|
357
|
+
if $pv_to_battery_col.nil?
|
358
|
+
$pv_to_battery_col = feature_report.timeseries_csv.column_names.length
|
359
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:ToBattery(kw)')
|
360
|
+
end
|
361
|
+
end
|
339
362
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
363
|
+
$pv_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:ToLoad(kw)')
|
364
|
+
if $pv_to_load_col.nil?
|
365
|
+
$pv_to_load_col = feature_report.timeseries_csv.column_names.length
|
366
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:ToLoad(kw)')
|
367
|
+
end
|
345
368
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
369
|
+
$pv_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:PV:ToGrid(kw)')
|
370
|
+
if $pv_to_grid_col.nil?
|
371
|
+
$pv_to_grid_col = feature_report.timeseries_csv.column_names.length
|
372
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:PV:ToGrid(kw)')
|
373
|
+
end
|
351
374
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
375
|
+
$pv_total = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
376
|
+
if !storage.nil?
|
377
|
+
$pv_to_battery = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
378
|
+
end
|
379
|
+
$pv_to_load = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
380
|
+
$pv_to_grid = Matrix[[0] * (8760 * feature_report.timesteps_per_hour)]
|
381
|
+
|
382
|
+
reopt_output['outputs']['PV'].each_with_index do |pv, i|
|
383
|
+
if (pv['size_kw'] || 0) > 0
|
384
|
+
# $pv_total += Matrix[convert_powerflow_resolution(pv['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)]
|
385
|
+
if !storage.nil?
|
386
|
+
$pv_to_battery += Matrix[convert_powerflow_resolution(pv['electric_to_storage_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)]
|
387
|
+
end
|
388
|
+
$pv_to_load += Matrix[convert_powerflow_resolution(pv['electric_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)]
|
389
|
+
$pv_to_grid += Matrix[convert_powerflow_resolution(pv['electric_to_grid_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)]
|
390
|
+
end
|
363
391
|
end
|
364
|
-
end
|
365
392
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
393
|
+
$pv_total = $pv_total.to_a[0]
|
394
|
+
if !storage.nil?
|
395
|
+
$pv_to_battery = $pv_to_battery.to_a[0]
|
396
|
+
end
|
397
|
+
$pv_to_load = $pv_to_load.to_a[0]
|
398
|
+
$pv_to_grid = $pv_to_grid.to_a[0]
|
399
|
+
end
|
370
400
|
|
371
|
-
$wind_total = convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
401
|
+
# $wind_total = convert_powerflow_resolution(reopt_output['outputs']['Scenario']['Site']['Wind']['year_one_power_production_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
372
402
|
$wind_total_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:Total(kw)')
|
373
403
|
if $wind_total_col.nil?
|
374
404
|
$wind_total_col = feature_report.timeseries_csv.column_names.length
|
375
405
|
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:Total(kw)')
|
376
406
|
end
|
377
407
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
408
|
+
if !wind.nil?
|
409
|
+
if !storage.nil?
|
410
|
+
$wind_to_battery = convert_powerflow_resolution(reopt_output['outputs']['Wind']['electric_to_storage_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
411
|
+
$wind_to_battery_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToBattery(kw)')
|
412
|
+
if $wind_to_battery_col.nil?
|
413
|
+
$wind_to_battery_col = feature_report.timeseries_csv.column_names.length
|
414
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToBattery(kw)')
|
415
|
+
end
|
416
|
+
end
|
384
417
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
418
|
+
$wind_to_load = convert_powerflow_resolution(reopt_output['outputs']['Wind']['electric_to_load_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
419
|
+
$wind_to_load_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToLoad(kw)')
|
420
|
+
if $wind_to_load_col.nil?
|
421
|
+
$wind_to_load_col = feature_report.timeseries_csv.column_names.length
|
422
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToLoad(kw)')
|
423
|
+
end
|
391
424
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
425
|
+
$wind_to_grid = convert_powerflow_resolution(reopt_output['outputs']['Wind']['electric_to_grid_series_kw'], reopt_resolution, feature_report.timesteps_per_hour) || [0] * (8760 * feature_report.timesteps_per_hour)
|
426
|
+
$wind_to_grid_col = feature_report.timeseries_csv.column_names.index('REopt:ElectricityProduced:Wind:ToGrid(kw)')
|
427
|
+
if $wind_to_grid_col.nil?
|
428
|
+
$wind_to_grid_col = feature_report.timeseries_csv.column_names.length
|
429
|
+
feature_report.timeseries_csv.column_names.push('REopt:ElectricityProduced:Wind:ToGrid(kw)')
|
430
|
+
end
|
397
431
|
end
|
398
432
|
|
399
433
|
def modrow(x, i) # :nodoc:
|
400
434
|
x[$generation_timeseries_kwh_col] = $generation_timeseries_kwh[i] || 0
|
401
435
|
x[$load_col] = $load[i] || 0
|
402
436
|
x[$utility_to_load_col] = $utility_to_load[i] || 0
|
403
|
-
x[$utility_to_battery_col] = $utility_to_battery[i] || 0
|
404
|
-
x[$storage_to_load_col] = $storage_to_load[i] || 0
|
405
|
-
x[$storage_to_grid_col] = $storage_to_grid[i] || 0
|
406
|
-
x[$storage_soc_col] = $storage_soc[i] || 0
|
407
|
-
x[$generator_total_col] = $generator_total[i] || 0
|
408
|
-
x[$generator_to_battery_col] = $generator_to_battery[i] || 0
|
409
|
-
x[$generator_to_load_col] = $generator_to_load[i] || 0
|
410
|
-
x[$generator_to_grid_col] = $generator_to_grid[i] || 0
|
437
|
+
x[$utility_to_battery_col] = $utility_to_battery[i] || 0 if defined?(storage)
|
438
|
+
x[$storage_to_load_col] = $storage_to_load[i] || 0 if defined?(storage)
|
439
|
+
x[$storage_to_grid_col] = $storage_to_grid[i] || 0 if defined?(storage)
|
440
|
+
x[$storage_soc_col] = $storage_soc[i] || 0 if defined?(storage)
|
441
|
+
x[$generator_total_col] = $generator_total[i] || 0 if defined?(generator)
|
442
|
+
x[$generator_to_battery_col] = $generator_to_battery[i] || 0 if (defined?(generator) && defined?(storage))
|
443
|
+
x[$generator_to_load_col] = $generator_to_load[i] || 0 if defined?(generator)
|
444
|
+
x[$generator_to_grid_col] = $generator_to_grid[i] || 0 if defined?(generator)
|
411
445
|
x[$pv_total_col] = $pv_total[i] || 0
|
412
|
-
x[$pv_to_battery_col] = $pv_to_battery[i] || 0
|
446
|
+
x[$pv_to_battery_col] = $pv_to_battery[i] || 0 if defined?(storage)
|
413
447
|
x[$pv_to_load_col] = $pv_to_load[i] || 0
|
414
448
|
x[$pv_to_grid_col] = $pv_to_grid[i] || 0
|
415
|
-
x[$wind_total_col] = $wind_total[i] || 0
|
416
|
-
x[$wind_to_battery_col] = $wind_to_battery[i] || 0
|
417
|
-
x[$wind_to_load_col] = $wind_to_load[i] || 0
|
418
|
-
x[$wind_to_grid_col] = $wind_to_grid[i] || 0
|
449
|
+
x[$wind_total_col] = $wind_total[i] || 0 if defined?(wind)
|
450
|
+
x[$wind_to_battery_col] = $wind_to_battery[i] || 0 if (defined?(wind) && defined?(storage))
|
451
|
+
x[$wind_to_load_col] = $wind_to_load[i] || 0 if defined?(wind)
|
452
|
+
x[$wind_to_grid_col] = $wind_to_grid[i] || 0 if defined?(wind)
|
419
453
|
return x
|
420
454
|
end
|
421
455
|
|
@@ -425,8 +459,7 @@ module URBANopt # :nodoc:
|
|
425
459
|
(
|
426
460
|
((start_date.yday - 1) * 60.0 * 60.0 * 24) +
|
427
461
|
((start_date.hour - 1) * 60.0 * 60.0) +
|
428
|
-
(start_date.min * 60.0) + start_date.sec) /
|
429
|
-
((60 / feature_report.timesteps_per_hour) * 60)
|
462
|
+
(start_date.min * 60.0) + start_date.sec) / ((60 / feature_report.timesteps_per_hour) * 60)
|
430
463
|
).to_int
|
431
464
|
|
432
465
|
mod_data = old_data.map.with_index do |x, i|
|