Skip to content

Commit

Permalink
Merge pull request #444 from NREL/ghp-district
Browse files Browse the repository at this point in the history
ghp-district
  • Loading branch information
atpham88 authored Oct 2, 2024
2 parents c30ff01 + 5a324c7 commit e36b314
Show file tree
Hide file tree
Showing 5 changed files with 210,432 additions and 6 deletions.
14 changes: 10 additions & 4 deletions src/core/ghp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,9 @@ function GHP(response::Dict, d::Dict)
# Convert boolean input into an integer for the model
if typeof(ghp.require_ghp_purchase) == Bool && ghp.require_ghp_purchase
ghp.require_ghp_purchase = 1
else
elseif typeof(ghp.require_ghp_purchase) == Bool && ghp.require_ghp_purchase == false
ghp.require_ghp_purchase = 0
end

return ghp
end

Expand Down Expand Up @@ -227,8 +226,15 @@ function setup_installed_cost_curve!(ghp::GHP, response::Dict)
# and then use the value above for heat pump capacity to calculate the final absolute cost for GHP

if ghp.heat_pump_configuration == "WSHP"
ghp.installed_cost_per_kw = [0, (ghp.ghx_only_capital_cost + hydronic_loop_cost + aux_cooler_cost + aux_heater_cost) /
ghp.heatpump_capacity_ton + ghp.installed_cost_heatpump_per_ton]
if !(ghp.heatpump_capacity_ton == 0)
ghp.installed_cost_per_kw = [0, (ghp.ghx_only_capital_cost + hydronic_loop_cost + aux_cooler_cost + aux_heater_cost) /
ghp.heatpump_capacity_ton + ghp.installed_cost_heatpump_per_ton]
else # For just costing ground heat exchanger for URBANopt
# Approximate investment-based incentive (ibi) stacking, ignores max incentive for state and utility
base_cost = ghp.ghx_only_capital_cost + hydronic_loop_cost + aux_cooler_cost + aux_heater_cost
ibi_net_fraction = (1 - ghp.federal_itc_fraction) * (1 - ghp.state_ibi_fraction) * (1 - ghp.utility_ibi_fraction)
ghp.installed_cost_per_kw = [base_cost * ibi_net_fraction, 0.0]
end
elseif ghp.heat_pump_configuration == "WWHP"
# Divide by two to avoid double counting non-heatpump costs
ghp.wwhp_heating_pump_installed_cost_curve = [0, (ghp.ghx_only_capital_cost + aux_cooler_cost + aux_heater_cost) / 2 /
Expand Down
6 changes: 4 additions & 2 deletions src/core/reopt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,10 @@ function build_reopt!(m::JuMP.AbstractModel, p::REoptInputs)
m[:ResidualGHXCapCost] = 0.0
m[:ObjectivePenalties] = 0.0

if !isempty(p.techs.all)
add_tech_size_constraints(m, p)
if !isempty(p.techs.all) || !isempty(p.techs.ghp)
if !isempty(p.techs.all)
add_tech_size_constraints(m, p)
end

if !isempty(p.techs.no_curtail)
add_no_curtail_constraints(m, p)
Expand Down
45 changes: 45 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1891,6 +1891,8 @@ else # run HiGHS tests
4. GHP serves all the Cooling load
5. Input of a custom COP map for GHP and check the GHP performance to make sure it's using it correctly
6. Hybrid GHP capability functions as expected
7. Check GHP LCC calculation for URBANopt
8. Check GHX LCC calculation for URBANopt
"""
# Load base inputs
Expand Down Expand Up @@ -1963,6 +1965,49 @@ else # run HiGHS tests
# Average COP which includes pump power should be lower than Heat Pump only COP specified by the map
@test heating_cop_avg <= 4.0
@test cooling_cop_avg <= 8.0

# Check GHP LCC calculation for URBANopt
ghp_data = JSON.parsefile("scenarios/ghp_urbanopt.json")
s = Scenario(ghp_data)
ghp_inputs = REoptInputs(s)
m = Model(optimizer_with_attributes(HiGHS.Optimizer, "output_flag" => false, "log_to_console" => false, "mip_rel_gap" => 0.01))
results = run_reopt(m, ghp_inputs)
ghp_lcc = results["Financial"]["lcc"]
ghp_lccc = results["Financial"]["lifecycle_capital_costs"]
ghp_lccc_initial = results["Financial"]["initial_capital_costs"]
ghp_ebill = results["Financial"]["lifecycle_elecbill_after_tax"]
boreholes = results["GHP"]["ghpghx_chosen_outputs"]["number_of_boreholes"]
boreholes_len = results["GHP"]["ghpghx_chosen_outputs"]["length_boreholes_ft"]

# Initial capital cost = initial cap cost of GHP + initial cap cost of hydronic loop
@test ghp_lccc_initial - results["GHP"]["size_heat_pump_ton"]*1075 - ghp_data["GHP"]["building_sqft"]*1.7 0.0 atol = 0.1
# LCC = LCCC + Electricity Bill
@test ghp_lcc - ghp_lccc - ghp_ebill 0.0 atol = 0.1
# LCCC should be around be around 52% of initial capital cost due to incentive and bonus
@test ghp_lccc/ghp_lccc_initial 0.518 atol = 0.01
# GHX size must be 0
@test boreholes 0.0 atol = 0.01
@test boreholes_len 0.0 atol = 0.01

# Check GHX LCC calculation for URBANopt
ghx_data = JSON.parsefile("scenarios/ghx_urbanopt.json")
s = Scenario(ghx_data)
ghx_inputs = REoptInputs(s)
m = Model(optimizer_with_attributes(HiGHS.Optimizer, "output_flag" => false, "log_to_console" => false, "mip_rel_gap" => 0.01))
results = run_reopt(m, ghx_inputs)
ghx_lcc = results["Financial"]["lcc"]
ghx_lccc = results["Financial"]["lifecycle_capital_costs"]
ghx_lccc_initial = results["Financial"]["initial_capital_costs"]
ghp_size = results["GHP"]["size_heat_pump_ton"]
boreholes = results["GHP"]["ghpghx_chosen_outputs"]["number_of_boreholes"]
boreholes_len = results["GHP"]["ghpghx_chosen_outputs"]["length_boreholes_ft"]

# Initial capital cost = initial cap cost of GHX
@test ghx_lccc_initial - boreholes*boreholes_len*14 0.0 atol = 0.01
# GHP size must be 0
@test ghp_size 0.0 atol = 0.01
# LCCC should be around be around 52% of initial capital cost due to incentive and bonus
@test ghx_lccc/ghx_lccc_initial 0.518 atol = 0.01
end

@testset "Hybrid GHX and GHP calculated costs validation" begin
Expand Down
Loading

0 comments on commit e36b314

Please sign in to comment.