diff --git a/NEWS.md b/NEWS.md index 2f72ca8..5e68f50 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,11 @@ # Release Notes +## Version 0.9.1 (2026-04-16) + +### Bug fixes + +* Fixed a bug in the checks when using `StrategicProfile` for the stack replacement costs of `AbstractElectrolyzer` nodes and `TwoLevelTree` as time structure. + ## Version 0.9.0 (2026-04-14) ### Breaking changes diff --git a/Project.toml b/Project.toml index a7d41fb..bcbec81 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "EnergyModelsHydrogen" uuid = "44855f8b-b147-4985-ac18-48817d03c548" authors = ["Julian Straus , Avinash Subramanian"] -version = "0.9.0" +version = "0.9.1" [deps] EnergyModelsBase = "5d7e687e-f956-46f3-9045-6f5a5fd49f50" diff --git a/src/checks.jl b/src/checks.jl index 4d50c9c..c8d9423 100644 --- a/src/checks.jl +++ b/src/checks.jl @@ -21,7 +21,7 @@ additional check on the data. - The field `degradation_rate` is required to be in the range [0,1). - The `TimeProfile` of the field `stack_replacement` is required to be non-negative and accessible through a `StrategicPeriod` as outlined in the function - [`EMB.check_fixed_opex()`](@extref EnergyModelsBase.check_fixed_opex). + [`EMB.check_strategic_profile()`](@extref EnergyModelsBase.check_strategic_profile). - The field `stack_lifetime` is required to be non-negative. """ function EMB.check_node( @@ -39,13 +39,6 @@ function EMB.check_node( "The stack degradation rate must be in the range [0, 100)." ) - if isa(stack_replacement_cost(n), StrategicProfile) && check_timeprofiles - @assert_or_log( - length(stack_replacement_cost(n).vals) == length(𝒯ᴵⁿᵛ), - "The timeprofile provided for the field `stack_replacement_cost` does not " * - "match the strategic structure." - ) - end # Check for potential indexing problems message = "are not allowed for the field `stack_replacement_cost`." bool_sp = EMB.check_strategic_profile(stack_replacement_cost(n), message) diff --git a/test/test_checks.jl b/test/test_checks.jl index 873c46f..10ecd13 100644 --- a/test/test_checks.jl +++ b/test/test_checks.jl @@ -8,7 +8,7 @@ H2 = ResourceCarrier("Hydrogen", 0.0) CO2 = ResourceEmit("CO2", 1.0) # Function for setting up the system for testing an `AbstractElectrolyzer` node -function simple_graph_elec(; +function check_elec(; cap = FixedProfile(-25), # Installed capacity [MW] opex_var = FixedProfile(5), # Variable Opex opex_fixed = FixedProfile(100), # Fixed Opex @@ -16,8 +16,9 @@ function simple_graph_elec(; output = Dict(H2 => 0.62), # Ouput: Ratio of Output flow to characteristic throughput load_limits = LoadLimits(0, 1), # Minimum and maximum load degradation_rate = 0.1, # Degradation rate - stack_replacement_cost = FixedProfile(3e5), # Stack replacement costs + stack_rep_cost = FixedProfile(3e5), # Stack replacement costs stack_lifetime = 60000, # Stack lifetime in h + T = TwoLevel(2, 2, SimpleTimes(5, 2); op_per_strat=10), ) # Used source, network, and sink @@ -45,13 +46,11 @@ function simple_graph_elec(; ExtensionData[], load_limits, degradation_rate, - stack_replacement_cost, + stack_rep_cost, stack_lifetime ) resources = [Power, H2, CO2] - ops = SimpleTimes(5, 2) - T = TwoLevel(2, 2, ops; op_per_strat=10) nodes = [source, elec, sink] links = [ @@ -73,41 +72,55 @@ end @testset "Test checks - AbstractElectrolyzer" begin # Test that a wrong capacity is caught by the checks - @test_throws AssertionError simple_graph_elec(cap=FixedProfile(-25)) + @test_throws AssertionError check_elec(cap=FixedProfile(-25)) # Test that a wrong fixed OPEX is caught by the checks - @test_throws AssertionError simple_graph_elec(;opex_var=FixedProfile(5)) + @test_throws AssertionError check_elec(; opex_var=FixedProfile(5)) # Test that a wrong input dictionary is caught by the checks - @test_throws AssertionError simple_graph_elec(;input=Dict(Power => -1)) + @test_throws AssertionError check_elec(; input=Dict(Power => -1)) # Test that a wrong output dictionary is caught by the checks - @test_throws AssertionError simple_graph_elec(;output=Dict(H2 => -0.62)) + @test_throws AssertionError check_elec(; output=Dict(H2 => -0.62)) # Test that a wrong minimum load is caught by the checks - @test_throws AssertionError simple_graph_elec(;load_limits=LoadLimits(-0.5, 1.0)) + @test_throws AssertionError check_elec(; load_limits=LoadLimits(-0.5, 1.0)) # Test that a wrong maximum load is caught by the checks - @test_throws AssertionError simple_graph_elec(;load_limits=LoadLimits(1.5, 1.0)) + @test_throws AssertionError check_elec(; load_limits=LoadLimits(1.5, 1.0)) # Test that a wrong degradation rate load is caught by the checks - @test_throws AssertionError simple_graph_elec(;degradation_rate=-0.1) - @test_throws AssertionError simple_graph_elec(;degradation_rate=100) + @test_throws AssertionError check_elec(; degradation_rate=-0.1) + @test_throws AssertionError check_elec(; degradation_rate=100) # Test that a wrong stack replacement profile is caught by the checks - stack_replacement_cost = FixedProfile(-5) - @test_throws AssertionError simple_graph_elec(;stack_replacement_cost) - stack_replacement_cost = StrategicProfile([10]) - @test_throws AssertionError simple_graph_elec(;stack_replacement_cost) - stack_replacement_cost = OperationalProfile([10]) - @test_throws AssertionError simple_graph_elec(;stack_replacement_cost) + stack_rep_cost = FixedProfile(-5) + @test_throws AssertionError check_elec(; stack_rep_cost) + T = TwoLevelTree(2, [2], SimpleTimes(5, 1); op_per_strat=10.0) + oprofile = OperationalProfile(ones(4)) + profiles = [ + oprofile, + StrategicProfile([4]), + StrategicProfile([oprofile, oprofile, oprofile, oprofile]), + ] + for tp ∈ profiles + @test_throws AssertionError check_elec(; stack_rep_cost=tp) + @test_throws AssertionError check_elec(; stack_rep_cost=tp, T) + end + stack_rep_cost = StrategicStochasticProfile([[4]]) + @test_throws AssertionError check_elec(; stack_rep_cost, T) + + stack_rep_cost = StrategicProfile([10]) + @test_throws AssertionError check_elec(; stack_rep_cost) + stack_rep_cost = OperationalProfile([10]) + @test_throws AssertionError check_elec(; stack_rep_cost) # Test that a wrong lifetime is caught by the checks - @test_throws AssertionError simple_graph_elec(;stack_lifetime=-10) + @test_throws AssertionError check_elec(; stack_lifetime=-10) end # Function for setting up the system for testing a `Reformer` node -function simple_graph_ref(; +function check_ref(; cap = FixedProfile(-25), # Installed capacity [MW] opex_var = FixedProfile(5), # Variable Opex opex_fixed = FixedProfile(100), # Fixed Opex @@ -175,49 +188,49 @@ end @testset "Test checks - AbstractReformer" begin # Test that a wrong capacity is caught by the checks - @test_throws AssertionError simple_graph_ref(;cap=FixedProfile(-25)) + @test_throws AssertionError check_ref(; cap=FixedProfile(-25)) # Test that a wrong fixed OPEX is caught by the checks - @test_throws AssertionError simple_graph_ref(;opex_fixed=FixedProfile(-100)) + @test_throws AssertionError check_ref(; opex_fixed=FixedProfile(-100)) # Test that a wrong input dictionary is caught by the checks - @test_throws AssertionError simple_graph_ref(;input=Dict(NG => -1)) + @test_throws AssertionError check_ref(; input=Dict(NG => -1)) # Test that a wrong output dictionary is caught by the checks - @test_throws AssertionError simple_graph_ref(;output=Dict(H2 => -1.0)) + @test_throws AssertionError check_ref(; output=Dict(H2 => -1.0)) # Test that a wrong minimum load is caught by the checks - @test_throws AssertionError simple_graph_ref(;load_limits=LoadLimits(-0.5, 1.0)) + @test_throws AssertionError check_ref(; load_limits=LoadLimits(-0.5, 1.0)) # Test that a wrong maximum load is caught by the checks - @test_throws AssertionError simple_graph_ref(;load_limits=LoadLimits(1.5, 1.0)) + @test_throws AssertionError check_ref(; load_limits=LoadLimits(1.5, 1.0)) # Test that a wrong unit commitment times are caught by the checks commit_param = CommitParameters(FixedProfile(-1), FixedProfile(1)) - @test_throws AssertionError simple_graph_ref(;startup=commit_param) - @test_throws AssertionError simple_graph_ref(;shutdown=commit_param) - @test_throws AssertionError simple_graph_ref(;offline=commit_param) + @test_throws AssertionError check_ref(; startup=commit_param) + @test_throws AssertionError check_ref(; shutdown=commit_param) + @test_throws AssertionError check_ref(; offline=commit_param) # Test that a wrong profiles for minimum time of unit commitment are caught by the checks # - check_commitment_profile() startup = CommitParameters(FixedProfile(1), OperationalProfile([10])) - @test_throws AssertionError simple_graph_ref(;startup) + @test_throws AssertionError check_ref(; startup) startup = CommitParameters(FixedProfile(1), StrategicProfile([OperationalProfile([10])])) - @test_throws AssertionError simple_graph_ref(;startup) + @test_throws AssertionError check_ref(; startup) startup = CommitParameters(FixedProfile(1), FixedProfile(-5)) - @test_throws AssertionError simple_graph_ref(;startup) + @test_throws AssertionError check_ref(; startup) startup = CommitParameters(FixedProfile(1), StrategicProfile([-5])) - @test_throws AssertionError simple_graph_ref(;startup) + @test_throws AssertionError check_ref(; startup) startup = CommitParameters(FixedProfile(1), StrategicProfile([10, 10])) - @test_throws AssertionError simple_graph_ref(;startup) + @test_throws AssertionError check_ref(; startup) # Test that a wrong rate of change value is caught by the checks - @test_throws AssertionError simple_graph_ref(;rate_limit=RampBi(FixedProfile(-1))) - @test_throws AssertionError simple_graph_ref(;rate_limit=RampBi(FixedProfile(1.5))) + @test_throws AssertionError check_ref(; rate_limit=RampBi(FixedProfile(-1))) + @test_throws AssertionError check_ref(; rate_limit=RampBi(FixedProfile(1.5))) end # Function for setting up the system for testing a `SimpleHydrogenStorage` node -function simple_graph_simple_stor(; +function check_simple_stor(; charge_cap = FixedProfile(10), # Installed capacity [MW] level_cap = FixedProfile(1000), # Installed capacity [MWh] charge_opex_fixed = FixedProfile(5), # Fixed Opex @@ -279,29 +292,29 @@ end @testset "Test checks - SimpleHydrogenStorage" begin # Test that a wrong capacity is caught by the checks - @test_throws AssertionError simple_graph_simple_stor(;charge_cap=FixedProfile(-25)) - @test_throws AssertionError simple_graph_simple_stor(;level_cap=FixedProfile(-25)) + @test_throws AssertionError check_simple_stor(; charge_cap=FixedProfile(-25)) + @test_throws AssertionError check_simple_stor(; level_cap=FixedProfile(-25)) # Test that a wrong fixed OPEX is caught by the checks - @test_throws AssertionError simple_graph_simple_stor(;charge_opex_fixed=FixedProfile(-100)) - @test_throws AssertionError simple_graph_simple_stor(;level_opex_fixed=FixedProfile(-100)) + @test_throws AssertionError check_simple_stor(; charge_opex_fixed=FixedProfile(-100)) + @test_throws AssertionError check_simple_stor(; level_opex_fixed=FixedProfile(-100)) # Test that a wrong input dictionary is caught by the checks - @test_throws AssertionError simple_graph_simple_stor(;input=Dict(H2 => -1)) + @test_throws AssertionError check_simple_stor(; input=Dict(H2 => -1)) # Test that a wrong output dictionary is caught by the checks - @test_throws AssertionError simple_graph_simple_stor(;output=Dict(H2 => -1.0)) + @test_throws AssertionError check_simple_stor(; output=Dict(H2 => -1.0)) # Test that a wrong discharge to charge ratio is caught by the checks - @test_throws AssertionError simple_graph_simple_stor(;discharge_charge=-0.5) + @test_throws AssertionError check_simple_stor(; discharge_charge=-0.5) # Test that a wrong level to charge ratio is caught by the checks - @test_throws AssertionError simple_graph_simple_stor(;level_charge=-0.5) - @test_throws AssertionError simple_graph_simple_stor(;level_charge=1000.0) + @test_throws AssertionError check_simple_stor(; level_charge=-0.5) + @test_throws AssertionError check_simple_stor(; level_charge=1000.0) end # Function for setting up the system for testing a `HydrogenStorage` node -function simple_graph_h2_stor(; +function check_h2_stor(; charge_cap = FixedProfile(10), # Installed capacity [MW] level_cap = FixedProfile(1000), # Installed capacity [MWh] charge_opex_fixed = FixedProfile(5), # Fixed Opex @@ -366,34 +379,34 @@ end @testset "Test checks - HydrogenStorage" begin # Test that a wrong capacity is caught by the checks - @test_throws AssertionError simple_graph_h2_stor(;charge_cap=FixedProfile(-25)) - @test_throws AssertionError simple_graph_h2_stor(;level_cap=FixedProfile(-25)) + @test_throws AssertionError check_h2_stor(; charge_cap=FixedProfile(-25)) + @test_throws AssertionError check_h2_stor(; level_cap=FixedProfile(-25)) # Test that a wrong fixed OPEX is caught by the checks - @test_throws AssertionError simple_graph_h2_stor(;charge_opex_fixed=FixedProfile(-100)) - @test_throws AssertionError simple_graph_h2_stor(;level_opex_fixed=FixedProfile(-100)) + @test_throws AssertionError check_h2_stor(; charge_opex_fixed=FixedProfile(-100)) + @test_throws AssertionError check_h2_stor(; level_opex_fixed=FixedProfile(-100)) # Test that a wrong discharge to charge ratio is caught by the checks - @test_throws AssertionError simple_graph_h2_stor(;discharge_charge=-0.5) + @test_throws AssertionError check_h2_stor(; discharge_charge=-0.5) # Test that a wrong level to charge ratio is caught by the checks - @test_throws AssertionError simple_graph_h2_stor(;level_charge=-0.5) - @test_throws AssertionError simple_graph_h2_stor(;level_charge=1000.0) + @test_throws AssertionError check_h2_stor(; level_charge=-0.5) + @test_throws AssertionError check_h2_stor(; level_charge=1000.0) # Test that a wrong minimum pressure is caught by the checks - @test_throws AssertionError simple_graph_h2_stor(;p_min=-0.5) - @test_throws AssertionError simple_graph_h2_stor(;p_min=160.5) + @test_throws AssertionError check_h2_stor(; p_min=-0.5) + @test_throws AssertionError check_h2_stor(; p_min=160.5) # Test that a wrong minimum pressure is caught by the checks - @test_throws AssertionError simple_graph_h2_stor(;p_min=-0.5) - @test_throws AssertionError simple_graph_h2_stor(;p_min=160.5) + @test_throws AssertionError check_h2_stor(; p_min=-0.5) + @test_throws AssertionError check_h2_stor(; p_min=160.5) # Test that a wrong charge pressure is caught by the checks - @test_throws AssertionError simple_graph_h2_stor(;p_charge=-0.5) - @test_throws AssertionError simple_graph_h2_stor(;p_charge=160.5) + @test_throws AssertionError check_h2_stor(; p_charge=-0.5) + @test_throws AssertionError check_h2_stor(; p_charge=160.5) # Test that a wrong maximum pressure is caught by the checks - @test_throws AssertionError simple_graph_h2_stor(;p_max=-0.5) + @test_throws AssertionError check_h2_stor(; p_max=-0.5) end # Set the global again to false