diff --git a/CHANGELOG.md b/CHANGELOG.md index a2ccab445..f5e43a24e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,13 @@ Classify the change according to the following categories: ### Deprecated ### Removed -## v. 0.46.0 +## v0.46.1 +### 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 - Added new variables **dvHeatToStorage** and **dvHeatFromStorage** which are indexed on `p.heating_loads` and added reconciliation constraints so that **dvProductionToStorage** and **dvDischargeFromStorage** maintain their relationship to state of charge for Hot thermal energy storage. diff --git a/Project.toml b/Project.toml index cdf6edfce..ae911db36 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "REopt" uuid = "d36ad4e8-d74a-4f7a-ace1-eaea049febf6" authors = ["Nick Laws", "Hallie Dunham ", "Bill Becker ", "Bhavesh Rathod ", "Alex Zolan ", "Amanda Farthing "] -version = "0.46.0" +version = "0.46.1" [deps] ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3" 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