urbanopt-reopt 0.11.0 → 1.0.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.
@@ -8,7 +8,6 @@ require 'openssl'
8
8
  require 'uri'
9
9
  require 'json'
10
10
  require 'securerandom'
11
- require 'certified'
12
11
  require_relative '../../../developer_nrel_key'
13
12
  require 'urbanopt/reopt/reopt_logger'
14
13
 
@@ -16,7 +15,7 @@ module URBANopt # :nodoc:
16
15
  module REopt # :nodoc:
17
16
  class REoptLiteAPI
18
17
  ##
19
- # \REoptLiteAPI manages submitting optimization tasks to the \REopt API and recieving results.
18
+ # \REoptLiteAPI manages submitting optimization tasks to the \REopt API and receiving results.
20
19
  # Results can either be sourced from the production \REopt API with an API key from developer.nrel.gov, or from
21
20
  # a version running at localhost.
22
21
  ##
@@ -29,8 +28,8 @@ module URBANopt # :nodoc:
29
28
  def initialize(nrel_developer_key = nil, use_localhost = false)
30
29
  @use_localhost = use_localhost
31
30
  if @use_localhost
32
- @uri_submit = URI.parse('http//:127.0.0.1:8000/v2/job/')
33
- @uri_submit_outagesimjob = URI.parse('http//:127.0.0.1:8000/v2/outagesimjob/')
31
+ @uri_submit = URI.parse('http//:127.0.0.1:8000/v3/job/')
32
+ @uri_submit_outagesimjob = URI.parse('http//:127.0.0.1:8000/v3/outagesimjob/')
34
33
  else
35
34
  if [nil, '', '<insert your key here>'].include? nrel_developer_key
36
35
  if [nil, '', '<insert your key here>'].include? DEVELOPER_NREL_KEY
@@ -40,8 +39,8 @@ module URBANopt # :nodoc:
40
39
  end
41
40
  end
42
41
  @nrel_developer_key = nrel_developer_key
43
- @uri_submit = URI.parse("https://developer.nrel.gov/api/reopt/v2/job?api_key=#{@nrel_developer_key}")
44
- @uri_submit_outagesimjob = URI.parse("https://developer.nrel.gov/api/reopt/v2/outagesimjob?api_key=#{@nrel_developer_key}")
42
+ @uri_submit = URI.parse("https://developer.nrel.gov/api/reopt/v3/job?api_key=#{@nrel_developer_key}")
43
+ @uri_submit_outagesimjob = URI.parse("https://developer.nrel.gov/api/reopt/v3/outagesimjob?api_key=#{@nrel_developer_key}")
45
44
  # initialize @@logger
46
45
  @@logger ||= URBANopt::REopt.reopt_logger
47
46
  end
@@ -55,14 +54,14 @@ module URBANopt # :nodoc:
55
54
  #
56
55
  # * +run_uuid+ - _String_ - Unique run_uuid obtained from the \REopt job submittal URL for a specific optimization task.
57
56
  #
58
- # [*return:*] _URI_ - Returns URI object for use in calling the \REopt results endpoint for a specifc optimization task.
57
+ # [*return:*] _URI_ - Returns URI object for use in calling the \REopt results endpoint for a specific optimization task.
59
58
  ##
60
59
  def uri_results(run_uuid) # :nodoc:
61
60
  if @use_localhost
62
- return URI.parse("http://127.0.0.1:8000/v2/job/#{run_uuid}/results")
61
+ return URI.parse("http://127.0.0.1:8000/v3/job/#{run_uuid}/results")
63
62
  end
64
63
 
65
- return URI.parse("https://developer.nrel.gov/api/reopt/v2/job/#{run_uuid}/results?api_key=#{@nrel_developer_key}")
64
+ return URI.parse("https://developer.nrel.gov/api/reopt/v3/job/#{run_uuid}/results?api_key=#{@nrel_developer_key}")
66
65
  end
67
66
 
68
67
  ##
@@ -73,14 +72,14 @@ module URBANopt # :nodoc:
73
72
  #
74
73
  # * +run_uuid+ - _String_ - Resilience statistics for a unique run_uuid obtained from the \REopt job submittal URL for a specific optimization task.
75
74
  #
76
- # [*return:*] _URI_ - Returns URI object for use in calling the \REopt resilience statistics endpoint for a specifc optimization task.
75
+ # [*return:*] _URI_ - Returns URI object for use in calling the \REopt resilience statistics endpoint for a specific optimization task.
77
76
  ##
