urbanopt-reopt 0.10.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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|
|