parent
e8d8272510
commit
40270b0030
@ -1,5 +0,0 @@
|
|||||||
[deps]
|
|
||||||
JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899"
|
|
||||||
|
|
||||||
[compat]
|
|
||||||
JuliaFormatter = "0.14.4"
|
|
@ -1,9 +0,0 @@
|
|||||||
using JuliaFormatter
|
|
||||||
format(
|
|
||||||
[
|
|
||||||
"../../src",
|
|
||||||
"../../test",
|
|
||||||
"../../benchmark/run.jl",
|
|
||||||
],
|
|
||||||
verbose=true,
|
|
||||||
)
|
|
@ -1,75 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
|
|
||||||
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
|
|
||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
|
||||||
|
|
||||||
if [ ! -e Project.toml ]; then
|
|
||||||
echo "juliaw: Project.toml not found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -e Manifest.toml ]; then
|
|
||||||
julia --project=. -e 'using Pkg; Pkg.instantiate()' || exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -e build/sysimage.so -o Project.toml -nt build/sysimage.so ]; then
|
|
||||||
echo "juliaw: rebuilding system image..."
|
|
||||||
|
|
||||||
# Generate temporary project folder
|
|
||||||
rm -rf $HOME/.juliaw
|
|
||||||
mkdir -p $HOME/.juliaw/src
|
|
||||||
cp Project.toml Manifest.toml $HOME/.juliaw
|
|
||||||
NAME=$(julia -e 'using TOML; toml = TOML.parsefile("Project.toml"); "name" in keys(toml) && print(toml["name"])')
|
|
||||||
if [ ! -z $NAME ]; then
|
|
||||||
cat > $HOME/.juliaw/src/$NAME.jl << EOF
|
|
||||||
module $NAME
|
|
||||||
end
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add PackageCompiler dependencies to temporary project
|
|
||||||
julia --project=$HOME/.juliaw -e 'using Pkg; Pkg.add(["PackageCompiler", "TOML", "Logging"])'
|
|
||||||
|
|
||||||
# Generate system image scripts
|
|
||||||
cat > $HOME/.juliaw/sysimage.jl << EOF
|
|
||||||
using PackageCompiler
|
|
||||||
using TOML
|
|
||||||
using Logging
|
|
||||||
|
|
||||||
Logging.disable_logging(Logging.Info)
|
|
||||||
mkpath("$PWD/build")
|
|
||||||
|
|
||||||
println("juliaw: generating precompilation statements...")
|
|
||||||
run(\`julia --project="$PWD" --trace-compile="$PWD"/build/precompile.jl \$(ARGS)\`)
|
|
||||||
|
|
||||||
println("juliaw: finding dependencies...")
|
|
||||||
project = TOML.parsefile("Project.toml")
|
|
||||||
manifest = TOML.parsefile("Manifest.toml")
|
|
||||||
deps = Symbol[]
|
|
||||||
for dep in keys(project["deps"])
|
|
||||||
if dep in keys(manifest)
|
|
||||||
# Up to Julia 1.6
|
|
||||||
dep_entry = manifest[dep][1]
|
|
||||||
else
|
|
||||||
# Julia 1.7+
|
|
||||||
dep_entry = manifest["deps"][dep][1]
|
|
||||||
end
|
|
||||||
if "path" in keys(dep_entry)
|
|
||||||
println(" - \$(dep) [skip]")
|
|
||||||
else
|
|
||||||
println(" - \$(dep)")
|
|
||||||
push!(deps, Symbol(dep))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
println("juliaw: building system image...")
|
|
||||||
create_sysimage(
|
|
||||||
deps,
|
|
||||||
precompile_statements_file = "$PWD/build/precompile.jl",
|
|
||||||
sysimage_path = "$PWD/build/sysimage.so",
|
|
||||||
)
|
|
||||||
EOF
|
|
||||||
julia --project=$HOME/.juliaw $HOME/.juliaw/sysimage.jl $*
|
|
||||||
else
|
|
||||||
julia --project=. --sysimage build/sysimage.so $*
|
|
||||||
fi
|
|
@ -1,21 +0,0 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
@testset "read_egret_solution" begin
|
|
||||||
solution =
|
|
||||||
UnitCommitment.read_egret_solution("$FIXTURES/egret_output.json.gz")
|
|
||||||
for attr in
|
|
||||||
["Is on", "Thermal production (MW)", "Thermal production cost (\$)"]
|
|
||||||
@test attr in keys(solution)
|
|
||||||
@test "115_STEAM_1" in keys(solution[attr])
|
|
||||||
@test length(solution[attr]["115_STEAM_1"]) == 48
|
|
||||||
end
|
|
||||||
@test solution["Thermal production cost (\$)"]["315_CT_6"][15:20] ==
|
|
||||||
[0.0, 0.0, 884.44, 1470.71, 1470.71, 884.44]
|
|
||||||
@test solution["Startup cost (\$)"]["315_CT_6"][15:20] ==
|
|
||||||
[0.0, 0.0, 5665.23, 0.0, 0.0, 0.0]
|
|
||||||
@test length(keys(solution["Is on"])) == 154
|
|
||||||
end
|
|
@ -1,22 +0,0 @@
|
|||||||
# 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.scenarios) == 1
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
@test length(sc.reserves_by_name["r1"].amount) == 4
|
|
||||||
@test sc.thermal_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.scenarios) == 1
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
@test length(sc.thermal_units) == 6
|
|
||||||
@test length(sc.buses) == 14
|
|
||||||
@test length(sc.lines) == 20
|
|
||||||
end
|
|
@ -1,166 +0,0 @@
|
|||||||
# 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_benchmark" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14.json.gz")
|
|
||||||
|
|
||||||
@test repr(instance) == (
|
|
||||||
"UnitCommitmentInstance(1 scenarios, 6 thermal units, 0 profiled units, 14 buses, " *
|
|
||||||
"20 lines, 19 contingencies, 1 price sensitive loads, 4 time steps)"
|
|
||||||
)
|
|
||||||
|
|
||||||
@test length(instance.scenarios) == 1
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
@test length(sc.lines) == 20
|
|
||||||
@test length(sc.buses) == 14
|
|
||||||
@test length(sc.thermal_units) == 6
|
|
||||||
@test length(sc.contingencies) == 19
|
|
||||||
@test length(sc.price_sensitive_loads) == 1
|
|
||||||
@test instance.time == 4
|
|
||||||
|
|
||||||
@test sc.lines[5].name == "l5"
|
|
||||||
@test sc.lines[5].source.name == "b2"
|
|
||||||
@test sc.lines[5].target.name == "b5"
|
|
||||||
@test sc.lines[5].reactance ≈ 0.17388
|
|
||||||
@test sc.lines[5].susceptance ≈ 10.037550333
|
|
||||||
@test sc.lines[5].normal_flow_limit == [1e8 for t in 1:4]
|
|
||||||
@test sc.lines[5].emergency_flow_limit == [1e8 for t in 1:4]
|
|
||||||
@test sc.lines[5].flow_limit_penalty == [5e3 for t in 1:4]
|
|
||||||
@test sc.lines_by_name["l5"].name == "l5"
|
|
||||||
|
|
||||||
@test sc.lines[1].name == "l1"
|
|
||||||
@test sc.lines[1].source.name == "b1"
|
|
||||||
@test sc.lines[1].target.name == "b2"
|
|
||||||
@test sc.lines[1].reactance ≈ 0.059170
|
|
||||||
@test sc.lines[1].susceptance ≈ 29.496860773945
|
|
||||||
@test sc.lines[1].normal_flow_limit == [300.0 for t in 1:4]
|
|
||||||
@test sc.lines[1].emergency_flow_limit == [400.0 for t in 1:4]
|
|
||||||
@test sc.lines[1].flow_limit_penalty == [1e3 for t in 1:4]
|
|
||||||
|
|
||||||
@test sc.buses[9].name == "b9"
|
|
||||||
@test sc.buses[9].load == [35.36638, 33.25495, 31.67138, 31.14353]
|
|
||||||
@test sc.buses_by_name["b9"].name == "b9"
|
|
||||||
|
|
||||||
@test sc.reserves[1].name == "r1"
|
|
||||||
@test sc.reserves[1].type == "spinning"
|
|
||||||
@test sc.reserves[1].amount == [100.0, 100.0, 100.0, 100.0]
|
|
||||||
@test sc.reserves_by_name["r1"].name == "r1"
|
|
||||||
|
|
||||||
unit = sc.thermal_units[1]
|
|
||||||
@test unit.name == "g1"
|
|
||||||
@test unit.bus.name == "b1"
|
|
||||||
@test unit.ramp_up_limit == 1e6
|
|
||||||
@test unit.ramp_down_limit == 1e6
|
|
||||||
@test unit.startup_limit == 1e6
|
|
||||||
@test unit.shutdown_limit == 1e6
|
|
||||||
@test unit.must_run == [false for t in 1:4]
|
|
||||||
@test unit.min_power_cost == [1400.0 for t in 1:4]
|
|
||||||
@test unit.min_uptime == 1
|
|
||||||
@test unit.min_downtime == 1
|
|
||||||
for t in 1:1
|
|
||||||
@test unit.cost_segments[1].mw[t] == 10.0
|
|
||||||
@test unit.cost_segments[2].mw[t] == 20.0
|
|
||||||
@test unit.cost_segments[3].mw[t] == 5.0
|
|
||||||
@test unit.cost_segments[1].cost[t] ≈ 20.0
|
|
||||||
@test unit.cost_segments[2].cost[t] ≈ 30.0
|
|
||||||
@test unit.cost_segments[3].cost[t] ≈ 40.0
|
|
||||||
end
|
|
||||||
@test length(unit.startup_categories) == 3
|
|
||||||
@test unit.startup_categories[1].delay == 1
|
|
||||||
@test unit.startup_categories[2].delay == 2
|
|
||||||
@test unit.startup_categories[3].delay == 3
|
|
||||||
@test unit.startup_categories[1].cost == 1000.0
|
|
||||||
@test unit.startup_categories[2].cost == 1500.0
|
|
||||||
@test unit.startup_categories[3].cost == 2000.0
|
|
||||||
@test length(unit.reserves) == 0
|
|
||||||
@test sc.thermal_units_by_name["g1"].name == "g1"
|
|
||||||
|
|
||||||
unit = sc.thermal_units[2]
|
|
||||||
@test unit.name == "g2"
|
|
||||||
@test unit.must_run == [false for t in 1:4]
|
|
||||||
@test length(unit.reserves) == 1
|
|
||||||
|
|
||||||
unit = sc.thermal_units[3]
|
|
||||||
@test unit.name == "g3"
|
|
||||||
@test unit.bus.name == "b3"
|
|
||||||
@test unit.ramp_up_limit == 70.0
|
|
||||||
@test unit.ramp_down_limit == 70.0
|
|
||||||
@test unit.startup_limit == 70.0
|
|
||||||
@test unit.shutdown_limit == 70.0
|
|
||||||
@test unit.must_run == [true for t in 1:4]
|
|
||||||
@test unit.min_power_cost == [0.0 for t in 1:4]
|
|
||||||
@test unit.min_uptime == 1
|
|
||||||
@test unit.min_downtime == 1
|
|
||||||
for t in 1:4
|
|
||||||
@test unit.cost_segments[1].mw[t] ≈ 33
|
|
||||||
@test unit.cost_segments[2].mw[t] ≈ 33
|
|
||||||
@test unit.cost_segments[3].mw[t] ≈ 34
|
|
||||||
@test unit.cost_segments[1].cost[t] ≈ 33.75
|
|
||||||
@test unit.cost_segments[2].cost[t] ≈ 38.04
|
|
||||||
@test unit.cost_segments[3].cost[t] ≈ 44.77853
|
|
||||||
end
|
|
||||||
@test length(unit.reserves) == 1
|
|
||||||
@test unit.reserves[1].name == "r1"
|
|
||||||
|
|
||||||
@test sc.contingencies[1].lines == [sc.lines[1]]
|
|
||||||
@test sc.contingencies[1].thermal_units == []
|
|
||||||
@test sc.contingencies[1].name == "c1"
|
|
||||||
@test sc.contingencies_by_name["c1"].name == "c1"
|
|
||||||
|
|
||||||
load = sc.price_sensitive_loads[1]
|
|
||||||
@test load.name == "ps1"
|
|
||||||
@test load.bus.name == "b3"
|
|
||||||
@test load.revenue == [100.0 for t in 1:4]
|
|
||||||
@test load.demand == [50.0 for t in 1:4]
|
|
||||||
@test sc.price_sensitive_loads_by_name["ps1"].name == "ps1"
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "read_benchmark sub-hourly" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14-sub-hourly.json.gz")
|
|
||||||
@test instance.time == 4
|
|
||||||
unit = instance.scenarios[1].thermal_units[1]
|
|
||||||
@test unit.name == "g1"
|
|
||||||
@test unit.min_uptime == 2
|
|
||||||
@test unit.min_downtime == 2
|
|
||||||
@test length(unit.startup_categories) == 3
|
|
||||||
@test unit.startup_categories[1].delay == 2
|
|
||||||
@test unit.startup_categories[2].delay == 4
|
|
||||||
@test unit.startup_categories[3].delay == 6
|
|
||||||
@test unit.initial_status == -200
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "read_benchmark profiled-units" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14-profiled.json.gz")
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
@test length(sc.profiled_units) == 2
|
|
||||||
|
|
||||||
first_pu = sc.profiled_units[1]
|
|
||||||
@test first_pu.name == "g7"
|
|
||||||
@test first_pu.bus.name == "b4"
|
|
||||||
@test first_pu.cost == [100.0 for t in 1:4]
|
|
||||||
@test first_pu.min_power == [60.0 for t in 1:4]
|
|
||||||
@test first_pu.max_power == [100.0 for t in 1:4]
|
|
||||||
@test sc.profiled_units_by_name["g7"].name == "g7"
|
|
||||||
|
|
||||||
second_pu = sc.profiled_units[2]
|
|
||||||
@test second_pu.name == "g8"
|
|
||||||
@test second_pu.bus.name == "b5"
|
|
||||||
@test second_pu.cost == [50.0 for t in 1:4]
|
|
||||||
@test second_pu.min_power == [0.0 for t in 1:4]
|
|
||||||
@test second_pu.max_power == [120.0 for t in 1:4]
|
|
||||||
@test sc.profiled_units_by_name["g8"].name == "g8"
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "read_benchmark commitmemt-status" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14-fixed-status.json.gz")
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
|
|
||||||
@test sc.thermal_units[1].commitment_status == [nothing for t in 1:4]
|
|
||||||
@test sc.thermal_units[2].commitment_status == [true for t in 1:4]
|
|
||||||
@test sc.thermal_units[4].commitment_status == [false for t in 1:4]
|
|
||||||
@test sc.thermal_units[6].commitment_status ==
|
|
||||||
[false, nothing, true, nothing]
|
|
||||||
end
|
|
@ -1,35 +0,0 @@
|
|||||||
# 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, Cbc, HiGHS, JuMP
|
|
||||||
import UnitCommitment: AELMP
|
|
||||||
|
|
||||||
@testset "aelmp" begin
|
|
||||||
path = "$FIXTURES/aelmp_simple.json.gz"
|
|
||||||
# model has to be solved first
|
|
||||||
instance = UnitCommitment.read(path)
|
|
||||||
model = UnitCommitment.build_model(
|
|
||||||
instance = instance,
|
|
||||||
optimizer = Cbc.Optimizer,
|
|
||||||
variable_names = true,
|
|
||||||
)
|
|
||||||
JuMP.set_silent(model)
|
|
||||||
UnitCommitment.optimize!(model)
|
|
||||||
|
|
||||||
# policy 1: allow offlines; consider startups
|
|
||||||
aelmp_1 =
|
|
||||||
UnitCommitment.compute_lmp(model, AELMP(), optimizer = HiGHS.Optimizer)
|
|
||||||
@test aelmp_1["s1", "B1", 1] ≈ 231.7 atol = 0.1
|
|
||||||
|
|
||||||
# policy 2: do not allow offlines; but consider startups
|
|
||||||
aelmp_2 = UnitCommitment.compute_lmp(
|
|
||||||
model,
|
|
||||||
AELMP(
|
|
||||||
allow_offline_participation = false,
|
|
||||||
consider_startup_costs = true,
|
|
||||||
),
|
|
||||||
optimizer = HiGHS.Optimizer,
|
|
||||||
)
|
|
||||||
@test aelmp_2["s1", "B1", 1] ≈ 274.3 atol = 0.1
|
|
||||||
end
|
|
@ -1,51 +0,0 @@
|
|||||||
# 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, Cbc, HiGHS, JuMP
|
|
||||||
import UnitCommitment: ConventionalLMP
|
|
||||||
|
|
||||||
function solve_conventional_testcase(path::String)
|
|
||||||
instance = UnitCommitment.read(path)
|
|
||||||
model = UnitCommitment.build_model(
|
|
||||||
instance = instance,
|
|
||||||
optimizer = Cbc.Optimizer,
|
|
||||||
variable_names = true,
|
|
||||||
)
|
|
||||||
JuMP.set_silent(model)
|
|
||||||
UnitCommitment.optimize!(model)
|
|
||||||
lmp = UnitCommitment.compute_lmp(
|
|
||||||
model,
|
|
||||||
ConventionalLMP(),
|
|
||||||
optimizer = HiGHS.Optimizer,
|
|
||||||
)
|
|
||||||
return lmp
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "conventional" begin
|
|
||||||
# instance 1
|
|
||||||
path = "$FIXTURES/lmp_simple_test_1.json.gz"
|
|
||||||
lmp = solve_conventional_testcase(path)
|
|
||||||
@test lmp["s1", "A", 1] == 50.0
|
|
||||||
@test lmp["s1", "B", 1] == 50.0
|
|
||||||
|
|
||||||
# instance 2
|
|
||||||
path = "$FIXTURES/lmp_simple_test_2.json.gz"
|
|
||||||
lmp = solve_conventional_testcase(path)
|
|
||||||
@test lmp["s1", "A", 1] == 50.0
|
|
||||||
@test lmp["s1", "B", 1] == 60.0
|
|
||||||
|
|
||||||
# instance 3
|
|
||||||
path = "$FIXTURES/lmp_simple_test_3.json.gz"
|
|
||||||
lmp = solve_conventional_testcase(path)
|
|
||||||
@test lmp["s1", "A", 1] == 50.0
|
|
||||||
@test lmp["s1", "B", 1] == 70.0
|
|
||||||
@test lmp["s1", "C", 1] == 100.0
|
|
||||||
|
|
||||||
# instance 4
|
|
||||||
path = "$FIXTURES/lmp_simple_test_4.json.gz"
|
|
||||||
lmp = solve_conventional_testcase(path)
|
|
||||||
@test lmp["s1", "A", 1] == 50.0
|
|
||||||
@test lmp["s1", "B", 1] == 70.0
|
|
||||||
@test lmp["s1", "C", 1] == 90.0
|
|
||||||
end
|
|
@ -1,84 +0,0 @@
|
|||||||
# 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
|
|
||||||
using JuMP
|
|
||||||
using Cbc
|
|
||||||
using JSON
|
|
||||||
import UnitCommitment:
|
|
||||||
ArrCon2000,
|
|
||||||
CarArr2006,
|
|
||||||
DamKucRajAta2016,
|
|
||||||
Formulation,
|
|
||||||
Gar1962,
|
|
||||||
KnuOstWat2018,
|
|
||||||
MorLatRam2013,
|
|
||||||
PanGua2016,
|
|
||||||
XavQiuWanThi2019,
|
|
||||||
WanHob2016
|
|
||||||
|
|
||||||
function _test(
|
|
||||||
formulation::Formulation;
|
|
||||||
instances = ["case14"],
|
|
||||||
dump::Bool = false,
|
|
||||||
)::Nothing
|
|
||||||
for instance_name in instances
|
|
||||||
instance = UnitCommitment.read("$(FIXTURES)/$(instance_name).json.gz")
|
|
||||||
model = UnitCommitment.build_model(
|
|
||||||
instance = instance,
|
|
||||||
formulation = formulation,
|
|
||||||
optimizer = Cbc.Optimizer,
|
|
||||||
variable_names = true,
|
|
||||||
)
|
|
||||||
set_silent(model)
|
|
||||||
UnitCommitment.optimize!(model)
|
|
||||||
solution = UnitCommitment.solution(model)
|
|
||||||
if dump
|
|
||||||
open("/tmp/ucjl.json", "w") do f
|
|
||||||
return write(f, JSON.json(solution, 2))
|
|
||||||
end
|
|
||||||
write_to_file(model, "/tmp/ucjl.lp")
|
|
||||||
end
|
|
||||||
@test UnitCommitment.validate(instance, solution)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "formulations" begin
|
|
||||||
@testset "default" begin
|
|
||||||
_test(Formulation())
|
|
||||||
end
|
|
||||||
@testset "ArrCon2000" begin
|
|
||||||
_test(Formulation(ramping = ArrCon2000.Ramping()))
|
|
||||||
end
|
|
||||||
@testset "DamKucRajAta2016" begin
|
|
||||||
_test(Formulation(ramping = DamKucRajAta2016.Ramping()))
|
|
||||||
end
|
|
||||||
@testset "MorLatRam2013" begin
|
|
||||||
_test(
|
|
||||||
Formulation(
|
|
||||||
ramping = MorLatRam2013.Ramping(),
|
|
||||||
startup_costs = MorLatRam2013.StartupCosts(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
@testset "PanGua2016" begin
|
|
||||||
_test(Formulation(ramping = PanGua2016.Ramping()))
|
|
||||||
end
|
|
||||||
@testset "Gar1962" begin
|
|
||||||
_test(Formulation(pwl_costs = Gar1962.PwlCosts()))
|
|
||||||
end
|
|
||||||
@testset "CarArr2006" begin
|
|
||||||
_test(Formulation(pwl_costs = CarArr2006.PwlCosts()))
|
|
||||||
end
|
|
||||||
@testset "KnuOstWat2018" begin
|
|
||||||
_test(Formulation(pwl_costs = KnuOstWat2018.PwlCosts()))
|
|
||||||
end
|
|
||||||
@testset "WanHob2016" begin
|
|
||||||
_test(
|
|
||||||
Formulation(ramping = WanHob2016.Ramping()),
|
|
||||||
instances = ["case14-flex"],
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,46 +0,0 @@
|
|||||||
# 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 Test
|
|
||||||
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
|
|
||||||
include("import/egret_test.jl")
|
|
||||||
end
|
|
||||||
@testset "instance" begin
|
|
||||||
include("instance/read_test.jl")
|
|
||||||
include("instance/migrate_test.jl")
|
|
||||||
end
|
|
||||||
@testset "model" begin
|
|
||||||
include("model/formulations_test.jl")
|
|
||||||
end
|
|
||||||
@testset "solution" begin
|
|
||||||
@testset "XavQiuWanThi19" begin
|
|
||||||
include("solution/methods/XavQiuWanThi19/filter_test.jl")
|
|
||||||
include("solution/methods/XavQiuWanThi19/find_test.jl")
|
|
||||||
include("solution/methods/XavQiuWanThi19/sensitivity_test.jl")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@testset "transform" begin
|
|
||||||
include("transform/initcond_test.jl")
|
|
||||||
include("transform/slice_test.jl")
|
|
||||||
@testset "randomize" begin
|
|
||||||
include("transform/randomize/XavQiuAhm2021_test.jl")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@testset "validation" begin
|
|
||||||
include("validation/repair_test.jl")
|
|
||||||
end
|
|
||||||
@testset "lmp" begin
|
|
||||||
include("lmp/conventional_test.jl")
|
|
||||||
include("lmp/aelmp_test.jl")
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,83 +0,0 @@
|
|||||||
# 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, Test, LinearAlgebra
|
|
||||||
import UnitCommitment: _Violation, _offer, _query
|
|
||||||
|
|
||||||
@testset "_ViolationFilter" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14.json.gz")
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
filter = UnitCommitment._ViolationFilter(max_per_line = 1, max_total = 2)
|
|
||||||
_offer(
|
|
||||||
filter,
|
|
||||||
_Violation(
|
|
||||||
time = 1,
|
|
||||||
monitored_line = sc.lines[1],
|
|
||||||
outage_line = nothing,
|
|
||||||
amount = 100.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
_offer(
|
|
||||||
filter,
|
|
||||||
_Violation(
|
|
||||||
time = 1,
|
|
||||||
monitored_line = sc.lines[1],
|
|
||||||
outage_line = sc.lines[1],
|
|
||||||
amount = 300.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
_offer(
|
|
||||||
filter,
|
|
||||||
_Violation(
|
|
||||||
time = 1,
|
|
||||||
monitored_line = sc.lines[1],
|
|
||||||
outage_line = sc.lines[5],
|
|
||||||
amount = 500.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
_offer(
|
|
||||||
filter,
|
|
||||||
_Violation(
|
|
||||||
time = 1,
|
|
||||||
monitored_line = sc.lines[1],
|
|
||||||
outage_line = sc.lines[4],
|
|
||||||
amount = 400.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
_offer(
|
|
||||||
filter,
|
|
||||||
_Violation(
|
|
||||||
time = 1,
|
|
||||||
monitored_line = sc.lines[2],
|
|
||||||
outage_line = sc.lines[1],
|
|
||||||
amount = 200.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
_offer(
|
|
||||||
filter,
|
|
||||||
_Violation(
|
|
||||||
time = 1,
|
|
||||||
monitored_line = sc.lines[2],
|
|
||||||
outage_line = sc.lines[8],
|
|
||||||
amount = 100.0,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
actual = _query(filter)
|
|
||||||
expected = [
|
|
||||||
_Violation(
|
|
||||||
time = 1,
|
|
||||||
monitored_line = sc.lines[2],
|
|
||||||
outage_line = sc.lines[1],
|
|
||||||
amount = 200.0,
|
|
||||||
),
|
|
||||||
_Violation(
|
|
||||||
time = 1,
|
|
||||||
monitored_line = sc.lines[1],
|
|
||||||
outage_line = sc.lines[5],
|
|
||||||
amount = 500.0,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@test actual == expected
|
|
||||||
end
|
|
@ -1,37 +0,0 @@
|
|||||||
# 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, Test, LinearAlgebra
|
|
||||||
import UnitCommitment: _Violation, _offer, _query
|
|
||||||
|
|
||||||
@testset "find_violations" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14.json.gz")
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
for line in sc.lines, t in 1:instance.time
|
|
||||||
line.normal_flow_limit[t] = 1.0
|
|
||||||
line.emergency_flow_limit[t] = 1.0
|
|
||||||
end
|
|
||||||
isf = UnitCommitment._injection_shift_factors(
|
|
||||||
lines = sc.lines,
|
|
||||||
buses = sc.buses,
|
|
||||||
)
|
|
||||||
lodf = UnitCommitment._line_outage_factors(
|
|
||||||
lines = sc.lines,
|
|
||||||
buses = sc.buses,
|
|
||||||
isf = isf,
|
|
||||||
)
|
|
||||||
inj = [1000.0 for b in 1:13, t in 1:instance.time]
|
|
||||||
overflow = [0.0 for l in sc.lines, t in 1:instance.time]
|
|
||||||
violations = UnitCommitment._find_violations(
|
|
||||||
instance = instance,
|
|
||||||
sc = sc,
|
|
||||||
net_injections = inj,
|
|
||||||
overflow = overflow,
|
|
||||||
isf = isf,
|
|
||||||
lodf = lodf,
|
|
||||||
max_per_line = 1,
|
|
||||||
max_per_period = 5,
|
|
||||||
)
|
|
||||||
@test length(violations) == 20
|
|
||||||
end
|
|
@ -1,147 +0,0 @@
|
|||||||
# 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, Test, LinearAlgebra
|
|
||||||
|
|
||||||
@testset "_susceptance_matrix" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14.json.gz")
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
actual = UnitCommitment._susceptance_matrix(sc.lines)
|
|
||||||
@test size(actual) == (20, 20)
|
|
||||||
expected = Diagonal([
|
|
||||||
29.5,
|
|
||||||
7.83,
|
|
||||||
8.82,
|
|
||||||
9.9,
|
|
||||||
10.04,
|
|
||||||
10.2,
|
|
||||||
41.45,
|
|
||||||
8.35,
|
|
||||||
3.14,
|
|
||||||
6.93,
|
|
||||||
8.77,
|
|
||||||
6.82,
|
|
||||||
13.4,
|
|
||||||
9.91,
|
|
||||||
15.87,
|
|
||||||
20.65,
|
|
||||||
6.46,
|
|
||||||
9.09,
|
|
||||||
8.73,
|
|
||||||
5.02,
|
|
||||||
])
|
|
||||||
@test round.(actual, digits = 2) == expected
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "_reduced_incidence_matrix" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14.json.gz")
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
actual = UnitCommitment._reduced_incidence_matrix(
|
|
||||||
lines = sc.lines,
|
|
||||||
buses = sc.buses,
|
|
||||||
)
|
|
||||||
@test size(actual) == (20, 13)
|
|
||||||
@test actual[1, 1] == -1.0
|
|
||||||
@test actual[3, 1] == 1.0
|
|
||||||
@test actual[4, 1] == 1.0
|
|
||||||
@test actual[5, 1] == 1.0
|
|
||||||
@test actual[3, 2] == -1.0
|
|
||||||
@test actual[6, 2] == 1.0
|
|
||||||
@test actual[4, 3] == -1.0
|
|
||||||
@test actual[6, 3] == -1.0
|
|
||||||
@test actual[7, 3] == 1.0
|
|
||||||
@test actual[8, 3] == 1.0
|
|
||||||
@test actual[9, 3] == 1.0
|
|
||||||
@test actual[2, 4] == -1.0
|
|
||||||
@test actual[5, 4] == -1.0
|
|
||||||
@test actual[7, 4] == -1.0
|
|
||||||
@test actual[10, 4] == 1.0
|
|
||||||
@test actual[10, 5] == -1.0
|
|
||||||
@test actual[11, 5] == 1.0
|
|
||||||
@test actual[12, 5] == 1.0
|
|
||||||
@test actual[13, 5] == 1.0
|
|
||||||
@test actual[8, 6] == -1.0
|
|
||||||
@test actual[14, 6] == 1.0
|
|
||||||
@test actual[15, 6] == 1.0
|
|
||||||
@test actual[14, 7] == -1.0
|
|
||||||
@test actual[9, 8] == -1.0
|
|
||||||
@test actual[15, 8] == -1.0
|
|
||||||
@test actual[16, 8] == 1.0
|
|
||||||
@test actual[17, 8] == 1.0
|
|
||||||
@test actual[16, 9] == -1.0
|
|
||||||
@test actual[18, 9] == 1.0
|
|
||||||
@test actual[11, 10] == -1.0
|
|
||||||
@test actual[18, 10] == -1.0
|
|
||||||
@test actual[12, 11] == -1.0
|
|
||||||
@test actual[19, 11] == 1.0
|
|
||||||
@test actual[13, 12] == -1.0
|
|
||||||
@test actual[19, 12] == -1.0
|
|
||||||
@test actual[20, 12] == 1.0
|
|
||||||
@test actual[17, 13] == -1.0
|
|
||||||
@test actual[20, 13] == -1.0
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "_injection_shift_factors" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14.json.gz")
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
actual = UnitCommitment._injection_shift_factors(
|
|
||||||
lines = sc.lines,
|
|
||||||
buses = sc.buses,
|
|
||||||
)
|
|
||||||
@test size(actual) == (20, 13)
|
|
||||||
@test round.(actual, digits = 2) == [
|
|
||||||
-0.84 -0.75 -0.67 -0.61 -0.63 -0.66 -0.66 -0.65 -0.65 -0.64 -0.63 -0.63 -0.64
|
|
||||||
-0.16 -0.25 -0.33 -0.39 -0.37 -0.34 -0.34 -0.35 -0.35 -0.36 -0.37 -0.37 -0.36
|
|
||||||
0.03 -0.53 -0.15 -0.1 -0.12 -0.14 -0.14 -0.14 -0.13 -0.13 -0.12 -0.12 -0.13
|
|
||||||
0.06 -0.14 -0.32 -0.22 -0.25 -0.3 -0.3 -0.29 -0.28 -0.27 -0.25 -0.26 -0.27
|
|
||||||
0.08 -0.07 -0.2 -0.29 -0.26 -0.22 -0.22 -0.22 -0.23 -0.25 -0.26 -0.26 -0.24
|
|
||||||
0.03 0.47 -0.15 -0.1 -0.12 -0.14 -0.14 -0.14 -0.13 -0.13 -0.12 -0.12 -0.13
|
|
||||||
0.08 0.31 0.5 -0.3 -0.03 0.36 0.36 0.28 0.23 0.1 -0.0 0.02 0.17
|
|
||||||
0.0 0.01 0.02 -0.01 -0.22 -0.63 -0.63 -0.45 -0.41 -0.32 -0.24 -0.25 -0.36
|
|
||||||
0.0 0.01 0.01 -0.01 -0.12 -0.17 -0.17 -0.26 -0.24 -0.18 -0.14 -0.14 -0.21
|
|
||||||
-0.0 -0.02 -0.03 0.02 -0.66 -0.2 -0.2 -0.29 -0.36 -0.5 -0.63 -0.61 -0.43
|
|
||||||
-0.0 -0.01 -0.02 0.01 0.21 -0.12 -0.12 -0.17 -0.28 -0.53 0.18 0.15 -0.03
|
|
||||||
-0.0 -0.0 -0.0 0.0 0.03 -0.02 -0.02 -0.03 -0.02 0.01 -0.52 -0.17 -0.09
|
|
||||||
-0.0 -0.01 -0.01 0.01 0.11 -0.06 -0.06 -0.09 -0.05 0.02 -0.28 -0.59 -0.31
|
|
||||||
-0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -1.0 -0.0 -0.0 -0.0 -0.0 -0.0 0.0
|
|
||||||
0.0 0.01 0.02 -0.01 -0.22 0.37 0.37 -0.45 -0.41 -0.32 -0.24 -0.25 -0.36
|
|
||||||
0.0 0.01 0.02 -0.01 -0.21 0.12 0.12 0.17 -0.72 -0.47 -0.18 -0.15 0.03
|
|
||||||
0.0 0.01 0.01 -0.01 -0.14 0.08 0.08 0.12 0.07 -0.03 -0.2 -0.24 -0.6
|
|
||||||
0.0 0.01 0.02 -0.01 -0.21 0.12 0.12 0.17 0.28 -0.47 -0.18 -0.15 0.03
|
|
||||||
-0.0 -0.0 -0.0 0.0 0.03 -0.02 -0.02 -0.03 -0.02 0.01 0.48 -0.17 -0.09
|
|
||||||
-0.0 -0.01 -0.01 0.01 0.14 -0.08 -0.08 -0.12 -0.07 0.03 0.2 0.24 -0.4
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "_line_outage_factors" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14.json.gz")
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
isf_before = UnitCommitment._injection_shift_factors(
|
|
||||||
lines = sc.lines,
|
|
||||||
buses = sc.buses,
|
|
||||||
)
|
|
||||||
lodf = UnitCommitment._line_outage_factors(
|
|
||||||
lines = sc.lines,
|
|
||||||
buses = sc.buses,
|
|
||||||
isf = isf_before,
|
|
||||||
)
|
|
||||||
for contingency in sc.contingencies
|
|
||||||
for lc in contingency.lines
|
|
||||||
prev_susceptance = lc.susceptance
|
|
||||||
lc.susceptance = 0.0
|
|
||||||
isf_after = UnitCommitment._injection_shift_factors(
|
|
||||||
lines = sc.lines,
|
|
||||||
buses = sc.buses,
|
|
||||||
)
|
|
||||||
lc.susceptance = prev_susceptance
|
|
||||||
for lm in sc.lines
|
|
||||||
expected = isf_after[lm.offset, :]
|
|
||||||
actual =
|
|
||||||
isf_before[lm.offset, :] +
|
|
||||||
lodf[lm.offset, lc.offset] * isf_before[lc.offset, :]
|
|
||||||
@test norm(expected - actual) < 1e-6
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -0,0 +1,58 @@
|
|||||||
|
module UnitCommitmentT
|
||||||
|
|
||||||
|
using JuliaFormatter
|
||||||
|
using UnitCommitment
|
||||||
|
using Test
|
||||||
|
|
||||||
|
include("usage.jl")
|
||||||
|
include("import/egret_test.jl")
|
||||||
|
include("instance/read_test.jl")
|
||||||
|
include("instance/migrate_test.jl")
|
||||||
|
include("model/formulations_test.jl")
|
||||||
|
include("solution/methods/XavQiuWanThi19/filter_test.jl")
|
||||||
|
include("solution/methods/XavQiuWanThi19/find_test.jl")
|
||||||
|
include("solution/methods/XavQiuWanThi19/sensitivity_test.jl")
|
||||||
|
include("transform/initcond_test.jl")
|
||||||
|
include("transform/slice_test.jl")
|
||||||
|
include("transform/randomize/XavQiuAhm2021_test.jl")
|
||||||
|
include("validation/repair_test.jl")
|
||||||
|
include("lmp/conventional_test.jl")
|
||||||
|
include("lmp/aelmp_test.jl")
|
||||||
|
|
||||||
|
basedir = dirname(@__FILE__)
|
||||||
|
|
||||||
|
function fixture(path::String)::String
|
||||||
|
return "$basedir/../fixtures/$path"
|
||||||
|
end
|
||||||
|
|
||||||
|
function runtests()
|
||||||
|
println("Running tests...")
|
||||||
|
UnitCommitment._setup_logger(level = Base.CoreLogging.Error)
|
||||||
|
@testset "UnitCommitment" begin
|
||||||
|
usage_test()
|
||||||
|
import_egret_test()
|
||||||
|
instance_read_test()
|
||||||
|
instance_migrate_test()
|
||||||
|
model_formulations_test()
|
||||||
|
solution_methods_XavQiuWanThi19_filter_test()
|
||||||
|
solution_methods_XavQiuWanThi19_find_test()
|
||||||
|
solution_methods_XavQiuWanThi19_sensitivity_test()
|
||||||
|
transform_initcond_test()
|
||||||
|
transform_slice_test()
|
||||||
|
transform_randomize_XavQiuAhm2021_test()
|
||||||
|
validation_repair_test()
|
||||||
|
lmp_conventional_test()
|
||||||
|
lmp_aelmp_test()
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
function format()
|
||||||
|
JuliaFormatter.format(basedir, verbose = true)
|
||||||
|
JuliaFormatter.format("$basedir/../../src", verbose = true)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
export runtests, format
|
||||||
|
|
||||||
|
end # module UnitCommitmentT
|
@ -0,0 +1,23 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
function import_egret_test()
|
||||||
|
@testset "read_egret_solution" begin
|
||||||
|
solution =
|
||||||
|
UnitCommitment.read_egret_solution(fixture("egret_output.json.gz"))
|
||||||
|
for attr in
|
||||||
|
["Is on", "Thermal production (MW)", "Thermal production cost (\$)"]
|
||||||
|
@test attr in keys(solution)
|
||||||
|
@test "115_STEAM_1" in keys(solution[attr])
|
||||||
|
@test length(solution[attr]["115_STEAM_1"]) == 48
|
||||||
|
end
|
||||||
|
@test solution["Thermal production cost (\$)"]["315_CT_6"][15:20] ==
|
||||||
|
[0.0, 0.0, 884.44, 1470.71, 1470.71, 884.44]
|
||||||
|
@test solution["Startup cost (\$)"]["315_CT_6"][15:20] ==
|
||||||
|
[0.0, 0.0, 5665.23, 0.0, 0.0, 0.0]
|
||||||
|
@test length(keys(solution["Is on"])) == 154
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,24 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
function instance_migrate_test()
|
||||||
|
@testset "read v0.2" begin
|
||||||
|
instance = UnitCommitment.read(fixture("/ucjl-0.2.json.gz"))
|
||||||
|
@test length(instance.scenarios) == 1
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
@test length(sc.reserves_by_name["r1"].amount) == 4
|
||||||
|
@test sc.thermal_units_by_name["g2"].reserves[1].name == "r1"
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "read v0.3" begin
|
||||||
|
instance = UnitCommitment.read(fixture("/ucjl-0.3.json.gz"))
|
||||||
|
@test length(instance.scenarios) == 1
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
@test length(sc.thermal_units) == 6
|
||||||
|
@test length(sc.buses) == 14
|
||||||
|
@test length(sc.lines) == 20
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,168 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
function instance_read_test()
|
||||||
|
@testset "read_benchmark" begin
|
||||||
|
instance = UnitCommitment.read(fixture("case14.json.gz"))
|
||||||
|
|
||||||
|
@test repr(instance) == (
|
||||||
|
"UnitCommitmentInstance(1 scenarios, 6 thermal units, 0 profiled units, 14 buses, " *
|
||||||
|
"20 lines, 19 contingencies, 1 price sensitive loads, 4 time steps)"
|
||||||
|
)
|
||||||
|
|
||||||
|
@test length(instance.scenarios) == 1
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
@test length(sc.lines) == 20
|
||||||
|
@test length(sc.buses) == 14
|
||||||
|
@test length(sc.thermal_units) == 6
|
||||||
|
@test length(sc.contingencies) == 19
|
||||||
|
@test length(sc.price_sensitive_loads) == 1
|
||||||
|
@test instance.time == 4
|
||||||
|
|
||||||
|
@test sc.lines[5].name == "l5"
|
||||||
|
@test sc.lines[5].source.name == "b2"
|
||||||
|
@test sc.lines[5].target.name == "b5"
|
||||||
|
@test sc.lines[5].reactance ≈ 0.17388
|
||||||
|
@test sc.lines[5].susceptance ≈ 10.037550333
|
||||||
|
@test sc.lines[5].normal_flow_limit == [1e8 for t in 1:4]
|
||||||
|
@test sc.lines[5].emergency_flow_limit == [1e8 for t in 1:4]
|
||||||
|
@test sc.lines[5].flow_limit_penalty == [5e3 for t in 1:4]
|
||||||
|
@test sc.lines_by_name["l5"].name == "l5"
|
||||||
|
|
||||||
|
@test sc.lines[1].name == "l1"
|
||||||
|
@test sc.lines[1].source.name == "b1"
|
||||||
|
@test sc.lines[1].target.name == "b2"
|
||||||
|
@test sc.lines[1].reactance ≈ 0.059170
|
||||||
|
@test sc.lines[1].susceptance ≈ 29.496860773945
|
||||||
|
@test sc.lines[1].normal_flow_limit == [300.0 for t in 1:4]
|
||||||
|
@test sc.lines[1].emergency_flow_limit == [400.0 for t in 1:4]
|
||||||
|
@test sc.lines[1].flow_limit_penalty == [1e3 for t in 1:4]
|
||||||
|
|
||||||
|
@test sc.buses[9].name == "b9"
|
||||||
|
@test sc.buses[9].load == [35.36638, 33.25495, 31.67138, 31.14353]
|
||||||
|
@test sc.buses_by_name["b9"].name == "b9"
|
||||||
|
|
||||||
|
@test sc.reserves[1].name == "r1"
|
||||||
|
@test sc.reserves[1].type == "spinning"
|
||||||
|
@test sc.reserves[1].amount == [100.0, 100.0, 100.0, 100.0]
|
||||||
|
@test sc.reserves_by_name["r1"].name == "r1"
|
||||||
|
|
||||||
|
unit = sc.thermal_units[1]
|
||||||
|
@test unit.name == "g1"
|
||||||
|
@test unit.bus.name == "b1"
|
||||||
|
@test unit.ramp_up_limit == 1e6
|
||||||
|
@test unit.ramp_down_limit == 1e6
|
||||||
|
@test unit.startup_limit == 1e6
|
||||||
|
@test unit.shutdown_limit == 1e6
|
||||||
|
@test unit.must_run == [false for t in 1:4]
|
||||||
|
@test unit.min_power_cost == [1400.0 for t in 1:4]
|
||||||
|
@test unit.min_uptime == 1
|
||||||
|
@test unit.min_downtime == 1
|
||||||
|
for t in 1:1
|
||||||
|
@test unit.cost_segments[1].mw[t] == 10.0
|
||||||
|
@test unit.cost_segments[2].mw[t] == 20.0
|
||||||
|
@test unit.cost_segments[3].mw[t] == 5.0
|
||||||
|
@test unit.cost_segments[1].cost[t] ≈ 20.0
|
||||||
|
@test unit.cost_segments[2].cost[t] ≈ 30.0
|
||||||
|
@test unit.cost_segments[3].cost[t] ≈ 40.0
|
||||||
|
end
|
||||||
|
@test length(unit.startup_categories) == 3
|
||||||
|
@test unit.startup_categories[1].delay == 1
|
||||||
|
@test unit.startup_categories[2].delay == 2
|
||||||
|
@test unit.startup_categories[3].delay == 3
|
||||||
|
@test unit.startup_categories[1].cost == 1000.0
|
||||||
|
@test unit.startup_categories[2].cost == 1500.0
|
||||||
|
@test unit.startup_categories[3].cost == 2000.0
|
||||||
|
@test length(unit.reserves) == 0
|
||||||
|
@test sc.thermal_units_by_name["g1"].name == "g1"
|
||||||
|
|
||||||
|
unit = sc.thermal_units[2]
|
||||||
|
@test unit.name == "g2"
|
||||||
|
@test unit.must_run == [false for t in 1:4]
|
||||||
|
@test length(unit.reserves) == 1
|
||||||
|
|
||||||
|
unit = sc.thermal_units[3]
|
||||||
|
@test unit.name == "g3"
|
||||||
|
@test unit.bus.name == "b3"
|
||||||
|
@test unit.ramp_up_limit == 70.0
|
||||||
|
@test unit.ramp_down_limit == 70.0
|
||||||
|
@test unit.startup_limit == 70.0
|
||||||
|
@test unit.shutdown_limit == 70.0
|
||||||
|
@test unit.must_run == [true for t in 1:4]
|
||||||
|
@test unit.min_power_cost == [0.0 for t in 1:4]
|
||||||
|
@test unit.min_uptime == 1
|
||||||
|
@test unit.min_downtime == 1
|
||||||
|
for t in 1:4
|
||||||
|
@test unit.cost_segments[1].mw[t] ≈ 33
|
||||||
|
@test unit.cost_segments[2].mw[t] ≈ 33
|
||||||
|
@test unit.cost_segments[3].mw[t] ≈ 34
|
||||||
|
@test unit.cost_segments[1].cost[t] ≈ 33.75
|
||||||
|
@test unit.cost_segments[2].cost[t] ≈ 38.04
|
||||||
|
@test unit.cost_segments[3].cost[t] ≈ 44.77853
|
||||||
|
end
|
||||||
|
@test length(unit.reserves) == 1
|
||||||
|
@test unit.reserves[1].name == "r1"
|
||||||
|
|
||||||
|
@test sc.contingencies[1].lines == [sc.lines[1]]
|
||||||
|
@test sc.contingencies[1].thermal_units == []
|
||||||
|
@test sc.contingencies[1].name == "c1"
|
||||||
|
@test sc.contingencies_by_name["c1"].name == "c1"
|
||||||
|
|
||||||
|
load = sc.price_sensitive_loads[1]
|
||||||
|
@test load.name == "ps1"
|
||||||
|
@test load.bus.name == "b3"
|
||||||
|
@test load.revenue == [100.0 for t in 1:4]
|
||||||
|
@test load.demand == [50.0 for t in 1:4]
|
||||||
|
@test sc.price_sensitive_loads_by_name["ps1"].name == "ps1"
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "read_benchmark sub-hourly" begin
|
||||||
|
instance = UnitCommitment.read(fixture("case14-sub-hourly.json.gz"))
|
||||||
|
@test instance.time == 4
|
||||||
|
unit = instance.scenarios[1].thermal_units[1]
|
||||||
|
@test unit.name == "g1"
|
||||||
|
@test unit.min_uptime == 2
|
||||||
|
@test unit.min_downtime == 2
|
||||||
|
@test length(unit.startup_categories) == 3
|
||||||
|
@test unit.startup_categories[1].delay == 2
|
||||||
|
@test unit.startup_categories[2].delay == 4
|
||||||
|
@test unit.startup_categories[3].delay == 6
|
||||||
|
@test unit.initial_status == -200
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "read_benchmark profiled-units" begin
|
||||||
|
instance = UnitCommitment.read(fixture("case14-profiled.json.gz"))
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
@test length(sc.profiled_units) == 2
|
||||||
|
|
||||||
|
first_pu = sc.profiled_units[1]
|
||||||
|
@test first_pu.name == "g7"
|
||||||
|
@test first_pu.bus.name == "b4"
|
||||||
|
@test first_pu.cost == [100.0 for t in 1:4]
|
||||||
|
@test first_pu.min_power == [60.0 for t in 1:4]
|
||||||
|
@test first_pu.max_power == [100.0 for t in 1:4]
|
||||||
|
@test sc.profiled_units_by_name["g7"].name == "g7"
|
||||||
|
|
||||||
|
second_pu = sc.profiled_units[2]
|
||||||
|
@test second_pu.name == "g8"
|
||||||
|
@test second_pu.bus.name == "b5"
|
||||||
|
@test second_pu.cost == [50.0 for t in 1:4]
|
||||||
|
@test second_pu.min_power == [0.0 for t in 1:4]
|
||||||
|
@test second_pu.max_power == [120.0 for t in 1:4]
|
||||||
|
@test sc.profiled_units_by_name["g8"].name == "g8"
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "read_benchmark commitmemt-status" begin
|
||||||
|
instance = UnitCommitment.read(fixture("case14-fixed-status.json.gz"))
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
|
||||||
|
@test sc.thermal_units[1].commitment_status == [nothing for t in 1:4]
|
||||||
|
@test sc.thermal_units[2].commitment_status == [true for t in 1:4]
|
||||||
|
@test sc.thermal_units[4].commitment_status == [false for t in 1:4]
|
||||||
|
@test sc.thermal_units[6].commitment_status ==
|
||||||
|
[false, nothing, true, nothing]
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,40 @@
|
|||||||
|
# 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, Cbc, HiGHS, JuMP
|
||||||
|
import UnitCommitment: AELMP
|
||||||
|
|
||||||
|
function lmp_aelmp_test()
|
||||||
|
@testset "aelmp" begin
|
||||||
|
path = fixture("aelmp_simple.json.gz")
|
||||||
|
# model has to be solved first
|
||||||
|
instance = UnitCommitment.read(path)
|
||||||
|
model = UnitCommitment.build_model(
|
||||||
|
instance = instance,
|
||||||
|
optimizer = Cbc.Optimizer,
|
||||||
|
variable_names = true,
|
||||||
|
)
|
||||||
|
JuMP.set_silent(model)
|
||||||
|
UnitCommitment.optimize!(model)
|
||||||
|
|
||||||
|
# policy 1: allow offlines; consider startups
|
||||||
|
aelmp_1 = UnitCommitment.compute_lmp(
|
||||||
|
model,
|
||||||
|
AELMP(),
|
||||||
|
optimizer = HiGHS.Optimizer,
|
||||||
|
)
|
||||||
|
@test aelmp_1["s1", "B1", 1] ≈ 231.7 atol = 0.1
|
||||||
|
|
||||||
|
# policy 2: do not allow offlines; but consider startups
|
||||||
|
aelmp_2 = UnitCommitment.compute_lmp(
|
||||||
|
model,
|
||||||
|
AELMP(
|
||||||
|
allow_offline_participation = false,
|
||||||
|
consider_startup_costs = true,
|
||||||
|
),
|
||||||
|
optimizer = HiGHS.Optimizer,
|
||||||
|
)
|
||||||
|
@test aelmp_2["s1", "B1", 1] ≈ 274.3 atol = 0.1
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,53 @@
|
|||||||
|
# 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, Cbc, HiGHS, JuMP
|
||||||
|
import UnitCommitment: ConventionalLMP
|
||||||
|
|
||||||
|
function solve_conventional_testcase(path::String)
|
||||||
|
instance = UnitCommitment.read(path)
|
||||||
|
model = UnitCommitment.build_model(
|
||||||
|
instance = instance,
|
||||||
|
optimizer = Cbc.Optimizer,
|
||||||
|
variable_names = true,
|
||||||
|
)
|
||||||
|
JuMP.set_silent(model)
|
||||||
|
UnitCommitment.optimize!(model)
|
||||||
|
lmp = UnitCommitment.compute_lmp(
|
||||||
|
model,
|
||||||
|
ConventionalLMP(),
|
||||||
|
optimizer = HiGHS.Optimizer,
|
||||||
|
)
|
||||||
|
return lmp
|
||||||
|
end
|
||||||
|
|
||||||
|
function lmp_conventional_test()
|
||||||
|
@testset "conventional" begin
|
||||||
|
# instance 1
|
||||||
|
path = fixture("lmp_simple_test_1.json.gz")
|
||||||
|
lmp = solve_conventional_testcase(path)
|
||||||
|
@test lmp["s1", "A", 1] == 50.0
|
||||||
|
@test lmp["s1", "B", 1] == 50.0
|
||||||
|
|
||||||
|
# instance 2
|
||||||
|
path = fixture("lmp_simple_test_2.json.gz")
|
||||||
|
lmp = solve_conventional_testcase(path)
|
||||||
|
@test lmp["s1", "A", 1] == 50.0
|
||||||
|
@test lmp["s1", "B", 1] == 60.0
|
||||||
|
|
||||||
|
# instance 3
|
||||||
|
path = fixture("lmp_simple_test_3.json.gz")
|
||||||
|
lmp = solve_conventional_testcase(path)
|
||||||
|
@test lmp["s1", "A", 1] == 50.0
|
||||||
|
@test lmp["s1", "B", 1] == 70.0
|
||||||
|
@test lmp["s1", "C", 1] == 100.0
|
||||||
|
|
||||||
|
# instance 4
|
||||||
|
path = fixture("lmp_simple_test_4.json.gz")
|
||||||
|
lmp = solve_conventional_testcase(path)
|
||||||
|
@test lmp["s1", "A", 1] == 50.0
|
||||||
|
@test lmp["s1", "B", 1] == 70.0
|
||||||
|
@test lmp["s1", "C", 1] == 90.0
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,86 @@
|
|||||||
|
# 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
|
||||||
|
using JuMP
|
||||||
|
using Cbc
|
||||||
|
using JSON
|
||||||
|
import UnitCommitment:
|
||||||
|
ArrCon2000,
|
||||||
|
CarArr2006,
|
||||||
|
DamKucRajAta2016,
|
||||||
|
Formulation,
|
||||||
|
Gar1962,
|
||||||
|
KnuOstWat2018,
|
||||||
|
MorLatRam2013,
|
||||||
|
PanGua2016,
|
||||||
|
XavQiuWanThi2019,
|
||||||
|
WanHob2016
|
||||||
|
|
||||||
|
function _test(
|
||||||
|
formulation::Formulation;
|
||||||
|
instances = ["case14"],
|
||||||
|
dump::Bool = false,
|
||||||
|
)::Nothing
|
||||||
|
for instance_name in instances
|
||||||
|
instance = UnitCommitment.read(fixture("$(instance_name).json.gz"))
|
||||||
|
model = UnitCommitment.build_model(
|
||||||
|
instance = instance,
|
||||||
|
formulation = formulation,
|
||||||
|
optimizer = Cbc.Optimizer,
|
||||||
|
variable_names = true,
|
||||||
|
)
|
||||||
|
set_silent(model)
|
||||||
|
UnitCommitment.optimize!(model)
|
||||||
|
solution = UnitCommitment.solution(model)
|
||||||
|
if dump
|
||||||
|
open("/tmp/ucjl.json", "w") do f
|
||||||
|
return write(f, JSON.json(solution, 2))
|
||||||
|
end
|
||||||
|
write_to_file(model, "/tmp/ucjl.lp")
|
||||||
|
end
|
||||||
|
@test UnitCommitment.validate(instance, solution)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
function model_formulations_test()
|
||||||
|
@testset "formulations" begin
|
||||||
|
@testset "default" begin
|
||||||
|
_test(Formulation())
|
||||||
|
end
|
||||||
|
@testset "ArrCon2000" begin
|
||||||
|
_test(Formulation(ramping = ArrCon2000.Ramping()))
|
||||||
|
end
|
||||||
|
@testset "DamKucRajAta2016" begin
|
||||||
|
_test(Formulation(ramping = DamKucRajAta2016.Ramping()))
|
||||||
|
end
|
||||||
|
@testset "MorLatRam2013" begin
|
||||||
|
_test(
|
||||||
|
Formulation(
|
||||||
|
ramping = MorLatRam2013.Ramping(),
|
||||||
|
startup_costs = MorLatRam2013.StartupCosts(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
@testset "PanGua2016" begin
|
||||||
|
_test(Formulation(ramping = PanGua2016.Ramping()))
|
||||||
|
end
|
||||||
|
@testset "Gar1962" begin
|
||||||
|
_test(Formulation(pwl_costs = Gar1962.PwlCosts()))
|
||||||
|
end
|
||||||
|
@testset "CarArr2006" begin
|
||||||
|
_test(Formulation(pwl_costs = CarArr2006.PwlCosts()))
|
||||||
|
end
|
||||||
|
@testset "KnuOstWat2018" begin
|
||||||
|
_test(Formulation(pwl_costs = KnuOstWat2018.PwlCosts()))
|
||||||
|
end
|
||||||
|
@testset "WanHob2016" begin
|
||||||
|
_test(
|
||||||
|
Formulation(ramping = WanHob2016.Ramping()),
|
||||||
|
instances = ["case14-flex"],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,86 @@
|
|||||||
|
# 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, Test, LinearAlgebra
|
||||||
|
import UnitCommitment: _Violation, _offer, _query
|
||||||
|
|
||||||
|
function solution_methods_XavQiuWanThi19_filter_test()
|
||||||
|
@testset "_ViolationFilter" begin
|
||||||
|
instance = UnitCommitment.read(fixture("case14.json.gz"))
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
filter =
|
||||||
|
UnitCommitment._ViolationFilter(max_per_line = 1, max_total = 2)
|
||||||
|
_offer(
|
||||||
|
filter,
|
||||||
|
_Violation(
|
||||||
|
time = 1,
|
||||||
|
monitored_line = sc.lines[1],
|
||||||
|
outage_line = nothing,
|
||||||
|
amount = 100.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_offer(
|
||||||
|
filter,
|
||||||
|
_Violation(
|
||||||
|
time = 1,
|
||||||
|
monitored_line = sc.lines[1],
|
||||||
|
outage_line = sc.lines[1],
|
||||||
|
amount = 300.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_offer(
|
||||||
|
filter,
|
||||||
|
_Violation(
|
||||||
|
time = 1,
|
||||||
|
monitored_line = sc.lines[1],
|
||||||
|
outage_line = sc.lines[5],
|
||||||
|
amount = 500.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_offer(
|
||||||
|
filter,
|
||||||
|
_Violation(
|
||||||
|
time = 1,
|
||||||
|
monitored_line = sc.lines[1],
|
||||||
|
outage_line = sc.lines[4],
|
||||||
|
amount = 400.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_offer(
|
||||||
|
filter,
|
||||||
|
_Violation(
|
||||||
|
time = 1,
|
||||||
|
monitored_line = sc.lines[2],
|
||||||
|
outage_line = sc.lines[1],
|
||||||
|
amount = 200.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
_offer(
|
||||||
|
filter,
|
||||||
|
_Violation(
|
||||||
|
time = 1,
|
||||||
|
monitored_line = sc.lines[2],
|
||||||
|
outage_line = sc.lines[8],
|
||||||
|
amount = 100.0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
actual = _query(filter)
|
||||||
|
expected = [
|
||||||
|
_Violation(
|
||||||
|
time = 1,
|
||||||
|
monitored_line = sc.lines[2],
|
||||||
|
outage_line = sc.lines[1],
|
||||||
|
amount = 200.0,
|
||||||
|
),
|
||||||
|
_Violation(
|
||||||
|
time = 1,
|
||||||
|
monitored_line = sc.lines[1],
|
||||||
|
outage_line = sc.lines[5],
|
||||||
|
amount = 500.0,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
@test actual == expected
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,39 @@
|
|||||||
|
# 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, Test, LinearAlgebra
|
||||||
|
import UnitCommitment: _Violation, _offer, _query
|
||||||
|
|
||||||
|
function solution_methods_XavQiuWanThi19_find_test()
|
||||||
|
@testset "find_violations" begin
|
||||||
|
instance = UnitCommitment.read(fixture("case14.json.gz"))
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
for line in sc.lines, t in 1:instance.time
|
||||||
|
line.normal_flow_limit[t] = 1.0
|
||||||
|
line.emergency_flow_limit[t] = 1.0
|
||||||
|
end
|
||||||
|
isf = UnitCommitment._injection_shift_factors(
|
||||||
|
lines = sc.lines,
|
||||||
|
buses = sc.buses,
|
||||||
|
)
|
||||||
|
lodf = UnitCommitment._line_outage_factors(
|
||||||
|
lines = sc.lines,
|
||||||
|
buses = sc.buses,
|
||||||
|
isf = isf,
|
||||||
|
)
|
||||||
|
inj = [1000.0 for b in 1:13, t in 1:instance.time]
|
||||||
|
overflow = [0.0 for l in sc.lines, t in 1:instance.time]
|
||||||
|
violations = UnitCommitment._find_violations(
|
||||||
|
instance = instance,
|
||||||
|
sc = sc,
|
||||||
|
net_injections = inj,
|
||||||
|
overflow = overflow,
|
||||||
|
isf = isf,
|
||||||
|
lodf = lodf,
|
||||||
|
max_per_line = 1,
|
||||||
|
max_per_period = 5,
|
||||||
|
)
|
||||||
|
@test length(violations) == 20
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,149 @@
|
|||||||
|
# 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, Test, LinearAlgebra
|
||||||
|
|
||||||
|
function solution_methods_XavQiuWanThi19_sensitivity_test()
|
||||||
|
@testset "_susceptance_matrix" begin
|
||||||
|
instance = UnitCommitment.read(fixture("/case14.json.gz"))
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
actual = UnitCommitment._susceptance_matrix(sc.lines)
|
||||||
|
@test size(actual) == (20, 20)
|
||||||
|
expected = Diagonal([
|
||||||
|
29.5,
|
||||||
|
7.83,
|
||||||
|
8.82,
|
||||||
|
9.9,
|
||||||
|
10.04,
|
||||||
|
10.2,
|
||||||
|
41.45,
|
||||||
|
8.35,
|
||||||
|
3.14,
|
||||||
|
6.93,
|
||||||
|
8.77,
|
||||||
|
6.82,
|
||||||
|
13.4,
|
||||||
|
9.91,
|
||||||
|
15.87,
|
||||||
|
20.65,
|
||||||
|
6.46,
|
||||||
|
9.09,
|
||||||
|
8.73,
|
||||||
|
5.02,
|
||||||
|
])
|
||||||
|
@test round.(actual, digits = 2) == expected
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "_reduced_incidence_matrix" begin
|
||||||
|
instance = UnitCommitment.read(fixture("/case14.json.gz"))
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
actual = UnitCommitment._reduced_incidence_matrix(
|
||||||
|
lines = sc.lines,
|
||||||
|
buses = sc.buses,
|
||||||
|
)
|
||||||
|
@test size(actual) == (20, 13)
|
||||||
|
@test actual[1, 1] == -1.0
|
||||||
|
@test actual[3, 1] == 1.0
|
||||||
|
@test actual[4, 1] == 1.0
|
||||||
|
@test actual[5, 1] == 1.0
|
||||||
|
@test actual[3, 2] == -1.0
|
||||||
|
@test actual[6, 2] == 1.0
|
||||||
|
@test actual[4, 3] == -1.0
|
||||||
|
@test actual[6, 3] == -1.0
|
||||||
|
@test actual[7, 3] == 1.0
|
||||||
|
@test actual[8, 3] == 1.0
|
||||||
|
@test actual[9, 3] == 1.0
|
||||||
|
@test actual[2, 4] == -1.0
|
||||||
|
@test actual[5, 4] == -1.0
|
||||||
|
@test actual[7, 4] == -1.0
|
||||||
|
@test actual[10, 4] == 1.0
|
||||||
|
@test actual[10, 5] == -1.0
|
||||||
|
@test actual[11, 5] == 1.0
|
||||||
|
@test actual[12, 5] == 1.0
|
||||||
|
@test actual[13, 5] == 1.0
|
||||||
|
@test actual[8, 6] == -1.0
|
||||||
|
@test actual[14, 6] == 1.0
|
||||||
|
@test actual[15, 6] == 1.0
|
||||||
|
@test actual[14, 7] == -1.0
|
||||||
|
@test actual[9, 8] == -1.0
|
||||||
|
@test actual[15, 8] == -1.0
|
||||||
|
@test actual[16, 8] == 1.0
|
||||||
|
@test actual[17, 8] == 1.0
|
||||||
|
@test actual[16, 9] == -1.0
|
||||||
|
@test actual[18, 9] == 1.0
|
||||||
|
@test actual[11, 10] == -1.0
|
||||||
|
@test actual[18, 10] == -1.0
|
||||||
|
@test actual[12, 11] == -1.0
|
||||||
|
@test actual[19, 11] == 1.0
|
||||||
|
@test actual[13, 12] == -1.0
|
||||||
|
@test actual[19, 12] == -1.0
|
||||||
|
@test actual[20, 12] == 1.0
|
||||||
|
@test actual[17, 13] == -1.0
|
||||||
|
@test actual[20, 13] == -1.0
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "_injection_shift_factors" begin
|
||||||
|
instance = UnitCommitment.read(fixture("/case14.json.gz"))
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
actual = UnitCommitment._injection_shift_factors(
|
||||||
|
lines = sc.lines,
|
||||||
|
buses = sc.buses,
|
||||||
|
)
|
||||||
|
@test size(actual) == (20, 13)
|
||||||
|
@test round.(actual, digits = 2) == [
|
||||||
|
-0.84 -0.75 -0.67 -0.61 -0.63 -0.66 -0.66 -0.65 -0.65 -0.64 -0.63 -0.63 -0.64
|
||||||
|
-0.16 -0.25 -0.33 -0.39 -0.37 -0.34 -0.34 -0.35 -0.35 -0.36 -0.37 -0.37 -0.36
|
||||||
|
0.03 -0.53 -0.15 -0.1 -0.12 -0.14 -0.14 -0.14 -0.13 -0.13 -0.12 -0.12 -0.13
|
||||||
|
0.06 -0.14 -0.32 -0.22 -0.25 -0.3 -0.3 -0.29 -0.28 -0.27 -0.25 -0.26 -0.27
|
||||||
|
0.08 -0.07 -0.2 -0.29 -0.26 -0.22 -0.22 -0.22 -0.23 -0.25 -0.26 -0.26 -0.24
|
||||||
|
0.03 0.47 -0.15 -0.1 -0.12 -0.14 -0.14 -0.14 -0.13 -0.13 -0.12 -0.12 -0.13
|
||||||
|
0.08 0.31 0.5 -0.3 -0.03 0.36 0.36 0.28 0.23 0.1 -0.0 0.02 0.17
|
||||||
|
0.0 0.01 0.02 -0.01 -0.22 -0.63 -0.63 -0.45 -0.41 -0.32 -0.24 -0.25 -0.36
|
||||||
|
0.0 0.01 0.01 -0.01 -0.12 -0.17 -0.17 -0.26 -0.24 -0.18 -0.14 -0.14 -0.21
|
||||||
|
-0.0 -0.02 -0.03 0.02 -0.66 -0.2 -0.2 -0.29 -0.36 -0.5 -0.63 -0.61 -0.43
|
||||||
|
-0.0 -0.01 -0.02 0.01 0.21 -0.12 -0.12 -0.17 -0.28 -0.53 0.18 0.15 -0.03
|
||||||
|
-0.0 -0.0 -0.0 0.0 0.03 -0.02 -0.02 -0.03 -0.02 0.01 -0.52 -0.17 -0.09
|
||||||
|
-0.0 -0.01 -0.01 0.01 0.11 -0.06 -0.06 -0.09 -0.05 0.02 -0.28 -0.59 -0.31
|
||||||
|
-0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -1.0 -0.0 -0.0 -0.0 -0.0 -0.0 0.0
|
||||||
|
0.0 0.01 0.02 -0.01 -0.22 0.37 0.37 -0.45 -0.41 -0.32 -0.24 -0.25 -0.36
|
||||||
|
0.0 0.01 0.02 -0.01 -0.21 0.12 0.12 0.17 -0.72 -0.47 -0.18 -0.15 0.03
|
||||||
|
0.0 0.01 0.01 -0.01 -0.14 0.08 0.08 0.12 0.07 -0.03 -0.2 -0.24 -0.6
|
||||||
|
0.0 0.01 0.02 -0.01 -0.21 0.12 0.12 0.17 0.28 -0.47 -0.18 -0.15 0.03
|
||||||
|
-0.0 -0.0 -0.0 0.0 0.03 -0.02 -0.02 -0.03 -0.02 0.01 0.48 -0.17 -0.09
|
||||||
|
-0.0 -0.01 -0.01 0.01 0.14 -0.08 -0.08 -0.12 -0.07 0.03 0.2 0.24 -0.4
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "_line_outage_factors" begin
|
||||||
|
instance = UnitCommitment.read(fixture("/case14.json.gz"))
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
isf_before = UnitCommitment._injection_shift_factors(
|
||||||
|
lines = sc.lines,
|
||||||
|
buses = sc.buses,
|
||||||
|
)
|
||||||
|
lodf = UnitCommitment._line_outage_factors(
|
||||||
|
lines = sc.lines,
|
||||||
|
buses = sc.buses,
|
||||||
|
isf = isf_before,
|
||||||
|
)
|
||||||
|
for contingency in sc.contingencies
|
||||||
|
for lc in contingency.lines
|
||||||
|
prev_susceptance = lc.susceptance
|
||||||
|
lc.susceptance = 0.0
|
||||||
|
isf_after = UnitCommitment._injection_shift_factors(
|
||||||
|
lines = sc.lines,
|
||||||
|
buses = sc.buses,
|
||||||
|
)
|
||||||
|
lc.susceptance = prev_susceptance
|
||||||
|
for lm in sc.lines
|
||||||
|
expected = isf_after[lm.offset, :]
|
||||||
|
actual =
|
||||||
|
isf_before[lm.offset, :] +
|
||||||
|
lodf[lm.offset, lc.offset] * isf_before[lc.offset, :]
|
||||||
|
@test norm(expected - actual) < 1e-6
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,30 @@
|
|||||||
|
# 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, Cbc, JuMP
|
||||||
|
|
||||||
|
function transform_initcond_test()
|
||||||
|
@testset "generate_initial_conditions!" begin
|
||||||
|
# Load instance
|
||||||
|
instance = UnitCommitment.read(fixture("case118-initcond.json.gz"))
|
||||||
|
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
||||||
|
sc = instance.scenarios[1]
|
||||||
|
# All units should have unknown initial conditions
|
||||||
|
for g in sc.thermal_units
|
||||||
|
@test g.initial_power === nothing
|
||||||
|
@test g.initial_status === nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate initial conditions
|
||||||
|
UnitCommitment.generate_initial_conditions!(sc, optimizer)
|
||||||
|
|
||||||
|
# All units should now have known initial conditions
|
||||||
|
for g in sc.thermal_units
|
||||||
|
@test g.initial_power !== nothing
|
||||||
|
@test g.initial_status !== nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: Check that initial conditions are feasible
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,106 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import Random
|
||||||
|
import UnitCommitment: XavQiuAhm2021
|
||||||
|
|
||||||
|
using Distributions
|
||||||
|
using Random
|
||||||
|
using UnitCommitment, Cbc, JuMP
|
||||||
|
|
||||||
|
function get_scenario()
|
||||||
|
return UnitCommitment.read_benchmark(
|
||||||
|
"matpower/case118/2017-02-01",
|
||||||
|
).scenarios[1]
|
||||||
|
end
|
||||||
|
system_load(sc) = sum(b.load for b in sc.buses)
|
||||||
|
test_approx(x, y) = @test isapprox(x, y, atol = 1e-3)
|
||||||
|
|
||||||
|
function transform_randomize_XavQiuAhm2021_test()
|
||||||
|
@testset "XavQiuAhm2021" begin
|
||||||
|
@testset "cost and load share" begin
|
||||||
|
sc = get_scenario()
|
||||||
|
# Check original costs
|
||||||
|
unit = sc.thermal_units[10]
|
||||||
|
test_approx(unit.min_power_cost[1], 825.023)
|
||||||
|
test_approx(unit.cost_segments[1].cost[1], 36.659)
|
||||||
|
test_approx(unit.startup_categories[1].cost[1], 7570.42)
|
||||||
|
|
||||||
|
# Check original load share
|
||||||
|
bus = sc.buses[1]
|
||||||
|
prev_system_load = system_load(sc)
|
||||||
|
test_approx(bus.load[1] / prev_system_load[1], 0.012)
|
||||||
|
|
||||||
|
randomize!(
|
||||||
|
sc,
|
||||||
|
XavQiuAhm2021.Randomization(randomize_load_profile = false),
|
||||||
|
rng = MersenneTwister(42),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check randomized costs
|
||||||
|
test_approx(unit.min_power_cost[1], 831.977)
|
||||||
|
test_approx(unit.cost_segments[1].cost[1], 36.968)
|
||||||
|
test_approx(unit.startup_categories[1].cost[1], 7634.226)
|
||||||
|
|
||||||
|
# Check randomized load share
|
||||||
|
curr_system_load = system_load(sc)
|
||||||
|
test_approx(bus.load[1] / curr_system_load[1], 0.013)
|
||||||
|
|
||||||
|
# System load should not change
|
||||||
|
@test prev_system_load ≈ curr_system_load
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "load profile" begin
|
||||||
|
sc = get_scenario()
|
||||||
|
# Check original load profile
|
||||||
|
@test round.(system_load(sc), digits = 1)[1:8] ≈ [
|
||||||
|
3059.5,
|
||||||
|
2983.2,
|
||||||
|
2937.5,
|
||||||
|
2953.9,
|
||||||
|
3073.1,
|
||||||
|
3356.4,
|
||||||
|
4068.5,
|
||||||
|
4018.8,
|
||||||
|
]
|
||||||
|
|
||||||
|
randomize!(
|
||||||
|
sc,
|
||||||
|
XavQiuAhm2021.Randomization();
|
||||||
|
rng = MersenneTwister(42),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check randomized load profile
|
||||||
|
@test round.(system_load(sc), digits = 1)[1:8] ≈ [
|
||||||
|
4854.7,
|
||||||
|
4849.2,
|
||||||
|
4732.7,
|
||||||
|
4848.2,
|
||||||
|
4948.4,
|
||||||
|
5231.1,
|
||||||
|
5874.8,
|
||||||
|
5934.8,
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "profiled unit cost" begin
|
||||||
|
sc = UnitCommitment.read(
|
||||||
|
fixture("case14-profiled.json.gz"),
|
||||||
|
).scenarios[1]
|
||||||
|
# Check original costs
|
||||||
|
pu1 = sc.profiled_units[1]
|
||||||
|
pu2 = sc.profiled_units[2]
|
||||||
|
test_approx(pu1.cost[1], 100.0)
|
||||||
|
test_approx(pu2.cost[1], 50.0)
|
||||||
|
randomize!(
|
||||||
|
sc,
|
||||||
|
XavQiuAhm2021.Randomization(randomize_load_profile = false),
|
||||||
|
rng = MersenneTwister(42),
|
||||||
|
)
|
||||||
|
# Check randomized costs
|
||||||
|
test_approx(pu1.cost[1], 98.039)
|
||||||
|
test_approx(pu2.cost[1], 48.385)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,68 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
function transform_slice_test()
|
||||||
|
@testset "slice" begin
|
||||||
|
instance = UnitCommitment.read(fixture("case14.json.gz"))
|
||||||
|
modified = UnitCommitment.slice(instance, 1:2)
|
||||||
|
sc = modified.scenarios[1]
|
||||||
|
|
||||||
|
# Should update all time-dependent fields
|
||||||
|
@test modified.time == 2
|
||||||
|
@test length(sc.power_balance_penalty) == 2
|
||||||
|
@test length(sc.reserves_by_name["r1"].amount) == 2
|
||||||
|
for u in sc.thermal_units
|
||||||
|
@test length(u.max_power) == 2
|
||||||
|
@test length(u.min_power) == 2
|
||||||
|
@test length(u.must_run) == 2
|
||||||
|
@test length(u.min_power_cost) == 2
|
||||||
|
for s in u.cost_segments
|
||||||
|
@test length(s.mw) == 2
|
||||||
|
@test length(s.cost) == 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for b in sc.buses
|
||||||
|
@test length(b.load) == 2
|
||||||
|
end
|
||||||
|
for l in sc.lines
|
||||||
|
@test length(l.normal_flow_limit) == 2
|
||||||
|
@test length(l.emergency_flow_limit) == 2
|
||||||
|
@test length(l.flow_limit_penalty) == 2
|
||||||
|
end
|
||||||
|
for ps in sc.price_sensitive_loads
|
||||||
|
@test length(ps.demand) == 2
|
||||||
|
@test length(ps.revenue) == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
# Should be able to build model without errors
|
||||||
|
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
||||||
|
model = UnitCommitment.build_model(
|
||||||
|
instance = modified,
|
||||||
|
optimizer = optimizer,
|
||||||
|
variable_names = true,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "slice profiled units" begin
|
||||||
|
instance = UnitCommitment.read(fixture("case14-profiled.json.gz"))
|
||||||
|
modified = UnitCommitment.slice(instance, 1:2)
|
||||||
|
sc = modified.scenarios[1]
|
||||||
|
|
||||||
|
# Should update all time-dependent fields
|
||||||
|
for pu in sc.profiled_units
|
||||||
|
@test length(pu.max_power) == 2
|
||||||
|
@test length(pu.min_power) == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
# Should be able to build model without errors
|
||||||
|
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
||||||
|
model = UnitCommitment.build_model(
|
||||||
|
instance = modified,
|
||||||
|
optimizer = optimizer,
|
||||||
|
variable_names = true,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,66 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
function _set_flow_limits!(instance)
|
||||||
|
for sc in instance.scenarios
|
||||||
|
sc.power_balance_penalty = [100_000 for _ in 1:instance.time]
|
||||||
|
for line in sc.lines, t in 1:4
|
||||||
|
line.normal_flow_limit[t] = 10.0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function usage_test()
|
||||||
|
@testset "usage" begin
|
||||||
|
@testset "deterministic" begin
|
||||||
|
instance = UnitCommitment.read(fixture("case14.json.gz"))
|
||||||
|
_set_flow_limits!(instance)
|
||||||
|
optimizer =
|
||||||
|
optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
||||||
|
model = UnitCommitment.build_model(
|
||||||
|
instance = instance,
|
||||||
|
optimizer = optimizer,
|
||||||
|
variable_names = true,
|
||||||
|
)
|
||||||
|
@test name(model[:is_on]["g1", 1]) == "is_on[g1,1]"
|
||||||
|
|
||||||
|
# Optimize and retrieve solution
|
||||||
|
UnitCommitment.optimize!(model)
|
||||||
|
solution = UnitCommitment.solution(model)
|
||||||
|
|
||||||
|
# Write solution to a file
|
||||||
|
filename = tempname()
|
||||||
|
UnitCommitment.write(filename, solution)
|
||||||
|
loaded = JSON.parsefile(filename)
|
||||||
|
@test length(loaded["Is on"]) == 6
|
||||||
|
|
||||||
|
# Verify solution
|
||||||
|
@test UnitCommitment.validate(instance, solution)
|
||||||
|
|
||||||
|
# Reoptimize with fixed solution
|
||||||
|
UnitCommitment.fix!(model, solution)
|
||||||
|
UnitCommitment.optimize!(model)
|
||||||
|
@test UnitCommitment.validate(instance, solution)
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "stochastic" begin
|
||||||
|
instance = UnitCommitment.read([
|
||||||
|
fixture("case14.json.gz"),
|
||||||
|
fixture("case14.json.gz"),
|
||||||
|
])
|
||||||
|
_set_flow_limits!(instance)
|
||||||
|
@test length(instance.scenarios) == 2
|
||||||
|
optimizer =
|
||||||
|
optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
||||||
|
model = UnitCommitment.build_model(
|
||||||
|
instance = instance,
|
||||||
|
optimizer = optimizer,
|
||||||
|
)
|
||||||
|
UnitCommitment.optimize!(model)
|
||||||
|
solution = UnitCommitment.solution(model)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,43 @@
|
|||||||
|
# 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, JSON, GZip, DataStructures
|
||||||
|
|
||||||
|
function parse_case14()
|
||||||
|
return JSON.parse(
|
||||||
|
GZip.gzopen(fixture("case14.json.gz")),
|
||||||
|
dicttype = () -> DefaultOrderedDict(nothing),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function validation_repair_test()
|
||||||
|
@testset "repair!" begin
|
||||||
|
@testset "Cost curve should be convex" begin
|
||||||
|
json = parse_case14()
|
||||||
|
json["Generators"]["g1"]["Production cost curve (MW)"] =
|
||||||
|
[100, 150, 200]
|
||||||
|
json["Generators"]["g1"]["Production cost curve (\$)"] =
|
||||||
|
[10, 25, 30]
|
||||||
|
sc = UnitCommitment._from_json(json, repair = false)
|
||||||
|
@test UnitCommitment.repair!(sc) == 4
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "Startup limit must be greater than Pmin" begin
|
||||||
|
json = parse_case14()
|
||||||
|
json["Generators"]["g1"]["Production cost curve (MW)"] = [100, 150]
|
||||||
|
json["Generators"]["g1"]["Production cost curve (\$)"] = [100, 150]
|
||||||
|
json["Generators"]["g1"]["Startup limit (MW)"] = 80
|
||||||
|
sc = UnitCommitment._from_json(json, repair = false)
|
||||||
|
@test UnitCommitment.repair!(sc) == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
@testset "Startup costs and delays must be increasing" begin
|
||||||
|
json = parse_case14()
|
||||||
|
json["Generators"]["g1"]["Startup costs (\$)"] = [300, 200, 100]
|
||||||
|
json["Generators"]["g1"]["Startup delays (h)"] = [8, 4, 2]
|
||||||
|
sc = UnitCommitment._from_json(json, repair = false)
|
||||||
|
@test UnitCommitment.repair!(sc) == 4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,28 +0,0 @@
|
|||||||
# 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, Cbc, JuMP
|
|
||||||
|
|
||||||
@testset "generate_initial_conditions!" begin
|
|
||||||
# Load instance
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case118-initcond.json.gz")
|
|
||||||
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
|
||||||
sc = instance.scenarios[1]
|
|
||||||
# All units should have unknown initial conditions
|
|
||||||
for g in sc.thermal_units
|
|
||||||
@test g.initial_power === nothing
|
|
||||||
@test g.initial_status === nothing
|
|
||||||
end
|
|
||||||
|
|
||||||
# Generate initial conditions
|
|
||||||
UnitCommitment.generate_initial_conditions!(sc, optimizer)
|
|
||||||
|
|
||||||
# All units should now have known initial conditions
|
|
||||||
for g in sc.thermal_units
|
|
||||||
@test g.initial_power !== nothing
|
|
||||||
@test g.initial_status !== nothing
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: Check that initial conditions are feasible
|
|
||||||
end
|
|
@ -1,83 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
import Random
|
|
||||||
import UnitCommitment: XavQiuAhm2021
|
|
||||||
|
|
||||||
using Distributions
|
|
||||||
using Random
|
|
||||||
using UnitCommitment, Cbc, JuMP
|
|
||||||
|
|
||||||
function get_scenario()
|
|
||||||
return UnitCommitment.read_benchmark(
|
|
||||||
"matpower/case118/2017-02-01",
|
|
||||||
).scenarios[1]
|
|
||||||
end
|
|
||||||
system_load(sc) = sum(b.load for b in sc.buses)
|
|
||||||
test_approx(x, y) = @test isapprox(x, y, atol = 1e-3)
|
|
||||||
|
|
||||||
@testset "XavQiuAhm2021" begin
|
|
||||||
@testset "cost and load share" begin
|
|
||||||
sc = get_scenario()
|
|
||||||
# Check original costs
|
|
||||||
unit = sc.thermal_units[10]
|
|
||||||
test_approx(unit.min_power_cost[1], 825.023)
|
|
||||||
test_approx(unit.cost_segments[1].cost[1], 36.659)
|
|
||||||
test_approx(unit.startup_categories[1].cost[1], 7570.42)
|
|
||||||
|
|
||||||
# Check original load share
|
|
||||||
bus = sc.buses[1]
|
|
||||||
prev_system_load = system_load(sc)
|
|
||||||
test_approx(bus.load[1] / prev_system_load[1], 0.012)
|
|
||||||
|
|
||||||
randomize!(
|
|
||||||
sc,
|
|
||||||
XavQiuAhm2021.Randomization(randomize_load_profile = false),
|
|
||||||
rng = MersenneTwister(42),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check randomized costs
|
|
||||||
test_approx(unit.min_power_cost[1], 831.977)
|
|
||||||
test_approx(unit.cost_segments[1].cost[1], 36.968)
|
|
||||||
test_approx(unit.startup_categories[1].cost[1], 7634.226)
|
|
||||||
|
|
||||||
# Check randomized load share
|
|
||||||
curr_system_load = system_load(sc)
|
|
||||||
test_approx(bus.load[1] / curr_system_load[1], 0.013)
|
|
||||||
|
|
||||||
# System load should not change
|
|
||||||
@test prev_system_load ≈ curr_system_load
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "load profile" begin
|
|
||||||
sc = get_scenario()
|
|
||||||
# Check original load profile
|
|
||||||
@test round.(system_load(sc), digits = 1)[1:8] ≈
|
|
||||||
[3059.5, 2983.2, 2937.5, 2953.9, 3073.1, 3356.4, 4068.5, 4018.8]
|
|
||||||
|
|
||||||
randomize!(sc, XavQiuAhm2021.Randomization(); rng = MersenneTwister(42))
|
|
||||||
|
|
||||||
# Check randomized load profile
|
|
||||||
@test round.(system_load(sc), digits = 1)[1:8] ≈
|
|
||||||
[4854.7, 4849.2, 4732.7, 4848.2, 4948.4, 5231.1, 5874.8, 5934.8]
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "profiled unit cost" begin
|
|
||||||
sc =
|
|
||||||
UnitCommitment.read("$FIXTURES/case14-profiled.json.gz").scenarios[1]
|
|
||||||
# Check original costs
|
|
||||||
pu1 = sc.profiled_units[1]
|
|
||||||
pu2 = sc.profiled_units[2]
|
|
||||||
test_approx(pu1.cost[1], 100.0)
|
|
||||||
test_approx(pu2.cost[1], 50.0)
|
|
||||||
randomize!(
|
|
||||||
sc,
|
|
||||||
XavQiuAhm2021.Randomization(randomize_load_profile = false),
|
|
||||||
rng = MersenneTwister(42),
|
|
||||||
)
|
|
||||||
# Check randomized costs
|
|
||||||
test_approx(pu1.cost[1], 98.039)
|
|
||||||
test_approx(pu2.cost[1], 48.385)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,66 +0,0 @@
|
|||||||
# 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 "slice" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14.json.gz")
|
|
||||||
modified = UnitCommitment.slice(instance, 1:2)
|
|
||||||
sc = modified.scenarios[1]
|
|
||||||
|
|
||||||
# Should update all time-dependent fields
|
|
||||||
@test modified.time == 2
|
|
||||||
@test length(sc.power_balance_penalty) == 2
|
|
||||||
@test length(sc.reserves_by_name["r1"].amount) == 2
|
|
||||||
for u in sc.thermal_units
|
|
||||||
@test length(u.max_power) == 2
|
|
||||||
@test length(u.min_power) == 2
|
|
||||||
@test length(u.must_run) == 2
|
|
||||||
@test length(u.min_power_cost) == 2
|
|
||||||
for s in u.cost_segments
|
|
||||||
@test length(s.mw) == 2
|
|
||||||
@test length(s.cost) == 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
for b in sc.buses
|
|
||||||
@test length(b.load) == 2
|
|
||||||
end
|
|
||||||
for l in sc.lines
|
|
||||||
@test length(l.normal_flow_limit) == 2
|
|
||||||
@test length(l.emergency_flow_limit) == 2
|
|
||||||
@test length(l.flow_limit_penalty) == 2
|
|
||||||
end
|
|
||||||
for ps in sc.price_sensitive_loads
|
|
||||||
@test length(ps.demand) == 2
|
|
||||||
@test length(ps.revenue) == 2
|
|
||||||
end
|
|
||||||
|
|
||||||
# Should be able to build model without errors
|
|
||||||
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
|
||||||
model = UnitCommitment.build_model(
|
|
||||||
instance = modified,
|
|
||||||
optimizer = optimizer,
|
|
||||||
variable_names = true,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "slice profiled units" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14-profiled.json.gz")
|
|
||||||
modified = UnitCommitment.slice(instance, 1:2)
|
|
||||||
sc = modified.scenarios[1]
|
|
||||||
|
|
||||||
# Should update all time-dependent fields
|
|
||||||
for pu in sc.profiled_units
|
|
||||||
@test length(pu.max_power) == 2
|
|
||||||
@test length(pu.min_power) == 2
|
|
||||||
end
|
|
||||||
|
|
||||||
# Should be able to build model without errors
|
|
||||||
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
|
||||||
model = UnitCommitment.build_model(
|
|
||||||
instance = modified,
|
|
||||||
optimizer = optimizer,
|
|
||||||
variable_names = true,
|
|
||||||
)
|
|
||||||
end
|
|
@ -1,62 +0,0 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
function _set_flow_limits!(instance)
|
|
||||||
for sc in instance.scenarios
|
|
||||||
sc.power_balance_penalty = [100_000 for _ in 1:instance.time]
|
|
||||||
for line in sc.lines, t in 1:4
|
|
||||||
line.normal_flow_limit[t] = 10.0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "usage" begin
|
|
||||||
@testset "deterministic" begin
|
|
||||||
instance = UnitCommitment.read("$FIXTURES/case14.json.gz")
|
|
||||||
_set_flow_limits!(instance)
|
|
||||||
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
|
||||||
model = UnitCommitment.build_model(
|
|
||||||
instance = instance,
|
|
||||||
optimizer = optimizer,
|
|
||||||
variable_names = true,
|
|
||||||
)
|
|
||||||
@test name(model[:is_on]["g1", 1]) == "is_on[g1,1]"
|
|
||||||
|
|
||||||
# Optimize and retrieve solution
|
|
||||||
UnitCommitment.optimize!(model)
|
|
||||||
solution = UnitCommitment.solution(model)
|
|
||||||
|
|
||||||
# Write solution to a file
|
|
||||||
filename = tempname()
|
|
||||||
UnitCommitment.write(filename, solution)
|
|
||||||
loaded = JSON.parsefile(filename)
|
|
||||||
@test length(loaded["Is on"]) == 6
|
|
||||||
|
|
||||||
# Verify solution
|
|
||||||
@test UnitCommitment.validate(instance, solution)
|
|
||||||
|
|
||||||
# Reoptimize with fixed solution
|
|
||||||
UnitCommitment.fix!(model, solution)
|
|
||||||
UnitCommitment.optimize!(model)
|
|
||||||
@test UnitCommitment.validate(instance, solution)
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "stochastic" begin
|
|
||||||
instance = UnitCommitment.read([
|
|
||||||
"$FIXTURES/case14.json.gz",
|
|
||||||
"$FIXTURES/case14.json.gz",
|
|
||||||
])
|
|
||||||
_set_flow_limits!(instance)
|
|
||||||
@test length(instance.scenarios) == 2
|
|
||||||
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
|
||||||
model = UnitCommitment.build_model(
|
|
||||||
instance = instance,
|
|
||||||
optimizer = optimizer,
|
|
||||||
)
|
|
||||||
UnitCommitment.optimize!(model)
|
|
||||||
solution = UnitCommitment.solution(model)
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,39 +0,0 @@
|
|||||||
# 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, JSON, GZip, DataStructures
|
|
||||||
|
|
||||||
function parse_case14()
|
|
||||||
return JSON.parse(
|
|
||||||
GZip.gzopen("$FIXTURES/case14.json.gz"),
|
|
||||||
dicttype = () -> DefaultOrderedDict(nothing),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "repair!" begin
|
|
||||||
@testset "Cost curve should be convex" begin
|
|
||||||
json = parse_case14()
|
|
||||||
json["Generators"]["g1"]["Production cost curve (MW)"] = [100, 150, 200]
|
|
||||||
json["Generators"]["g1"]["Production cost curve (\$)"] = [10, 25, 30]
|
|
||||||
sc = UnitCommitment._from_json(json, repair = false)
|
|
||||||
@test UnitCommitment.repair!(sc) == 4
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "Startup limit must be greater than Pmin" begin
|
|
||||||
json = parse_case14()
|
|
||||||
json["Generators"]["g1"]["Production cost curve (MW)"] = [100, 150]
|
|
||||||
json["Generators"]["g1"]["Production cost curve (\$)"] = [100, 150]
|
|
||||||
json["Generators"]["g1"]["Startup limit (MW)"] = 80
|
|
||||||
sc = UnitCommitment._from_json(json, repair = false)
|
|
||||||
@test UnitCommitment.repair!(sc) == 1
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "Startup costs and delays must be increasing" begin
|
|
||||||
json = parse_case14()
|
|
||||||
json["Generators"]["g1"]["Startup costs (\$)"] = [300, 200, 100]
|
|
||||||
json["Generators"]["g1"]["Startup delays (h)"] = [8, 4, 2]
|
|
||||||
sc = UnitCommitment._from_json(json, repair = false)
|
|
||||||
@test UnitCommitment.repair!(sc) == 4
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in new issue