urbanopt-reopt 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
data/docs/package.json CHANGED
@@ -13,7 +13,6 @@
13
13
  "highlight.js": "^10.4.1",
14
14
  "json-schema-ref-parser": "^6.1.0",
15
15
  "json-schema-view-js": "git+https://git@github.com/bgschiller/json-schema-view-js.git",
16
- "vuepress": "^1.8.2",
17
16
  "webpack-dev-middleware": "^3.6.0"
18
17
  },
19
18
  "devDependencies": {
@@ -23,8 +22,12 @@
23
22
  "gh-pages": "^2.0.1",
24
23
  "ini": "^2.0.0",
25
24
  "is-svg": "4.3.1",
25
+ "lodash": "^4.17.21",
26
+ "postcss": "^8.2.15",
26
27
  "serialize-javascript": "^5.0.1",
27
28
  "ssri": "8.0.1",
29
+ "url-parse": "^1.5.1",
30
+ "vuepress": "^1.8.2",
28
31
  "yargs-parser": "^18.1.1"
29
32
  }
30
33
  }
@@ -68,7 +68,7 @@ module URBANopt # :nodoc:
68
68
  #
69
69
  # [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
70
70
  ##
71
- def reopt_json_from_feature_report(feature_report, reopt_assumptions_hash = nil)
71
+ def reopt_json_from_feature_report(feature_report, reopt_assumptions_hash = nil, groundmount_photovoltaic = nil)
72
72
  name = feature_report.name.delete ' '
73
73
  description = "feature_report_#{name}_#{feature_report.id}"
74
74
  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 } } } }
@@ -103,8 +103,18 @@ module URBANopt # :nodoc:
103
103
  reopt_inputs[:Scenario][:Site][:roof_squarefeet] = feature_report.program.roof_area_sqft[:available_roof_area_sqft]
104
104
  end
105
105
 
106
- if reopt_inputs[:Scenario][:Site][:land_acres].nil? && !feature_report.program.site_area_sqft.nil?
107
- reopt_inputs[:Scenario][:Site][:land_acres] = feature_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
106
+ if reopt_inputs[:Scenario][:Site][:land_acres].nil?
107
+ # Check if ground-mount PV is specified with the Feature ID and take footprint area of PV
108
+ # constrain for REopt optimization
109
+ begin
110
+ if !groundmount_photovoltaic[feature_report.id].nil?
111
+ reopt_inputs[:Scenario][:Site][:land_acres] = groundmount_photovoltaic[feature_report.id] * 1.0 / 43560 # acres/sqft
112
+ # If no ground-mount PV associated with feature use site area as constrain for REopt optimization
113
+ elsif !feature_report.program.site_area_sqft.nil?
114
+ reopt_inputs[:Scenario][:Site][:land_acres] = feature_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
115
+ end
116
+ rescue StandardError
117
+ end
108
118
  end
109
119
 
110
120
  if reopt_inputs[:Scenario][:time_steps_per_hour].nil?
@@ -210,8 +220,19 @@ module URBANopt # :nodoc:
210
220
  reopt_output['outputs']['Scenario']['Site']['PV'] = []
211
221
  end
212
222
 
223
+ # Store the PV name and location in a hash
224
+ location = {}
225
+ # Check whether multi PV assumption input file is used or single PV
226
+ if reopt_output['inputs']['Scenario']['Site']['PV'].is_a?(Array)
227
+ reopt_output['inputs']['Scenario']['Site']['PV'].each do |pv|
228
+ location[pv['pv_name']] = pv['location']
229
+ end
230
+ else
231
+ location[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['location']
232
+ end
233
+
213
234
  reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
214
- feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i })
235
+ feature_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i, location: location[pv['pv_name']] })
215
236
  end
216
237
 
217
238
  wind = reopt_output['outputs']['Scenario']['Site']['Wind']
@@ -130,12 +130,26 @@ module URBANopt # :nodoc:
130
130
  @@logger.fatal('Exceeded the REopt-Lite API limit of 300 requests per hour')
131
131
  puts 'Using the URBANopt CLI to submit a Scenario optimization counts as one request per scenario'
132
132
  puts 'Using the URBANopt CLI to submit a Feature optimization counts as one request per feature'
133
- abort('Please wait and try again once the time period has elapsed')
133
+ abort('Please wait and try again once the time period has elapsed. The URBANopt CLI flag --reopt-keep-existing can be used to resume the optimization')
134
134
  elsif (result.code != '201') && (result.code != '200') # Anything in the 200s is success
