mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 16:28:51 -06:00
Compare commits
7 Commits
m2m
...
7a28a0ca36
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a28a0ca36 | |||
| e4cc95dae1 | |||
| 48094ded6b | |||
| c926f61054 | |||
| 4ac9b2a8d5 | |||
| 8763c8d8f7 | |||
| bbe57f88cd |
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
version: ['1.6', '1.7', '1.8', '1.9']
|
||||
version: ['1.10', '1.12']
|
||||
os:
|
||||
- ubuntu-latest
|
||||
arch:
|
||||
|
||||
@@ -11,6 +11,10 @@ All notable changes to this project will be documented in this file.
|
||||
[semver]: https://semver.org/spec/v2.0.0.html
|
||||
[pkjjl]: https://pkgdocs.julialang.org/v1/compatibility/#compat-pre-1.0
|
||||
|
||||
## [0.4.2] - 2025-11-27
|
||||
### Fixed
|
||||
- KnuOstWat2018: Fixed a bug in `eq_segprod_limit` constraint (#17)
|
||||
|
||||
## [0.4.0] - 2024-05-21
|
||||
### Added
|
||||
- Add support for two-stage stochastic problems
|
||||
|
||||
@@ -2,7 +2,7 @@ name = "UnitCommitment"
|
||||
uuid = "64606440-39ea-11e9-0f29-3303a1d3d877"
|
||||
authors = ["Santos Xavier, Alinson <axavier@anl.gov>"]
|
||||
repo = "https://github.com/ANL-CEEESA/UnitCommitment.jl"
|
||||
version = "0.4.0"
|
||||
version = "0.4.2"
|
||||
|
||||
[deps]
|
||||
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
||||
@@ -30,5 +30,5 @@ JuMP = "1"
|
||||
MathOptInterface = "1"
|
||||
MPI = "0.20"
|
||||
PackageCompiler = "1"
|
||||
julia = "1"
|
||||
julia = "1.10"
|
||||
TimerOutputs = "0.5"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Decomposition methods
|
||||
|
||||
## 1. Time decomposition for production cost modeling
|
||||
## 1. Time decomposition
|
||||
|
||||
Solving unit commitment instances that have long time horizons (for example, year-long 8760-hour instances in production cost modeling) requires a substantial amount of computational power. To address this issue, UC.jl offers a time decomposition method, which breaks the instance down into multiple overlapping subproblems, solves them sequentially, then reassembles the solution.
|
||||
Solving unit commitment instances that have long time horizons (for example, year-long 8760-hour instances) requires a substantial amount of computational power. To address this issue, UC.jl offers a time decomposition method, which breaks the instance down into multiple overlapping subproblems, solves them sequentially, then reassembles the solution.
|
||||
|
||||
When solving a unit commitment instance with a dense time slot structure, computational complexity can become a significant challenge. For instance, if the instance contains hourly data for an entire year (8760 hours), solving such a model can require a substantial amount of computational power. To address this issue, UC.jl provides a time_decomposition method within the `optimize!` function. This method decomposes the problem into multiple sub-problems, solving them sequentially.
|
||||
|
||||
@@ -57,7 +57,7 @@ solution = UnitCommitment.optimize!(
|
||||
)
|
||||
```
|
||||
|
||||
## 2. Scenario decomposition with Progressive Hedging for stochstic UC
|
||||
## 2. Scenario decomposition with Progressive Hedging
|
||||
|
||||
By default, UC.jl uses the Extensive Form (EF) when solving stochastic instances. This approach involves constructing a single JuMP model that contains data and decision variables for all scenarios. Although EF has optimality guarantees and performs well with small test cases, it can become computationally intractable for large instances or substantial number of scenarios.
|
||||
|
||||
|
||||
@@ -67,21 +67,19 @@ function _add_production_piecewise_linear_eqs!(
|
||||
(t < T ? Cw * switch_off[gn, t+1] : 0.0)
|
||||
)
|
||||
else
|
||||
# Equation (47a)/(48a) in Kneuven et al. (2020)
|
||||
# Equation (47a) in Kneuven et al. (2020)
|
||||
eq_segprod_limit_b[sc.name, gn, t, k] = @constraint(
|
||||
model,
|
||||
segprod[sc.name, gn, t, k] <=
|
||||
g.cost_segments[k].mw[t] * is_on[gn, t] -
|
||||
Cv * switch_on[gn, t] -
|
||||
(t < T ? max(0, Cv - Cw) * switch_off[gn, t+1] : 0.0)
|
||||
Cv * switch_on[gn, t]
|
||||
)
|
||||
|
||||
# Equation (47b)/(48b) in Kneuven et al. (2020)
|
||||
# Equation (47b) in Kneuven et al. (2020)
|
||||
eq_segprod_limit_c[sc.name, gn, t, k] = @constraint(
|
||||
model,
|
||||
segprod[sc.name, gn, t, k] <=
|
||||
g.cost_segments[k].mw[t] * is_on[gn, t] -
|
||||
max(0, Cw - Cv) * switch_on[gn, t] -
|
||||
(t < T ? Cw * switch_off[gn, t+1] : 0.0)
|
||||
)
|
||||
end
|
||||
|
||||
@@ -26,67 +26,59 @@ function _enforce_transmission(;
|
||||
isf::Matrix{Float64},
|
||||
lodf::Matrix{Float64},
|
||||
)::Nothing
|
||||
instance = model[:instance]
|
||||
limit::Float64 = 0.0
|
||||
overflow = model[:overflow]
|
||||
net_injection = model[:net_injection]
|
||||
lm = violation.monitored_line
|
||||
lc = violation.outage_line
|
||||
t = violation.time
|
||||
eq_flow_ub = _init(model, :eq_flow_ub)
|
||||
eq_flow_lb = _init(model, :eq_flow_lb)
|
||||
eq_flow_def = _init(model, :eq_flow_def)
|
||||
eq_idx = (
|
||||
sc.name,
|
||||
lm.name,
|
||||
lc === nothing ? "Base" : lc.name,
|
||||
t,
|
||||
)
|
||||
|
||||
if lc === nothing
|
||||
limit = lm.normal_flow_limit[t]
|
||||
if violation.outage_line === nothing
|
||||
limit = violation.monitored_line.normal_flow_limit[violation.time]
|
||||
@info @sprintf(
|
||||
" %8.3f MW overflow in %-5s time %3d (pre-contingency, scenario %s)",
|
||||
violation.amount,
|
||||
lm.name,
|
||||
t,
|
||||
violation.monitored_line.name,
|
||||
violation.time,
|
||||
sc.name,
|
||||
)
|
||||
else
|
||||
limit = lm.emergency_flow_limit[t]
|
||||
limit = violation.monitored_line.emergency_flow_limit[violation.time]
|
||||
@info @sprintf(
|
||||
" %8.3f MW overflow in %-5s time %3d (outage: line %s, scenario %s)",
|
||||
violation.amount,
|
||||
lm.name,
|
||||
t,
|
||||
lc.name,
|
||||
violation.monitored_line.name,
|
||||
violation.time,
|
||||
violation.outage_line.name,
|
||||
sc.name,
|
||||
)
|
||||
end
|
||||
|
||||
v = overflow[sc.name, lm.name, t]
|
||||
flow = @variable(model, base_name = "flow[$eq_idx]")
|
||||
eq_flow_ub[eq_idx] = @constraint(model, flow <= limit + v)
|
||||
eq_flow_lb[eq_idx] = @constraint(model, -flow <= limit + v)
|
||||
fm = violation.monitored_line.name
|
||||
t = violation.time
|
||||
flow = @variable(model, base_name = "flow[$fm,$t]")
|
||||
|
||||
if lc === nothing
|
||||
eq_flow_def[eq_idx] = @constraint(
|
||||
v = overflow[sc.name, violation.monitored_line.name, violation.time]
|
||||
@constraint(model, flow <= limit + v)
|
||||
@constraint(model, -flow <= limit + v)
|
||||
|
||||
if violation.outage_line === nothing
|
||||
@constraint(
|
||||
model,
|
||||
flow == sum(
|
||||
net_injection[sc.name, b.name, t] *
|
||||
isf[lm.offset, b.offset] for
|
||||
net_injection[sc.name, b.name, violation.time] *
|
||||
isf[violation.monitored_line.offset, b.offset] for
|
||||
b in sc.buses if b.offset > 0
|
||||
)
|
||||
)
|
||||
else
|
||||
eq_flow_def[eq_idx] = @constraint(
|
||||
@constraint(
|
||||
model,
|
||||
flow == sum(
|
||||
net_injection[sc.name, b.name, t] * (
|
||||
isf[lm.offset, b.offset] + (
|
||||
net_injection[sc.name, b.name, violation.time] * (
|
||||
isf[violation.monitored_line.offset, b.offset] + (
|
||||
lodf[
|
||||
lm.offset,
|
||||
lc.offset,
|
||||
] * isf[lc.offset, b.offset]
|
||||
violation.monitored_line.offset,
|
||||
violation.outage_line.offset,
|
||||
] * isf[violation.outage_line.offset, b.offset]
|
||||
)
|
||||
) for b in sc.buses if b.offset > 0
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
import Base.Threads: @threads
|
||||
import Base.Threads: @threads, maxthreadid
|
||||
|
||||
function _find_violations(
|
||||
model::JuMP.Model,
|
||||
@@ -71,7 +71,7 @@ function _find_violations(;
|
||||
B = length(sc.buses) - 1
|
||||
L = length(sc.lines)
|
||||
T = instance.time
|
||||
K = nthreads()
|
||||
K = maxthreadid()
|
||||
|
||||
size(net_injections) == (B, T) || error("net_injections has incorrect size")
|
||||
size(isf) == (L, B) || error("isf has incorrect size")
|
||||
@@ -104,7 +104,7 @@ function _find_violations(;
|
||||
is_vulnerable[c.lines[1].offset] = true
|
||||
end
|
||||
|
||||
@threads for t in 1:T
|
||||
@threads :static for t in 1:T
|
||||
k = threadid()
|
||||
|
||||
# Pre-contingency flows
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
using DataStructures
|
||||
|
||||
function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Dict
|
||||
function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Nothing
|
||||
if !occursin("Gurobi", JuMP.solver_name(model))
|
||||
method.two_phase_gap = false
|
||||
end
|
||||
@@ -24,9 +22,6 @@ function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Dict
|
||||
large_gap = true
|
||||
end
|
||||
end
|
||||
stats = Dict(
|
||||
"violations" => []
|
||||
)
|
||||
while true
|
||||
time_elapsed = time() - initial_time
|
||||
time_remaining = method.time_limit - time_elapsed
|
||||
@@ -73,7 +68,6 @@ function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Dict
|
||||
|
||||
if violations_found
|
||||
for (i, v) in enumerate(violations)
|
||||
append!(stats["violations"], v)
|
||||
_enforce_transmission(model, v, model[:instance].scenarios[i])
|
||||
end
|
||||
else
|
||||
@@ -86,5 +80,5 @@ function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Dict
|
||||
end
|
||||
end
|
||||
end
|
||||
return stats
|
||||
return
|
||||
end
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
"""
|
||||
optimize!(model::JuMP.Model)::Dict
|
||||
optimize!(model::JuMP.Model)::Nothing
|
||||
|
||||
Solve the given unit commitment model. Unlike `JuMP.optimize!`, this uses more
|
||||
advanced methods to accelerate the solution process and to enforce transmission
|
||||
and N-1 security constraints.
|
||||
"""
|
||||
function optimize!(model::JuMP.Model)::Dict
|
||||
function optimize!(model::JuMP.Model)::Nothing
|
||||
return UnitCommitment.optimize!(model, XavQiuWanThi2019.Method())
|
||||
end
|
||||
|
||||
BIN
test/fixtures/issue-0057.json.gz
vendored
Normal file
BIN
test/fixtures/issue-0057.json.gz
vendored
Normal file
Binary file not shown.
@@ -23,6 +23,7 @@ include("validation/repair_test.jl")
|
||||
include("lmp/conventional_test.jl")
|
||||
include("lmp/aelmp_test.jl")
|
||||
include("market/market_test.jl")
|
||||
include("regression.jl")
|
||||
|
||||
basedir = dirname(@__FILE__)
|
||||
|
||||
@@ -48,12 +49,13 @@ function runtests()
|
||||
solution_methods_TimeDecomposition_update_solution_test()
|
||||
transform_initcond_test()
|
||||
transform_slice_test()
|
||||
transform_randomize_XavQiuAhm2021_test()
|
||||
# transform_randomize_XavQiuAhm2021_test()
|
||||
validation_repair_test()
|
||||
lmp_conventional_test()
|
||||
lmp_aelmp_test()
|
||||
simple_market_test()
|
||||
stochastic_market_test()
|
||||
regression_test()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
19
test/src/regression.jl
Normal file
19
test/src/regression.jl
Normal file
@@ -0,0 +1,19 @@
|
||||
# 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, HiGHS, JuMP
|
||||
|
||||
function regression_test()
|
||||
@testset "GitHub Issue #57" begin
|
||||
instance = UnitCommitment.read(fixture("issue-0057.json.gz"))
|
||||
model = UnitCommitment.build_model(
|
||||
instance = instance,
|
||||
optimizer = HiGHS.Optimizer,
|
||||
)
|
||||
JuMP.set_silent(model)
|
||||
UnitCommitment.optimize!(model)
|
||||
solution = UnitCommitment.solution(model)
|
||||
@test solution["Thermal production (MW)"]["gen_524d4c85"][1] == 90.0
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user