Skip to content

Commit 795a502

Browse files
An PhamAn Pham
authored andcommitted
Merge branch 'ghp-select-hybrid-aux-unit' into test-hvac-costs-with-presized-ghp
2 parents 775b332 + bf6bb23 commit 795a502

4 files changed

Lines changed: 104 additions & 43 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ Classify the change according to the following categories:
7777
## v0.50.0
7878
### Added
7979
- New parameter `force_dispatch` in the **ASHPSpaceHeater** and **ASHPWaterHeater** technologies (default = `true`). When kept at `true`, the ASHP's thermal output will be the minimum of the site load(s) served and the system size (adjusted for timestep-specific capacity factor) in each period. If set to `false`, ASHP will do economic dispatch considering COP and CF along with electricity prices.
80+
- Allow the user to overwrite "Automatic" GHP hybrid sizing by specifying if the auxiliary unit is a "Heater" or "Cooler" through the `GHP.ghpghx_inputs.hybrid_ghx_sizing_method` variable.
8081
### Fixed
8182
- Align heating and cooling load profiles based on electric load year input, if using custom electric load profile with simulated (CRB or schedule-based flatloads) heating/cooling loads
8283
- Handling of leap years for `ElectricLoad.loads_kw` inputs to align with URDB rate structures

src/core/ghp.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ Base.@kwdef mutable struct GHP <: AbstractGHP
125125

126126
# Process and populate these parameters needed more directly by the model
127127
om_cost_year_one::Float64 = NaN
128+
test_hybrid_case::String = "" # automatic_guess_correct, flipped_guess, nonhybrid_solution
129+
number_of_boreholes_auto_guess::Float64 = -1.0
130+
number_of_boreholes_flipped_guess::Float64 = -1.0
131+
number_of_boreholes_nonhybrid::Float64 = -1.0
132+
133+
iterations_auto_guess::Float64 = -1.0
134+
iterations_flipped_guess::Float64 = -1.0
135+
iterations_nonhybrid::Float64 = -1.0
128136

129137
# Account for expenses avoided by addition of GHP.
130138
avoided_capex_by_ghp_present_value::Float64 = 0.0

src/core/scenario.jl

Lines changed: 85 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -561,33 +561,63 @@ function Scenario(d::Dict; flex_hvac_from_json=false)
561561

562562
if hybrid_ghx_sizing_method == "Automatic"
563563

