-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #153 from urbanopt/ghp_lcca
REopt GHP LCCA Analysis
- Loading branch information
Showing
19 changed files
with
38,211 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
# ********************************************************************************* | ||
# URBANopt (tm), Copyright (c) Alliance for Sustainable Energy, LLC. | ||
# See also https://github.com/urbanopt/urbanopt-reopt-gem/blob/develop/LICENSE.md | ||
# ********************************************************************************* | ||
|
||
require 'net/https' | ||
require 'openssl' | ||
require 'uri' | ||
require 'json' | ||
require 'securerandom' | ||
require 'certified' | ||
require_relative '../../../developer_nrel_key' | ||
require 'urbanopt/reopt/reopt_logger' | ||
|
||
module URBANopt # :nodoc: | ||
module REopt # :nodoc: | ||
class REoptLiteGHPAPI | ||
|
||
def initialize(reopt_input_file, nrel_developer_key = nil, reopt_output_file, use_localhost) | ||
|
||
# Store developer key | ||
if [nil, '', '<insert your key here>'].include? nrel_developer_key | ||
if [nil, '', '<insert your key here>'].include? DEVELOPER_NREL_KEY | ||
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' | ||
else | ||
#Store the NREL developer key | ||
nrel_developer_key = DEVELOPER_NREL_KEY | ||
end | ||
end | ||
|
||
@use_localhost = use_localhost | ||
if @use_localhost | ||
@root_url = "http://localhost:8000/v3" | ||
else | ||
@root_url = "https://developer.nrel.gov/api/reopt/v3" | ||
end | ||
# add REopt URL | ||
@nrel_developer_key = nrel_developer_key | ||
@reopt_input_file = reopt_input_file | ||
@reopt_output_file = reopt_output_file | ||
# initialize @@logger | ||
@@logger ||= URBANopt::REopt.reopt_logger | ||
@@logger.level = Logger::INFO | ||
end | ||
|
||
|
||
def get_api_results(run_id=nil) | ||
|
||
reopt_input_file = @reopt_input_file | ||
nrel_developer_key = @nrel_developer_key | ||
root_url = @root_url | ||
reopt_output_file = @reopt_output_file | ||
|
||
if run_id.nil? | ||
run_id = get_run_uuid(reopt_input_file, nrel_developer_key, reopt_output_file) | ||
end | ||
if !run_id.nil? | ||
results_url = "#{@root_url}/job/#{run_id}/results/?api_key=#{nrel_developer_key}" | ||
puts "This is results URL #{results_url}" | ||
results = reopt_request(results_url) | ||
|
||
File.open(reopt_output_file, 'w') do |f| | ||
f.write(JSON.pretty_generate(results)) | ||
@@logger.info("Saved results to #{reopt_output_file}") | ||
end | ||
else | ||
results = nil | ||
@@logger.error("Unable to get results: no UUID returned.") | ||
end | ||
results | ||
end | ||
|
||
def get_run_uuid(reopt_input_file, nrel_developer_key, root_url) | ||
|
||
reopt_input_file = @reopt_input_file | ||
nrel_developer_key = @nrel_developer_key | ||
root_url = @root_url | ||
post_url = "#{root_url}/job/?api_key=#{nrel_developer_key}" | ||
puts "This is URL: #{post_url}" | ||
@@logger.info("Connecting to #{post_url}") | ||
|
||
# Parse the URL and prepare the HTTP request | ||
uri = URI.parse(post_url) | ||
request = Net::HTTP::Post.new(uri) | ||
request.content_type = 'application/json' | ||
|
||
# Add the JSON payload (assuming 'post' is the body data) | ||
request.body = reopt_input_file.to_json | ||
|
||
# Send the HTTP request | ||
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http| | ||
http.request(request) | ||
end | ||
|
||
run_id = nil | ||
|
||
if !response.is_a?(Net::HTTPSuccess) | ||
@@logger.error("Status code #{response.code}. #{response.body}") | ||
@@logger.error("Status code #{response.code}") | ||
else | ||
@@logger.info("Response OK from #{post_url}.") | ||
run_id_dict = JSON.parse(response.body) | ||
|
||
begin | ||
run_id = run_id_dict['run_uuid'] | ||
rescue KeyError | ||
msg = "Response from #{post_url} did not contain run_uuid." | ||
@@logger.error(msg) | ||
end | ||
end | ||
# Return run_id | ||
run_id | ||
end | ||
|
||
def reopt_request(results_url, poll_interval = 5, max_timeout = 300) | ||
|
||
key_error_count = 0 | ||
key_error_threshold = 3 | ||
status = "Optimizing..." | ||
@@logger.info("Polling #{results_url} for results with interval of #{poll_interval}...") | ||
resp_dict = {} | ||
start_time = Time.now | ||
|
||
loop do | ||
uri = URI.parse(results_url) | ||
response = Net::HTTP.get_response(uri) | ||
resp_dict = JSON.parse(response.body) | ||
|
||
begin | ||
status = resp_dict['status'] | ||
rescue KeyError | ||
key_error_count += 1 | ||
@@logger.info("KeyError count: #{key_error_count}") | ||
if key_error_count > key_error_threshold | ||
@@logger.info("Breaking polling loop due to KeyError count threshold of #{key_error_threshold} exceeded.") | ||
break | ||
end | ||
end | ||
|
||
if status != "Optimizing..." | ||
break | ||
end | ||
|
||
if Time.now - start_time > max_timeout | ||
@@logger.info("Breaking polling loop due to max timeout of #{max_timeout} seconds exceeded.") | ||
break | ||
end | ||
|
||
sleep(poll_interval) | ||
|
||
end | ||
resp_dict | ||
end | ||
|
||
end | ||
end | ||
end |
26 changes: 26 additions & 0 deletions
26
lib/urbanopt/reopt/reopt_ghp_files/reopt_ghp_assumption.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"Site": { | ||
"latitude": 42.81428490645775, | ||
"longitude": -78.84701778930912 | ||
}, | ||
"SpaceHeatingLoad": { | ||
}, | ||
"DomesticHotWaterLoad": { | ||
}, | ||
"ElectricLoad": { | ||
}, | ||
"ElectricTariff": { | ||
"urdb_label": "594976725457a37b1175d089" | ||
}, | ||
"GHP":{ | ||
"installed_cost_heatpump_per_ton": 1075, | ||
"installed_cost_ghx_per_ft": 14, | ||
"installed_cost_building_hydronic_loop_per_sqft": 1.7, | ||
"om_cost_per_sqft_year": 0, | ||
"macrs_bonus_fraction": 0.6, | ||
"macrs_itc_reduction": 0.5, | ||
"federal_itc_fraction": 0.3 | ||
}, | ||
"ExistingBoiler": { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# ********************************************************************************* | ||
# URBANopt (tm), Copyright (c) Alliance for Sustainable Energy, LLC. | ||
# See also https://github.com/urbanopt/urbanopt-reopt-gem/blob/develop/LICENSE.md | ||
# ********************************************************************************* | ||
|
||
require 'bundler/setup' | ||
require 'urbanopt/reopt/reopt_logger' | ||
require 'urbanopt/reopt/reopt_ghp_api' | ||
require 'csv' | ||
require 'json' | ||
require 'fileutils' | ||
|
||
module URBANopt # :nodoc: | ||
module REopt # :nodoc: | ||
class REoptGHPPostProcessor | ||
|
||
def initialize(run_dir, system_parameter, modelica_result, reopt_ghp_assumptions = nil, nrel_developer_key = nil, localhost) | ||
# initialize @@logger | ||
@@logger ||= URBANopt::REopt.reopt_logger | ||
|
||
@nrel_developer_key = nrel_developer_key | ||
@localhost = localhost | ||
@reopt_ghp_output_district = nil | ||
@reopt_ghp_output_building = [] | ||
@reopt_ghp_assumptions_hash = nil | ||
@reopt_ghp_assumptions = nil | ||
@system_parameter = nil | ||
@system_parameter_hash = nil | ||
@modelica_result = nil | ||
@building_ids = nil | ||
@run_dir = run_dir | ||
|
||
if !reopt_ghp_assumptions.nil? | ||
@reopt_ghp_assumptions = reopt_ghp_assumptions | ||
File.open(reopt_ghp_assumptions, 'r') do |file| | ||
@reopt_ghp_assumptions_input_hash = JSON.parse(file.read, symbolize_names: true) | ||
end | ||
end | ||
|
||
if !system_parameter.nil? | ||
@system_parameter = system_parameter | ||
File.open(system_parameter, 'r') do |file| | ||
@system_parameter_input_hash = JSON.parse(file.read, symbolize_names: true) | ||
end | ||
#Determine loop order | ||
loop_order = File.join(File.dirname(system_parameter), '_loop_order.json') | ||
if File.exist?(loop_order) | ||
File.open(loop_order, 'r') do |file| | ||
loop_order_input= JSON.parse(file.read, symbolize_names: true) | ||
# Check the type of the parsed data | ||
if loop_order_input.is_a?(Array) | ||
@loop_order_input_hash = loop_order_input | ||
@loop_order_input_hash.each do |item| | ||
puts "Building IDs in group: #{item[:list_bldg_ids_in_group].inspect}" | ||
puts "GHE IDs in group: #{item[:list_ghe_ids_in_group].inspect}" | ||
end | ||
elsif loop_order_input.is_a?(Hash) | ||
@loop_order_input_hash = [loop_order_input] # Wrap in array if a single object | ||
@loop_order_input_hash.each do |item| | ||
puts "Building IDs in group: #{item[:list_bldg_ids_in_group].inspect}" | ||
puts "GHE IDs in group: #{item[:list_ghe_ids_in_group].inspect}" | ||
end | ||
else | ||
puts "Unexpected JSON structure" | ||
end | ||
end | ||
end | ||
|
||
end | ||
|
||
if !modelica_result.nil? | ||
@modelica_result_input = modelica_result | ||
end | ||
end | ||
|
||
attr_accessor :run_dir, :system_parameter_input_hash, :reopt_ghp_assumptions_input_hash, :loop_order_input_hash, :modelica_result_input | ||
|
||
# # Create REopt input and output building report | ||
def run_reopt_lcca(system_parameter_hash: nil, reopt_ghp_assumptions_hash: nil, modelica_result: nil) | ||
|
||
adapter = URBANopt::REopt::REoptGHPAdapter.new | ||
|
||
# if these arguments are specified, use them | ||
if !system_parameter_hash.nil? | ||
@system_parameter_input_hash = system_parameter_hash | ||
end | ||
|
||
if !reopt_ghp_assumptions_hash.nil? | ||
@reopt_ghp_assumptions_input_hash = reopt_ghp_assumptions_hash | ||
end | ||
|
||
|
||
if !modelica_result.nil? | ||
@modelica_result_input = modelica_result | ||
end | ||
|
||
# Create folder for REopt input files only if they dont exist | ||
reopt_ghp_dir = File.join(@run_dir, "reopt_ghp") | ||
reopt_ghp_input = File.join(reopt_ghp_dir, "reopt_ghp_inputs") | ||
unless Dir.exist?(reopt_ghp_dir) | ||
FileUtils.mkdir_p(reopt_ghp_dir) | ||
end | ||
unless Dir.exist?(reopt_ghp_input) | ||
FileUtils.mkdir_p(reopt_ghp_input) | ||
end | ||
|
||
reopt_ghp_output = File.join(reopt_ghp_dir, "reopt_ghp_outputs") | ||
unless Dir.exist?(reopt_ghp_output) | ||
FileUtils.mkdir_p(reopt_ghp_output) | ||
end | ||
|
||
# get building IDs from _loop_order.json | ||
building_ids = [] | ||
ghp_ids = [] | ||
@loop_order_input_hash.each do |loop| | ||
building_ids.concat(loop[:list_bldg_ids_in_group].flatten) | ||
ghp_ids.concat(loop[:list_ghe_ids_in_group].flatten) | ||
end | ||
|
||
building_ids.each do |building_id| | ||
# create REopt building input file for all buildings in loop order list | ||
reopt_input_building = adapter.create_reopt_input_building(@run_dir, @system_parameter_input_hash, @reopt_ghp_assumptions_input_hash, building_id, @modelica_result_input) | ||
end | ||
ghp_ids.each do |ghp_id| | ||
# create REopt district input file | ||
reopt_input_district = adapter.create_reopt_input_district(@run_dir, @system_parameter_input_hash, @reopt_ghp_assumptions_input_hash, ghp_id, @modelica_result_input) | ||
end | ||
|
||
Dir.foreach(reopt_ghp_input) do |input_file| | ||
# Skip '.' and '..' (current and parent directory entries) | ||
next if input_file == '.' || input_file == '..' | ||
|
||
reopt_ghp_input_file_path = File.join(reopt_ghp_input, input_file) | ||
|
||
reopt_input_data = nil | ||
|
||
File.open(reopt_ghp_input_file_path, 'r') do |f| | ||
reopt_input_data = JSON.parse(f.read) | ||
end | ||
|
||
base_name = File.basename(input_file, '.json') | ||
|
||
# reopt_ghp_output_file | ||
reopt_output_file = File.join(reopt_ghp_output, "#{base_name}_output.json") | ||
#call the REopt API | ||
api = URBANopt::REopt::REoptLiteGHPAPI.new(reopt_input_data, DEVELOPER_NREL_KEY, reopt_output_file, @localhost) | ||
api.get_api_results() | ||
|
||
end | ||
|
||
end | ||
|
||
end #REoptGHPPostProcessor | ||
end #REopt | ||
end #URBANopt |
Oops, something went wrong.