78
77
  def uri_resilience(run_uuid) # :nodoc:
79
78
  if @use_localhost
80
- return URI.parse("http://127.0.0.1:8000/v2/job/#{run_uuid}/resilience_stats")
79
+ return URI.parse("http://127.0.0.1:8000/v3/job/#{run_uuid}/resilience_stats")
81
80
  end
82
81
 
83
- return URI.parse("https://developer.nrel.gov/api/reopt/v2/job/#{run_uuid}/resilience_stats?api_key=#{@nrel_developer_key}")
82
+ return URI.parse("https://developer.nrel.gov/api/reopt/v3/job/#{run_uuid}/resilience_stats?api_key=#{@nrel_developer_key}")
84
83
  end
85
84
 
86
85
  def make_request(http, req, max_tries = 3)
@@ -112,11 +111,11 @@ module URBANopt # :nodoc:
112
111
  end
113
112
  tries = max_tries
114
113
  rescue StandardError => e
115
- @@logger.debug("error from REopt API: #{e}")
114
+ @@logger.error("error from REopt API: #{e}")
116
115
  if tries + 1 < max_tries
117
116
  @@logger.debug('trying again...')
118
117
  else
119
- @@logger.debug('max tries reached!')
118
+ @@logger.warn('max tries reached!')
120
119
  return result
121
120
  end
122
121
  tries += 1
@@ -133,7 +132,7 @@ module URBANopt # :nodoc:
133
132
  #
134
133
  # * +data+ - _Hash_ - Default \REopt formatted post containing at least all the required parameters.
135
134
  #
136
- # [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
135
+ # [*return:*] _Bool_ - Returns true if the post succeeds. Otherwise returns false.
137
136
  ##
138
137
  def check_connection(data)
139
138
  header = { 'Content-Type' => 'application/json' }
@@ -166,7 +165,7 @@ module URBANopt # :nodoc:
166
165
  # * +reopt_input+ - _Hash_ - \REopt formatted post containing at least required parameters.
167
166
  # * +filename+ - _String_ - Path to file that will be created containing the full \REopt response.
168
167
  #
169
- # [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
168
+ # [*return:*] _Bool_ - Returns true if the post succeeds. Otherwise returns false.
170
169
  ##
171
170
  def resilience_request(run_uuid, filename)
172
171
  if File.directory? filename
@@ -199,21 +198,21 @@ module URBANopt # :nodoc:
199
198
  http.use_ssl = true
200
199
  end
201
200
 
202
- # Wait a few seconds for the REopt database to update before GETing results
203
- sleep 5
201
+ # Wait for the REopt API before attempting to GET results
202
+ sleep 30
204
203
  get_request = Net::HTTP::Get.new(uri.request_uri)
205
204
  response = make_request(http, get_request, 8)
206
205
 
207
206
  # Set a limit on retries when 404s are returned from REopt API
208
207
  elapsed_time = 0
209
- max_elapsed_time = 60 * 5
208
+ max_elapsed_time = 60 * 15
210
209
 
211
- # If database still hasn't updated, wait a little longer and try again
210
+ # If database still hasn't updated, wait longer and try again
212
211
  while (elapsed_time < max_elapsed_time) && (response && response.code == '404')
213
212
  response = make_request(http, get_request)
214
213
  @@logger.warn('GET request was too fast for REOpt-API. Retrying...')
215
- elapsed_time += 5
216
- sleep 5
214
+ elapsed_time += 15
215
+ sleep 15
217
216
  end
218
217
 
219
218
  data = JSON.parse(response.body, allow_nan: true)
@@ -246,10 +245,10 @@ module URBANopt # :nodoc:
246
245
  # * +reopt_input+ - _Hash_ - \REopt formatted post containing at least required parameters.
247
246
  # * +filename+ - _String_ - Path to file that will be created containing the full \REopt response.
248
247
  #
249
- # [*return:*] _Bool_ - Returns true if the post succeeeds. Otherwise returns false.
248
+ # [*return:*] _Bool_ - Returns true if the post succeeds. Otherwise returns false.
250
249
  ##
251
250
  def reopt_request(reopt_input, filename)
252
- description = reopt_input[:Scenario][:description]
251
+ description = reopt_input[:description]
253
252
 
254
253
  @@logger.info("Submitting #{description} to REopt API")
255
254
 
@@ -301,45 +300,84 @@ module URBANopt # :nodoc:
301
300
 
302
301
  get_request = Net::HTTP::Get.new(uri.request_uri)
303
302
 