564-
# Call GhpGhx.jl to size GHP and GHX
565-
determine_heat_cool_results_resp_dict = Dict()
564+
# Call GhpGhx.jl to size GHP and GHX in non-hybrid mode
565+
nonhybrid_results_resp_dict = Dict()
566566
try
567-
ghpghx_inputs["hybrid_auto_ghx_sizing_flag"] = true
568-
567+
ghpghx_inputs["hybrid_auto_ghx_sizing_flag"] = false
568+
ghpghx_inputs["hybrid_sizing_flag"] = 1.0
569569
# Call GhpGhx.jl to size GHP and GHX
570-
@info "Starting GhpGhx.jl for automatic hybrid GHX sizing"
570+
@info "Starting GhpGhx.jl for non-hybrid GHX sizing"
571571
# Call GhpGhx.jl to size GHP and GHX
572-
results, inputs_params = GhpGhx.ghp_model(ghpghx_inputs)
572+
results_nonhybrid, inputs_params_nonhybrid = GhpGhx.ghp_model(ghpghx_inputs)
573573
# Create a dictionary of the results data needed for REopt
574-
determine_heat_cool_results_resp_dict = GhpGhx.get_results_for_reopt(results, inputs_params)
575-
@info "Automatic hybrid GHX sizing complete using GhpGhx.jl"
574+
nonhybrid_results_resp_dict = GhpGhx.get_results_for_reopt(results_nonhybrid, inputs_params_nonhybrid)
575+
@info "Non-hybrid GHX sizing complete using GhpGhx.jl"
576576
catch e
577577
@info e
578578
throw(@error("The GhpGhx package was not added (add https://github.com/NREL/GhpGhx.jl) or
579579
loaded (using GhpGhx) to the active Julia environment"))
580580
end
581-
582-
temp_diff = determine_heat_cool_results_resp_dict["end_of_year_ghx_lft_f"][2] \
583-
- determine_heat_cool_results_resp_dict["end_of_year_ghx_lft_f"][1]
581+
d["GHP"]["number_of_boreholes_nonhybrid"] = nonhybrid_results_resp_dict["number_of_boreholes"]
582+
d["GHP"]["iterations_nonhybrid"] = nonhybrid_results_resp_dict["ghx_soln_number_of_iterations"]
583+
584+
# # Call GhpGhx.jl to size GHP and GHX
585+
# determine_heat_cool_results_resp_dict = Dict()
586+
# try
587+
# ghpghx_inputs["hybrid_auto_ghx_sizing_flag"] = true
588+
589+
# # Call GhpGhx.jl to size GHP and GHX
590+
# @info "Starting GhpGhx.jl for automatic hybrid GHX sizing"
591+
# # Call GhpGhx.jl to size GHP and GHX
592+
# results, inputs_params = GhpGhx.ghp_model(ghpghx_inputs)
593+
# # Create a dictionary of the results data needed for REopt
594+
# determine_heat_cool_results_resp_dict = GhpGhx.get_results_for_reopt(results, inputs_params)
595+
# @info "Automatic hybrid GHX sizing complete using GhpGhx.jl"
596+
# catch e
597+
# @info e
598+
# throw(@error("The GhpGhx package was not added (add https://github.com/NREL/GhpGhx.jl) or
599+
# loaded (using GhpGhx) to the active Julia environment"))
600+
# end
601+
602+
# temp_diff = determine_heat_cool_results_resp_dict["end_of_year_ghx_lft_f"][2] \
603+
# - determine_heat_cool_results_resp_dict["end_of_year_ghx_lft_f"][1]
604+
605+
# ORIGINAL GUESS
606+
temp_diff = nonhybrid_results_resp_dict["end_of_year_ghx_lft_f"][length(nonhybrid_results_resp_dict["end_of_year_ghx_lft_f"])] \
607+
- nonhybrid_results_resp_dict["end_of_year_ghx_lft_f"][1]
608+
609+
# FLIPPED GUESS
610+
# temp_diff = nonhybrid_results_resp_dict["end_of_year_ghx_lft_f"][1] - nonhybrid_results_resp_dict["end_of_year_ghx_lft_f"][length(nonhybrid_results_resp_dict["end_of_year_ghx_lft_f"])]
584611

585612
hybrid_sizing_flag = 1.0 # non hybrid
613+
hybrid_sizing_flag_opposite_guess = 1.0 # non hybrid
586614
if temp_diff > 0
587615
hybrid_sizing_flag = -2.0 #heating
616+
hybrid_sizing_flag_opposite_guess = -1.0 #cooling
588617
is_ghx_hybrid = true
589618
elseif temp_diff < 0
590619
hybrid_sizing_flag = -1.0 #cooling
620+
hybrid_sizing_flag_opposite_guess = -2.0 #heating
591621
is_ghx_hybrid = true
592622
else
593623
# non hybrid if exactly 0.
@@ -620,7 +650,7 @@ function Scenario(d::Dict; flex_hvac_from_json=false)
620650
end
621651

622652
ghpghx_results = Dict()
623-
653+
max_ghx_sizing_interations = 0
624654
try
625655
# Call GhpGhx.jl to size GHP and GHX
626656
@info "Starting GhpGhx.jl"
@@ -683,37 +713,6 @@ function Scenario(d::Dict; flex_hvac_from_json=false)
683713
end
684714
end
685715
results, inputs_params = GhpGhx.ghp_model(ghpghx_inputs)
686-
# If max_number_of_boreholes is specified, check if number of boreholes sized by GhpGhx.jl greater than user-specified max_number_of_boreholes,
687-
# and if max_number_of_boreholes is less, reduce thermal load served by GHP until max_number_of_boreholes = number of boreholses sized by GhpGhx.jl
688-
if haskey(d["GHP"],"max_number_of_boreholes")
689-
optimal_number_of_boreholes = GhpGhx.get_results_for_reopt(results, inputs_params)["number_of_boreholes"]
690-
if optimal_number_of_boreholes > d["GHP"]["max_number_of_boreholes"]
691-
@info "Max number of boreholes specified is less than number of boreholes sized in GhpGhx.jl, reducing thermal load served by GHP further"
692-
max_iter = 10
693-
for iter = 1:max_iter
694-
borehole_ratio = d["GHP"]["max_number_of_boreholes"]/optimal_number_of_boreholes
695-
heating_load_mmbtu .*= borehole_ratio
696-
if haskey(ghpghx_inputs,"cooling_thermal_load_ton")
697-
cooling_load_ton .*= borehole_ratio
698-
# if cooling load is not included, cut down heating load only and send to GhpGhx.jl
699-
end
700-
ghpghx_inputs["heating_thermal_load_mmbtu_per_hr"] = heating_load_mmbtu
701-
ghpghx_inputs["cooling_thermal_load_ton"] = cooling_load_ton
702-
703-
# Rerun GhpGhx.jl
704-
results, inputs_params = GhpGhx.ghp_model(ghpghx_inputs)
705-
optimal_number_of_boreholes = GhpGhx.get_results_for_reopt(results, inputs_params)["number_of_boreholes"]
706-
# Solution is found if the new optimal number of boreholes sized by GhpGhx.jl = user-specified max number of boreholes,
707-
# Otherwise, continue solving until reaching max iteration
708-
if -0.5 < optimal_number_of_boreholes-d["GHP"]["max_number_of_boreholes"] < 0.5
709-
break
710-
else
711-
iter += 1
712-
end
713-
end
714-
end
715-
end
716-
717716
# Create a dictionary of the results data needed for REopt
718717
ghpghx_results = GhpGhx.get_results_for_reopt(results, inputs_params)
719718
# Return results from GhpGhx.jl without load scaling if user does not provide GHP size or if user entered GHP size is greater than GHP size output
@@ -725,6 +724,49 @@ function Scenario(d::Dict; flex_hvac_from_json=false)
725724
to the GhpGhx.jl package."))
726725
end
727726

727+
if is_ghx_hybrid
728+
729+
d["GHP"]["number_of_boreholes_auto_guess"] = ghpghx_results["number_of_boreholes"]
730+
d["GHP"]["iterations_auto_guess"] = ghpghx_results["ghx_soln_number_of_iterations"]
731+
732+
# Invalid hybrid results
733+
if (ghpghx_results["number_of_boreholes"] >= nonhybrid_results_resp_dict["number_of_boreholes"]) || (ghpghx_results["ghx_soln_number_of_iterations"] == max_ghx_sizing_interations)
734+
new_hybrid_ghpghx_results = Dict()
735+
max_ghx_sizing_interations = 0
736+
try
737+
ghpghx_inputs["hybrid_sizing_flag"] = hybrid_sizing_flag_opposite_guess
738+
739+
# Call GhpGhx.jl to size GHP and GHX
740+
@info "Initial guess for hybrid GHP auxiliary unit was incorrect. Starting GhpGhx.jl for new hybrid GHX sizing"
741+
# Call GhpGhx.jl to size GHP and GHX
742+
results_new_hybrid, inputs_params_new_hybrid = GhpGhx.ghp_model(ghpghx_inputs)
743+
max_ghx_sizing_interations = inputs_params_new_hybrid.max_sizing_iterations
744+
# Create a dictionary of the results data needed for REopt
745+
new_hybrid_ghpghx_results = GhpGhx.get_results_for_reopt(results_new_hybrid, inputs_params_new_hybrid)
746+
@info "New hybrid GHX sizing complete using GhpGhx.jl"
747+
catch e
748+
@info e
749+
throw(@error("The GhpGhx package was not added (add https://github.com/NREL/GhpGhx.jl) or
750+
loaded (using GhpGhx) to the active Julia environment, or an error occurred during the call
751+
to the GhpGhx.jl package."))
752+
end
753+
d["GHP"]["number_of_boreholes_flipped_guess"] = new_hybrid_ghpghx_results["number_of_boreholes"]
754+
d["GHP"]["iterations_flipped_guess"] = new_hybrid_ghpghx_results["ghx_soln_number_of_iterations"]
755+
756+
if (new_hybrid_ghpghx_results["number_of_boreholes"] >= nonhybrid_results_resp_dict["number_of_boreholes"]) || (new_hybrid_ghpghx_results["ghx_soln_number_of_iterations"] == max_ghx_sizing_interations)
757+
@info "Hybrid GHX results are unavailable for this scenario. Returning non-hybrid results"
758+
is_ghx_hybrid = false
759+
ghpghx_results = nonhybrid_results_resp_dict
760+
d["GHP"]["test_hybrid_case"] = "nonhybrid_solution"
761+
else
762+
ghpghx_results = new_hybrid_ghpghx_results
763+
d["GHP"]["test_hybrid_case"] = "flipped_guess"
764+
end
765+
else
766+
d["GHP"]["test_hybrid_case"] = "automatic_guess_correct"
767+
end
768+
end
769+
728770
ghpghx_response = Dict([("inputs", ghpghx_inputs), ("outputs", ghpghx_results)])
729771
ghp_inputs_removed_ghpghx_params = deepcopy(d["GHP"])
730772
for param in ["ghpghx_inputs", "ghpghx_responses", "ghpghx_response_uuids"]

src/results/ghp.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ function add_ghp_results(m::JuMP.AbstractModel, p::REoptInputs, d::Dict; _n="")
3434
p.s.ghp_option_list[ghp_option_chosen].heatpump_capacity_sizing_factor_on_peak_load = 1.0
3535
end
3636
if ghp_option_chosen >= 1
37+
38+
r["test_hybrid_case"] = p.s.ghp_option_list[ghp_option_chosen].test_hybrid_case
39+
r["number_of_boreholes_nonhybrid"] = p.s.ghp_option_list[ghp_option_chosen].number_of_boreholes_nonhybrid
40+
r["number_of_boreholes_auto_guess"] = p.s.ghp_option_list[ghp_option_chosen].number_of_boreholes_auto_guess
41+
r["number_of_boreholes_flipped_guess"] = p.s.ghp_option_list[ghp_option_chosen].number_of_boreholes_flipped_guess
42+
43+
r["iterations_nonhybrid"] = p.s.ghp_option_list[ghp_option_chosen].iterations_nonhybrid
44+
r["iterations_auto_guess"] = p.s.ghp_option_list[ghp_option_chosen].iterations_auto_guess
45+
r["iterations_flipped_guess"] = p.s.ghp_option_list[ghp_option_chosen].iterations_flipped_guess
46+
3747
r["ghpghx_chosen_outputs"] = p.s.ghp_option_list[ghp_option_chosen].ghpghx_response["outputs"]
3848

3949
if r["ghpghx_chosen_outputs"]["heat_pump_configuration"] == "WSHP"

0 commit comments

Comments
 (0)