urbanopt-reopt 0.12.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/nightly_ci_build.yml +4 -7
- data/.gitignore +1 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile +14 -11
- data/LICENSE.md +1 -1
- data/doc_templates/LICENSE.md +1 -1
- data/doc_templates/copyright_erb.txt +1 -1
- data/docs/schemas/reopt-output-schema.md +1 -1
- data/lib/urbanopt/reopt/feature_report_adapter.rb +6 -7
- data/lib/urbanopt/reopt/reopt_ghp_adapter.rb +337 -0
- data/lib/urbanopt/reopt/reopt_ghp_api.rb +156 -0
- data/lib/urbanopt/reopt/reopt_ghp_files/reopt_ghp_assumption.json +27 -0
- data/lib/urbanopt/reopt/reopt_ghp_post_processor.rb +149 -0
- data/lib/urbanopt/reopt/reopt_lite_api.rb +8 -9
- data/lib/urbanopt/reopt/reopt_logger.rb +1 -1
- data/lib/urbanopt/reopt/reopt_post_processor.rb +2 -3
- data/lib/urbanopt/reopt/reopt_schema/REopt-GHP-input.json +148 -0
- data/lib/urbanopt/reopt/reopt_schema/reopt_input_schema.json +2 -2
- data/lib/urbanopt/reopt/reopt_schema/reopt_output_schema.json +40 -0
- data/lib/urbanopt/reopt/scenario_report_adapter.rb +6 -10
- data/lib/urbanopt/reopt/utilities.rb +2 -2
- data/lib/urbanopt/reopt/version.rb +1 -1
- data/lib/urbanopt/reopt.rb +3 -0
- data/urbanopt-reopt.gemspec +10 -8
- metadata +42 -37
@@ -0,0 +1,156 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt (tm), Copyright (c) Alliance for Sustainable Energy, LLC.
|
3
|
+
# See also https://github.com/urbanopt/urbanopt-reopt-gem/blob/develop/LICENSE.md
|
4
|
+
# *********************************************************************************
|
5
|
+
|
6
|
+
require 'net/https'
|
7
|
+
require 'openssl'
|
8
|
+
require 'uri'
|
9
|
+
require 'json'
|
10
|
+
require 'securerandom'
|
11
|
+
require_relative '../../../developer_nrel_key'
|
12
|
+
require 'urbanopt/reopt/reopt_logger'
|
13
|
+
|
14
|
+
module URBANopt # :nodoc:
|
15
|
+
module REopt # :nodoc:
|
16
|
+
class REoptLiteGHPAPI
|
17
|
+
|
18
|
+
def initialize(reopt_input_file, nrel_developer_key = nil, reopt_output_file, use_localhost)
|
19
|
+
|
20
|
+
# Store developer key
|
21
|
+
if [nil, '', '<insert your key here>'].include? nrel_developer_key
|
22
|
+
if [nil, '', '<insert your key here>'].include? DEVELOPER_NREL_KEY
|
23
|
+
raise 'A developer.nrel.gov API key is required. Please see https://developer.nrel.gov/signup/ then update the file urbanopt-reopt-gem/developer_nrel_key.rb'
|
24
|
+
else
|
25
|
+
#Store the NREL developer key
|
26
|
+
nrel_developer_key = DEVELOPER_NREL_KEY
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@use_localhost = use_localhost
|
31
|
+
if @use_localhost
|
32
|
+
@root_url = "http://localhost:8000/v3"
|
33
|
+
else
|
34
|
+
@root_url = "https://developer.nrel.gov/api/reopt/v3"
|
35
|
+
end
|
36
|
+
# add REopt URL
|
37
|
+
@nrel_developer_key = nrel_developer_key
|
38
|
+
@reopt_input_file = reopt_input_file
|
39
|
+
@reopt_output_file = reopt_output_file
|
40
|
+
# initialize @@logger
|
41
|
+
@@logger ||= URBANopt::REopt.reopt_logger
|
42
|
+
@@logger.level = Logger::INFO
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def get_api_results(run_id=nil)
|
47
|
+
|
48
|
+
reopt_input_file = @reopt_input_file
|
49
|
+
nrel_developer_key = @nrel_developer_key
|
50
|
+
root_url = @root_url
|
51
|
+
reopt_output_file = @reopt_output_file
|
52
|
+
|
53
|
+
if run_id.nil?
|
54
|
+
run_id = get_run_uuid(reopt_input_file, nrel_developer_key, reopt_output_file)
|
55
|
+
end
|
56
|
+
if !run_id.nil?
|
57
|
+
results_url = "#{@root_url}/job/#{run_id}/results/?api_key=#{nrel_developer_key}"
|
58
|
+
puts "This is results URL #{results_url}"
|
59
|
+
results = reopt_request(results_url)
|
60
|
+
|
61
|
+
File.open(reopt_output_file, 'w') do |f|
|
62
|
+
f.write(JSON.pretty_generate(results))
|
63
|
+
@@logger.info("Saved results to #{reopt_output_file}")
|
64
|
+
end
|
65
|
+
else
|
66
|
+
results = nil
|
67
|
+
@@logger.error("Unable to get results: no UUID returned.")
|
68
|
+
end
|
69
|
+
results
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_run_uuid(reopt_input_file, nrel_developer_key, root_url)
|
73
|
+
|
74
|
+
reopt_input_file = @reopt_input_file
|
75
|
+
nrel_developer_key = @nrel_developer_key
|
76
|
+
root_url = @root_url
|
77
|
+
post_url = "#{root_url}/job/?api_key=#{nrel_developer_key}"
|
78
|
+
puts "This is URL: #{post_url}"
|
79
|
+
@@logger.info("Connecting to #{post_url}")
|
80
|
+
|
81
|
+
# Parse the URL and prepare the HTTP request
|
82
|
+
uri = URI.parse(post_url)
|
83
|
+
request = Net::HTTP::Post.new(uri)
|
84
|
+
request.content_type = 'application/json'
|
85
|
+
|
86
|
+
# Add the JSON payload (assuming 'post' is the body data)
|
87
|
+
request.body = reopt_input_file.to_json
|
88
|
+
|
89
|
+
# Send the HTTP request
|
90
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
91
|
+
http.request(request)
|
92
|
+
end
|
93
|
+
|
94
|
+
run_id = nil
|
95
|
+
|
96
|
+
if !response.is_a?(Net::HTTPSuccess)
|
97
|
+
@@logger.error("Status code #{response.code}. #{response.body}")
|
98
|
+
@@logger.error("Status code #{response.code}")
|
99
|
+
else
|
100
|
+
@@logger.info("Response OK from #{post_url}.")
|
101
|
+
run_id_dict = JSON.parse(response.body)
|
102
|
+
|
103
|
+
begin
|
104
|
+
run_id = run_id_dict['run_uuid']
|
105
|
+
rescue KeyError
|
106
|
+
msg = "Response from #{post_url} did not contain run_uuid."
|
107
|
+
@@logger.error(msg)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
# Return run_id
|
111
|
+
run_id
|
112
|
+
end
|
113
|
+
|
114
|
+
def reopt_request(results_url, poll_interval = 5, max_timeout = 300)
|
115
|
+
|
116
|
+
key_error_count = 0
|
117
|
+
key_error_threshold = 3
|
118
|
+
status = "Optimizing..."
|
119
|
+
@@logger.info("Polling #{results_url} for results with interval of #{poll_interval}...")
|
120
|
+
resp_dict = {}
|
121
|
+
start_time = Time.now
|
122
|
+
|
123
|
+
loop do
|
124
|
+
uri = URI.parse(results_url)
|
125
|
+
response = Net::HTTP.get_response(uri)
|
126
|
+
resp_dict = JSON.parse(response.body)
|
127
|
+
|
128
|
+
begin
|
129
|
+
status = resp_dict['status']
|
130
|
+
rescue KeyError
|
131
|
+
key_error_count += 1
|
132
|
+
@@logger.info("KeyError count: #{key_error_count}")
|
133
|
+
if key_error_count > key_error_threshold
|
134
|
+
@@logger.info("Breaking polling loop due to KeyError count threshold of #{key_error_threshold} exceeded.")
|
135
|
+
break
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
if status != "Optimizing..."
|
140
|
+
break
|
141
|
+
end
|
142
|
+
|
143
|
+
if Time.now - start_time > max_timeout
|
144
|
+
@@logger.info("Breaking polling loop due to max timeout of #{max_timeout} seconds exceeded.")
|
145
|
+
break
|
146
|
+
end
|
147
|
+
|
148
|
+
sleep(poll_interval)
|
149
|
+
|
150
|
+
end
|
151
|
+
resp_dict
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"Site": {
|
3
|
+
"latitude": 42.81428490645775,
|
4
|
+
"longitude": -78.84701778930912
|
5
|
+
},
|
6
|
+
"SpaceHeatingLoad": {
|
7
|
+
},
|
8
|
+
"DomesticHotWaterLoad": {
|
9
|
+
},
|
10
|
+
"ElectricLoad": {
|
11
|
+
"year": 2017
|
12
|
+
},
|
13
|
+
"ElectricTariff": {
|
14
|
+
"urdb_label": "594976725457a37b1175d089"
|
15
|
+
},
|
16
|
+
"GHP":{
|
17
|
+
"installed_cost_heatpump_per_ton": 1075,
|
18
|
+
"installed_cost_ghx_per_ft": 14,
|
19
|
+
"installed_cost_building_hydronic_loop_per_sqft": 1.7,
|
20
|
+
"om_cost_per_sqft_year": 0,
|
21
|
+
"macrs_bonus_fraction": 0.6,
|
22
|
+
"macrs_itc_reduction": 0.5,
|
23
|
+
"federal_itc_fraction": 0.3
|
24
|
+
},
|
25
|
+
"ExistingBoiler": {
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# *********************************************************************************
|
2
|
+
# URBANopt (tm), Copyright (c) Alliance for Sustainable Energy, LLC.
|
3
|
+
# See also https://github.com/urbanopt/urbanopt-reopt-gem/blob/develop/LICENSE.md
|
4
|
+
# *********************************************************************************
|
5
|
+
|
6
|
+
require 'bundler/setup'
|
7
|
+
require 'urbanopt/reopt/reopt_logger'
|
8
|
+
require 'urbanopt/reopt/reopt_ghp_api'
|
9
|
+
require 'csv'
|
10
|
+
require 'json'
|
11
|
+
require 'fileutils'
|
12
|
+
|
13
|
+
module URBANopt # :nodoc:
|
14
|
+
module REopt # :nodoc:
|
15
|
+
class REoptGHPPostProcessor
|
16
|
+
def initialize(run_dir, system_parameter, modelica_result, reopt_ghp_assumptions = nil, nrel_developer_key = nil, localhost)
|
17
|
+
# initialize @@logger
|
18
|
+
@@logger ||= URBANopt::REopt.reopt_logger
|
19
|
+
|
20
|
+
@nrel_developer_key = nrel_developer_key
|
21
|
+
@localhost = localhost
|
22
|
+
@reopt_ghp_output_district = nil
|
23
|
+
@reopt_ghp_output_building = []
|
24
|
+
@reopt_ghp_assumptions_hash = nil
|
25
|
+
@reopt_ghp_assumptions = nil
|
26
|
+
@system_parameter = nil
|
27
|
+
@system_parameter_hash = nil
|
28
|
+
@modelica_result = nil
|
29
|
+
@building_ids = nil
|
30
|
+
@run_dir = run_dir
|
31
|
+
|
32
|
+
if !reopt_ghp_assumptions.nil?
|
33
|
+
@reopt_ghp_assumptions = reopt_ghp_assumptions
|
34
|
+
File.open(reopt_ghp_assumptions, 'r') do |file|
|
35
|
+
@reopt_ghp_assumptions_input_hash = JSON.parse(file.read, symbolize_names: true)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
if !system_parameter.nil?
|
40
|
+
@system_parameter = system_parameter
|
41
|
+
File.open(system_parameter, 'r') do |file|
|
42
|
+
@system_parameter_input_hash = JSON.parse(file.read, symbolize_names: true)
|
43
|
+
end
|
44
|
+
# Determine loop order
|
45
|
+
loop_order = File.join(File.dirname(system_parameter), '_loop_order.json')
|
46
|
+
if File.exist?(loop_order)
|
47
|
+
File.open(loop_order, 'r') do |file|
|
48
|
+
loop_order_input = JSON.parse(file.read, symbolize_names: true)
|
49
|
+
# Check the type of the parsed data
|
50
|
+
if loop_order_input.is_a?(Array)
|
51
|
+
@loop_order_input_hash = loop_order_input
|
52
|
+
@loop_order_input_hash.each do |item|
|
53
|
+
puts "Building IDs in group: #{item[:list_bldg_ids_in_group].inspect}"
|
54
|
+
puts "GHE IDs in group: #{item[:list_ghe_ids_in_group].inspect}"
|
55
|
+
end
|
56
|
+
elsif loop_order_input.is_a?(Hash)
|
57
|
+
@loop_order_input_hash = [loop_order_input] # Wrap in array if a single object
|
58
|
+
@loop_order_input_hash.each do |item|
|
59
|
+
puts "Building IDs in group: #{item[:list_bldg_ids_in_group].inspect}"
|
60
|
+
puts "GHE IDs in group: #{item[:list_ghe_ids_in_group].inspect}"
|
61
|
+
end
|
62
|
+
else
|
63
|
+
puts 'Unexpected JSON structure'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
if !modelica_result.nil?
|
71
|
+
@modelica_result_input = modelica_result
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
attr_accessor :run_dir, :system_parameter_input_hash, :reopt_ghp_assumptions_input_hash, :loop_order_input_hash, :modelica_result_input
|
76
|
+
|
77
|
+
# # Create REopt input and output building report
|
78
|
+
def run_reopt_lcca(system_parameter_hash: nil, reopt_ghp_assumptions_hash: nil, modelica_result: nil)
|
79
|
+
adapter = URBANopt::REopt::REoptGHPAdapter.new
|
80
|
+
|
81
|
+
# if these arguments are specified, use them
|
82
|
+
if !system_parameter_hash.nil?
|
83
|
+
@system_parameter_input_hash = system_parameter_hash
|
84
|
+
end
|
85
|
+
|
86
|
+
if !reopt_ghp_assumptions_hash.nil?
|
87
|
+
@reopt_ghp_assumptions_input_hash = reopt_ghp_assumptions_hash
|
88
|
+
end
|
89
|
+
|
90
|
+
if !modelica_result.nil?
|
91
|
+
@modelica_result_input = modelica_result
|
92
|
+
end
|
93
|
+
|
94
|
+
# Create folder for REopt input files only if they dont exist
|
95
|
+
reopt_ghp_dir = File.join(@run_dir, 'reopt_ghp')
|
96
|
+
reopt_ghp_input = File.join(reopt_ghp_dir, 'reopt_ghp_inputs')
|
97
|
+
unless Dir.exist?(reopt_ghp_dir)
|
98
|
+
FileUtils.mkdir_p(reopt_ghp_dir)
|
99
|
+
end
|
100
|
+
unless Dir.exist?(reopt_ghp_input)
|
101
|
+
FileUtils.mkdir_p(reopt_ghp_input)
|
102
|
+
end
|
103
|
+
|
104
|
+
reopt_ghp_output = File.join(reopt_ghp_dir, 'reopt_ghp_outputs')
|
105
|
+
unless Dir.exist?(reopt_ghp_output)
|
106
|
+
FileUtils.mkdir_p(reopt_ghp_output)
|
107
|
+
end
|
108
|
+
|
109
|
+
# get building IDs from _loop_order.json
|
110
|
+
building_ids = []
|
111
|
+
ghp_ids = []
|
112
|
+
@loop_order_input_hash.each do |loop|
|
113
|
+
building_ids.concat(loop[:list_bldg_ids_in_group].flatten)
|
114
|
+
ghp_ids.concat(loop[:list_ghe_ids_in_group].flatten)
|
115
|
+
end
|
116
|
+
|
117
|
+
building_ids.each do |building_id|
|
118
|
+
# create REopt building input file for all buildings in loop order list
|
119
|
+
reopt_input_building = adapter.create_reopt_input_building(@run_dir, @system_parameter_input_hash, @reopt_ghp_assumptions_input_hash, building_id, @modelica_result_input)
|
120
|
+
end
|
121
|
+
ghp_ids.each do |ghp_id|
|
122
|
+
# create REopt district input file
|
123
|
+
reopt_input_district = adapter.create_reopt_input_district(@run_dir, @system_parameter_input_hash, @reopt_ghp_assumptions_input_hash, ghp_id, @modelica_result_input)
|
124
|
+
end
|
125
|
+
|
126
|
+
Dir.foreach(reopt_ghp_input) do |input_file|
|
127
|
+
# Skip '.' and '..' (current and parent directory entries)
|
128
|
+
next if input_file == '.' || input_file == '..'
|
129
|
+
|
130
|
+
reopt_ghp_input_file_path = File.join(reopt_ghp_input, input_file)
|
131
|
+
|
132
|
+
reopt_input_data = nil
|
133
|
+
|
134
|
+
File.open(reopt_ghp_input_file_path, 'r') do |f|
|
135
|
+
reopt_input_data = JSON.parse(f.read)
|
136
|
+
end
|
137
|
+
|
138
|
+
base_name = File.basename(input_file, '.json')
|
139
|
+
|
140
|
+
# reopt_ghp_output_file
|
141
|
+
reopt_output_file = File.join(reopt_ghp_output, "#{base_name}_output.json")
|
142
|
+
# call the REopt API
|
143
|
+
api = URBANopt::REopt::REoptLiteGHPAPI.new(reopt_input_data, DEVELOPER_NREL_KEY, reopt_output_file, @localhost)
|
144
|
+
api.get_api_results
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end # REoptGHPPostProcessor
|
148
|
+
end # REopt
|
149
|
+
end # URBANopt
|
@@ -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
|
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
|
##
|
@@ -55,7 +54,7 @@ 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
|
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
|
@@ -73,7 +72,7 @@ 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
|
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
|
@@ -112,11 +111,11 @@ module URBANopt # :nodoc:
|
|
112
111
|
end
|
113
112
|
tries = max_tries
|
114
113
|
rescue StandardError => e
|
115
|
-
@@logger.
|
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.
|
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
|
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
|
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
|
@@ -246,7 +245,7 @@ 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
|
248
|
+
# [*return:*] _Bool_ - Returns true if the post succeeds. Otherwise returns false.
|
250
249
|
##
|
251
250
|
def reopt_request(reopt_input, filename)
|
252
251
|
description = reopt_input[:description]
|
@@ -12,7 +12,7 @@ module URBANopt
|
|
12
12
|
# Set Logger::DEBUG for development
|
13
13
|
@@reopt_logger.level = Logger::WARN
|
14
14
|
##
|
15
|
-
#
|
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
|
@@ -33,7 +33,6 @@ module URBANopt # :nodoc:
|
|
33
33
|
end
|
34
34
|
@nrel_developer_key = nrel_developer_key
|
35
35
|
@localhost = localhost
|
36
|
-
@reopt_base_post = { ElectricTariff: {}, ElectricLoad: {}, Wind: { max_kw: 0 } }
|
37
36
|
|
38
37
|
@scenario_reopt_default_output_file = nil
|
39
38
|
@scenario_timeseries_default_output_file = nil
|
@@ -182,7 +181,7 @@ module URBANopt # :nodoc:
|
|
182
181
|
#
|
183
182
|
# [*parameters:*]
|
184
183
|
#
|
185
|
-
# * +feature_reports+ - _Array_ - An array of _URBANopt::Reporting::DefaultReports::FeatureReport_
|
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.
|
186
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.
|
187
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.
|
188
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.
|
@@ -276,7 +275,7 @@ module URBANopt # :nodoc:
|
|
276
275
|
#
|
277
276
|
# [*parameters:*]
|
278
277
|
#
|
279
|
-
# * +scenario_report+ - _Array_ - A _URBANopt::Reporting::DefaultReports::ScenarioReport_ which will each be used to create (and is
|
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.
|
280
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.
|
281
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.
|
282
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.
|
@@ -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
|
+
}
|
@@ -101,7 +101,7 @@
|
|
101
101
|
"min": 0,
|
102
102
|
"max": 1000000.0,
|
103
103
|
"type": "number",
|
104
|
-
"description": "Value placed on unmet site load during grid outages. Units are US dollars per unmet kilowatt-hour. The value of lost load (VoLL) is used to determine the avoided outage costs by multiplying VoLL [$/kWh] with the average number of hours that the critical load can be met by the energy system (determined by simulating outages
|
104
|
+
"description": "Value placed on unmet site load during grid outages. Units are US dollars per unmet kilowatt-hour. The value of lost load (VoLL) is used to determine the avoided outage costs by multiplying VoLL [$/kWh] with the average number of hours that the critical load can be met by the energy system (determined by simulating outages occurring at every hour of the year), and multiplying by the mean critical load."
|
105
105
|
},
|
106
106
|
"analysis_years": {
|
107
107
|
"default": 20,
|
@@ -711,7 +711,7 @@
|
|
711
711
|
"min": 0,
|
712
712
|
"max": 1,
|
713
713
|
"type": "number",
|
714
|
-
"description": "Fraction of upfront project costs to depreciate under MACRS in year one in
|
714
|
+
"description": "Fraction of upfront project costs to depreciate under MACRS in year one in addition to scheduled depreciation"
|
715
715
|
},
|
716
716
|
"inverter_replacement_year": {
|
717
717
|
"default": 10,
|
@@ -103,6 +103,46 @@
|
|
103
103
|
}
|
104
104
|
}
|
105
105
|
},
|
106
|
+
"Financial_GHP": {
|
107
|
+
"type": "object",
|
108
|
+
"properties": {
|
109
|
+
"lcc": {
|
110
|
+
"type": "number",
|
111
|
+
"description": "Optimal lifecycle cost for GHP",
|
112
|
+
"units": "dollars"
|
113
|
+
},
|
114
|
+
"net_present_cost": {
|
115
|
+
"type": "number",
|
116
|
+
"description": "",
|
117
|
+
"units": "dollars"
|
118
|
+
},
|
119
|
+
"lifecycle_om_costs_before_tax_bau": {
|
120
|
+
"type": "number",
|
121
|
+
"description": "",
|
122
|
+
"units": "dollars"
|
123
|
+
},
|
124
|
+
"lifecycle_om_costs_after_tax": {
|
125
|
+
"type": "number",
|
126
|
+
"description": "",
|
127
|
+
"units": "$"
|
128
|
+
},
|
129
|
+
"year_one_om_costs_before_tax": {
|
130
|
+
"type": "number",
|
131
|
+
"description": "",
|
132
|
+
"units": "$"
|
133
|
+
},
|
134
|
+
"lifecycle_generation_tech_capital_costs": {
|
135
|
+
"type": "number",
|
136
|
+
"description": "",
|
137
|
+
"units": "$"
|
138
|
+
},
|
139
|
+
"lifecycle_offgrid_other_annual_costs_after_tax": {
|
140
|
+
"type": "number",
|
141
|
+
"description": "",
|
142
|
+
"units": "$"
|
143
|
+
}
|
144
|
+
}
|
145
|
+
},
|
106
146
|
"PV": {
|
107
147
|
"type": "object",
|
108
148
|
"properties": {
|