303
+ counter = 0
304
304
  while status == 'Optimizing...'
305
305
  response = make_request(http, get_request)
306
306
 
307
307
  data = JSON.parse(response.body, allow_nan: true)
308
308
 
309
- if data['outputs']['Scenario']['Site']['PV'].is_a?(Array)
309
+ if !data['outputs']['PV']
310
310
  pv_sizes = 0
311
- data['outputs']['Scenario']['Site']['PV'].each do |x|
312
- pv_sizes += x['size_kw'].to_f
313
- end
311
+ sizes = 0
314
312
  else
315
- pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
313
+ # there should be results in there now
314
+ if data['outputs']['PV'].is_a?(Array)
315
+ pv_sizes = 0
316
+ data['outputs']['PV'].each do |x|
317
+ pv_sizes += x['size_kw'].to_f
318
+ end
319
+ else
320
+ data['outputs'].each do |energy_source, data|
321
+ if data.is_a?(Hash) && data.key?('size_kw')
322
+ @@logger.debug("#{energy_source}: size_kw = #{data['size_kw'].to_f}")
323
+ sizes += data['size_kw'].to_f
324
+ end
325
+ end
326
+ end
327
+ end
328
+
329
+ status = data['status']
330
+ @@logger.debug("STATUS: #{status}")
331
+
332
+ if status == 'error'
333
+ puts "response.code: #{response.code}"
334
+ puts "message: #{response.message}"
335
+ error_message = data['messages']['errors']
336
+ raise "Error from REopt API - #{error_message}"
316
337
  end
317
- sizes = pv_sizes + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
318
- status = data['outputs']['Scenario']['status']
319
338
 
320
- sleep 5
339
+ sleep 15
321
340
  end
322
341
 
323
342
  max_retry = 5
324
343
  tries = 0
