mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 08:18:51 -06:00
Compare commits
3 Commits
feature/fi
...
feature/sy
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c566e0e79 | |||
| 5c3f7b15d3 | |||
| 7c907a6eb5 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,3 +18,4 @@ TODO.md
|
|||||||
docs/_build
|
docs/_build
|
||||||
.vscode
|
.vscode
|
||||||
Manifest.toml
|
Manifest.toml
|
||||||
|
*/Manifest.toml
|
||||||
|
|||||||
15
Makefile
15
Makefile
@@ -5,23 +5,20 @@
|
|||||||
JULIA := julia --color=yes --project=@.
|
JULIA := julia --color=yes --project=@.
|
||||||
VERSION := 0.2
|
VERSION := 0.2
|
||||||
|
|
||||||
build/sysimage.so: src/utils/sysimage.jl Project.toml Manifest.toml
|
build/sysimage.so: src/utils/sysimage.jl Project.toml
|
||||||
mkdir -p build
|
julia --project=. -e "using Pkg; Pkg.instantiate()"
|
||||||
mkdir -p benchmark/results/test
|
julia --project=test -e "using Pkg; Pkg.instantiate()"
|
||||||
cd benchmark; $(JULIA) --trace-compile=../build/precompile.jl benchmark.jl test/case14
|
$(JULIA) src/utils/sysimage.jl test/runtests.jl
|
||||||
$(JULIA) src/utils/sysimage.jl
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build/*
|
rm -rfv build
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
cd docs; make clean; make dirhtml
|
cd docs; make clean; make dirhtml
|
||||||
rsync -avP --delete-after docs/_build/dirhtml/ ../docs/$(VERSION)/
|
rsync -avP --delete-after docs/_build/dirhtml/ ../docs/$(VERSION)/
|
||||||
|
|
||||||
test: build/sysimage.so
|
test: build/sysimage.so
|
||||||
@echo Running tests...
|
$(JULIA) --sysimage build/sysimage.so test/runtests.jl
|
||||||
$(JULIA) --sysimage build/sysimage.so -e 'using Pkg; Pkg.test("UnitCommitment")' | tee build/test.log
|
|
||||||
|
|
||||||
|
|
||||||
format:
|
format:
|
||||||
julia -e 'using JuliaFormatter; format(["src", "test", "benchmark"], verbose=true);'
|
julia -e 'using JuliaFormatter; format(["src", "test", "benchmark"], verbose=true);'
|
||||||
|
|||||||
10
Project.toml
10
Project.toml
@@ -15,10 +15,10 @@ Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
|||||||
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
||||||
PackageCompiler = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d"
|
PackageCompiler = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d"
|
||||||
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||||
|
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||||
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
||||||
|
|
||||||
[compat]
|
[compat]
|
||||||
Cbc = "0.7"
|
|
||||||
DataStructures = "0.18"
|
DataStructures = "0.18"
|
||||||
Distributions = "0.25"
|
Distributions = "0.25"
|
||||||
GZip = "0.5"
|
GZip = "0.5"
|
||||||
@@ -27,11 +27,3 @@ JuMP = "0.21"
|
|||||||
MathOptInterface = "0.9"
|
MathOptInterface = "0.9"
|
||||||
PackageCompiler = "1"
|
PackageCompiler = "1"
|
||||||
julia = "1"
|
julia = "1"
|
||||||
|
|
||||||
[extras]
|
|
||||||
Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76"
|
|
||||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
|
||||||
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"
|
|
||||||
|
|
||||||
[targets]
|
|
||||||
test = ["Cbc", "Test", "Gurobi"]
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ include("solution/warmstart.jl")
|
|||||||
include("solution/write.jl")
|
include("solution/write.jl")
|
||||||
include("transform/initcond.jl")
|
include("transform/initcond.jl")
|
||||||
include("transform/slice.jl")
|
include("transform/slice.jl")
|
||||||
include("transform/randomize.jl")
|
include("transform/randomize/XavQiuAhm2021.jl")
|
||||||
include("utils/log.jl")
|
include("utils/log.jl")
|
||||||
include("validation/repair.jl")
|
include("validation/repair.jl")
|
||||||
include("validation/validate.jl")
|
include("validation/validate.jl")
|
||||||
|
|||||||
@@ -266,15 +266,20 @@ function _from_json(json; repair = true)
|
|||||||
end
|
end
|
||||||
|
|
||||||
instance = UnitCommitmentInstance(
|
instance = UnitCommitmentInstance(
|
||||||
T,
|
buses_by_name = Dict(b.name => b for b in buses),
|
||||||
power_balance_penalty,
|
buses = buses,
|
||||||
shortfall_penalty,
|
contingencies_by_name = Dict(c.name => c for c in contingencies),
|
||||||
units,
|
contingencies = contingencies,
|
||||||
buses,
|
lines_by_name = Dict(l.name => l for l in lines),
|
||||||
lines,
|
lines = lines,
|
||||||
reserves,
|
power_balance_penalty = power_balance_penalty,
|
||||||
contingencies,
|
price_sensitive_loads_by_name = Dict(ps.name => ps for ps in loads),
|
||||||
loads,
|
price_sensitive_loads = loads,
|
||||||
|
reserves = reserves,
|
||||||
|
shortfall_penalty = shortfall_penalty,
|
||||||
|
time = T,
|
||||||
|
units_by_name = Dict(g.name => g for g in units),
|
||||||
|
units = units,
|
||||||
)
|
)
|
||||||
if repair
|
if repair
|
||||||
UnitCommitment.repair!(instance)
|
UnitCommitment.repair!(instance)
|
||||||
|
|||||||
@@ -69,17 +69,21 @@ mutable struct PriceSensitiveLoad
|
|||||||
revenue::Vector{Float64}
|
revenue::Vector{Float64}
|
||||||
end
|
end
|
||||||
|
|
||||||
mutable struct UnitCommitmentInstance
|
Base.@kwdef mutable struct UnitCommitmentInstance
|
||||||
time::Int
|
buses_by_name::Dict{AbstractString,Bus}
|
||||||
power_balance_penalty::Vector{Float64}
|
|
||||||
"Penalty for failing to meet reserve requirement."
|
|
||||||
shortfall_penalty::Vector{Float64}
|
|
||||||
units::Vector{Unit}
|
|
||||||
buses::Vector{Bus}
|
buses::Vector{Bus}
|
||||||
lines::Vector{TransmissionLine}
|
contingencies_by_name::Dict{AbstractString,Contingency}
|
||||||
reserves::Reserves
|
|
||||||
contingencies::Vector{Contingency}
|
contingencies::Vector{Contingency}
|
||||||
|
lines_by_name::Dict{AbstractString,TransmissionLine}
|
||||||
|
lines::Vector{TransmissionLine}
|
||||||
|
power_balance_penalty::Vector{Float64}
|
||||||
|
price_sensitive_loads_by_name::Dict{AbstractString,PriceSensitiveLoad}
|
||||||
price_sensitive_loads::Vector{PriceSensitiveLoad}
|
price_sensitive_loads::Vector{PriceSensitiveLoad}
|
||||||
|
reserves::Reserves
|
||||||
|
shortfall_penalty::Vector{Float64}
|
||||||
|
time::Int
|
||||||
|
units_by_name::Dict{AbstractString,Unit}
|
||||||
|
units::Vector{Unit}
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.show(io::IO, instance::UnitCommitmentInstance)
|
function Base.show(io::IO, instance::UnitCommitmentInstance)
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
using Distributions
|
|
||||||
|
|
||||||
function randomize_unit_costs!(
|
|
||||||
instance::UnitCommitmentInstance;
|
|
||||||
distribution = Uniform(0.95, 1.05),
|
|
||||||
)::Nothing
|
|
||||||
for unit in instance.units
|
|
||||||
α = rand(distribution)
|
|
||||||
unit.min_power_cost *= α
|
|
||||||
for k in unit.cost_segments
|
|
||||||
k.cost *= α
|
|
||||||
end
|
|
||||||
for s in unit.startup_categories
|
|
||||||
s.cost *= α
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
function randomize_load_distribution!(
|
|
||||||
instance::UnitCommitmentInstance;
|
|
||||||
distribution = Uniform(0.90, 1.10),
|
|
||||||
)::Nothing
|
|
||||||
α = rand(distribution, length(instance.buses))
|
|
||||||
for t in 1:instance.time
|
|
||||||
total = sum(bus.load[t] for bus in instance.buses)
|
|
||||||
den = sum(
|
|
||||||
bus.load[t] / total * α[i] for
|
|
||||||
(i, bus) in enumerate(instance.buses)
|
|
||||||
)
|
|
||||||
for (i, bus) in enumerate(instance.buses)
|
|
||||||
bus.load[t] *= α[i] / den
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
function randomize_peak_load!(
|
|
||||||
instance::UnitCommitmentInstance;
|
|
||||||
distribution = Uniform(0.925, 1.075),
|
|
||||||
)::Nothing
|
|
||||||
α = rand(distribution)
|
|
||||||
for bus in instance.buses
|
|
||||||
bus.load *= α
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
export randomize_unit_costs!, randomize_load_distribution!, randomize_peak_load!
|
|
||||||
209
src/transform/randomize/XavQiuAhm2021.jl
Normal file
209
src/transform/randomize/XavQiuAhm2021.jl
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Methods described in:
|
||||||
|
|
||||||
|
Xavier, Álinson S., Feng Qiu, and Shabbir Ahmed. "Learning to solve
|
||||||
|
large-scale security-constrained unit commitment problems." INFORMS
|
||||||
|
Journal on Computing 33.2 (2021): 739-756. DOI: 10.1287/ijoc.2020.0976
|
||||||
|
"""
|
||||||
|
module XavQiuAhm2021
|
||||||
|
|
||||||
|
using Distributions
|
||||||
|
import ..UnitCommitmentInstance
|
||||||
|
|
||||||
|
"""
|
||||||
|
struct Randomization
|
||||||
|
cost = Uniform(0.95, 1.05)
|
||||||
|
load_profile_mu = [...]
|
||||||
|
load_profile_sigma = [...]
|
||||||
|
load_share = Uniform(0.90, 1.10)
|
||||||
|
peak_load = Uniform(0.6 * 0.925, 0.6 * 1.075)
|
||||||
|
randomize_costs = true
|
||||||
|
randomize_load_profile = true
|
||||||
|
randomize_load_share = true
|
||||||
|
end
|
||||||
|
|
||||||
|
Randomization method that changes: (1) production and startup costs, (2)
|
||||||
|
share of load coming from each bus, (3) peak system load, and (4) temporal
|
||||||
|
load profile, as follows:
|
||||||
|
|
||||||
|
1. **Production and startup costs:**
|
||||||
|
For each unit `u`, the vectors `u.min_power_cost` and `u.cost_segments`
|
||||||
|
are multiplied by a constant `α[u]` sampled from the provided `cost`
|
||||||
|
distribution. If `randomize_costs` is false, skips this step.
|
||||||
|
|
||||||
|
2. **Load share:**
|
||||||
|
For each bus `b` and time `t`, the value `b.load[t]` is multiplied by
|
||||||
|
`(β[b] * b.load[t]) / sum(β[b2] * b2.load[t] for b2 in buses)`, where
|
||||||
|
`β[b]` is sampled from the provided `load_share` distribution. If
|
||||||
|
`randomize_load_share` is false, skips this step.
|
||||||
|
|
||||||
|
3. **Peak system load and temporal load profile:**
|
||||||
|
Sets the peak load to `ρ * C`, where `ρ` is sampled from `peak_load` and `C`
|
||||||
|
is the maximum system capacity, at any time. Also scales the loads of all
|
||||||
|
buses, so that `system_load[t+1]` becomes equal to `system_load[t] * γ[t]`,
|
||||||
|
where `γ[t]` is sampled from `Normal(load_profile_mu[t], load_profile_sigma[t])`.
|
||||||
|
|
||||||
|
The system load for the first time period is set so that the peak load
|
||||||
|
matches `ρ * C`. If `load_profile_sigma` and `load_profile_mu` have fewer
|
||||||
|
elements than `instance.time`, wraps around. If `randomize_load_profile`
|
||||||
|
is false, skips this step.
|
||||||
|
|
||||||
|
The default parameters were obtained based on an analysis of publicly available
|
||||||
|
bid and hourly data from PJM, corresponding to the month of January, 2017. For
|
||||||
|
more details, see Section 4.2 of the paper.
|
||||||
|
"""
|
||||||
|
Base.@kwdef struct Randomization
|
||||||
|
cost = Uniform(0.95, 1.05)
|
||||||
|
load_profile_mu::Vector{Float64} = [
|
||||||
|
1.0,
|
||||||
|
0.978,
|
||||||
|
0.98,
|
||||||
|
1.004,
|
||||||
|
1.02,
|
||||||
|
1.078,
|
||||||
|
1.132,
|
||||||
|
1.018,
|
||||||
|
0.999,
|
||||||
|
1.006,
|
||||||
|
0.999,
|
||||||
|
0.987,
|
||||||
|
0.975,
|
||||||
|
0.984,
|
||||||
|
0.995,
|
||||||
|
1.005,
|
||||||
|
1.045,
|
||||||
|
1.106,
|
||||||
|
0.981,
|
||||||
|
0.981,
|
||||||
|
0.978,
|
||||||
|
0.948,
|
||||||
|
0.928,
|
||||||
|
0.953,
|
||||||
|
]
|
||||||
|
load_profile_sigma::Vector{Float64} = [
|
||||||
|
0.0,
|
||||||
|
0.011,
|
||||||
|
0.015,
|
||||||
|
0.01,
|
||||||
|
0.012,
|
||||||
|
0.029,
|
||||||
|
0.055,
|
||||||
|
0.027,
|
||||||
|
0.026,
|
||||||
|
0.023,
|
||||||
|
0.013,
|
||||||
|
0.012,
|
||||||
|
0.014,
|
||||||
|
0.011,
|
||||||
|
0.008,
|
||||||
|
0.008,
|
||||||
|
0.02,
|
||||||
|
0.02,
|
||||||
|
0.016,
|
||||||
|
0.012,
|
||||||
|
0.014,
|
||||||
|
0.015,
|
||||||
|
0.017,
|
||||||
|
0.024,
|
||||||
|
]
|
||||||
|
load_share = Uniform(0.90, 1.10)
|
||||||
|
peak_load = Uniform(0.6 * 0.925, 0.6 * 1.075)
|
||||||
|
randomize_load_profile::Bool = true
|
||||||
|
randomize_costs::Bool = true
|
||||||
|
randomize_load_share::Bool = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function _randomize_costs(
|
||||||
|
instance::UnitCommitmentInstance,
|
||||||
|
distribution,
|
||||||
|
)::Nothing
|
||||||
|
for unit in instance.units
|
||||||
|
α = rand(distribution)
|
||||||
|
unit.min_power_cost *= α
|
||||||
|
for k in unit.cost_segments
|
||||||
|
k.cost *= α
|
||||||
|
end
|
||||||
|
for s in unit.startup_categories
|
||||||
|
s.cost *= α
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
function _randomize_load_share(
|
||||||
|
instance::UnitCommitmentInstance,
|
||||||
|
distribution,
|
||||||
|
)::Nothing
|
||||||
|
α = rand(distribution, length(instance.buses))
|
||||||
|
for t in 1:instance.time
|
||||||
|
total = sum(bus.load[t] for bus in instance.buses)
|
||||||
|
den = sum(
|
||||||
|
bus.load[t] / total * α[i] for
|
||||||
|
(i, bus) in enumerate(instance.buses)
|
||||||
|
)
|
||||||
|
for (i, bus) in enumerate(instance.buses)
|
||||||
|
bus.load[t] *= α[i] / den
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
function _randomize_load_profile(
|
||||||
|
instance::UnitCommitmentInstance,
|
||||||
|
params::Randomization,
|
||||||
|
)::Nothing
|
||||||
|
# Generate new system load
|
||||||
|
system_load = [1.0]
|
||||||
|
for t in 2:instance.time
|
||||||
|
idx = (t - 1) % length(params.load_profile_mu) + 1
|
||||||
|
gamma = rand(
|
||||||
|
Normal(params.load_profile_mu[idx], params.load_profile_sigma[idx]),
|
||||||
|
)
|
||||||
|
push!(system_load, system_load[t-1] * gamma)
|
||||||
|
end
|
||||||
|
capacity = sum(maximum(u.max_power) for u in instance.units)
|
||||||
|
peak_load = rand(params.peak_load) * capacity
|
||||||
|
system_load = system_load ./ maximum(system_load) .* peak_load
|
||||||
|
|
||||||
|
# Scale bus loads to match the new system load
|
||||||
|
prev_system_load = sum(b.load for b in instance.buses)
|
||||||
|
for b in instance.buses
|
||||||
|
for t in 1:instance.time
|
||||||
|
b.load[t] *= system_load[t] / prev_system_load[t]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
function randomize!(
|
||||||
|
instance::UnitCommitment.UnitCommitmentInstance,
|
||||||
|
method::XavQiuAhm2021.Randomization,
|
||||||
|
)::Nothing
|
||||||
|
|
||||||
|
Randomize costs and loads based on the method described in XavQiuAhm2021.
|
||||||
|
"""
|
||||||
|
function randomize!(
|
||||||
|
instance::UnitCommitment.UnitCommitmentInstance,
|
||||||
|
method::XavQiuAhm2021.Randomization,
|
||||||
|
)::Nothing
|
||||||
|
if method.randomize_costs
|
||||||
|
XavQiuAhm2021._randomize_costs(instance, method.cost)
|
||||||
|
end
|
||||||
|
if method.randomize_load_share
|
||||||
|
XavQiuAhm2021._randomize_load_share(instance, method.load_share)
|
||||||
|
end
|
||||||
|
if method.randomize_load_profile
|
||||||
|
XavQiuAhm2021._randomize_load_profile(instance, method)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
export randomize!
|
||||||
@@ -3,26 +3,31 @@
|
|||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
using PackageCompiler
|
using PackageCompiler
|
||||||
|
using TOML
|
||||||
|
using Logging
|
||||||
|
|
||||||
using DataStructures
|
Logging.disable_logging(Logging.Info)
|
||||||
using Distributions
|
mkpath("build")
|
||||||
using JSON
|
|
||||||
using JuMP
|
|
||||||
using MathOptInterface
|
|
||||||
using SparseArrays
|
|
||||||
|
|
||||||
pkg = [
|
println("Generating precompilation statements...")
|
||||||
:DataStructures,
|
run(`julia --project=. --trace-compile=build/precompile.jl $(ARGS)`)
|
||||||
:Distributions,
|
|
||||||
:JSON,
|
|
||||||
:JuMP,
|
|
||||||
:MathOptInterface,
|
|
||||||
:SparseArrays,
|
|
||||||
]
|
|
||||||
|
|
||||||
@info "Building system image..."
|
println("Finding dependencies...")
|
||||||
|
project = TOML.parsefile("Project.toml")
|
||||||
|
manifest = TOML.parsefile("Manifest.toml")
|
||||||
|
deps = Symbol[]
|
||||||
|
for dep in keys(project["deps"])
|
||||||
|
if "path" in keys(manifest[dep][1])
|
||||||
|
println(" - $(dep) [skip]")
|
||||||
|
else
|
||||||
|
println(" - $(dep)")
|
||||||
|
push!(deps, Symbol(dep))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
println("Building system image...")
|
||||||
create_sysimage(
|
create_sysimage(
|
||||||
pkg,
|
deps,
|
||||||
precompile_statements_file = "build/precompile.jl",
|
precompile_statements_file = "build/precompile.jl",
|
||||||
sysimage_path = "build/sysimage.so",
|
sysimage_path = "build/sysimage.so",
|
||||||
)
|
)
|
||||||
|
|||||||
26
test/Project.toml
Normal file
26
test/Project.toml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[deps]
|
||||||
|
Cbc = "9961bab8-2fa3-5c5a-9d89-47fab24efd76"
|
||||||
|
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
||||||
|
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
|
||||||
|
GZip = "92fee26a-97fe-5a0c-ad85-20a5f3185b63"
|
||||||
|
Gurobi = "2e9cd046-0924-5485-92f1-d5272153d98b"
|
||||||
|
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
|
||||||
|
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
|
||||||
|
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||||
|
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||||
|
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
||||||
|
PackageCompiler = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d"
|
||||||
|
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||||
|
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||||
|
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
||||||
|
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
|
|
||||||
|
[compat]
|
||||||
|
DataStructures = "0.18"
|
||||||
|
Distributions = "0.25"
|
||||||
|
GZip = "0.5"
|
||||||
|
JSON = "0.21"
|
||||||
|
JuMP = "0.21"
|
||||||
|
MathOptInterface = "0.9"
|
||||||
|
PackageCompiler = "1"
|
||||||
|
julia = "1"
|
||||||
@@ -4,9 +4,12 @@
|
|||||||
|
|
||||||
using UnitCommitment
|
using UnitCommitment
|
||||||
|
|
||||||
|
basedir = @__DIR__
|
||||||
|
|
||||||
@testset "read_egret_solution" begin
|
@testset "read_egret_solution" begin
|
||||||
solution =
|
solution = UnitCommitment.read_egret_solution(
|
||||||
UnitCommitment.read_egret_solution("fixtures/egret_output.json.gz")
|
"$basedir/../fixtures/egret_output.json.gz",
|
||||||
|
)
|
||||||
for attr in ["Is on", "Production (MW)", "Production cost (\$)"]
|
for attr in ["Is on", "Production (MW)", "Production cost (\$)"]
|
||||||
@test attr in keys(solution)
|
@test attr in keys(solution)
|
||||||
@test "115_STEAM_1" in keys(solution[attr])
|
@test "115_STEAM_1" in keys(solution[attr])
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip
|
|||||||
@test instance.lines[5].normal_flow_limit == [1e8 for t in 1:4]
|
@test instance.lines[5].normal_flow_limit == [1e8 for t in 1:4]
|
||||||
@test instance.lines[5].emergency_flow_limit == [1e8 for t in 1:4]
|
@test instance.lines[5].emergency_flow_limit == [1e8 for t in 1:4]
|
||||||
@test instance.lines[5].flow_limit_penalty == [5e3 for t in 1:4]
|
@test instance.lines[5].flow_limit_penalty == [5e3 for t in 1:4]
|
||||||
|
@test instance.lines_by_name["l5"].name == "l5"
|
||||||
|
|
||||||
@test instance.lines[1].name == "l1"
|
@test instance.lines[1].name == "l1"
|
||||||
@test instance.lines[1].source.name == "b1"
|
@test instance.lines[1].source.name == "b1"
|
||||||
@@ -34,6 +35,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip
|
|||||||
|
|
||||||
@test instance.buses[9].name == "b9"
|
@test instance.buses[9].name == "b9"
|
||||||
@test instance.buses[9].load == [35.36638, 33.25495, 31.67138, 31.14353]
|
@test instance.buses[9].load == [35.36638, 33.25495, 31.67138, 31.14353]
|
||||||
|
@test instance.buses_by_name["b9"].name == "b9"
|
||||||
|
|
||||||
unit = instance.units[1]
|
unit = instance.units[1]
|
||||||
@test unit.name == "g1"
|
@test unit.name == "g1"
|
||||||
@@ -62,6 +64,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip
|
|||||||
@test unit.startup_categories[1].cost == 1000.0
|
@test unit.startup_categories[1].cost == 1000.0
|
||||||
@test unit.startup_categories[2].cost == 1500.0
|
@test unit.startup_categories[2].cost == 1500.0
|
||||||
@test unit.startup_categories[3].cost == 2000.0
|
@test unit.startup_categories[3].cost == 2000.0
|
||||||
|
@test instance.units_by_name["g1"].name == "g1"
|
||||||
|
|
||||||
unit = instance.units[2]
|
unit = instance.units[2]
|
||||||
@test unit.name == "g2"
|
@test unit.name == "g2"
|
||||||
@@ -92,12 +95,15 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip
|
|||||||
|
|
||||||
@test instance.contingencies[1].lines == [instance.lines[1]]
|
@test instance.contingencies[1].lines == [instance.lines[1]]
|
||||||
@test instance.contingencies[1].units == []
|
@test instance.contingencies[1].units == []
|
||||||
|
@test instance.contingencies[1].name == "c1"
|
||||||
|
@test instance.contingencies_by_name["c1"].name == "c1"
|
||||||
|
|
||||||
load = instance.price_sensitive_loads[1]
|
load = instance.price_sensitive_loads[1]
|
||||||
@test load.name == "ps1"
|
@test load.name == "ps1"
|
||||||
@test load.bus.name == "b3"
|
@test load.bus.name == "b3"
|
||||||
@test load.revenue == [100.0 for t in 1:4]
|
@test load.revenue == [100.0 for t in 1:4]
|
||||||
@test load.demand == [50.0 for t in 1:4]
|
@test load.demand == [50.0 for t in 1:4]
|
||||||
|
@test instance.price_sensitive_loads_by_name["ps1"].name == "ps1"
|
||||||
end
|
end
|
||||||
|
|
||||||
@testset "read_benchmark sub-hourly" begin
|
@testset "read_benchmark sub-hourly" begin
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
using Test
|
using Test
|
||||||
using UnitCommitment
|
using UnitCommitment
|
||||||
|
|
||||||
|
push!(Base.LOAD_PATH, @__DIR__)
|
||||||
UnitCommitment._setup_logger()
|
UnitCommitment._setup_logger()
|
||||||
|
|
||||||
const ENABLE_LARGE_TESTS = ("UCJL_LARGE_TESTS" in keys(ENV))
|
const ENABLE_LARGE_TESTS = ("UCJL_LARGE_TESTS" in keys(ENV))
|
||||||
@@ -28,7 +29,9 @@ const ENABLE_LARGE_TESTS = ("UCJL_LARGE_TESTS" in keys(ENV))
|
|||||||
@testset "transform" begin
|
@testset "transform" begin
|
||||||
include("transform/initcond_test.jl")
|
include("transform/initcond_test.jl")
|
||||||
include("transform/slice_test.jl")
|
include("transform/slice_test.jl")
|
||||||
include("transform/randomize_test.jl")
|
@testset "randomize" begin
|
||||||
|
include("transform/randomize/XavQiuAhm2021_test.jl")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@testset "validation" begin
|
@testset "validation" begin
|
||||||
include("validation/repair_test.jl")
|
include("validation/repair_test.jl")
|
||||||
|
|||||||
@@ -4,9 +4,12 @@
|
|||||||
|
|
||||||
using UnitCommitment, Cbc, JuMP
|
using UnitCommitment, Cbc, JuMP
|
||||||
|
|
||||||
|
basedir = @__DIR__
|
||||||
|
|
||||||
@testset "generate_initial_conditions!" begin
|
@testset "generate_initial_conditions!" begin
|
||||||
# Load instance
|
# Load instance
|
||||||
instance = UnitCommitment.read("$(pwd())/fixtures/case118-initcond.json.gz")
|
instance =
|
||||||
|
UnitCommitment.read("$basedir/../fixtures/case118-initcond.json.gz")
|
||||||
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
||||||
|
|
||||||
# All units should have unknown initial conditions
|
# All units should have unknown initial conditions
|
||||||
|
|||||||
63
test/transform/randomize/XavQiuAhm2021_test.jl
Normal file
63
test/transform/randomize/XavQiuAhm2021_test.jl
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# 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 UnitCommitment, Cbc, JuMP
|
||||||
|
|
||||||
|
get_instance() = UnitCommitment.read_benchmark("matpower/case118/2017-02-01")
|
||||||
|
system_load(instance) = sum(b.load for b in instance.buses)
|
||||||
|
test_approx(x, y) = @test isapprox(x, y, atol = 1e-3)
|
||||||
|
|
||||||
|
@testset "XavQiuAhm2021" begin
|
||||||
|
@testset "cost and load share" begin
|
||||||
|
instance = get_instance()
|
||||||
|
|
||||||
|
# Check original costs
|
||||||
|
unit = instance.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 = instance.buses[1]
|
||||||
|
prev_system_load = system_load(instance)
|
||||||
|
test_approx(bus.load[1] / prev_system_load[1], 0.012)
|
||||||
|
|
||||||
|
Random.seed!(42)
|
||||||
|
randomize!(
|
||||||
|
instance,
|
||||||
|
XavQiuAhm2021.Randomization(randomize_load_profile = false),
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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(instance)
|
||||||
|
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
|
||||||
|
instance = get_instance()
|
||||||
|
|
||||||
|
# Check original load profile
|
||||||
|
@test round.(system_load(instance), digits = 1)[1:8] ≈
|
||||||
|
[3059.5, 2983.2, 2937.5, 2953.9, 3073.1, 3356.4, 4068.5, 4018.8]
|
||||||
|
|
||||||
|
Random.seed!(42)
|
||||||
|
randomize!(instance, XavQiuAhm2021.Randomization())
|
||||||
|
|
||||||
|
# Check randomized load profile
|
||||||
|
@test round.(system_load(instance), digits = 1)[1:8] ≈
|
||||||
|
[4854.7, 4849.2, 4732.7, 4848.2, 4948.4, 5231.1, 5874.8, 5934.8]
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,43 +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
|
|
||||||
|
|
||||||
_get_instance() = UnitCommitment.read_benchmark("matpower/case118/2017-02-01")
|
|
||||||
_total_load(instance) = sum(b.load[1] for b in instance.buses)
|
|
||||||
|
|
||||||
@testset "randomize_unit_costs!" begin
|
|
||||||
instance = _get_instance()
|
|
||||||
unit = instance.units[10]
|
|
||||||
prev_min_power_cost = unit.min_power_cost
|
|
||||||
prev_prod_cost = unit.cost_segments[1].cost
|
|
||||||
prev_startup_cost = unit.startup_categories[1].cost
|
|
||||||
randomize_unit_costs!(instance)
|
|
||||||
@test prev_min_power_cost != unit.min_power_cost
|
|
||||||
@test prev_prod_cost != unit.cost_segments[1].cost
|
|
||||||
@test prev_startup_cost != unit.startup_categories[1].cost
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "randomize_load_distribution!" begin
|
|
||||||
instance = _get_instance()
|
|
||||||
bus = instance.buses[1]
|
|
||||||
prev_load = instance.buses[1].load[1]
|
|
||||||
prev_total_load = _total_load(instance)
|
|
||||||
randomize_load_distribution!(instance)
|
|
||||||
curr_total_load = _total_load(instance)
|
|
||||||
@test prev_load != instance.buses[1].load[1]
|
|
||||||
@test abs(prev_total_load - curr_total_load) < 1e-3
|
|
||||||
end
|
|
||||||
|
|
||||||
@testset "randomize_peak_load!" begin
|
|
||||||
instance = _get_instance()
|
|
||||||
bus = instance.buses[1]
|
|
||||||
prev_total_load = _total_load(instance)
|
|
||||||
prev_share = bus.load[1] / prev_total_load
|
|
||||||
randomize_peak_load!(instance)
|
|
||||||
curr_total_load = _total_load(instance)
|
|
||||||
curr_share = bus.load[1] / prev_total_load
|
|
||||||
@test curr_total_load != prev_total_load
|
|
||||||
@test abs(curr_share - prev_share) < 1e-3
|
|
||||||
end
|
|
||||||
@@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
using UnitCommitment, JSON, GZip, DataStructures
|
using UnitCommitment, JSON, GZip, DataStructures
|
||||||
|
|
||||||
|
basedir = @__DIR__
|
||||||
|
|
||||||
function parse_case14()
|
function parse_case14()
|
||||||
return JSON.parse(
|
return JSON.parse(
|
||||||
GZip.gzopen("../instances/test/case14.json.gz"),
|
GZip.gzopen("$basedir/../../instances/test/case14.json.gz"),
|
||||||
dicttype = () -> DefaultOrderedDict(nothing),
|
dicttype = () -> DefaultOrderedDict(nothing),
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user