mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 08:18:51 -06:00
Compare commits
2 Commits
6601191cc9
...
m2m
| Author | SHA1 | Date | |
|---|---|---|---|
| 7897adbee1 | |||
| bb880575ef |
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
version: ['1.10', '1.12']
|
version: ['1.6', '1.7', '1.8', '1.9']
|
||||||
os:
|
os:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
arch:
|
arch:
|
||||||
|
|||||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -1,4 +1,3 @@
|
|||||||
*-off.md
|
|
||||||
*.bak
|
*.bak
|
||||||
*.gz
|
*.gz
|
||||||
*.ipynb
|
*.ipynb
|
||||||
@@ -20,7 +19,6 @@
|
|||||||
.apdisk
|
.apdisk
|
||||||
.com.apple.timemachine.donotpresent
|
.com.apple.timemachine.donotpresent
|
||||||
.fseventsd
|
.fseventsd
|
||||||
.idea
|
|
||||||
.ipy*
|
.ipy*
|
||||||
.vscode
|
.vscode
|
||||||
Icon
|
Icon
|
||||||
@@ -34,11 +32,12 @@ benchmark/tables
|
|||||||
benchmark/tmp.json
|
benchmark/tmp.json
|
||||||
build
|
build
|
||||||
docs/_build
|
docs/_build
|
||||||
docs/src/tutorials/customizing.md
|
|
||||||
docs/src/tutorials/lmp.md
|
|
||||||
docs/src/tutorials/market.md
|
|
||||||
docs/src/tutorials/usage.md
|
|
||||||
instances/**/*.json
|
instances/**/*.json
|
||||||
instances/_source
|
instances/_source
|
||||||
local
|
local
|
||||||
notebooks
|
notebooks
|
||||||
|
docs/src/tutorials/usage.md
|
||||||
|
docs/src/tutorials/customizing.md
|
||||||
|
docs/src/tutorials/market.md
|
||||||
|
docs/src/tutorials/lmp.md
|
||||||
|
*-off.md
|
||||||
11
CHANGELOG.md
11
CHANGELOG.md
@@ -11,17 +11,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
[semver]: https://semver.org/spec/v2.0.0.html
|
[semver]: https://semver.org/spec/v2.0.0.html
|
||||||
[pkjjl]: https://pkgdocs.julialang.org/v1/compatibility/#compat-pre-1.0
|
[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 (#57)
|
|
||||||
|
|
||||||
## [0.4.1] - 2025-11-05
|
|
||||||
### Fixed
|
|
||||||
- Fix multi-threading issues in Julia 1.12
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- The package now requires Julia 1.10 or newer
|
|
||||||
|
|
||||||
## [0.4.0] - 2024-05-21
|
## [0.4.0] - 2024-05-21
|
||||||
### Added
|
### Added
|
||||||
- Add support for two-stage stochastic problems
|
- Add support for two-stage stochastic problems
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name = "UnitCommitment"
|
|||||||
uuid = "64606440-39ea-11e9-0f29-3303a1d3d877"
|
uuid = "64606440-39ea-11e9-0f29-3303a1d3d877"
|
||||||
authors = ["Santos Xavier, Alinson <axavier@anl.gov>"]
|
authors = ["Santos Xavier, Alinson <axavier@anl.gov>"]
|
||||||
repo = "https://github.com/ANL-CEEESA/UnitCommitment.jl"
|
repo = "https://github.com/ANL-CEEESA/UnitCommitment.jl"
|
||||||
version = "0.4.2"
|
version = "0.4.0"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
||||||
@@ -30,5 +30,5 @@ JuMP = "1"
|
|||||||
MathOptInterface = "1"
|
MathOptInterface = "1"
|
||||||
MPI = "0.20"
|
MPI = "0.20"
|
||||||
PackageCompiler = "1"
|
PackageCompiler = "1"
|
||||||
julia = "1.10"
|
julia = "1"
|
||||||
TimerOutputs = "0.5"
|
TimerOutputs = "0.5"
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ See official documentation at: https://anl-ceeesa.github.io/UnitCommitment.jl/
|
|||||||
|
|
||||||
If you use UnitCommitment.jl in your research (instances, models or algorithms), we kindly request that you cite the package as follows:
|
If you use UnitCommitment.jl in your research (instances, models or algorithms), we kindly request that you cite the package as follows:
|
||||||
|
|
||||||
* **Alinson S. Xavier, Aleksandr M. Kazachkov, Ogün Yurdakul, Jun He, Feng Qiu**. "UnitCommitment.jl: A Julia/JuMP Optimization Package for Security-Constrained Unit Commitment (Version 0.4)". Zenodo (2024). [DOI: 10.5281/zenodo.4269874](https://doi.org/10.5281/zenodo.4269874).
|
* **Alinson S. Xavier, Aleksandr M. Kazachkov, Ogün Yurdakul, Feng Qiu**. "UnitCommitment.jl: A Julia/JuMP Optimization Package for Security-Constrained Unit Commitment (Version 0.4)". Zenodo (2024). [DOI: 10.5281/zenodo.4269874](https://doi.org/10.5281/zenodo.4269874).
|
||||||
|
|
||||||
If you use the instances, we additionally request that you cite the original sources, as described in the documentation.
|
If you use the instances, we additionally request that you cite the original sources, as described in the documentation.
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ Note that this curve also specifies the production limits. Specifically, the fir
|
|||||||
|
|
||||||
```@raw html
|
```@raw html
|
||||||
<center>
|
<center>
|
||||||
<img src="../../assets/cost_curve.png" style="max-width: 500px"/>
|
<img src="../assets/cost_curve.png" style="max-width: 500px"/>
|
||||||
<div><b>Figure 1.</b> Piecewise-linear production cost curve.</div>
|
<div><b>Figure 1.</b> Piecewise-linear production cost curve.</div>
|
||||||
<br/>
|
<br/>
|
||||||
</center>
|
</center>
|
||||||
|
|||||||
@@ -67,19 +67,21 @@ function _add_production_piecewise_linear_eqs!(
|
|||||||
(t < T ? Cw * switch_off[gn, t+1] : 0.0)
|
(t < T ? Cw * switch_off[gn, t+1] : 0.0)
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
# Equation (47a) in Kneuven et al. (2020)
|
# Equation (47a)/(48a) in Kneuven et al. (2020)
|
||||||
eq_segprod_limit_b[sc.name, gn, t, k] = @constraint(
|
eq_segprod_limit_b[sc.name, gn, t, k] = @constraint(
|
||||||
model,
|
model,
|
||||||
segprod[sc.name, gn, t, k] <=
|
segprod[sc.name, gn, t, k] <=
|
||||||
g.cost_segments[k].mw[t] * is_on[gn, t] -
|
g.cost_segments[k].mw[t] * is_on[gn, t] -
|
||||||
Cv * switch_on[gn, t]
|
Cv * switch_on[gn, t] -
|
||||||
|
(t < T ? max(0, Cv - Cw) * switch_off[gn, t+1] : 0.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Equation (47b) in Kneuven et al. (2020)
|
# Equation (47b)/(48b) in Kneuven et al. (2020)
|
||||||
eq_segprod_limit_c[sc.name, gn, t, k] = @constraint(
|
eq_segprod_limit_c[sc.name, gn, t, k] = @constraint(
|
||||||
model,
|
model,
|
||||||
segprod[sc.name, gn, t, k] <=
|
segprod[sc.name, gn, t, k] <=
|
||||||
g.cost_segments[k].mw[t] * is_on[gn, t] -
|
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)
|
(t < T ? Cw * switch_off[gn, t+1] : 0.0)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -26,59 +26,67 @@ function _enforce_transmission(;
|
|||||||
isf::Matrix{Float64},
|
isf::Matrix{Float64},
|
||||||
lodf::Matrix{Float64},
|
lodf::Matrix{Float64},
|
||||||
)::Nothing
|
)::Nothing
|
||||||
instance = model[:instance]
|
|
||||||
limit::Float64 = 0.0
|
limit::Float64 = 0.0
|
||||||
overflow = model[:overflow]
|
overflow = model[:overflow]
|
||||||
net_injection = model[:net_injection]
|
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 violation.outage_line === nothing
|
if lc === nothing
|
||||||
limit = violation.monitored_line.normal_flow_limit[violation.time]
|
limit = lm.normal_flow_limit[t]
|
||||||
@info @sprintf(
|
@info @sprintf(
|
||||||
" %8.3f MW overflow in %-5s time %3d (pre-contingency, scenario %s)",
|
" %8.3f MW overflow in %-5s time %3d (pre-contingency, scenario %s)",
|
||||||
violation.amount,
|
violation.amount,
|
||||||
violation.monitored_line.name,
|
lm.name,
|
||||||
violation.time,
|
t,
|
||||||
sc.name,
|
sc.name,
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
limit = violation.monitored_line.emergency_flow_limit[violation.time]
|
limit = lm.emergency_flow_limit[t]
|
||||||
@info @sprintf(
|
@info @sprintf(
|
||||||
" %8.3f MW overflow in %-5s time %3d (outage: line %s, scenario %s)",
|
" %8.3f MW overflow in %-5s time %3d (outage: line %s, scenario %s)",
|
||||||
violation.amount,
|
violation.amount,
|
||||||
violation.monitored_line.name,
|
lm.name,
|
||||||
violation.time,
|
t,
|
||||||
violation.outage_line.name,
|
lc.name,
|
||||||
sc.name,
|
sc.name,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
fm = violation.monitored_line.name
|
v = overflow[sc.name, lm.name, t]
|
||||||
t = violation.time
|
flow = @variable(model, base_name = "flow[$eq_idx]")
|
||||||
flow = @variable(model, base_name = "flow[$fm,$t]")
|
eq_flow_ub[eq_idx] = @constraint(model, flow <= limit + v)
|
||||||
|
eq_flow_lb[eq_idx] = @constraint(model, -flow <= limit + v)
|
||||||
|
|
||||||
v = overflow[sc.name, violation.monitored_line.name, violation.time]
|
if lc === nothing
|
||||||
@constraint(model, flow <= limit + v)
|
eq_flow_def[eq_idx] = @constraint(
|
||||||
@constraint(model, -flow <= limit + v)
|
|
||||||
|
|
||||||
if violation.outage_line === nothing
|
|
||||||
@constraint(
|
|
||||||
model,
|
model,
|
||||||
flow == sum(
|
flow == sum(
|
||||||
net_injection[sc.name, b.name, violation.time] *
|
net_injection[sc.name, b.name, t] *
|
||||||
isf[violation.monitored_line.offset, b.offset] for
|
isf[lm.offset, b.offset] for
|
||||||
b in sc.buses if b.offset > 0
|
b in sc.buses if b.offset > 0
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
@constraint(
|
eq_flow_def[eq_idx] = @constraint(
|
||||||
model,
|
model,
|
||||||
flow == sum(
|
flow == sum(
|
||||||
net_injection[sc.name, b.name, violation.time] * (
|
net_injection[sc.name, b.name, t] * (
|
||||||
isf[violation.monitored_line.offset, b.offset] + (
|
isf[lm.offset, b.offset] + (
|
||||||
lodf[
|
lodf[
|
||||||
violation.monitored_line.offset,
|
lm.offset,
|
||||||
violation.outage_line.offset,
|
lc.offset,
|
||||||
] * isf[violation.outage_line.offset, b.offset]
|
] * isf[lc.offset, b.offset]
|
||||||
)
|
)
|
||||||
) for b in sc.buses if b.offset > 0
|
) for b in sc.buses if b.offset > 0
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
import Base.Threads: @threads, maxthreadid
|
import Base.Threads: @threads
|
||||||
|
|
||||||
function _find_violations(
|
function _find_violations(
|
||||||
model::JuMP.Model,
|
model::JuMP.Model,
|
||||||
@@ -71,7 +71,7 @@ function _find_violations(;
|
|||||||
B = length(sc.buses) - 1
|
B = length(sc.buses) - 1
|
||||||
L = length(sc.lines)
|
L = length(sc.lines)
|
||||||
T = instance.time
|
T = instance.time
|
||||||
K = maxthreadid()
|
K = nthreads()
|
||||||
|
|
||||||
size(net_injections) == (B, T) || error("net_injections has incorrect size")
|
size(net_injections) == (B, T) || error("net_injections has incorrect size")
|
||||||
size(isf) == (L, B) || error("isf 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
|
is_vulnerable[c.lines[1].offset] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@threads :static for t in 1:T
|
@threads for t in 1:T
|
||||||
k = threadid()
|
k = threadid()
|
||||||
|
|
||||||
# Pre-contingency flows
|
# Pre-contingency flows
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Nothing
|
using DataStructures
|
||||||
|
|
||||||
|
function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Dict
|
||||||
if !occursin("Gurobi", JuMP.solver_name(model))
|
if !occursin("Gurobi", JuMP.solver_name(model))
|
||||||
method.two_phase_gap = false
|
method.two_phase_gap = false
|
||||||
end
|
end
|
||||||
@@ -22,6 +24,9 @@ function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Nothing
|
|||||||
large_gap = true
|
large_gap = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
stats = Dict(
|
||||||
|
"violations" => []
|
||||||
|
)
|
||||||
while true
|
while true
|
||||||
time_elapsed = time() - initial_time
|
time_elapsed = time() - initial_time
|
||||||
time_remaining = method.time_limit - time_elapsed
|
time_remaining = method.time_limit - time_elapsed
|
||||||
@@ -68,6 +73,7 @@ function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Nothing
|
|||||||
|
|
||||||
if violations_found
|
if violations_found
|
||||||
for (i, v) in enumerate(violations)
|
for (i, v) in enumerate(violations)
|
||||||
|
append!(stats["violations"], v)
|
||||||
_enforce_transmission(model, v, model[:instance].scenarios[i])
|
_enforce_transmission(model, v, model[:instance].scenarios[i])
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -80,5 +86,5 @@ function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Nothing
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return
|
return stats
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
optimize!(model::JuMP.Model)::Nothing
|
optimize!(model::JuMP.Model)::Dict
|
||||||
|
|
||||||
Solve the given unit commitment model. Unlike `JuMP.optimize!`, this uses more
|
Solve the given unit commitment model. Unlike `JuMP.optimize!`, this uses more
|
||||||
advanced methods to accelerate the solution process and to enforce transmission
|
advanced methods to accelerate the solution process and to enforce transmission
|
||||||
and N-1 security constraints.
|
and N-1 security constraints.
|
||||||
"""
|
"""
|
||||||
function optimize!(model::JuMP.Model)::Nothing
|
function optimize!(model::JuMP.Model)::Dict
|
||||||
return UnitCommitment.optimize!(model, XavQiuWanThi2019.Method())
|
return UnitCommitment.optimize!(model, XavQiuWanThi2019.Method())
|
||||||
end
|
end
|
||||||
|
|||||||
BIN
test/fixtures/issue-0057.json.gz
vendored
BIN
test/fixtures/issue-0057.json.gz
vendored
Binary file not shown.
BIN
test/fixtures/ucjl-0.2.json.gz
vendored
BIN
test/fixtures/ucjl-0.2.json.gz
vendored
Binary file not shown.
@@ -23,7 +23,6 @@ include("validation/repair_test.jl")
|
|||||||
include("lmp/conventional_test.jl")
|
include("lmp/conventional_test.jl")
|
||||||
include("lmp/aelmp_test.jl")
|
include("lmp/aelmp_test.jl")
|
||||||
include("market/market_test.jl")
|
include("market/market_test.jl")
|
||||||
include("regression.jl")
|
|
||||||
|
|
||||||
basedir = dirname(@__FILE__)
|
basedir = dirname(@__FILE__)
|
||||||
|
|
||||||
@@ -49,13 +48,12 @@ function runtests()
|
|||||||
solution_methods_TimeDecomposition_update_solution_test()
|
solution_methods_TimeDecomposition_update_solution_test()
|
||||||
transform_initcond_test()
|
transform_initcond_test()
|
||||||
transform_slice_test()
|
transform_slice_test()
|
||||||
# transform_randomize_XavQiuAhm2021_test()
|
transform_randomize_XavQiuAhm2021_test()
|
||||||
validation_repair_test()
|
validation_repair_test()
|
||||||
lmp_conventional_test()
|
lmp_conventional_test()
|
||||||
lmp_aelmp_test()
|
lmp_aelmp_test()
|
||||||
simple_market_test()
|
simple_market_test()
|
||||||
stochastic_market_test()
|
stochastic_market_test()
|
||||||
regression_test()
|
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,19 +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, 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