-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ADD: Transformer SOC relaxations #412
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -90,3 +90,130 @@ function constraint_mc_voltage_psd(pm::SOCConicUBFModel, nw::Int, i::Int) | |
|
||
relaxation_psd_to_soc_conic(pm.model, Wr, Wi) | ||
end | ||
|
||
|
||
@doc raw""" | ||
constraint_mc_transformer_power_yy(pm::SOCUBFModels, nw::Int, trans_id::Int, f_bus::Int, t_bus::Int, f_idx::Tuple{Int,Int,Int}, t_idx::Tuple{Int,Int,Int}, f_connections::Vector{Int}, t_connections::Vector{Int}, pol::Int, tm_set::Vector{<:Real}, tm_fixed::Vector{Bool}, tm_scale::Real) | ||
|
||
Constraints to model a two-winding, wye-wye connected transformer. | ||
|
||
```math | ||
\begin{align} | ||
& {W}_{fr} = {T}_{m}{T}_{m}^{H} {W}_{to} \\ | ||
& {s}_{fr} + {s}_{to} = 0 | ||
\end{align} | ||
``` | ||
""" | ||
function constraint_mc_transformer_power_yy(pm::SOCUBFModels, nw::Int, trans_id::Int, f_bus::Int, t_bus::Int, f_idx::Tuple{Int,Int,Int}, t_idx::Tuple{Int,Int,Int}, f_connections::Vector{Int}, t_connections::Vector{Int}, pol::Int, tm_set::Vector{<:Real}, tm_fixed::Vector{Bool}, tm_scale::Real) | ||
transformer = ref(pm, nw, :transformer, trans_id) | ||
tm = [tm_fixed[idx] ? tm_set[idx] : var(pm, nw, :tap, trans_id)[idx] for (idx,(fc,tc)) in enumerate(zip(f_connections,t_connections))] | ||
|
||
Wr_fr = var(pm, nw, :Wr, f_bus) | ||
Wr_to = var(pm, nw, :Wr, t_bus) | ||
Wi_fr = var(pm, nw, :Wi, f_bus) | ||
Wi_to = var(pm, nw, :Wi, t_bus) | ||
|
||
p_fr = [var(pm, nw, :pt, f_idx)[c] for c in f_connections] | ||
p_to = [var(pm, nw, :pt, t_idx)[c] for c in t_connections] | ||
q_fr = [var(pm, nw, :qt, f_idx)[c] for c in f_connections] | ||
q_to = [var(pm, nw, :qt, t_idx)[c] for c in t_connections] | ||
|
||
for (f_idx,fc) in enumerate(f_connections) | ||
if haskey(transformer,"controls") # regcontrol settings | ||
w_fr = var(pm, nw, :w)[f_bus] | ||
w_to = var(pm, nw, :w)[t_bus] | ||
v_ref = transformer["controls"]["vreg"][f_idx] | ||
δ = transformer["controls"]["band"][f_idx] | ||
r = transformer["controls"]["r"][f_idx] | ||
x = transformer["controls"]["x"][f_idx] | ||
end | ||
|
||
for (t_idx,tc) in enumerate(t_connections) | ||
if tm_fixed[t_idx] | ||
JuMP.@constraint(pm.model, Wr_fr[fc,tc] == (pol*tm_scale)^2*tm[f_idx]*tm[t_idx]*Wr_to[fc,tc]) | ||
JuMP.@constraint(pm.model, Wi_fr[fc,tc] == (pol*tm_scale)^2*tm[f_idx]*tm[t_idx]*Wi_to[fc,tc]) | ||
else | ||
PolyhedralRelaxations.construct_univariate_relaxation!(pm.model, x->x^2, tm[idx], tmsqr[idx], [JuMP.has_lower_bound(tm[idx]) ? JuMP.lower_bound(tm[idx]) : 0.9, JuMP.has_upper_bound(tm[idx]) ? JuMP.upper_bound(tm[idx]) : 1.1], false) | ||
tmsqr_Wr = JuMP.@variable(pm.model, base_name="$(nw)_tmsqr_Wr_to_$(trans_id)_$(f_bus)_$(fc)_$(t_bus)_$(tc)") | ||
tmsqr_Wi = JuMP.@variable(pm.model, base_name="$(nw)_tmsqr_Wi_to_$(trans_id)_$(f_bus)_$(fc)_$(t_bus)_$(tc)") | ||
PolyhedralRelaxations.construct_bilinear_relaxation!(pm.model, tmsqr[idx], Wr_to[fc,tc], tmsqr_Wr, [JuMP.lower_bound(tmsqr[idx]), JuMP.upper_bound(tmsqr[idx])], [JuMP.has_lower_bound(Wr_to[fc,tc]) ? JuMP.lower_bound(Wr_to[fc,tc]) : -(1.1^2), JuMP.has_upper_bound(Wr_to[fc,tc]) ? JuMP.upper_bound(Wr_to[fc,tc]) : 1.1^2]) | ||
PolyhedralRelaxations.construct_bilinear_relaxation!(pm.model, tmsqr[idx], Wi_to[fc,tc], tmsqr_Wi, [JuMP.lower_bound(tmsqr[idx]), JuMP.upper_bound(tmsqr[idx])], [JuMP.has_lower_bound(Wi_to[fc,tc]) ? JuMP.lower_bound(Wi_to[fc,tc]) : -(1.1^2), JuMP.has_upper_bound(Wi_to[fc,tc]) ? JuMP.upper_bound(Wi_to[fc,tc]) : 1.1^2]) | ||
|
||
JuMP.@constraint(pm.model, Wr_fr[fc,tc] == (pol*tm_scale)^2*tmsqr_Wr_to) | ||
JuMP.@constraint(pm.model, Wi_fr[fc,tc] == (pol*tm_scale)^2*tmsqr_Wi_to) | ||
|
||
# with regcontrol | ||
if haskey(transformer,"controls") | ||
# voltage squared ignoring losses: w_drop = (2⋅r⋅p+2⋅x⋅q) | ||
w_drop = JuMP.@expression(pm.model, 2*r*p_to[idx] + 2*x*q_to[idx]) | ||
|
||
# (v_ref-δ)^2 ≤ w_fr-w_drop ≤ (v_ref+δ)^2 | ||
# w_fr/1.1^2 ≤ w_to ≤ w_fr/0.9^2 | ||
JuMP.@constraint(pm.model, w_fr[fc] - w_drop ≥ (v_ref - δ)^2) | ||
JuMP.@constraint(pm.model, w_fr[fc] - w_drop ≤ (v_ref + δ)^2) | ||
JuMP.@constraint(pm.model, w_fr[fc]/1.1^2 ≤ w_to[tc]) | ||
JuMP.@constraint(pm.model, w_fr[fc]/0.9^2 ≥ w_to[tc]) | ||
end | ||
end | ||
end | ||
end | ||
|
||
JuMP.@constraint(pm.model, p_fr + p_to .== 0) | ||
JuMP.@constraint(pm.model, q_fr + q_to .== 0) | ||
end | ||
|
||
@doc raw""" | ||
constraint_mc_transformer_power_dy(pm::SOCUBFModels, nw::Int, trans_id::Int, f_bus::Int, t_bus::Int, f_idx::Tuple{Int,Int,Int}, t_idx::Tuple{Int,Int,Int}, f_connections::Vector{Int}, t_connections::Vector{Int}, pol::Int, tm_set::Vector{<:Real}, tm_fixed::Vector{Bool}, tm_scale::Real) | ||
|
||
Constraints to model a two-winding, delta-wye connected transformer. | ||
|
||
```math | ||
\begin{align} | ||
&{W}_{fr}^{ij}-{W}_{fr}^{ik}-{W}_{fr}^{lj}+{W}_{fr}^{lk} = t_m^2{W}_{to}^{ij} ~\forall i,j \in \{1,2,3\}~ \text{and}~ k,l \in \{2,3,1\} \\ | ||
&{S}_{fr} = X_tT_t \\ | ||
&{S}_{fr}^\Delta = T_tX_t \\ | ||
& {s}_{fr}^\Delta + {s}_{to} = 0\\ | ||
& {M}_{\Delta} = | ||
\begin{bmatrix} | ||
{W}_{fr} & {X}_{t} \\ | ||
{X}_{t}^{\text{H}} & {L}_{\Delta} | ||
\end{bmatrix} \succeq 0 | ||
\end{align} | ||
``` | ||
""" | ||
function constraint_mc_transformer_power_dy(pm::SOCUBFModels, nw::Int, trans_id::Int, f_bus::Int, t_bus::Int, f_idx::Tuple{Int,Int,Int}, t_idx::Tuple{Int,Int,Int}, f_connections::Vector{Int}, t_connections::Vector{Int}, pol::Int, tm_set::Vector{<:Real}, tm_fixed::Vector{Bool}, tm_scale::Real) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the alternative is to use the code path for the n-winding transformer decomposition, and do lifting to SW separately for the idealized single-phase transformer component (i.e. the wye model) and the internal losses (composable of normal branches/shunts in the SW variable space). |
||
nph = length(tm_set) | ||
@assert length(f_connections) == length(t_connections) && nph == 3 "only phases == 3 dy transformers are currently supported" | ||
next = Dict(c=>f_connections[idx%nph+1] for (idx,c) in enumerate(f_connections)) | ||
|
||
tm = [tm_fixed[idx] ? tm_set[idx] : var(pm, nw, :tap, trans_id)[idx] for (idx,(fc,tc)) in enumerate(zip(f_connections,t_connections))] | ||
|
||
Wr_fr = var(pm, nw, :Wr, f_bus) | ||
Wr_to = var(pm, nw, :Wr, t_bus) | ||
Wi_fr = var(pm, nw, :Wi, f_bus) | ||
Wi_to = var(pm, nw, :Wi, t_bus) | ||
Xtr = var(pm, nw, :Xtr, trans_id) | ||
Xti = var(pm, nw, :Xti, trans_id) | ||
CCtr = var(pm, nw, :CCtr, trans_id) | ||
CCti = var(pm, nw, :CCti, trans_id) | ||
|
||
P_fr = var(pm, nw, :Pt, f_idx) | ||
Q_fr = var(pm, nw, :Qt, f_idx) | ||
p_to = [var(pm, nw, :pt, t_idx)[c] for c in t_connections] | ||
q_to = [var(pm, nw, :qt, t_idx)[c] for c in t_connections] | ||
|
||
for (f_idx,fc) in enumerate(f_connections) | ||
for (t_idx,tc) in enumerate(t_connections) | ||
JuMP.@constraint(pm.model, Wr_fr[fc,tc]-Wr_fr[fc,next[tc]]-Wr_fr[next[fc],tc]+Wr_fr[next[fc],next[tc]] == (pol*tm_scale)^2*tm[f_idx]*tm[t_idx]*Wr_to[fc,tc]) | ||
JuMP.@constraint(pm.model, Wi_fr[fc,tc]-Wi_fr[fc,next[tc]]-Wi_fr[next[fc],tc]+Wi_fr[next[fc],next[tc]] == (pol*tm_scale)^2*tm[f_idx]*tm[t_idx]*Wi_to[fc,tc]) | ||
end | ||
end | ||
|
||
Tt = [1 -1 0; 0 1 -1; -1 0 1] # TODO | ||
constraint_SWL_psd(pm.model, Xtr, Xti, Wr_fr, Wi_fr, CCtr, CCti) # link W, CCt and Xt | ||
# define powers as affine transformations of X | ||
JuMP.@constraint(pm.model, P_fr .== Xtr*Tt) | ||
JuMP.@constraint(pm.model, Q_fr .== Xti*Tt) | ||
JuMP.@constraint(pm.model, LinearAlgebra.diag(Tt*Xtr) + p_to .== 0) | ||
JuMP.@constraint(pm.model, LinearAlgebra.diag(Tt*Xti) + q_to .== 0) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure it matters here, but the convential angle rotation order is phase a - 0 degrees, b -120 degrees, c -240=+120 degrees
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is correct. It looks like
va
could have multiple solutions since it is being calculated using off-diagonals ofWr
andWi
(which only have information about angle differences). Without lines 82-84, the angles don't seem to follow conventional order of (0,-120deg, 120deg).