135
135
  @@logger.debug("REopt-Lite has returned a '#{result.code}' status code. Visit https://developer.nrel.gov/docs/errors/ for more status code information")
136
+ # display error messages
137
+ json_res = JSON.parse(result.body, allow_nan: true)
138
+ json_res['messages'].delete('warnings') if json_res['messages']['warnings']
139
+ json_res['messages'].delete('Deprecations') if json_res['messages']['Deprecations']
140
+ if json_res['messages']
141
+ @@logger.error("MESSAGES: #{json_res['messages']}")
142
+ end
143
+ end
144
+ tries = max_tries
145
+ rescue StandardError => e
146
+ @@logger.debug("error from REopt lite API: #{e}")
147
+ if tries + 1 < max_tries
148
+ @@logger.debug('trying again...')
149
+ else
150
+ @@logger.debug('max tries reached!')
151
+ return
136
152
  end
137
- tries = 4
138
- rescue StandardError
139
153
  tries += 1
140
154
  end
141
155
  end
@@ -219,7 +233,7 @@ module URBANopt # :nodoc:
219
233
  # Wait a few seconds for the REopt database to update before GETing results
220
234
  sleep 5
221
235
  get_request = Net::HTTP::Get.new(uri.request_uri)
222
- response = make_request(http, get_request)
236
+ response = make_request(http, get_request, 8)
223
237
 
224
238
  # Set a limit on retries when 404s are returned from REopt API
225
239
  elapsed_time = 0
@@ -233,14 +247,15 @@ module URBANopt # :nodoc:
233
247
  sleep 5
234
248
  end
235
249
 
236
- data = JSON.parse(response.body)
237
- text = ::JSON.generate(data, allow_nan: true)
250
+ data = JSON.parse(response.body, allow_nan: true)
251
+ text = JSON.pretty_generate(data)
238
252
  begin
239
253
  File.open(filename, 'w+') do |f|
240
254
  f.puts(text)
241
255
  end
242
- rescue StandardError
256
+ rescue StandardError => e
243
257
  @@logger.error("Cannot write - #{filename}")
258
+ @@logger.error("ERROR: #{e}")
244
259
  end
245
260
 
246
261
  if response.code == '200'
@@ -280,6 +295,10 @@ module URBANopt # :nodoc:
280
295
 
281
296
  # Send the request
282
297
  response = make_request(http, post_request)
298
+ if !response.is_a?(Net::HTTPSuccess)
299
+ @@logger.error('make_request Failed')
300
+ raise 'Check_connection Failed'
301
+ end
283
302
 
284
303
  # Get UUID
285
304
  run_uuid = JSON.parse(response.body, allow_nan: true)['run_uuid']
@@ -295,12 +314,11 @@ module URBANopt # :nodoc:
295
314
  @@logger.info("REopt results saved to #{filename}")
296
315
  end
297
316
 
298
- text = ::JSON.generate(response.body, allow_nan: true)
317
+ text = JSON.parse(response.body, allow_nan: true)
299
318
  if response.code != '201'
300
319
  File.open(filename, 'w+') do |f|
301
- f.puts(text)
320
+ f.puts(JSON.pretty_generate(text))
302
321
  end
303
- @@logger.error("Cannot write - #{filename}")
304
322
  raise "Error in REopt optimization post - see #{filename}"
305
323
  end
306
324
 
@@ -353,8 +371,8 @@ module URBANopt # :nodoc:
353
371
  _tries += 1
354
372
  end
355
373
 
356
- data = JSON.parse(response.body)
357
- text = ::JSON.generate(data, allow_nan: true)
374
+ data = JSON.parse(response.body, allow_nan: true)
375
+ text = JSON.pretty_generate(data)
358
376
  begin
359
377
  File.open(filename, 'w+') do |f|
360
378
  f.puts(text)
@@ -166,7 +166,7 @@ module URBANopt # :nodoc:
166
166
  # * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
167
167
  #
168
168
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
169
- def run_scenario_report(scenario_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: true)
169
+ def run_scenario_report(scenario_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: true, community_photovoltaic: nil)
170
170
  if !reopt_assumptions_hash.nil?
171
171
  @scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
172
172
  end
@@ -180,7 +180,7 @@ module URBANopt # :nodoc:
180
180
  api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
181
181
  adapter = URBANopt::REopt::ScenarioReportAdapter.new
