You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
5.3 KiB
175 lines
5.3 KiB
# 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 Base.Threads: @threads
|
|
|
|
function _find_violations(
|
|
model::JuMP.Model,
|
|
sc::UnitCommitmentScenario;
|
|
max_per_line::Int,
|
|
max_per_period::Int,
|
|
)
|
|
instance = model[:instance]
|
|
net_injection = model[:net_injection]
|
|
overflow = model[:overflow]
|
|
length(sc.buses) > 1 || return []
|
|
violations = []
|
|
|
|
non_slack_buses = [b for b in sc.buses if b.offset > 0]
|
|
net_injection_values = [
|
|
value(net_injection[sc.name, b.name, t]) for b in non_slack_buses,
|
|
t in 1:instance.time
|
|
]
|
|
overflow_values = [
|
|
value(overflow[sc.name, lm.name, t]) for lm in sc.lines,
|
|
t in 1:instance.time
|
|
]
|
|
violations = UnitCommitment._find_violations(
|
|
instance = instance,
|
|
sc = sc,
|
|
net_injections = net_injection_values,
|
|
overflow = overflow_values,
|
|
isf = sc.isf,
|
|
lodf = sc.lodf,
|
|
max_per_line = max_per_line,
|
|
max_per_period = max_per_period,
|
|
)
|
|
return violations
|
|
end
|
|
|
|
"""
|
|
function _find_violations(
|
|
instance::UnitCommitmentInstance,
|
|
net_injections::Array{Float64, 2};
|
|
isf::Array{Float64,2},
|
|
lodf::Array{Float64,2},
|
|
max_per_line::Int,
|
|
max_per_period::Int,
|
|
)::Array{_Violation, 1}
|
|
|
|
Find transmission constraint violations (both pre-contingency, as well as
|
|
post-contingency).
|
|
|
|
The argument `net_injection` should be a (B-1) x T matrix, where B is the
|
|
number of buses and T is the number of time periods. The arguments `isf` and
|
|
`lodf` can be computed using UnitCommitment.injection_shift_factors and
|
|
UnitCommitment.line_outage_factors. The argument `overflow` specifies how much
|
|
flow above the transmission limits (in MW) is allowed. It should be an L x T
|
|
matrix, where L is the number of transmission lines.
|
|
"""
|
|
function _find_violations(;
|
|
instance::UnitCommitmentInstance,
|
|
sc::UnitCommitmentScenario,
|
|
net_injections::Array{Float64,2},
|
|
overflow::Array{Float64,2},
|
|
isf::Array{Float64,2},
|
|
lodf::Array{Float64,2},
|
|
max_per_line::Int,
|
|
max_per_period::Int,
|
|
)::Array{_Violation,1}
|
|
B = length(sc.buses) - 1
|
|
L = length(sc.lines)
|
|
T = instance.time
|
|
K = nthreads()
|
|
|
|
size(net_injections) == (B, T) || error("net_injections has incorrect size")
|
|
size(isf) == (L, B) || error("isf has incorrect size")
|
|
size(lodf) == (L, L) || error("lodf has incorrect size")
|
|
|
|
filters = Dict(
|
|
t => _ViolationFilter(
|
|
max_total = max_per_period,
|
|
max_per_line = max_per_line,
|
|
) for t in 1:T
|
|
)
|
|
|
|
pre_flow::Array{Float64} = zeros(L, K) # pre_flow[lm, thread]
|
|
post_flow::Array{Float64} = zeros(L, L, K) # post_flow[lm, lc, thread]
|
|
pre_v::Array{Float64} = zeros(L, K) # pre_v[lm, thread]
|
|
post_v::Array{Float64} = zeros(L, L, K) # post_v[lm, lc, thread]
|
|
|
|
normal_limits::Array{Float64,2} = [
|
|
l.normal_flow_limit[t] + overflow[l.offset, t] for l in sc.lines,
|
|
t in 1:T
|
|
]
|
|
|
|
emergency_limits::Array{Float64,2} = [
|
|
l.emergency_flow_limit[t] + overflow[l.offset, t] for l in sc.lines,
|
|
t in 1:T
|
|
]
|
|
|
|
is_vulnerable::Array{Bool} = zeros(Bool, L)
|
|
for c in sc.contingencies
|
|
is_vulnerable[c.lines[1].offset] = true
|
|
end
|
|
|
|
@threads for t in 1:T
|
|
k = threadid()
|
|
|
|
# Pre-contingency flows
|
|
pre_flow[:, k] = isf * net_injections[:, t]
|
|
|
|
# Post-contingency flows
|
|
for lc in 1:L, lm in 1:L
|
|
post_flow[lm, lc, k] =
|
|
pre_flow[lm, k] + pre_flow[lc, k] * lodf[lm, lc]
|
|
end
|
|
|
|
# Pre-contingency violations
|
|
for lm in 1:L
|
|
pre_v[lm, k] = max(
|
|
0.0,
|
|
pre_flow[lm, k] - normal_limits[lm, t],
|
|
-pre_flow[lm, k] - normal_limits[lm, t],
|
|
)
|
|
end
|
|
|
|
# Post-contingency violations
|
|
for lc in 1:L, lm in 1:L
|
|
post_v[lm, lc, k] = max(
|
|
0.0,
|
|
post_flow[lm, lc, k] - emergency_limits[lm, t],
|
|
-post_flow[lm, lc, k] - emergency_limits[lm, t],
|
|
)
|
|
end
|
|
|
|
# Offer pre-contingency violations
|
|
for lm in 1:L
|
|
if pre_v[lm, k] > 1e-5
|
|
_offer(
|
|
filters[t],
|
|
_Violation(
|
|
time = t,
|
|
monitored_line = sc.lines[lm],
|
|
outage_line = nothing,
|
|
amount = pre_v[lm, k],
|
|
),
|
|
)
|
|
end
|
|
end
|
|
|
|
# Offer post-contingency violations
|
|
for lm in 1:L, lc in 1:L
|
|
if post_v[lm, lc, k] > 1e-5 && is_vulnerable[lc]
|
|
_offer(
|
|
filters[t],
|
|
_Violation(
|
|
time = t,
|
|
monitored_line = sc.lines[lm],
|
|
outage_line = sc.lines[lc],
|
|
amount = post_v[lm, lc, k],
|
|
),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
violations = _Violation[]
|
|
for t in 1:instance.time
|
|
append!(violations, _query(filters[t]))
|
|
end
|
|
|
|
return violations
|
|
end
|