325
- (check_complete = sizes == 0) && ((data['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0) > 0)
326
- while (tries < max_retry) && check_complete
327
- sleep 3
328
- response = make_request(http, get_request)
329
- data = JSON.parse(response.body, allow_nan: true)
330
- if data['outputs']['Scenario']['Site']['PV'].is_a?(Array)
331
- pv_sizes = 0
332
- data['outputs']['Scenario']['Site']['PV'].each do |x|
333
- pv_sizes += x['size_kw'].to_f
344
+
345
+ check_complete = sizes == 0
346
+ # I don't know what this line does:
347
+ # (((data['outputs'] && data['outputs'].key?('Financial') && data['outputs']['Financial']['npv']) || 0) > 0)
348
+
349
+ if check_complete
350
+ @@logger.info('sizes are 0...checking optimization complete')
351
+
352
+ while (tries < max_retry) && check_complete
353
+ sleep 3
354
+ response = make_request(http, get_request)
355
+ data = JSON.parse(response.body, allow_nan: true)
356
+
357
+ if data['outputs'].key?('PV') && data['outputs']['PV'].is_a?(Array)
358
+ pv_sizes = 0
359
+ data['outputs']['PV'].each do |x|
360
+ pv_sizes += x['size_kw'].to_f
361
+ end
362
+ else
363
+ data['outputs'].each do |energy_source, data|
364
+ if data.is_a?(Hash) && data.key?('size_kw')
365
+ @@logger.debug("#{energy_source}: size_kw = #{data['size_kw'].to_f}")
366
+ sizes += data['size_kw'].to_f
367
+ end
368
+ end
334
369
  end
335
- else
336
- pv_sizes = data['outputs']['Scenario']['Site']['PV']['size_kw'] || 0
370
+
371
+ # I don't understand this line fully:
372
+ #(check_complete = sizes == 0) && ((data['outputs']['Financial']['npv'] || 0) > 0)
373
+
374
+ check_complete = sizes == 0
375
+ tries += 1
337
376
  end
338
- sizes = pv_sizes + (data['outputs']['Scenario']['Site']['Storage']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Wind']['size_kw'] || 0) + (data['outputs']['Scenario']['Site']['Generator']['size_kw'] || 0)
339
- (check_complete = sizes == 0) && ((data['outputs']['Scenario']['Site']['Financial']['npv_us_dollars'] || 0) > 0)
340
- tries += 1
341
377
  end
342
378
 
379
+ @@logger.info('REopt optimization complete and processed')
380
+
343
381
  data = JSON.parse(response.body, allow_nan: true)
344
382
  text = JSON.pretty_generate(data)
345
383
  begin
@@ -12,7 +12,7 @@ module URBANopt
12
12
  # Set Logger::DEBUG for development
13
13
  @@reopt_logger.level = Logger::WARN
14
14
  ##
15
- # Definining class variable "@@logger" to log errors, info and warning messages.
15
+ # Defining class variable "@@logger" to log errors, info and warning messages.
16
16
  def self.reopt_logger
17
17
  @@reopt_logger
18
18
  end
@@ -7,6 +7,7 @@ require 'bundler/setup'
7
7
  require 'urbanopt/reporting/default_reports'
8
8
  require 'urbanopt/reopt/reopt_logger'
9
9
  require 'csv'
10
+ require 'json'
10
11
 
11
12
  module URBANopt # :nodoc:
12
13
  module REopt # :nodoc:
@@ -32,7 +33,6 @@ module URBANopt # :nodoc:
32
33
  end
33
34
  @nrel_developer_key = nrel_developer_key
34
35
  @localhost = localhost
35
- @reopt_base_post = { Scenario: { Site: { ElectricTariff: {}, LoadProfile: {}, Wind: { max_kw: 0 } } } }
36
36
 
37
37
  @scenario_reopt_default_output_file = nil
38
38
  @scenario_timeseries_default_output_file = nil
@@ -92,7 +92,7 @@ module URBANopt # :nodoc:
92
92
  #
93
93
  # [*return:*] _URBANopt::Reporting::DefaultReports::FeatureReport_ - Returns an updated FeatureReport
94
94
  ##
95
- def run_feature_report(feature_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: true)
95
+ def run_feature_report(feature_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: false)
96
96
  api = URBANopt::REopt::REoptLiteAPI.new(@nrel_developer_key, @localhost)
97
97
  adapter = URBANopt::REopt::FeatureReportAdapter.new
98
98
 
@@ -103,7 +103,7 @@ module URBANopt # :nodoc:
103
103
  reopt_output = api.reopt_request(reopt_input, reopt_output_file)
104
104
  @@logger.debug("REOpt output file: #{reopt_output_file}")
105
105
  if run_resilience
106
- run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
106
+ run_uuid = reopt_output['outputs']['run_uuid']
107
107
  if File.directory? reopt_output_file
108
108
  resilience_stats = api.resilience_request(run_uuid, reopt_output_file)
109
109
  else
@@ -112,6 +112,7 @@ module URBANopt # :nodoc:
112
112
  else
113
113
  resilience_stats = nil
114
114
  end
115
+
115
116
  result = adapter.update_feature_report(feature_report, reopt_output, timeseries_csv_path, resilience_stats)
116
117
  if !save_name.nil?
117
118
  result.save save_name
@@ -131,8 +132,7 @@ module URBANopt # :nodoc:
131
132
  # * +timeseries_csv_path+ - _String_ - Optional. Path to a file at which the new timeseries CSV for the ScenarioReport will be saved.
132
133
  #
133
134
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ Returns an updated ScenarioReport
134
- 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)
135
- puts 'run scenario report'
135
+ def run_scenario_report(scenario_report:, reopt_assumptions_hash: nil, reopt_output_file: nil, timeseries_csv_path: nil, save_name: nil, run_resilience: false, community_photovoltaic: nil)
136
136
  @save_assumptions_filepath = false
137
137
  if !reopt_assumptions_hash.nil?
138
138
  @scenario_reopt_default_assumptions_hash = reopt_assumptions_hash
@@ -150,10 +150,10 @@ module URBANopt # :nodoc:
150
150
  adapter = URBANopt::REopt::ScenarioReportAdapter.new
151
151
 
152
152
  reopt_input = adapter.reopt_json_from_scenario_report(scenario_report, @scenario_reopt_default_assumptions_hash, community_photovoltaic)
153
-
154
153
  reopt_output = api.reopt_request(reopt_input, @scenario_reopt_default_output_file)
154
+
155
155
  if run_resilience
156
- run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
156
+ run_uuid = reopt_output['outputs']['run_uuid']
157
157
  if File.directory? @scenario_reopt_default_output_file
158
158
  resilience_stats = api.resilience_request(run_uuid, @scenario_reopt_default_output_file)
159
159
  else
@@ -181,13 +181,13 @@ module URBANopt # :nodoc:
181
181
  #
182
182
  # [*parameters:*]
183
183
  #
184
- # * +feature_reports+ - _Array_ - An array of _URBANopt::Reporting::DefaultReports::FeatureReport_ objetcs which will each be used to create (and are subsquently updated by) a \REopt opimization response.
184
+ # * +feature_reports+ - _Array_ - An array of _URBANopt::Reporting::DefaultReports::FeatureReport_ objects which will each be used to create (and are subsequently updated by) a \REopt opimization response.
185
185
  # * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of \REopt formatted hashes containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability). The number and order of the hashes should match the feature_reports array.
186
186
  # * +reopt_output_files+ - _Array_ - Optional. A array of paths to files at which REpopt responses will be saved. The number and order of the paths should match the feature_reports array.
187
187
  # * +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.
188
188
  #
189
189
  # [*return:*] _Array_ Returns an array of updated _URBANopt::Scenario::DefaultReports::FeatureReport_ objects
190
- 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)
190
+ def run_feature_reports(feature_reports:, reopt_assumptions_hashes: [], reopt_output_files: [], timeseries_csv_paths: [], save_names: nil, run_resilience: false, keep_existing_output: false, groundmount_photovoltaic: nil)
191
191
  if !reopt_assumptions_hashes.empty?
192
192
  @feature_reports_reopt_default_assumption_hashes = reopt_assumptions_hashes
193
193
  end
@@ -222,7 +222,7 @@ module URBANopt # :nodoc:
222
222
  reopt_input = feature_adapter.reopt_json_from_feature_report(feature_report, @feature_reports_reopt_default_assumption_hashes[idx], groundmount_photovoltaic)
223
223
  reopt_output = api.reopt_request(reopt_input, @feature_reports_reopt_default_output_files[idx])
224
224
  if run_resilience
225
- run_uuid = reopt_output['outputs']['Scenario']['run_uuid']
225
+ run_uuid = reopt_output['outputs']['run_uuid']
226
226
  if File.directory? @feature_reports_reopt_default_output_files[idx]
227
227
  resilience_stats = api.resilience_request(run_uuid, @feature_reports_reopt_default_output_files[idx])
228
228
  else
@@ -275,13 +275,13 @@ module URBANopt # :nodoc:
275
275
  #
276
276
  # [*parameters:*]
277
277
  #
278
- # * +scenario_report+ - _Array_ - A _URBANopt::Reporting::DefaultReports::ScenarioReport_ which will each be used to create (and is subsquently updated by) \REopt opimization responses for each of its FeatureReports.
278
+ # * +scenario_report+ - _Array_ - A _URBANopt::Reporting::DefaultReports::ScenarioReport_ which will each be used to create (and is subsequently updated by) \REopt opimization responses for each of its FeatureReports.
279
279
  # * +reopt_assumptions_hashes+ - _Array_ - Optional. An array of \REopt formatted hashes containing default parameters (i.e. utility rate, escalation rate) which will be updated by the ScenarioReport (i.e. location, roof availability). The number and order of the hashes should match the array in ScenarioReport.feature_reports.
280
280
  # * +reopt_output_files+ - _Array_ - Optional. An array of paths to files at which REpopt responses will be saved. The number and order of the paths should match the array in ScenarioReport.feature_reports.
281
281
  # * +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.
282
282
  #
283
283
  # [*return:*] _URBANopt::Scenario::DefaultReports::ScenarioReport_ - Returns an updated ScenarioReport
284
- 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)
284
+ 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: false, keep_existing_output: false, groundmount_photovoltaic: nil)
285
285
  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)