182
182
 
183
- reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash)
183
+ reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash, community_photovoltaic)
184
184
 
185
185
  reopt_output = api.reopt_request(reopt_input, @scenario_reopt_default_output_file)
186
186
  if run_resilience
@@ -212,7 +212,7 @@ module URBANopt # :nodoc:
212
212
  # * +timeseries_csv_path+ - _Array_ - Optional. A array of paths to files at which the new timeseries CSV for the FeatureReports will be saved. The number and order of the paths should match the feature_reports array.
213
213
  #
214
214
  # [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
215
- def run_feature_reports(feature_reports:, reopt_assumptions_hashes: [], reopt_output_files: [], timeseries_csv_paths: [], save_names: nil, run_resilience: true, keep_existing_output: false)
215
+ def run_feature_reports(feature_reports:, reopt_assumptions_hashes: [], reopt_output_files: [], timeseries_csv_paths: [], save_names: nil, run_resilience: true, keep_existing_output: false, groundmount_photovoltaic: nil)
216
216
  if !reopt_assumptions_hashes.empty?
217
217
  @feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
218
218
  end
@@ -244,7 +244,7 @@ module URBANopt # :nodoc:
244
244
  # check if we should rerun
245
245
  if !(keep_existing_output && output_exists(@feature_reports_reopt_default_output_files[idx]))
246
246
  begin
247
- reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx])
247
+ reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx], groundmount_photovoltaic)
248
248
  reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
249
249
  if run_resilience
250
250
  run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
@@ -265,8 +265,9 @@ module URBANopt # :nodoc:
265
265
  warn 'Could not save feature reports - the number of save names provided did not match the number of feature reports'
266
266
  end
267
267
  end
268
- rescue StandardError
268
+ rescue StandardError => e
269
269
  @@logger.info("Could not optimize Feature Report #{feature_report.name} #{feature_report.id}")
270
+ @@logger.error("ERROR: #{e}")
270
271
  end
271
272
  else
272
273
  puts('Output file already exists...skipping')
@@ -305,8 +306,8 @@ module URBANopt # :nodoc:
305
306
  # * +feature_report_timeseries_csv_paths+ - _Array_ - Optional. An array of paths to files at which the new timeseries CSV for the FeatureReports will be saved. The number and order of the paths should match the array in ScenarioReport.feature_reports.
306
307
  #
307
308
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
308
- def run_scenario_report_features(scenario_report:, reopt_assumptions_hashes: [], reopt_output_files: [], feature_report_timeseries_csv_paths: [], save_names_feature_reports: nil, save_name_scenario_report: nil, run_resilience: true, keep_existing_output: false)
309
- new_feature_reports = run_feature_reports(feature_reports: scenario_report.feature_reports, reopt_assumptions_hashes: reopt_assumptions_hashes, reopt_output_files: reopt_output_files, timeseries_csv_paths: feature_report_timeseries_csv_paths, save_names: save_names_feature_reports, run_resilience: run_resilience, keep_existing_output: keep_existing_output)
309
+ def run_scenario_report_features(scenario_report:, reopt_assumptions_hashes: [], reopt_output_files: [], feature_report_timeseries_csv_paths: [], save_names_feature_reports: nil, save_name_scenario_report: nil, run_resilience: true, keep_existing_output: false, groundmount_photovoltaic: nil)
310
+ new_feature_reports = run_feature_reports(feature_reports: scenario_report.feature_reports, reopt_assumptions_hashes: reopt_assumptions_hashes, reopt_output_files: reopt_output_files, timeseries_csv_paths: feature_report_timeseries_csv_paths, save_names: save_names_feature_reports, run_resilience: run_resilience, keep_existing_output: keep_existing_output, groundmount_photovoltaic: groundmount_photovoltaic)
310
311
  puts("KEEP EXISTING? #{keep_existing_output}")
311
312
  # only do this if you have run feature reports
312
313
  new_scenario_report = URBANopt::Reporting::DefaultReports::ScenarioReport.new
@@ -371,6 +371,11 @@
371
371
  "description": "State rebate based on installed capacity",
372
372
  "min": 0
373
373
  },
