diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fd1c7dd..0b8a2ad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - none +## v3.6.0 + +- Added capability to evaluate the optimality of a given partition against a set of load scenarios to judge robustness + ## v3.5.0 - Added support for individually shedable loads to block LinDistFlow formulation diff --git a/Project.toml b/Project.toml index 90e65490..b119829a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PowerModelsONM" uuid = "25264005-a304-4053-a338-565045d392ac" authors = ["David M Fobes "] -version = "3.5.0" +version = "3.6.0" [deps] ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" diff --git a/src/PowerModelsONM.jl b/src/PowerModelsONM.jl index 77c067ca..e966a98c 100644 --- a/src/PowerModelsONM.jl +++ b/src/PowerModelsONM.jl @@ -114,6 +114,7 @@ module PowerModelsONM include("prob/mld_block.jl") include("prob/partitions.jl") include("prob/mld_block_robust.jl") + include("prob/robust_evaluation.jl") include("prob/stability.jl") include("prob/switch.jl") diff --git a/src/prob/robust_evaluation.jl b/src/prob/robust_evaluation.jl new file mode 100644 index 00000000..560f37e6 --- /dev/null +++ b/src/prob/robust_evaluation.jl @@ -0,0 +1,63 @@ +""" + evaluate_partition_optimality( + data, + load_scenarios, + model_type, + solver; + save_partial_results, + partial_result_folder, + time_elapsed, + kwargs... + ) + +Function to evaluate the optimality of a specific partition by considering a collection of load scenarios. + +`data` has the partition configuration applied. +""" +function evaluate_partition_optimality( + data::Dict{String,<:Any}, + load_scenarios, + model_type::Type, + solver; + save_partial_results::Bool=false, + partial_result_folder::String=".", + time_elapsed::Float64 = missing, + kwargs...) + + _results = Dict{String,Any}() + + for ls in keys(load_scenarios) + single_load_scenario = Dict{String,Dict{String,Any}}() + single_load_scenario["1"] = load_scenarios[ls] + + eng = deepcopy(data) + + if !ismissing(time_elapsed) + eng["time_elapsed"] = time_elapsed + end + + @debug "starting load scenario evaluation $(ls)/$(length(load_scenarios))" + + result = solve_robust_block_mld(eng, model_type, solver, single_load_scenario; kwargs...) + + if save_partial_results + open("$(partial_result_folder)/result_$(ls).json", "w") do io + JSON.print(io, result) + end + end + + _results[ls] = result + end + + return _results +end + + +""" +retrieve_load_scenario_optimality(results::Dict) + +Returns a Dict of objectives for the different load scenarios considered in the robust partition evaluation. +""" +function retrieve_load_scenario_optimality(results::Dict)::Dict{String,Float64} + return Dict{String,Float64}("$i" => results["$i"]["1"]["objective"] for i in 1:length(results)) +end diff --git a/test/robust_eval.jl b/test/robust_eval.jl new file mode 100644 index 00000000..d27c3d65 --- /dev/null +++ b/test/robust_eval.jl @@ -0,0 +1,43 @@ +function randomize_partition_config( + case, + num_closed_switches) + + if num_closed_switches > length(keys(case["switch"])) + error("Number of closed switches exceeds the total number of switches") + end + + part_config = Dict{String,Any}() + + switch_keys = collect(keys(case["switch"])) + shuffled_keys = shuffle(switch_keys) + closed_switches = shuffled_keys[1:num_closed_switches] + + # Set the status of the selected switches to "CLOSED" and the rest to "OPEN" + for key in switch_keys + if key in closed_switches + part_config[key] = PMD.SwitchState(1) + else + part_config[key] = PMD.SwitchState(0) + end + end + + case["fixed_partition_config"] = part_config + + return case +end + + +@testset "robust partition evaluation" begin + case = ONM.parse_file("test/data/ieee13_feeder.dss") + case = randomize_partition_config(case, 2) + + num_load_scenarios = 20 + uncertainty_val = 0.2 + ls = ONM.generate_load_scenarios(case, num_load_scenarios, uncertainty_val) + + solver = ONM.optimizer_with_attributes(HiGHS.Optimizer, "primal_feasibility_tolerance" => 1e-6, "dual_feasibility_tolerance" => 1e-6, "small_matrix_value" => 1e-12, "allow_unbounded_or_infeasible" => true) + + results_eval_optimality = ONM.evaluate_partition_optimality(case, ls, PMD.LPUBFDiagPowerModel, solver) + + optimality = ONM.retrieve_load_scenario_optimality(results_eval_optimality) +end diff --git a/test/runtests.jl b/test/runtests.jl index f3d176fe..c15d91d6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -59,6 +59,8 @@ silence!() # problems @info "Running tests in mld.jl" include("mld.jl") + @info "Running tests in robust_eval.jl" + include("robust_eval.jl") @info "Running tests in nlp.jl" include("nlp.jl") @info "Running tests in opf.jl"