diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a18c001a..4b254dee7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: version: - - '1.0' + - '1.6' - '1' - 'nightly' os: diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a7b2b0fe..7c1a4068e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ PowerModels.jl Change Log ### Staged - nothing +### v0.19.3 +- Add support for JuMP v0.23 +- Update minimum Julia requirement v1.6 (LTS) +- Replace CBC with HiGHS in tests +- Updates for SCS v0.9 + ### v0.19.2 - Rename `run_*` methods to `solve_*` and add depreciation warnings (#649) - Made case name recovery optional in PTI parsing diff --git a/Project.toml b/Project.toml index ff914df80..f11b7d834 100644 --- a/Project.toml +++ b/Project.toml @@ -2,7 +2,7 @@ name = "PowerModels" uuid = "c36e90e8-916a-50a6-bd94-075b64ef4655" authors = ["Carleton Coffrin"] repo = "https://github.com/lanl-ansi/PowerModels.jl" -version = "0.19.2" +version = "0.19.3" [deps] InfrastructureModels = "2030c09a-7f63-5d83-885d-db604e0e9cc0" @@ -14,23 +14,23 @@ NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [compat] -Cbc = ">= 0.9" +HiGHS = "~0.3, ~1" InfrastructureModels = "~0.6, ~0.7" -Ipopt = ">= 0.8" +Ipopt = "~0.8, ~0.9, ~1" JSON = "~0.18, ~0.19, ~0.20, ~0.21" -JuMP = "~0.22" -Juniper = ">= 0.8" +JuMP = "~0.22, ~0.23" +Juniper = "~0.8, ~0.9" Memento = "~1.0, ~1.1, ~1.2, ~1.3" NLsolve = "4.0" -SCS = "~0.8" -julia = "^1" +SCS = "~0.9, ~1" +julia = "1.6" [extras] -Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76" +HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b" Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9" Juniper = "2ddba703-00a4-53a7-87a5-e8b9971dde84" SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "Ipopt", "SCS", "Cbc", "Juniper"] +test = ["Test", "Ipopt", "SCS", "HiGHS", "Juniper"] diff --git a/test/docs.jl b/test/docs.jl index da510fb2c..9e4c1810b 100644 --- a/test/docs.jl +++ b/test/docs.jl @@ -30,7 +30,7 @@ #pretty print the model to the terminal #print(pm.model) - @test JuMP.num_nl_constraints(pm.model) == 12 + @test num_nonlinear_constraints(pm.model) == 12 @test JuMP.num_variables(pm.model) == 28 result = optimize_model!(pm, optimizer=JuMP.optimizer_with_attributes(Ipopt.Optimizer, "print_level"=>0)) diff --git a/test/model.jl b/test/model.jl index 74a563bfd..bddd3e5ee 100644 --- a/test/model.jl +++ b/test/model.jl @@ -15,7 +15,7 @@ x = JuMP.@variable(m, my_var >= 0, start=0.0) pm = instantiate_model("../test/data/matpower/case5.m", ACPPowerModel, PowerModels.build_opf, jump_model=m) - @test JuMP.num_nl_constraints(pm.model) == 28 + @test num_nonlinear_constraints(pm.model) == 28 @test JuMP.num_variables(pm.model) == 49 @test pm.model[:my_var] == x @@ -36,11 +36,11 @@ end end @testset "optimizer_with_attributes and LP status" begin - result = run_opf("../test/data/matpower/case5.m", DCPPowerModel, optimizer_with_attributes(Cbc.Optimizer, "logLevel"=>0)) + result = run_opf("../test/data/matpower/case5.m", DCPPowerModel, optimizer_with_attributes(HiGHS.Optimizer, "output_flag"=>false)) @test result["termination_status"] == OPTIMAL @test result["primal_status"] == FEASIBLE_POINT - @test result["dual_status"] == NO_SOLUTION + @test result["dual_status"] == FEASIBLE_POINT @test isapprox(result["objective"], 17613.2; atol = 1e0) end end diff --git a/test/multinetwork.jl b/test/multinetwork.jl index 171152d75..0a6472833 100644 --- a/test/multinetwork.jl +++ b/test/multinetwork.jl @@ -206,7 +206,8 @@ TESTLOG = Memento.getlogger(PowerModels) @testset "test sdp with constraint decomposition opf" begin result = PowerModels.run_mn_opf(mn_data, SparseSDPWRMPowerModel, scs_solver) - @test result["termination_status"] == OPTIMAL + # hits iteration limit in SCS v0.9, sense ALMOST_OPTIMAL + @test result["termination_status"] == ALMOST_OPTIMAL # tolerance relaxed for cross platform compat. @test isapprox(result["objective"], 33321.9; atol = 1e2) @test isapprox( diff --git a/test/opf-var.jl b/test/opf-var.jl index 49a911acb..136042b3e 100644 --- a/test/opf-var.jl +++ b/test/opf-var.jl @@ -127,24 +127,24 @@ end result = PowerModels._solve_opf_cl(data, SDPWRMPowerModel, scs_solver) @test result["termination_status"] == OPTIMAL - @test isapprox(result["objective"], 5747.32; atol = 1e0) + @test isapprox(result["objective"], 5728.62; atol = 1e0) end - #@testset "5-bus case" begin - # data = build_current_data("../test/data/matpower/case5.m") - # result = PowerModels._solve_opf_cl(data, SDPWRMPowerModel, scs_solver) - - # @test result["termination_status"] == OPTIMAL - # @test isapprox(result["objective"], 15418.4; atol = 1e0) - #end + @testset "5-bus case" begin + data = build_current_data("../test/data/matpower/case5.m") + result = PowerModels._solve_opf_cl(data, SDPWRMPowerModel, scs_solver) - # too slow of unit tests - # @testset "14-bus case" begin - # data = build_current_data("../test/data/matpower/case14.m") - # result = PowerModels._solve_opf_cl(data, SDPWRMPowerModel, scs_solver) + @test result["termination_status"] == OPTIMAL + #@test isapprox(result["objective"], 15418.4; atol = 1e0) + # relaxed for cross platform compat with SCS v1.0.1 + @test isapprox(result["objective"], 15402.05; atol = 2e1) + end + @testset "14-bus case" begin + data = build_current_data("../test/data/matpower/case14.m") + result = PowerModels._solve_opf_cl(data, SDPWRMPowerModel, scs_solver) - # @test result["termination_status"] == OPTIMAL - # @test isapprox(result["objective"], 8081.52; atol = 1e0) - # end + @test result["termination_status"] == OPTIMAL + @test isapprox(result["objective"], 7505.33; atol = 1e0) + end end end @@ -293,7 +293,7 @@ end @test result["termination_status"] == OPTIMAL @test isapprox(result["objective"], 10.0; atol = 1e-2) @test isapprox(active_power_served(result), 10.0; atol = 1e-2) - @test all_loads_on(result; atol=1e-4) + @test all_loads_on(result, atol=5e-3) @test all_shunts_on(result) end @testset "14-bus case" begin @@ -302,8 +302,8 @@ end @test result["termination_status"] == OPTIMAL @test isapprox(result["objective"], 3.59; atol = 1e-2) @test isapprox(active_power_served(result), 2.59; atol = 1e-2) - @test all_loads_on(result; atol=1e-4) - @test all_shunts_on(result; atol=1e-4) + @test all_loads_on(result, atol=1e-4) + @test all_shunts_on(result, atol=5e-3) end end @@ -314,7 +314,7 @@ end @test result["termination_status"] == OPTIMAL @test isapprox(result["objective"], 10.0; atol = 1e-2) @test isapprox(active_power_served(result), 10.0; atol = 1e-2) - @test all_loads_on(result; atol=1e-4) + @test all_loads_on(result, atol=5e-3) @test all_shunts_on(result) end @testset "14-bus case" begin @@ -324,7 +324,7 @@ end @test isapprox(result["objective"], 3.59; atol = 1e-2) @test isapprox(active_power_served(result), 2.59; atol = 1e-2) @test all_loads_on(result, atol=1e-4) - @test all_shunts_on(result, atol=1e-4) + @test all_shunts_on(result, atol=5e-3) end end @@ -640,7 +640,7 @@ end @test isapprox(result["objective"], 15141.2; atol = 1e0) switch_status_total = sum(switch["status"] for (i,switch) in result["solution"]["switch"]) - @test switch_status_total <= 13.000 && switch_status_total >= 12.000 # 1 to 2 swtiches off + @test switch_status_total <= 13.000 && switch_status_total >= 11.000 # 1 to 3 swtiches off branch_status_total = sum(branch["br_status"] for (i,branch) in result["solution"]["branch"]) @test branch_status_total >= 5.0 && branch_status_total <= 7.0 # zero-two branches off diff --git a/test/opf.jl b/test/opf.jl index 90cc06a86..48a14184f 100644 --- a/test/opf.jl +++ b/test/opf.jl @@ -590,7 +590,7 @@ end result = run_opf("../test/data/matpower/case3.m", SOCWRConicPowerModel, scs_solver) @test result["termination_status"] == OPTIMAL - @test isapprox(result["objective"], 5746.61; atol = 2e0) + @test isapprox(result["objective"], 5736.94; atol = 2e0) end @testset "5-bus transformer swap case" begin result = run_opf("../test/data/matpower/case5.m", SOCWRConicPowerModel, scs_solver) @@ -598,14 +598,12 @@ end @test result["termination_status"] == OPTIMAL @test isapprox(result["objective"], 15051.4; atol = 1e1) end - # convergence issue encountered when update to, SCS.jl v0.6.3 - #@testset "5-bus asymmetric case" begin - # result = run_opf("../test/data/matpower/case5_asym.m", SOCWRConicPowerModel, scs_solver) + @testset "5-bus asymmetric case" begin + result = run_opf("../test/data/matpower/case5_asym.m", SOCWRConicPowerModel, scs_solver) - # @test result["termination_status"] == OPTIMAL - # @test isapprox(result["objective"], 14999.7; atol = 1e0) - #end - # convergence issue encountered when linear objective used, SCS.jl v0.4.1 + @test result["termination_status"] == OPTIMAL + @test isapprox(result["objective"], 14999.7; atol = 1e0) + end @testset "5-bus gap case" begin result = run_opf("../test/data/matpower/case5_gap.m", SOCWRConicPowerModel, scs_solver) @@ -618,33 +616,31 @@ end @test result["termination_status"] == OPTIMAL @test isapprox(result["objective"], 1005.27; atol = 1e0) end - # does not converge in SCS.jl v0.4.0 - #@testset "5-bus with negative generators" begin - # result = run_opf("../test/data/matpower/case5_npg.m", SOCWRConicPowerModel, scs_solver) + @testset "5-bus with negative generators" begin + result = run_opf("../test/data/matpower/case5_npg.m", SOCWRConicPowerModel, scs_solver) - # @test result["termination_status"] == OPTIMAL - # @test isapprox(result["objective"], 3613.72; atol = 40) - #end - # TODO: figure out why this test fails - # @testset "5-bus with pwl costs" begin - # result = run_opf("../test/data/matpower/case5_pwlc.m", SOCWRConicPowerModel, scs_solver) - # - # @test result["termination_status"] == OPTIMAL - # @test isapprox(result["objective"], 42895; atol = 1e0) - # end - # Turn off due to numerical stability + @test result["termination_status"] == OPTIMAL + @test isapprox(result["objective"], 3551.71; atol = 40) + end + @testset "5-bus with pwl costs" begin + result = run_opf("../test/data/matpower/case5_pwlc.m", SOCWRConicPowerModel, scs_solver) + + @test result["termination_status"] == OPTIMAL + @test isapprox(result["objective"], 42889; atol = 1e0) + end @testset "6-bus case" begin result = run_opf("../test/data/matpower/case6.m", SOCWRConicPowerModel, scs_solver) @test result["termination_status"] == OPTIMAL - @test isapprox(result["objective"], 11472.2; atol = 3e0) + #@test isapprox(result["objective"], 11472.2; atol = 3e0) + @test isapprox(result["objective"], 11451.5; atol = 3e0) end @testset "24-bus rts case" begin result = run_opf("../test/data/matpower/case24.m", SOCWRConicPowerModel, scs_solver) @test result["termination_status"] == OPTIMAL - #@test isapprox(result["objective"], 70688.5; atol = 1e0) - @test isapprox(result["objective"], 70693.9; atol = 1e0) + #@test isapprox(result["objective"], 70693.9; atol = 1e0) + @test isapprox(result["objective"], 70670.0; atol = 1e0) end @testset "14-bus variable bounds" begin pm = instantiate_model("../test/data/matpower/case14.m", SOCWRConicPowerModel, PowerModels.build_opf) @@ -905,9 +901,8 @@ end result = run_opf("../test/data/matpower/case3.m", SDPWRMPowerModel, scs_solver) @test result["termination_status"] == OPTIMAL - @test isapprox(result["objective"], 5851.23; atol = 1e1) + @test isapprox(result["objective"], 5818.00; atol = 1e1) end - # TODO see if convergence time can be improved @testset "5-bus asymmetric case" begin result = run_opf("../test/data/matpower/case5_asym.m", SDPWRMPowerModel, scs_solver) @@ -920,18 +915,18 @@ end @test result["termination_status"] == OPTIMAL @test isapprox(result["objective"], -28236.1; atol = 1e1) end - # convergence issue encounterd when updated to SCS v0.6.3 - #@testset "5-bus with asymmetric line charge" begin - # result = run_opf("../test/data/pti/case5_alc.raw", SDPWRMPowerModel, scs_solver) + @testset "5-bus with asymmetric line charge" begin + result = run_opf("../test/data/pti/case5_alc.raw", SDPWRMPowerModel, scs_solver) - # @test result["termination_status"] == OPTIMAL - # @test isapprox(result["objective"], 1005.31; atol = 1e-1) - #end + @test result["termination_status"] == OPTIMAL + @test isapprox(result["objective"], 1005.31; atol = 1e0) + end @testset "5-bus with negative generators" begin result = run_opf("../test/data/matpower/case5_npg.m", SDPWRMPowerModel, scs_solver) @test result["termination_status"] == OPTIMAL - @test isapprox(result["objective"], 6827.34; atol = 1e0) + #@test isapprox(result["objective"], 6827.34; atol = 1e0) + @test isapprox(result["objective"], 6735.17; atol = 1e0) end # too slow for unit tests # @testset "14-bus case" begin @@ -944,15 +939,9 @@ end result = run_opf("../test/data/matpower/case6.m", SDPWRMPowerModel, scs_solver) @test result["termination_status"] == OPTIMAL - @test isapprox(result["objective"], 11580.8; atol = 1e1) + #@test isapprox(result["objective"], 11580.8; atol = 1e1) + @test isapprox(result["objective"], 11507.7; atol = 1e1) end - # TODO replace this with smaller case, way too slow for unit testing - #@testset "24-bus rts case" begin - # result = run_opf("../test/data/matpower/case24.m", SDPWRMPowerModel, scs_solver) - - # @test result["termination_status"] == OPTIMAL - # @test isapprox(result["objective"], 75153; atol = 1e0) - #end @testset "14-bus variable bounds" begin pm = instantiate_model("../test/data/matpower/case14.m", SDPWRMPowerModel, PowerModels.build_opf) @test check_variable_bounds(pm.model) @@ -965,7 +954,8 @@ end result = run_opf("../test/data/matpower/case3.m", SparseSDPWRMPowerModel, scs_solver) @test result["termination_status"] == OPTIMAL - @test isapprox(result["objective"], 5851.23; atol = 1e1) + #@test isapprox(result["objective"], 5851.23; atol = 1e1) + @test isapprox(result["objective"], 5818.00; atol = 1e1) end @testset "5-bus with asymmetric line charge" begin result = run_opf("../test/data/pti/case5_alc.raw", SparseSDPWRMPowerModel, scs_solver) @@ -979,28 +969,23 @@ end @test result["termination_status"] == OPTIMAL @test isapprox(result["objective"], 347.746; atol = 1e0) end - # too slow for unit tests - # @testset "14-bus case" begin - # result = run_opf("../test/data/matpower/case14.m", SparseSDPWRMPowerModel, scs_solver) - - # @test result["termination_status"] == OPTIMAL - # @test isapprox(result["objective"], 8081.5; atol = 1e0) - # end - - # multiple components are not currently supported by this form - #= - @testset "6-bus case" begin - result = run_opf("../test/data/matpower/case6.m", SparseSDPWRMPowerModel, scs_solver) + @testset "14-bus case" begin + result = run_opf("../test/data/matpower/case14.m", SparseSDPWRMPowerModel, scs_solver) @test result["termination_status"] == OPTIMAL - @test isapprox(result["objective"], 11578.8; atol = 1e0) + @test isapprox(result["objective"], 8081.5; atol = 1e0) end - =# + # multiple components are not currently supported by this form + # @testset "6-bus case" begin + # result = run_opf("../test/data/matpower/case6.m", SparseSDPWRMPowerModel, scs_solver) + + # @test result["termination_status"] == OPTIMAL + # @test isapprox(result["objective"], 11578.8; atol = 1e0) + # end @testset "14-bus variable bounds" begin pm = instantiate_model("../test/data/matpower/case14.m", SparseSDPWRMPowerModel, PowerModels.build_opf) @test check_variable_bounds(pm.model) end - @testset "passing in decomposition" begin # too slow for unit tests #data = PowerModels.parse_file("../test/data/matpower/case14.m") @@ -1024,5 +1009,4 @@ end @test result["termination_status"] == OPTIMAL @test isapprox(result["objective"], 1005.31; atol = 1e0) end - end diff --git a/test/runtests.jl b/test/runtests.jl index c8dd8b050..f18113fca 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,7 +6,7 @@ import Memento Memento.setlevel!(Memento.getlogger(InfrastructureModels), "error") PowerModels.logger_config!("error") -import Cbc +import HiGHS import Ipopt import SCS import Juniper @@ -18,13 +18,23 @@ import LinearAlgebra import SparseArrays using Test + +# compat for JuMP v0.22/v0.23 transition +# can be removed after dropping support for v0.22 +if !isdefined(JuMP, :num_nonlinear_constraints) + num_nonlinear_constraints = JuMP.num_nl_constraints +else + num_nonlinear_constraints = JuMP.num_nonlinear_constraints +end + + # default setup for solvers ipopt_solver = JuMP.optimizer_with_attributes(Ipopt.Optimizer, "tol"=>1e-6, "print_level"=>0) ipopt_ws_solver = JuMP.optimizer_with_attributes(Ipopt.Optimizer, "tol"=>1e-6, "mu_init"=>1e-4, "print_level"=>0) -cbc_solver = JuMP.optimizer_with_attributes(Cbc.Optimizer, "logLevel"=>0) +cbc_solver = JuMP.optimizer_with_attributes(HiGHS.Optimizer, "output_flag"=>false) juniper_solver = JuMP.optimizer_with_attributes(Juniper.Optimizer, "nl_solver"=>JuMP.optimizer_with_attributes(Ipopt.Optimizer, "tol"=>1e-4, "print_level"=>0), "log_levels"=>[]) -scs_solver = JuMP.optimizer_with_attributes(SCS.Optimizer, "max_iters"=>100000, "eps"=>1e-4, "verbose"=>0) +scs_solver = JuMP.optimizer_with_attributes(SCS.Optimizer, "verbose"=>false) include("common.jl")