374
+ "location": {
375
+ "default": "roof",
376
+ "type": "string",
377
+ "description": "Indicates location of PV. Available options are roof and ground."
378
+ },
374
379
  "max_kw": {
375
380
  "default": 1000000000.0,
376
381
  "max": 1000000000.0,
@@ -142,6 +142,11 @@
142
142
  "PV": {
143
143
  "type": "object",
144
144
  "properties": {
145
+ "location": {
146
+ "default": "roof",
147
+ "type": "string",
148
+ "description": "Indicates location of PV. Available options are roof and ground."
149
+ },
145
150
  "size_kw": {
146
151
  "type": "float",
147
152
  "description": "Optimal PV system size",
@@ -67,7 +67,7 @@ module URBANopt # :nodoc:
67
67
  #
68
68
  # [*return:*] _Hash_ - Returns hash formatted for submittal to the \REopt Lite API
69
69
  ##
70
- def reopt_json_from_scenario_report(scenario_report, reopt_assumptions_json = nil)
70
+ def reopt_json_from_scenario_report(scenario_report, reopt_assumptions_json = nil, community_photovoltaic = nil)
71
71
  name = scenario_report.name.delete ' '
72
72
  scenario_id = scenario_report.id.delete ' '
73
73
  description = "scenario_report_#{name}_#{scenario_id}"
@@ -122,8 +122,11 @@ module URBANopt # :nodoc:
122
122
  reopt_inputs[:Scenario][:Site][:roof_squarefeet] = scenario_report.program.roof_area_sqft[:available_roof_area_sqft]
123
123
  end
124
124
 
125
- if reopt_inputs[:Scenario][:Site][:land_acres].nil? && !scenario_report.program.site_area_sqft.nil?
126
- reopt_inputs[:Scenario][:Site][:land_acres] = scenario_report.program.site_area_sqft * 1.0 / 43560 # acres/sqft
125
+ begin
126
+ if reopt_inputs[:Scenario][:Site][:land_acres].nil? && !community_photovoltaic[0][:properties][:footprint_area].nil?
127
+ reopt_inputs[:Scenario][:Site][:land_acres] = community_photovoltaic[0][:properties][:footprint_area] * 1.0 / 43560 # acres/sqft
128
+ end
129
+ rescue StandardError
127
130
  end
128
131
 
129
132
  if reopt_inputs[:Scenario][:time_steps_per_hour].nil?
@@ -247,8 +250,19 @@ module URBANopt # :nodoc:
247
250
  reopt_output['outputs']['Scenario']['Site']['PV'] = []
248
251
  end
249
252
 
253
+ # Store the PV name and location in a hash
254
+ location = {}
255
+ # Check whether multi PV assumption input file is used or single PV
256
+ if reopt_output['inputs']['Scenario']['Site']['PV'].is_a?(Array)
257
+ reopt_output['inputs']['Scenario']['Site']['PV'].each do |pv|
258
+ location[pv['pv_name']] = pv['location']
259
+ end
260
+ else
261
+ location[reopt_output['inputs']['Scenario']['Site']['PV']['pv_name']] = reopt_output['inputs']['Scenario']['Site']['PV']['location']
262
+ end
263
+
250
264
  reopt_output['outputs']['Scenario']['Site']['PV'].each_with_index do |pv, i|
251
- scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i })
265
+ scenario_report.distributed_generation.add_tech 'solar_pv', URBANopt::Reporting::DefaultReports::SolarPV.new({ size_kw: (pv['size_kw'] || 0), id: i, location: location[pv['pv_name']] })
252
266
  end
253
267
 
254
268
  wind = reopt_output['outputs']['Scenario']['Site']['Wind']
@@ -40,6 +40,6 @@
40
40
 
41
41
  module URBANopt # :nodoc:
42
42
  module REopt # :nodoc:
43
- VERSION = '0.6.1'.freeze
43
+ VERSION = '0.6.2'.freeze
44
44
  end
45
45
  end
@@ -31,5 +31,5 @@ Gem::Specification.new do |spec|
31
31
 
32
32
  spec.add_dependency 'certified', '~> 1'
33
33
  spec.add_dependency 'json_pure', '~> 2'
34
- spec.add_dependency 'urbanopt-scenario', '~> 0.6.2'
34
+ spec.add_dependency 'urbanopt-scenario', '~> 0.6.3'
35
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: urbanopt-reopt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ''
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-02 00:00:00.000000000 Z
11
+ date: 2021-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 0.6.2
103
+ version: 0.6.3
104
104
  type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 0.6.2
110
+ version: 0.6.3
111
111
  description: Classes and measures for utilizing the REopt Lite API within OpenStudio
112
112
  workflows.
113
113
  email: