diff --git a/docs/format.md b/docs/format.md index 86e7fb7..a537c58 100644 --- a/docs/format.md +++ b/docs/format.md @@ -24,7 +24,9 @@ Instances are specified by JSON files containing the following main sections: * Reserves * Contingencies -Each section is described in detail below. +Each section is described in detail below. See [case118/2017-01-01.json.gz][example] for a complete example. + +[example]: https://axavier.org/UnitCommitment.jl/0.3/instances/matpower/case118/2017-01-01.json.gz ### Parameters @@ -303,3 +305,4 @@ Current limitations * Only N-1 transmission contingencies are supported. Generator contingencies are not currently supported. * Time-varying minimum production amounts are not currently compatible with ramp/startup/shutdown limits. * Flexible ramping products can only be acquired under the `WanHob2016` formulation, which does not support spinning reserves. + diff --git a/docs/instances.md b/docs/instances.md index b8bfa24..67a81fc 100644 --- a/docs/instances.md +++ b/docs/instances.md @@ -9,7 +9,7 @@ suffix: . Instances ========= -UnitCommitment.jl provides a large collection of benchmark instances collected from the literature and converted to a [common data format](format.md). In some cases, as indicated below, the original instances have been extended, with realistic parameters, using data-driven methods. If you use these instances in your research, we request that you cite UnitCommitment.jl, as well as the original sources, as listed below. Benchmark instances can be loaded with `UnitCommitment.read_benchmark(name)`, as explained in the [usage section](usage.md). +UnitCommitment.jl provides a large collection of benchmark instances collected from the literature and converted to a [common data format](format.md). In some cases, as indicated below, the original instances have been extended, with realistic parameters, using data-driven methods. If you use these instances in your research, we request that you cite UnitCommitment.jl, as well as the original sources, as listed below. Benchmark instances can be loaded with `UnitCommitment.read_benchmark(name)`, as explained in the [usage section](usage.md). Instance files can also be [directly downloaded from our website](https://axavier.org/UnitCommitment.jl/0.3/instances/). ```{warning} The instances included in UC.jl are still under development and may change in the future. If you use these instances in your research, for reproducibility, you should specify what version of UC.jl they came from. diff --git a/instances/test/case14-flex.json.gz b/instances/test/case14-flex.json.gz deleted file mode 100644 index 8e88af3..0000000 Binary files a/instances/test/case14-flex.json.gz and /dev/null differ diff --git a/src/UnitCommitment.jl b/src/UnitCommitment.jl index 89049a1..57e795b 100644 --- a/src/UnitCommitment.jl +++ b/src/UnitCommitment.jl @@ -20,6 +20,7 @@ include("model/formulations/WanHob2016/structs.jl") include("import/egret.jl") include("instance/read.jl") +include("instance/migrate.jl") include("model/build.jl") include("model/formulations/ArrCon2000/ramp.jl") include("model/formulations/base/bus.jl") diff --git a/src/instance/migrate.jl b/src/instance/migrate.jl new file mode 100644 index 0000000..becb912 --- /dev/null +++ b/src/instance/migrate.jl @@ -0,0 +1,38 @@ +# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment +# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. +# Released under the modified BSD license. See COPYING.md for more details. + +using DataStructures +using JSON + +function _migrate(json) + version = json["Parameters"]["Version"] + if version === nothing + error( + "The provided input file cannot be loaded because it does not " * + "specify what version of UnitCommitment.jl it was written for. " * + "Please modify the \"Parameters\" section of the file and include " * + "a \"Version\" entry. For example: {\"Parameters\":{\"Version\":\"0.3\"}}", + ) + end + version = VersionNumber(version) + version >= v"0.3" || _migrate_to_v03(json) + return +end + +function _migrate_to_v03(json) + # Migrate reserves + if json["Reserves"] !== nothing && + json["Reserves"]["Spinning (MW)"] !== nothing + amount = json["Reserves"]["Spinning (MW)"] + json["Reserves"] = DefaultOrderedDict(nothing) + json["Reserves"]["r1"] = DefaultOrderedDict(nothing) + json["Reserves"]["r1"]["Type"] = "spinning" + json["Reserves"]["r1"]["Amount (MW)"] = amount + for (gen_name, gen) in json["Generators"] + if gen["Provides spinning reserves?"] == true + gen["Reserve eligibility"] = ["r1"] + end + end + end +end diff --git a/src/instance/read.jl b/src/instance/read.jl index 90cb169..b50cdd5 100644 --- a/src/instance/read.jl +++ b/src/instance/read.jl @@ -80,6 +80,7 @@ function _read_json(path::String)::OrderedDict end function _from_json(json; repair = true) + _migrate(json) units = Unit[] buses = Bus[] contingencies = Contingency[] diff --git a/test/fixtures/case118-initcond.json.gz b/test/fixtures/case118-initcond.json.gz index fabcbc8..c496914 100644 Binary files a/test/fixtures/case118-initcond.json.gz and b/test/fixtures/case118-initcond.json.gz differ diff --git a/test/fixtures/case14-flex.json.gz b/test/fixtures/case14-flex.json.gz new file mode 100644 index 0000000..9cc45f2 Binary files /dev/null and b/test/fixtures/case14-flex.json.gz differ diff --git a/test/fixtures/case14-sub-hourly.json.gz b/test/fixtures/case14-sub-hourly.json.gz new file mode 100644 index 0000000..d7ebdfe Binary files /dev/null and b/test/fixtures/case14-sub-hourly.json.gz differ diff --git a/test/fixtures/case14.json.gz b/test/fixtures/case14.json.gz new file mode 100644 index 0000000..3c38c28 Binary files /dev/null and b/test/fixtures/case14.json.gz differ diff --git a/test/fixtures/ucjl-0.2.json.gz b/test/fixtures/ucjl-0.2.json.gz new file mode 100644 index 0000000..216e0f0 Binary files /dev/null and b/test/fixtures/ucjl-0.2.json.gz differ diff --git a/test/fixtures/ucjl-0.3.json.gz b/test/fixtures/ucjl-0.3.json.gz new file mode 100644 index 0000000..ec337a6 Binary files /dev/null and b/test/fixtures/ucjl-0.3.json.gz differ diff --git a/test/import/egret_test.jl b/test/import/egret_test.jl index 494067a..0ca54ab 100644 --- a/test/import/egret_test.jl +++ b/test/import/egret_test.jl @@ -4,12 +4,9 @@ using UnitCommitment -basedir = @__DIR__ - @testset "read_egret_solution" begin - solution = UnitCommitment.read_egret_solution( - "$basedir/../fixtures/egret_output.json.gz", - ) + solution = + UnitCommitment.read_egret_solution("$FIXTURES/egret_output.json.gz") for attr in ["Is on", "Production (MW)", "Production cost (\$)"] @test attr in keys(solution) @test "115_STEAM_1" in keys(solution[attr]) diff --git a/test/instance/migrate_test.jl b/test/instance/migrate_test.jl new file mode 100644 index 0000000..2ea6d46 --- /dev/null +++ b/test/instance/migrate_test.jl @@ -0,0 +1,18 @@ +# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment +# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. +# Released under the modified BSD license. See COPYING.md for more details. + +using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip + +@testset "read v0.2" begin + instance = UnitCommitment.read("$FIXTURES/ucjl-0.2.json.gz") + @test length(instance.reserves_by_name["r1"].amount) == 4 + @test instance.units_by_name["g2"].reserves[1].name == "r1" +end + +@testset "read v0.3" begin + instance = UnitCommitment.read("$FIXTURES/ucjl-0.3.json.gz") + @test length(instance.units) == 6 + @test length(instance.buses) == 14 + @test length(instance.lines) == 20 +end diff --git a/test/instance/read_test.jl b/test/instance/read_test.jl index ecf7f81..cc9e332 100644 --- a/test/instance/read_test.jl +++ b/test/instance/read_test.jl @@ -5,7 +5,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip @testset "read_benchmark" begin - instance = UnitCommitment.read_benchmark("test/case14") + instance = UnitCommitment.read("$FIXTURES/case14.json.gz") @test length(instance.lines) == 20 @test length(instance.buses) == 14 @@ -112,7 +112,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip end @testset "read_benchmark sub-hourly" begin - instance = UnitCommitment.read_benchmark("test/case14-sub-hourly") + instance = UnitCommitment.read("$FIXTURES/case14-sub-hourly.json.gz") @test instance.time == 4 unit = instance.units[1] @test unit.name == "g1" diff --git a/test/model/formulations_test.jl b/test/model/formulations_test.jl index fa829cb..09cfeca 100644 --- a/test/model/formulations_test.jl +++ b/test/model/formulations_test.jl @@ -20,11 +20,11 @@ import UnitCommitment: function _test( formulation::Formulation; - instances = ["test/case14"], + instances = ["case14"], dump::Bool = false, )::Nothing for instance_name in instances - instance = UnitCommitment.read_benchmark(instance_name) + instance = UnitCommitment.read("$(FIXTURES)/$(instance_name).json.gz") model = UnitCommitment.build_model( instance = instance, formulation = formulation, @@ -78,7 +78,7 @@ end @testset "WanHob2016" begin _test( Formulation(ramping = WanHob2016.Ramping()), - instances = ["test/case14-flex"], + instances = ["case14-flex"], ) end end diff --git a/test/runtests.jl b/test/runtests.jl index 8d43ca4..9a61bf2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,6 +8,8 @@ using UnitCommitment push!(Base.LOAD_PATH, @__DIR__) UnitCommitment._setup_logger(level = Base.CoreLogging.Error) +FIXTURES = "$(@__DIR__)/fixtures" + @testset "UnitCommitment" begin include("usage.jl") @testset "import" begin @@ -15,6 +17,7 @@ UnitCommitment._setup_logger(level = Base.CoreLogging.Error) end @testset "instance" begin include("instance/read_test.jl") + include("instance/migrate_test.jl") end @testset "model" begin include("model/formulations_test.jl") diff --git a/test/solution/methods/XavQiuWanThi19/filter_test.jl b/test/solution/methods/XavQiuWanThi19/filter_test.jl index 961dc4b..e4a8b48 100644 --- a/test/solution/methods/XavQiuWanThi19/filter_test.jl +++ b/test/solution/methods/XavQiuWanThi19/filter_test.jl @@ -6,7 +6,7 @@ using UnitCommitment, Test, LinearAlgebra import UnitCommitment: _Violation, _offer, _query @testset "_ViolationFilter" begin - instance = UnitCommitment.read_benchmark("test/case14") + instance = UnitCommitment.read("$FIXTURES/case14.json.gz") filter = UnitCommitment._ViolationFilter(max_per_line = 1, max_total = 2) _offer( diff --git a/test/solution/methods/XavQiuWanThi19/find_test.jl b/test/solution/methods/XavQiuWanThi19/find_test.jl index ddd02c1..27110cb 100644 --- a/test/solution/methods/XavQiuWanThi19/find_test.jl +++ b/test/solution/methods/XavQiuWanThi19/find_test.jl @@ -6,7 +6,7 @@ using UnitCommitment, Test, LinearAlgebra import UnitCommitment: _Violation, _offer, _query @testset "find_violations" begin - instance = UnitCommitment.read_benchmark("test/case14") + instance = UnitCommitment.read("$FIXTURES/case14.json.gz") for line in instance.lines, t in 1:instance.time line.normal_flow_limit[t] = 1.0 line.emergency_flow_limit[t] = 1.0 diff --git a/test/solution/methods/XavQiuWanThi19/sensitivity_test.jl b/test/solution/methods/XavQiuWanThi19/sensitivity_test.jl index fda4272..5d64a15 100644 --- a/test/solution/methods/XavQiuWanThi19/sensitivity_test.jl +++ b/test/solution/methods/XavQiuWanThi19/sensitivity_test.jl @@ -5,7 +5,7 @@ using UnitCommitment, Test, LinearAlgebra @testset "_susceptance_matrix" begin - instance = UnitCommitment.read_benchmark("test/case14") + instance = UnitCommitment.read("$FIXTURES/case14.json.gz") actual = UnitCommitment._susceptance_matrix(instance.lines) @test size(actual) == (20, 20) expected = Diagonal([ @@ -34,7 +34,7 @@ using UnitCommitment, Test, LinearAlgebra end @testset "_reduced_incidence_matrix" begin - instance = UnitCommitment.read_benchmark("test/case14") + instance = UnitCommitment.read("$FIXTURES/case14.json.gz") actual = UnitCommitment._reduced_incidence_matrix( lines = instance.lines, buses = instance.buses, @@ -81,7 +81,7 @@ end end @testset "_injection_shift_factors" begin - instance = UnitCommitment.read_benchmark("test/case14") + instance = UnitCommitment.read("$FIXTURES/case14.json.gz") actual = UnitCommitment._injection_shift_factors( lines = instance.lines, buses = instance.buses, @@ -112,7 +112,7 @@ end end @testset "_line_outage_factors" begin - instance = UnitCommitment.read_benchmark("test/case14") + instance = UnitCommitment.read("$FIXTURES/case14.json.gz") isf_before = UnitCommitment._injection_shift_factors( lines = instance.lines, buses = instance.buses, diff --git a/test/transform/initcond_test.jl b/test/transform/initcond_test.jl index 3d891f3..56c9831 100644 --- a/test/transform/initcond_test.jl +++ b/test/transform/initcond_test.jl @@ -4,12 +4,9 @@ using UnitCommitment, Cbc, JuMP -basedir = @__DIR__ - @testset "generate_initial_conditions!" begin # Load instance - instance = - UnitCommitment.read("$basedir/../fixtures/case118-initcond.json.gz") + instance = UnitCommitment.read("$FIXTURES/case118-initcond.json.gz") optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0) # All units should have unknown initial conditions diff --git a/test/transform/slice_test.jl b/test/transform/slice_test.jl index b736d16..9985144 100644 --- a/test/transform/slice_test.jl +++ b/test/transform/slice_test.jl @@ -5,7 +5,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip @testset "slice" begin - instance = UnitCommitment.read_benchmark("test/case14") + instance = UnitCommitment.read("$FIXTURES/case14.json.gz") modified = UnitCommitment.slice(instance, 1:2) # Should update all time-dependent fields diff --git a/test/usage.jl b/test/usage.jl index 0cbf799..4b941f4 100644 --- a/test/usage.jl +++ b/test/usage.jl @@ -5,7 +5,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON @testset "usage" begin - instance = UnitCommitment.read_benchmark("test/case14") + instance = UnitCommitment.read("$FIXTURES/case14.json.gz") for line in instance.lines, t in 1:4 line.normal_flow_limit[t] = 10.0 end diff --git a/test/validation/repair_test.jl b/test/validation/repair_test.jl index 70e8751..d7e5663 100644 --- a/test/validation/repair_test.jl +++ b/test/validation/repair_test.jl @@ -4,11 +4,9 @@ using UnitCommitment, JSON, GZip, DataStructures -basedir = @__DIR__ - function parse_case14() return JSON.parse( - GZip.gzopen("$basedir/../../instances/test/case14.json.gz"), + GZip.gzopen("$FIXTURES/case14.json.gz"), dicttype = () -> DefaultOrderedDict(nothing), ) end