286
286
 
287
287
  # only do this if you have run feature reports
@@ -0,0 +1,148 @@
1
+ {
2
+ "Site": {
3
+ "latitude": {
4
+ "type": "float",
5
+ "required": true
6
+ },
7
+ "longitude": {
8
+ "type": "float",
9
+ "required": true
10
+ }
11
+ },
12
+ "SpaceHeatingLoad": {
13
+ "fuel_loads_mmbtu_per_hour": {
14
+ "type": "array",
15
+ "required": true,
16
+ "description": "8760 timeseries",
17
+ "note": "This parameter is required to run REopt's GHP module but is not used to calculate URBANopt's GHP system LCCA. Users can set this to small numbers in GHP scenario"
18
+ }
19
+ },
20
+ "DomesticHotWaterLoad": {
21
+ "fuel_loads_mmbtu_per_hour": {
22
+ "type": "array",
23
+ "required": true,
24
+ "description": "8760 timeseries, if GHP not providing DHW set to zeros"
25
+ }
26
+ },
27
+ "ElectricLoad": {
28
+ "load_kw": {
29
+ "type": "array",
30
+ "required": true,
31
+ "description": "8760 timeseries",
32
+ "note": "15 min interval data do not work in GHP module. Please make sure all timeseries are 8760"
33
+ }
34
+ },
35
+ "ElectricTarriff": {
36
+ "urdb_label": "string",
37
+ "required": true
38
+ },
39
+ "GHP": {
40
+ "require_ghp_purchase": {
41
+ "type": "boolean",
42
+ "required": true,
43
+ "description": "choices of 0 or 1. Always set at 1 for URBANopt's LCCA"
44
+ },
45
+ "building_sqft": {
46
+ "type": "float",
47
+ "required": true,
48
+ "description": "to calculate cost of hydronic loop",
49
+ "note": "for the GHX iteration, set building_sqft to a number close to 0 but not exactly 0"
50
+ },
51
+ "heatpump_capacity_sizing_factor_on_peak_load": {
52
+ "type": "float",
53
+ "required": true,
54
+ "description": "always set to 1"
55
+ },
56
+ "ghpghx_responses":{
57
+ "outputs": {
58
+ "heat_pump_configuration": {
59
+ "type": "string",
60
+ "required": true,
61
+ "description": "set as WSHP"
62
+ },
63
+ "peak_combined_heatpump_thermal_ton": {
64
+ "type": "float",
65
+ "required": true,
66
+ "description": "size of GHP in ton",
67
+ "note": "in the GHX iteration, set this value to a number close to 0 but NOT exactly 0S"
68
+ },
69
+ "number_of_boreholes": {
70
+ "type": "int",
71
+ "required": true,
72
+ "note": "in the GHP iteration, set this value to 0"
73
+ },
74
+ "length_boreholes_ft": {
75
+ "type": "float",
76
+ "required": true,
77
+ "note": "in the GHP iteration, set this value to 0"
78
+ },
79
+ "yearly_total_electric_consumption_series_kw": {
80
+ "type": "array",
81
+ "required": true,
82
+ "description": "8760 timeseries of building total electric consumption",
83
+ "note": "in the GHX iteration, set this value to a number close to 0 but not exactly 0"
84
+ },
85
+ "yearly_ghx_pump_electric_consumption_series_kw": {
86
+ "type": "array",
87
+ "required": true,
88
+ "description": "8760 timeseries of ghx's total electric consumption",
89
+ "note": "in the GHP iteration, set this value to 0"
90
+ }
91
+ },
92
+ "inputs": {
93
+ "heating_thermal_load_mmbtu_per_hr": {
94
+ "type": "array",
95
+ "required": true,
96
+ "description": "not used for URBANopt's GHP LCCA but required for formatting, set to number close to 0"
97
+ },
98
+ "cooling_thermal_load_ton": {
99
+ "type": "array",
100
+ "required": true,
101
+ "description": "not used for URBANopt's GHP LCCA but required for formatting, can set as zeros"
102
+ }
103
+ }
104
+ },
105
+ "installed_cost_heatpump_per_ton": {
106
+ "type": "float",
107
+ "required": false,
108
+ "description": "installation cost per unit (ton) of GHP"
109
+ },
110
+ "installed_cost_ghx_per_ft": {
111
+ "type": "float",
112
+ "required": false,
113
+ "description": "installation cost per unit (ft) of GHX"
114
+ },
115
+ "installed_cost_building_hydronic_loop_per_sqft": {
116
+ "type": "float",
117
+ "required": false,
118
+ "description": "installation cost per sqft of building hydronic loop"
119
+ },
120
+ "om_cost_per_sqft_year": {
121
+ "type": "float",
122
+ "required": false,
123
+ "description": "if not specified, the value is -$0.51, capturing saving from HVAC operation. Recommended setting at 0"
124
+ },
125
+ "macrs_bonus_fraction": {
126
+ "type": "float",
127
+ "required": false,
128
+ "description": "Percentage of macrs benefits for GHP. Value between 0 and 1"
129
+ },
130
+ "macrs_itc_reduction": {
131
+ "type": "float",
132
+ "required": false,
133
+ "description": "Percentage of macrs benefits for GHP. Value between 0 and 1"
134
+ },
135
+ "federal_itc_fraction": {
136
+ "type": "float",
137
+ "required": false,
138
+ "description": "Percentage of ITC benefits for GHP. Value between 0 and 1"
139
+ }
140
+ },
141
+ "ExistingBoiler": {
142
+ "fuel_cost_per_mmbtu": {
143
+ "type": "float",
144
+ "required": true,
145
+ "description": "to calculate BAU cost, and required for formatting in GHP scenario"
146
+ }
147
+ }
148
+ }