Skip to content

Commit

Permalink
Merge pull request #153 from urbanopt/ghp_lcca
Browse files Browse the repository at this point in the history
REopt GHP LCCA Analysis
  • Loading branch information
kflemin authored Nov 12, 2024
2 parents 612bfbb + 62fdf73 commit 58dda24
Show file tree
Hide file tree
Showing 19 changed files with 38,211 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/lib/measures/test_results
/lib/measures/.rubocop*
# test files
spec/run/
spec/files/run/baseline_scenario_ghe/reopt_ghp

developer_nrel_key.rb
# rspec failure tracking
Expand Down
3 changes: 3 additions & 0 deletions lib/urbanopt/reopt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@
require 'urbanopt/reopt/scenario_report_adapter'
require 'urbanopt/reopt/reopt_post_processor'
require 'urbanopt/reopt/version'
require 'urbanopt/reopt/reopt_ghp_post_processor'
require 'urbanopt/reopt/reopt_ghp_adapter'
require 'urbanopt/reopt/reopt_ghp_api'
336 changes: 336 additions & 0 deletions lib/urbanopt/reopt/reopt_ghp_adapter.rb

Large diffs are not rendered by default.

157 changes: 157 additions & 0 deletions lib/urbanopt/reopt/reopt_ghp_api.rb
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 lib/urbanopt/reopt/reopt_ghp_files/reopt_ghp_assumption.json
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": {
}
}
155 changes: 155 additions & 0 deletions lib/urbanopt/reopt/reopt_ghp_post_processor.rb
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
Loading

0 comments on commit 58dda24

Please sign in to comment.