From 1102d329826fe9ee58a7c2408e2a922cbbd465f4 Mon Sep 17 00:00:00 2001 From: Luca Bonaldo <39280783+lbonaldo@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:47:56 -0400 Subject: [PATCH] Add can_retire validation for multi-stage optimization (#683) --- CHANGELOG.md | 2 + src/case_runners/case_runner.jl | 4 ++ .../configure_multi_stage_inputs.jl | 34 +++++++++ test/test_multistage.jl | 70 +++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 199da72a7a..f9dc73862e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added versioned doc-pages for v0.3.6 and v0.4.0 - Add constraint in mga to compute total capacity in each zone from a given technology type (#681) - New settings parameter MGAAnnualGeneration to switch between different MGA formulations (#681) +- Add validation for `Can_Retire` column in multi-stage GenX since the current implementation + does not allow a resource to switch from can_retire = 0 to can_retire = 1 between stages. (#683) ### Fixed - Set MUST_RUN=1 for RealSystemExample/small_hydro plants (#517). diff --git a/src/case_runners/case_runner.jl b/src/case_runners/case_runner.jl index 13b30ac7b5..c96af53dbc 100644 --- a/src/case_runners/case_runner.jl +++ b/src/case_runners/case_runner.jl @@ -159,6 +159,10 @@ function run_genx_case_multistage!(case::AbstractString, mysetup::Dict, optimize model_dict[t] = generate_model(mysetup, inputs_dict[t], OPTIMIZER) end + # check that resources do not switch from can_retire = 0 to can_retire = 1 between stages + validate_can_retire_multistage( + inputs_dict, mysetup["MultiStageSettingsDict"]["NumStages"]) + ### Solve model println("Solving Model") diff --git a/src/multi_stage/configure_multi_stage_inputs.jl b/src/multi_stage/configure_multi_stage_inputs.jl index bbf4bb3431..d2f024d9a8 100644 --- a/src/multi_stage/configure_multi_stage_inputs.jl +++ b/src/multi_stage/configure_multi_stage_inputs.jl @@ -217,3 +217,37 @@ function configure_multi_stage_inputs(inputs_d::Dict, return inputs_d end + +@doc raw""" + validate_can_retire_multistage(inputs_dict::Dict, num_stages::Int) + +This function validates that all the resources do not switch from havig `can_retire = 0` to `can_retire = 1` during the multi-stage optimization. + +# Arguments +- `inputs_dict::Dict`: A dictionary containing the inputs for each stage. +- `num_stages::Int`: The number of stages in the multi-stage optimization. + +# Returns +- Throws an error if a resource switches from `can_retire = 0` to `can_retire = 1` between stages. +""" +function validate_can_retire_multistage(inputs_dict::Dict, num_stages::Int) + for stage in 2:num_stages # note: loop starts from 2 because we are comparing stage t with stage t-1 + can_retire_current = can_retire.(inputs_dict[stage]["RESOURCES"]) + can_retire_previous = can_retire.(inputs_dict[stage - 1]["RESOURCES"]) + + # Check if any resource switched from can_retire = 0 to can_retire = 1 between stage t-1 and t + if any(can_retire_current .- can_retire_previous .> 0) + # Find the resources that switched from can_retire = 0 to can_retire = 1 and throw an error + retire_switch_ids = findall(can_retire_current .- can_retire_previous .> 0) + resources_switched = inputs_dict[stage]["RESOURCES"][retire_switch_ids] + for resource in resources_switched + @warn "Resource `$(resource_name(resource))` with id = $(resource_id(resource)) switched " * + "from can_retire = 0 to can_retire = 1 between stages $(stage - 1) and $stage" + end + msg = "Current implementation of multi-stage optimization does not allow resources " * + "to switch from can_retire = 0 to can_retire = 1 between stages." + error(msg) + end + end + return nothing +end diff --git a/test/test_multistage.jl b/test/test_multistage.jl index 4215bca5fc..9798f3789b 100644 --- a/test/test_multistage.jl +++ b/test/test_multistage.jl @@ -185,4 +185,74 @@ end test_update_cumulative_min_ret!() +function test_can_retire_validation() + @testset "No resources switch from can_retire = 0 to can_retire = 1" begin + inputs = Dict{Int, Dict}() + inputs[1] = Dict("RESOURCES" => [ + GenX.Thermal(Dict(:resource => "thermal", :id => 1, + :can_retire => 1)), + GenX.Vre(Dict(:resource => "vre", :id => 2, + :can_retire => 1)), + GenX.Hydro(Dict(:resource => "hydro", :id => 3, + :can_retire => 1)), + GenX.FlexDemand(Dict(:resource => "flex_demand", :id => 4, + :can_retire => 1))]) + inputs[2] = Dict("RESOURCES" => [ + GenX.Thermal(Dict(:resource => "thermal", :id => 1, + :can_retire => 0)), + GenX.Vre(Dict(:resource => "vre", :id => 2, + :can_retire => 1)), + GenX.Hydro(Dict(:resource => "hydro", :id => 3, + :can_retire => 1)), + GenX.FlexDemand(Dict(:resource => "flex_demand", :id => 4, + :can_retire => 1))]) + inputs[3] = Dict("RESOURCES" => [ + GenX.Thermal(Dict(:resource => "thermal", :id => 1, + :can_retire => 0)), + GenX.Vre(Dict(:resource => "vre", :id => 2, + :can_retire => 0)), + GenX.Hydro(Dict(:resource => "hydro", :id => 3, + :can_retire => 1)), + GenX.FlexDemand(Dict(:resource => "flex_demand", :id => 4, + :can_retire => 1))]) + @test isnothing(GenX.validate_can_retire_multistage(inputs, 3)) + end + + @testset "One resource switches from can_retire = 0 to can_retire = 1" begin + inputs = Dict{Int, Dict}() + inputs[1] = Dict("RESOURCES" => [ + GenX.Thermal(Dict(:resource => "thermal", :id => 1, + :can_retire => 0)), + GenX.Vre(Dict(:resource => "vre", :id => 2, + :can_retire => 0)), + GenX.Hydro(Dict(:resource => "hydro", :id => 3, + :can_retire => 0)), + GenX.FlexDemand(Dict(:resource => "flex_demand", :id => 4, + :can_retire => 1))]) + inputs[2] = Dict("RESOURCES" => [ + GenX.Thermal(Dict(:resource => "thermal", :id => 1, + :can_retire => 0)), + GenX.Vre(Dict(:resource => "vre", :id => 2, + :can_retire => 0)), + GenX.Hydro(Dict(:resource => "hydro", :id => 3, + :can_retire => 1)), + GenX.FlexDemand(Dict(:resource => "flex_demand", :id => 4, + :can_retire => 1))]) + inputs[3] = Dict("RESOURCES" => [ + GenX.Thermal(Dict(:resource => "thermal", :id => 1, + :can_retire => 0)), + GenX.Vre(Dict(:resource => "vre", :id => 2, + :can_retire => 0)), + GenX.Hydro(Dict(:resource => "hydro", :id => 3, + :can_retire => 1)), + GenX.FlexDemand(Dict(:resource => "flex_demand", :id => 4, + :can_retire => 1))]) + @test_throws ErrorException GenX.validate_can_retire_multistage(inputs, 3) + end +end + +with_logger(ConsoleLogger(stderr, Logging.Error)) do + test_can_retire_validation() +end + end # module TestMultiStage