From bc3d1521f796ba3474016956ca5a9a43345f9ab2 Mon Sep 17 00:00:00 2001 From: lixiangk1 <72464565+lixiangk1@users.noreply.github.com> Date: Mon, 24 Jul 2023 20:56:50 -0600 Subject: [PATCH] Debug hydrogen components --- src/REopt.jl | 5 +++ src/constraints/hydrogen_constraints.jl | 6 +-- src/constraints/storage_constraints.jl | 23 ++++++++++ .../energy_storage/hydrogen_storage_HP.jl | 4 +- .../energy_storage/hydrogen_storage_LP.jl | 6 +-- src/core/energy_storage/storage.jl | 42 ++++++++++++------- src/core/reopt.jl | 29 ++++++++++--- src/core/scenario.jl | 16 ++++++- src/core/techs.jl | 6 +-- src/core/utils.jl | 2 +- 10 files changed, 106 insertions(+), 33 deletions(-) diff --git a/src/REopt.jl b/src/REopt.jl index 7adc058c4..76beed048 100644 --- a/src/REopt.jl +++ b/src/REopt.jl @@ -138,6 +138,10 @@ include("core/wind.jl") include("core/energy_storage/storage.jl") include("core/energy_storage/electric_storage.jl") include("core/energy_storage/thermal_storage.jl") +include("core/energy_storage/hydrogen_storage_LP.jl") +include("core/energy_storage/hydrogen_storage_HP.jl") +include("core/electrolyzer.jl") +include("core/compressor.jl") include("core/generator.jl") include("core/doe_commercial_reference_building_loads.jl") include("core/electric_load.jl") @@ -179,6 +183,7 @@ include("constraints/ghp_constraints.jl") include("constraints/steam_turbine_constraints.jl") include("constraints/renewable_energy_constraints.jl") include("constraints/emissions_constraints.jl") +include("constraints/hydrogen_constraints.jl") include("mpc/structs.jl") include("mpc/scenario.jl") diff --git a/src/constraints/hydrogen_constraints.jl b/src/constraints/hydrogen_constraints.jl index e472b27f0..db4c7c74a 100644 --- a/src/constraints/hydrogen_constraints.jl +++ b/src/constraints/hydrogen_constraints.jl @@ -40,7 +40,7 @@ function add_compressor_constraints(m, p; _n="") @constraint(m, [ts in p.time_steps], sum(p.production_factor[t, ts] * p.levelization_factor[t] * m[Symbol("dvRatedProduction"*_n)][t,ts] for t in p.techs.compressor) == - sum(m[Symbol("dvProductionToStorage"*_n)][b,ts] for b in p.s.storage.types.hydrogen_hp) + sum(m[Symbol("dvProductionToStorage"*_n)][b,t,ts] for b in p.s.storage.types.hydrogen_hp, t in p.techs.elec) ) @constraint(m, [ts in p.time_steps_with_grid], sum(p.s.compressor.efficiency_kwh_per_kg * p.production_factor[t, ts] * p.levelization_factor[t] * @@ -64,11 +64,11 @@ end function add_electrolyzer_constraints(m, p; _n="") ##Constraint: Compressor takes hydrogen from LP storage to charge HP storage while consuming electricity - if !isempty(t in p.techs.electrolyzer) + if !isempty(p.techs.electrolyzer) @constraint(m, [ts in p.time_steps], sum(p.production_factor[t, ts] * p.levelization_factor[t] * m[Symbol("dvRatedProduction"*_n)][t,ts] for t in p.techs.electrolyzer) == - sum(m[Symbol("dvProductionToStorage"*_n)][b,ts] for b in p.s.storage.types.hydrogen_lp) + sum(m[Symbol("dvProductionToStorage"*_n)][b,t,ts] for b in p.s.storage.types.hydrogen_lp, t in p.techs.elec) ) @constraint(m, [ts in p.time_steps_with_grid], sum(p.s.electrolyzer.efficiency_kwh_per_kg * p.production_factor[t, ts] * p.levelization_factor[t] * diff --git a/src/constraints/storage_constraints.jl b/src/constraints/storage_constraints.jl index 4044847a3..0f3e13f28 100644 --- a/src/constraints/storage_constraints.jl +++ b/src/constraints/storage_constraints.jl @@ -51,6 +51,29 @@ function add_storage_size_constraints(m, p, b; _n="") ) end +function add_hydrogen_storage_size_constraints(m, p, b; _n="") + # TODO add formal types for storage (i.e. "b") + + # Constraint (4b)-1: Lower bound on Storage Energy Capacity + @constraint(m, + m[Symbol("dvStorageEnergy"*_n)][b] >= p.s.storage.attr[b].min_kg + ) + + # Constraint (4b)-2: Upper bound on Storage Energy Capacity + @constraint(m, + m[Symbol("dvStorageEnergy"*_n)][b] <= p.s.storage.attr[b].max_kg + ) + + # # Constraint (4c)-1: Lower bound on Storage Power Capacity + # @constraint(m, + # m[Symbol("dvStoragePower"*_n)][b] >= p.s.storage.attr[b].min_kw + # ) + + # # Constraint (4c)-2: Upper bound on Storage Power Capacity + # @constraint(m, + # m[Symbol("dvStoragePower"*_n)][b] <= p.s.storage.attr[b].max_kw + # ) +end function add_general_storage_dispatch_constraints(m, p, b; _n="") # Constraint (4a): initial state of charge diff --git a/src/core/energy_storage/hydrogen_storage_HP.jl b/src/core/energy_storage/hydrogen_storage_HP.jl index 119ae6f00..68b9c8f7d 100644 --- a/src/core/energy_storage/hydrogen_storage_HP.jl +++ b/src/core/energy_storage/hydrogen_storage_HP.jl @@ -52,7 +52,7 @@ Base.@kwdef struct HydrogenStorageHPDefaults soc_min_fraction::Float64 = 0.05 soc_init_fraction::Float64 = 0.5 installed_cost_per_kg::Real = 1500.0 - replace_cost_per_kg:Real = 1000.0 + replace_cost_per_kg::Real = 1000.0 replacement_year::Int = 25 macrs_option_years::Int = 7 macrs_bonus_fraction::Float64 = 0.8 @@ -112,7 +112,7 @@ struct HydrogenStorageHP <: AbstractHydrogenStorage s.soc_min_fraction, s.soc_init_fraction, s.installed_cost_per_kg, - replace_cost_per_kg, + s.replace_cost_per_kg, s.replacement_year, s.macrs_option_years, s.macrs_bonus_fraction, diff --git a/src/core/energy_storage/hydrogen_storage_LP.jl b/src/core/energy_storage/hydrogen_storage_LP.jl index 08b948f33..ea771f74f 100644 --- a/src/core/energy_storage/hydrogen_storage_LP.jl +++ b/src/core/energy_storage/hydrogen_storage_LP.jl @@ -48,11 +48,11 @@ """ Base.@kwdef struct HydrogenStorageLPDefaults min_kg::Real = 0.0 - max_kg::Real = 1.0e9 + max_kg::Real = 1.0e6 soc_min_fraction::Float64 = 0.05 soc_init_fraction::Float64 = 0.5 installed_cost_per_kg::Real = 1500.0 - replace_cost_per_kg:Real = 1000.0 + replace_cost_per_kg::Real = 1000.0 replacement_year::Int = 25 macrs_option_years::Int = 7 macrs_bonus_fraction::Float64 = 0.8 @@ -112,7 +112,7 @@ struct HydrogenStorageLP <: AbstractHydrogenStorage s.soc_min_fraction, s.soc_init_fraction, s.installed_cost_per_kg, - replace_cost_per_kg, + s.replace_cost_per_kg, s.replacement_year, s.macrs_option_years, s.macrs_bonus_fraction, diff --git a/src/core/energy_storage/storage.jl b/src/core/energy_storage/storage.jl index 9e488bed6..b7fef4e64 100644 --- a/src/core/energy_storage/storage.jl +++ b/src/core/energy_storage/storage.jl @@ -59,6 +59,7 @@ mutable struct StorageTypes hydrogen::Vector{String} hydrogen_lp::Vector{String} hydrogen_hp::Vector{String} + nonhydrogen::Vector{String} function StorageTypes() new( @@ -81,16 +82,16 @@ mutable struct StorageTypes hydrogen_storage = String[] hydrogen_lp_storage = String[] hydrogen_hp_storage = String[] - + non_hydrogen_storage = String[] + for (k,v) in d - if v.max_kw > 0.0 && v.max_kwh > 0.0 - push!(all_storage, k) + if typeof(v) <: AbstractHydrogenStorage + + if v.max_kg > 0.0 - if typeof(v) <: AbstractElectricStorage - push!(elec_storage, k) + push!(all_storage, k) - elseif typeof(v) <: AbstractHydrogenStorage if occursin("LP", k) push!(hydrogen_lp_storage, k) elseif occursin("HP", k) @@ -99,14 +100,26 @@ mutable struct StorageTypes throw(@error("Hydrogen Storage not labeled as LP or HP.")) end - elseif typeof(v) <: ThermalStorage - if occursin("Hot", k) - push!(hot_storage, k) - elseif occursin("Cold", k) - push!(cold_storage, k) - else - throw(@error("Thermal Storage not labeled as Hot or Cold.")) + end + else + if v.max_kw > 0.0 && v.max_kwh > 0.0 + + push!(all_storage, k) + push!(non_hydrogen_storage, k) + + if typeof(v) <: AbstractElectricStorage + push!(elec_storage, k) + + elseif typeof(v) <: ThermalStorage + if occursin("Hot", k) + push!(hot_storage, k) + elseif occursin("Cold", k) + push!(cold_storage, k) + else + throw(@error("Thermal Storage not labeled as Hot or Cold.")) + end end + end end end @@ -122,7 +135,8 @@ mutable struct StorageTypes cold_storage, hydrogen_storage, hydrogen_lp_storage, - hydrogen_hp_storage + hydrogen_hp_storage, + non_hydrogen_storage ) end end diff --git a/src/core/reopt.jl b/src/core/reopt.jl index 195f0cdbc..2bfcc85ec 100644 --- a/src/core/reopt.jl +++ b/src/core/reopt.jl @@ -232,7 +232,25 @@ function build_reopt!(m::JuMP.AbstractModel, p::REoptInputs) end for b in p.s.storage.types.all - if p.s.storage.attr[b].max_kw == 0 || p.s.storage.attr[b].max_kwh == 0 + + if b in p.s.storage.types.hydrogen + if p.s.storage.attr[b].max_kg == 0 + @constraint(m, [ts in p.time_steps], m[:dvStoredEnergy][b, ts] == 0) + @constraint(m, m[:dvStorageEnergy][b] == 0) + @constraint(m, [ts in p.time_steps], m[:dvDischargeFromStorage][b, ts] == 0) + @constraint(m, [ts in p.time_steps], m[:dvGridToStorage][b, ts] == 0) + @constraint(m, [t in p.techs.elec, ts in p.time_steps_with_grid], + m[:dvProductionToStorage][b, t, ts] == 0) + else + add_hydrogen_storage_size_constraints(m, p, b) + add_general_storage_dispatch_constraints(m, p, b) + if b in p.s.storage.types.hydrogen_lp + add_lp_hydrogen_storage_dispatch_constraints(m, p, b) + elseif b in p.s.storage.types.hydrogen_hp + add_hp_hydrogen_storage_dispatch_constraints(m, p, b) + end + end + elseif p.s.storage.attr[b].max_kw == 0 || p.s.storage.attr[b].max_kwh == 0 @constraint(m, [ts in p.time_steps], m[:dvStoredEnergy][b, ts] == 0) @constraint(m, m[:dvStorageEnergy][b] == 0) @constraint(m, [ts in p.time_steps], m[:dvDischargeFromStorage][b, ts] == 0) @@ -251,10 +269,6 @@ function build_reopt!(m::JuMP.AbstractModel, p::REoptInputs) add_hot_thermal_storage_dispatch_constraints(m, p, b) elseif b in p.s.storage.types.cold add_cold_thermal_storage_dispatch_constraints(m, p, b) - elseif b in p.s.storage.types.hydrogen_lp - add_lp_hydrogen_storage_dispatch_constraints(m, p, b) - elseif b in p.s.storage.types.hydrogen_hp - add_hp_hydrogen_storage_dispatch_constraints(m, p, b) else throw(@error("Invalid storage does not fall in a thermal or electrical set")) end @@ -263,6 +277,8 @@ function build_reopt!(m::JuMP.AbstractModel, p::REoptInputs) if any(max_kw->max_kw > 0, (p.s.storage.attr[b].max_kw for b in p.s.storage.types.elec)) add_storage_sum_constraints(m, p) + elseif any(max_kg->max_kg > 0, (p.s.storage.attr[b].max_kg for b in p.s.storage.types.hydrogen)) + add_storage_sum_constraints(m, p) end add_production_constraints(m, p) @@ -394,7 +410,8 @@ function build_reopt!(m::JuMP.AbstractModel, p::REoptInputs) @expression(m, TotalStorageCapCosts, p.third_party_factor * ( sum( p.s.storage.attr[b].net_present_cost_per_kw * m[:dvStoragePower][b] for b in p.s.storage.types.elec) + - sum( p.s.storage.attr[b].net_present_cost_per_kwh * m[:dvStorageEnergy][b] for b in p.s.storage.types.all ) + sum( p.s.storage.attr[b].net_present_cost_per_kwh * m[:dvStorageEnergy][b] for b in p.s.storage.types.nonhydrogen) + + sum( p.s.storage.attr[b].net_present_cost_per_kg * m[:dvStorageEnergy][b] for b in p.s.storage.types.hydrogen) )) @expression(m, TotalPerUnitSizeOMCosts, p.third_party_factor * p.pwf_om * diff --git a/src/core/scenario.jl b/src/core/scenario.jl index 914885d79..70765acec 100644 --- a/src/core/scenario.jl +++ b/src/core/scenario.jl @@ -52,6 +52,8 @@ struct Scenario <: AbstractScenario space_heating_thermal_load_reduction_with_ghp_kw::Union{Vector{Float64}, Nothing} cooling_thermal_load_reduction_with_ghp_kw::Union{Vector{Float64}, Nothing} steam_turbine::Union{SteamTurbine, Nothing} + electrolyzer::Union{Electrolyzer, Nothing} + compressor::Union{Compressor, Nothing} end """ @@ -237,6 +239,16 @@ function Scenario(d::Dict; flex_hvac_from_json=false) ) end + electrolyzer = nothing + compressor = nothing + if haskey(d, "Electrolyzer") + electrolyzer = Electrolyzer(; dictkeys_tosymbols(d["Electrolyzer"])...) + end + + if haskey(d, "Compressor") + compressor = Compressor(; dictkeys_tosymbols(d["Compressor"])...) + end + max_heat_demand_kw = 0.0 if haskey(d, "DomesticHotWaterLoad") && !haskey(d, "FlexibleHVAC") add_doe_reference_names_from_elec_to_thermal_loads(d["ElectricLoad"], d["DomesticHotWaterLoad"]) @@ -596,7 +608,9 @@ function Scenario(d::Dict; flex_hvac_from_json=false) ghp_option_list, space_heating_thermal_load_reduction_with_ghp_kw, cooling_thermal_load_reduction_with_ghp_kw, - steam_turbine + steam_turbine, + electrolyzer, + compressor ) end diff --git a/src/core/techs.jl b/src/core/techs.jl index 410526250..8b9326b12 100644 --- a/src/core/techs.jl +++ b/src/core/techs.jl @@ -207,14 +207,14 @@ function Techs(s::Scenario) thermal_techs = union(heating_techs, boiler_techs, chp_techs, cooling_techs) fuel_burning_techs = union(gentechs, boiler_techs, chp_techs) - if s.electrolyzer.max_kw > 0 + if !isnothing(s.electrolyzer) push!(all_techs, "Electrolyzer") push!(electrolyzer_techs, "Electrolyzer") end - if s.compressor.max_kw > 0 + if !isnothing(s.compressor) push!(all_techs, "Compressor") - push!(electrolyzer_techs, "Compressor") + push!(compressor_techs, "Compressor") end Techs( diff --git a/src/core/utils.jl b/src/core/utils.jl index 067cd9852..dfd8ace56 100644 --- a/src/core/utils.jl +++ b/src/core/utils.jl @@ -168,7 +168,7 @@ function dictkeys_tosymbols(d::Dict) for (k, v) in d # handling array type conversions for API inputs and JSON if k in [ - "loads_kw", "critical_loads_kw", + "loads_kw", "critical_loads_kw", "loads_kg", "thermal_loads_ton", "fuel_loads_mmbtu_per_hour", "monthly_totals_kwh",