diff --git a/CHANGELOG.md b/CHANGELOG.md index a2ccab445..61bfef7b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,12 @@ Classify the change according to the following categories: ### Deprecated ### Removed +## Develop 2024-05-09 +### Changed +- Updated the GHP testset .json `./test/scenarios/ghp_inputs.json` to include a nominal HotThermalStorage and ColdThermalStorage system. +### Fixed +- Fixed a bug in which the model fails to build when both GHP and either Hot or Cold Thermal Storage are present. + ## v. 0.46.0 ### Added - In `src/core/absorption_chiller.jl` struct, added field **heating_load_input** to the AbsorptionChiller struct diff --git a/src/constraints/storage_constraints.jl b/src/constraints/storage_constraints.jl index 12af6c5ef..782076dd4 100644 --- a/src/constraints/storage_constraints.jl +++ b/src/constraints/storage_constraints.jl @@ -146,7 +146,7 @@ function add_hot_thermal_storage_dispatch_constraints(m, p, b; _n="") if !isempty(p.techs.chp) if !isempty(p.techs.steam_turbine) && p.s.chp.can_supply_steam_turbine @constraint(m, CHPTechProductionFlowCon[b in p.s.storage.types.hot, t in p.techs.chp, q in p.heating_loads, ts in p.time_steps], - m[Symbol("dvHeatToStorage"*_n)][b,tq,ts] + m[Symbol("dvProductionToWaste"*_n)][t,q,ts] + m[Symbol("dvThermalToSteamTurbine"*_n)][t,q,ts] <= + m[Symbol("dvHeatToStorage"*_n)][b,t,q,ts] + m[Symbol("dvProductionToWaste"*_n)][t,q,ts] + m[Symbol("dvThermalToSteamTurbine"*_n)][t,q,ts] <= m[Symbol("dvHeatingProduction"*_n)][t,q,ts] ) else @@ -181,6 +181,13 @@ function add_hot_thermal_storage_dispatch_constraints(m, p, b; _n="") sum(m[Symbol("dvHeatFromStorage"*_n)][b,q,ts] for q in p.heating_loads) ) + #Do not allow GHP to charge storage + if !isempty(p.techs.ghp) + for b in p.s.storage.types.hot, t in p.techs.ghp, q in p.heating_loads, ts in p.time_steps + fix(m[Symbol("dvHeatToStorage"*_n)][b,t,q,ts], 0.0, force=true) + end + end + end function add_cold_thermal_storage_dispatch_constraints(m, p, b; _n="") @@ -207,6 +214,13 @@ function add_cold_thermal_storage_dispatch_constraints(m, p, b; _n="") m[Symbol("dvStoragePower"*_n)][b] >= m[Symbol("dvDischargeFromStorage"*_n)][b,ts] + sum(m[Symbol("dvProductionToStorage"*_n)][b,t,ts] for t in p.techs.cooling) ) + + #Do not allow GHP to charge storage + if !isempty(p.techs.ghp) + for b in p.s.storage.types.cold, t in p.techs.ghp, ts in p.time_steps + fix(m[Symbol("dvProductionToStorage"*_n)][b,t,ts], 0.0, force=true) + end + end end function add_storage_sum_constraints(m, p; _n="") diff --git a/src/core/reopt.jl b/src/core/reopt.jl index 7baefaed3..e3a821a46 100644 --- a/src/core/reopt.jl +++ b/src/core/reopt.jl @@ -216,19 +216,19 @@ function build_reopt!(m::JuMP.AbstractModel, p::REoptInputs) elseif b in p.s.storage.types.hot @constraint(m, [q in q in setdiff(p.heating_loads, p.heating_loads_served_by_tes[b]), ts in p.time_steps], m[:dvHeatFromStorage][b,q,ts] == 0) if "DomesticHotWater" in p.heating_loads_served_by_tes[b] - @constraint(m, [t in setdiff(p.heating_techs, p.techs_can_serve_dhw), ts in p.time_steps], m[:dvHeatToStorage][b,"DomesticHotWater",ts] == 0) + @constraint(m, [t in setdiff(p.heating_techs, p.techs_can_serve_dhw), ts in p.time_steps], m[:dvHeatToStorage][b,t,"DomesticHotWater",ts] == 0) else - @constraint(m, [t in p.heating_techs, ts in p.time_steps], m[:dvHeatToStorage][b,"DomesticHotWater",ts] == 0) + @constraint(m, [t in p.heating_techs, ts in p.time_steps], m[:dvHeatToStorage][b,t,"DomesticHotWater",ts] == 0) end if "SpaceHeating" in p.heating_loads_served_by_tes[b] - @constraint(m, [t in setdiff(p.heating_techs, p.techs_can_serve_space_heating), ts in p.time_steps], m[:dvHeatToStorage][b,"SpaceHeating",ts] == 0) + @constraint(m, [t in setdiff(p.heating_techs, p.techs_can_serve_space_heating), ts in p.time_steps], m[:dvHeatToStorage][b,t,"SpaceHeating",ts] == 0) else - @constraint(m, [t in p.heating_techs, ts in p.time_steps], m[:dvHeatToStorage][b,"SpaceHeating",ts] == 0) + @constraint(m, [t in p.heating_techs, ts in p.time_steps], m[:dvHeatToStorage][b,t,"SpaceHeating",ts] == 0) end if "ProcessHeat" in p.heating_loads_served_by_tes[b] - @constraint(m, [t in setdiff(p.heating_techs, p.techs_can_serve_process_heat), ts in p.time_steps], m[:dvHeatToStorage][b,"ProcessHeat",ts] == 0) + @constraint(m, [t in setdiff(p.heating_techs, p.techs_can_serve_process_heat), ts in p.time_steps], m[:dvHeatToStorage][b,t,"ProcessHeat",ts] == 0) else - @constraint(m, [t in p.heating_techs, ts in p.time_steps], m[:dvHeatToStorage][b,"ProcessHeat",ts] == 0) + @constraint(m, [t in p.heating_techs, ts in p.time_steps], m[:dvHeatToStorage][b,t,"ProcessHeat",ts] == 0) end end else @@ -584,7 +584,7 @@ function add_variables!(m::JuMP.AbstractModel, p::REoptInputs) dvGridPurchase[p.time_steps, 1:p.s.electric_tariff.n_energy_tiers] >= 0 # Power from grid dispatched to meet electrical load [kW] dvRatedProduction[p.techs.all, p.time_steps] >= 0 # Rated production of technology t [kW] dvCurtail[p.techs.all, p.time_steps] >= 0 # [kW] - dvProductionToStorage[p.s.storage.types.all, p.techs.all, p.time_steps] >= 0 # Power from technology t used to charge storage system b [kW] + dvProductionToStorage[p.s.storage.types.all, union(p.techs.ghp,p.techs.all), p.time_steps] >= 0 # Power from technology t used to charge storage system b [kW] dvDischargeFromStorage[p.s.storage.types.all, p.time_steps] >= 0 # Power discharged from storage system b [kW] dvGridToStorage[p.s.storage.types.elec, p.time_steps] >= 0 # Electrical power delivered to storage by the grid [kW] dvStoredEnergy[p.s.storage.types.all, 0:p.time_steps[end]] >= 0 # State of charge of storage system b diff --git a/src/core/steam_turbine.jl b/src/core/steam_turbine.jl index fc7b951de..ca114888f 100644 --- a/src/core/steam_turbine.jl +++ b/src/core/steam_turbine.jl @@ -25,6 +25,9 @@ can_wholesale::Bool = false can_export_beyond_nem_limit::Bool = false can_curtail::Bool = false + can_serve_dhw::Bool = true + can_serve_space_heating::Bool = true + can_serve_process_heat::Bool = true macrs_option_years::Int = 0 macrs_bonus_fraction::Float64 = 0.0 diff --git a/test/scenarios/ghp_inputs.json b/test/scenarios/ghp_inputs.json index 46dfdd472..43b816527 100644 --- a/test/scenarios/ghp_inputs.json +++ b/test/scenarios/ghp_inputs.json @@ -55,5 +55,15 @@ "ElectricStorage": { "max_kw": 0.0, "max_kwh": 0.0 + }, + "ColdThermalStorage": { + "min_gal": 10, + "max_gal": 10, + "thermal_decay_rate_fraction": 0.0 + }, + "HotThermalStorage": { + "min_gal": 10, + "max_gal": 10, + "thermal_decay_rate_fraction": 0.0 } } \ No newline at end of file