urbanopt-reopt 0.6.1 → 0.6.2

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