Skip to content

Commit 9e25005

Browse files
frederikgethccoffrin
authored andcommitted
Current voltage form (lanl-ansi#622)
* add current-voltage formulation in rectangular coordinates * add tests for OPF and PF problem types * add initial docs * constraint formulation tweak * remove unneeded constraints * adding doc strings * Debugged and unit tested * IVRPowerModel subtype of ACRPowerModel * dcline power limits through variable definitions * separate series and total branch current variables * Update CHANGELOG.md * add check variable bounds test
1 parent b80f4fe commit 9e25005

19 files changed

+1291
-35
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ PowerModels.jl Change Log
99
- Removed `Inf` bounds from variables (#630)
1010
- Removal of unused functions in `solution.jl`: `get_solution`, `add_generator_power_setpoint`, `add_storage_setpoint`, `add_branch_flow_setpoint`, `add_dcline_flow_setpoint` (breaking) (#637)
1111
- Fixed a solution reporting bug when the optimizer result count is zero
12+
- Added IVRForm (rectangular current-voltage formulation) (#622)
13+
1214

1315
### v0.13.1
1416
- Added DCMPPowerModel for replication of Matpower's DC model (#612)
@@ -17,6 +19,7 @@ PowerModels.jl Change Log
1719
- Fixed quadratic terms in nonlinear objectives (#621)
1820
- Fixed incorrect flow bounds based on current flow limits (#631)
1921

22+
2023
### v0.13.0
2124
- Added SOC formulation of the storage model
2225
- Added support for optional line flow constraints (#576)

CONTRIBUTING.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Comments, Questions, Feature Requests, Bug Reports
22

3-
At this time, any kind of comment, question, feature request or bug report is welcome in the issue tracker. If you are asking a question, please search the issues for an answer before making a post.
3+
At this time, any kind of comment, question, feature request or bug report is welcome in the issue tracker. If you are asking a question, please search the issues for an answer before making a post.
44

55
## Code Contributions
66

@@ -9,7 +9,7 @@ Community-driven development and enhancement of PowerModels is welcomed and enco
99
That said it is important to keep in mind the code-quality requirements and scope of PowerModels in mind, before preparing a contribution.
1010

1111
### PowerModels Scope
12-
The primary motivation for PowerModels is to help researchers quickly and easily replicate _established_ results in power system optimization.
12+
The primary motivation for PowerModels is to help researchers quickly and easily replicate _established_ results in power system optimization.
1313
More preliminary research and development efforts are best left to other packages, which extend PowerModels.
1414
That said, the PowerModels package should have a software design that makes building such extensions as easy as possible.
1515

@@ -19,7 +19,7 @@ To that end, the following code quality controls are necessary for all contribut
1919
- all features must have unit tests
2020
- naming conventions must be consistent
2121
- core features must have documentation
22-
- code style must be consistent (e.g. 4 spaces for indetnation)
22+
- code style must be consistent (e.g. 4 spaces for indentation)
2323

2424
Due to these quality control requirements, pull request to PowerModels may require lengthy discussion and code review (e.g. see [#111](https://github.com/lanl-ansi/PowerModels.jl/pull/111)).
2525

README.md

+8-6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ This enables the definition of a wide variety of power network formulations and
2828
* SDP Relaxation (W-space)
2929
* SOC Relaxation (W-space)
3030
* QC Relaxation (W+L-space)
31+
* IV (rectangular coordinates)
32+
3133

3234
**Network Data Formats**
3335
* Matpower ".m" files
@@ -58,7 +60,7 @@ The primary developer is Carleton Coffrin(@ccoffrin) with support from the follo
5860
- Hakan Ergun (@hakanergun) KU Leuven, HVDC lines
5961
- David Fobes (@pseudocubic) LANL, PSS(R)E v33 data support
6062
- Rory Finnegan (@rofinn) Invenia, Memento Logging
61-
- Frederik Geth (@frederikgeth) CSIRO, storage modeling advise, Branch Flow formulation
63+
- Frederik Geth (@frederikgeth) CSIRO, storage modeling advise, Branch Flow and current-voltage formulation
6264
- Jonas Kersulis (@kersulis) University of Michigan, Sparse SDP formulation
6365
- Miles Lubin (@mlubin) MIT, Julia/JuMP advise
6466
- Yeesian Ng (@yeesian) MIT, Documenter.jl setup
@@ -69,13 +71,13 @@ The primary developer is Carleton Coffrin(@ccoffrin) with support from the follo
6971

7072
If you find PowerModels useful in your work, we kindly request that you cite the following [publication](https://ieeexplore.ieee.org/document/8442948/):
7173
```
72-
@inproceedings{8442948,
73-
author = {Carleton Coffrin and Russell Bent and Kaarthik Sundar and Yeesian Ng and Miles Lubin},
74-
title = {PowerModels.jl: An Open-Source Framework for Exploring Power Flow Formulations},
75-
booktitle = {2018 Power Systems Computation Conference (PSCC)},
74+
@inproceedings{8442948,
75+
author = {Carleton Coffrin and Russell Bent and Kaarthik Sundar and Yeesian Ng and Miles Lubin},
76+
title = {PowerModels.jl: An Open-Source Framework for Exploring Power Flow Formulations},
77+
booktitle = {2018 Power Systems Computation Conference (PSCC)},
7678
year = {2018},
7779
month = {June},
78-
pages = {1-8},
80+
pages = {1-8},
7981
doi = {10.23919/PSCC.2018.8442948}
8082
}
8183
```

docs/src/formulations.md

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ AbstractACPModel <: AbstractPowerModel
77
AbstractDCPModel <: AbstractPowerModel
88
AbstractWRModel <: AbstractPowerModel
99
AbstractWModel <: AbstractPowerModel
10+
AbstractIVRModel <: AbstractPowerModel
1011
```
1112

1213
## Power Models
@@ -16,6 +17,8 @@ ACPPowerModel <: AbstractACPForm
1617

1718
DCPPowerModel <: AbstractDCPForm
1819

20+
IVRPowerModel <: AbstractIVRModel
21+
1922
SOCWRPowerModel <: AbstractWRForm
2023
QCRMPowerModel <: AbstractWRForm
2124

docs/src/math-model.md

+39
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,42 @@ Note that constraints $\eqref{eq_line_losses} - \eqref{eq_ohms_bfm}$ replace $\e
124124
- Eq. $\eqref{eq_series_power_flow}$ - implicit, substituted out before implementation
125125
- Eq. $\eqref{eq_complex_power_definition}$ - `constraint_model_voltage` in `constraint_template.jl`
126126
- Eq. $\eqref{eq_ohms_bfm}$ - `constraint_voltage_magnitude_difference` in `constraint_template.jl`
127+
128+
129+
## AC Optimal Power Flow in Current-Voltage Variables
130+
A variable $I^{s}_{ij}$, representing the current in the direction $i$ to $j$, through the series part of the pi-section, is used.
131+
The mathematical structure for a current-voltage formulation is conceived as:
132+
133+
```math
134+
\begin{align}
135+
%
136+
\mbox{variables: } & \nonumber \\
137+
& I^g_k \;\; \forall k\in G \nonumber \\
138+
& V_i \;\; \forall i\in N \nonumber \\
139+
& I^{s}_{ij} \;\; \forall (i,j) \in E \cup E^R \mbox{ - branch complex (series) current}\\
140+
& I_{ij} \;\; \forall (i,j) \in E \cup E^R \mbox{ - branch complex (total) current} \label{var_total_current}\\
141+
%
142+
\mbox{minimize: } & \sum_{k \in G} c_{2k} (\Re(S^g_k))^2 + c_{1k}\Re(S^g_k) + c_{0k} \nonumber\\
143+
%
144+
\mbox{subject to: } & \nonumber \\
145+
& \angle V_{r} = 0 \;\; \forall r \in R \nonumber \\
146+
& S^{gl}_k \leq \Re(V_i (I^g_k)^*) + j \Im(V_i (I^g_k)^*) \leq S^{gu}_k \;\; \forall k \in G \label{eq_complex_power_definition_gen}\\
147+
& v^l_i \leq |V_i| \leq v^u_i \;\; \forall i \in N \nonumber\\
148+
& \sum_{\substack{k \in G_i}} I^g_k - \sum_{\substack{k \in L_i}} (S^d_k/V_i)^{*} - \sum_{\substack{k \in S_i}} Y^s_k V_i = \sum_{\substack{(i,j)\in E_i \cup E_i^R}} I_{ij} \;\; \forall i\in N \label{eq_kcl_current} \\
149+
& I_{ij} = \frac{I^{s}_{ij}}{T_{ij}^*} + Y^c_{ij} \frac{V_i}{|T_{ij}|^2} \;\; \forall (i,j)\in E \label{eq_current_from} \\
150+
& I_{ji} = -I^{s}_{ij} + Y^c_{ji} V_j \;\; \forall (i,j)\in E \label{eq_current_to} \\
151+
& \frac{V_i}{{T}_{ij}} = V_j + z_{ij} I^{s}_{ij} \;\; \forall (i,j) \in E \label{eq_ohms_iv} \\
152+
& |S_{ij}| = |V_{i}| |I_{ij}| \leq s^u_{ij} \;\; \forall (i,j) \in E \cup E^R \nonumber\\
153+
& |I_{ij}| \leq i^u_{ij} \;\; \forall (i,j) \in E \cup E^R \nonumber\\
154+
& \theta^{\Delta l}_{ij} \leq \angle (V_i V^*_j) \leq \theta^{\Delta u}_{ij} \;\; \forall (i,j) \in E \nonumber
155+
%
156+
\end{align}
157+
```
158+
159+
### Mapping to function names
160+
- Eq. $\eqref{var_total_current}$ - total current flow into a branch on either end `variable_branch_current`
161+
- Eq. $\eqref{eq_complex_power_definition_gen}$ - models active and reactive power range of a generator `constraint_gen`
162+
- Eq. $\eqref{eq_kcl_current}$ - Kirchhoff's current law in current variables `constraint_current_balance`
163+
- Eq. $\eqref{eq_current_from}$ - branch from-side current constraint in `constraint_current_from`
164+
- Eq. $\eqref{eq_current_to}$ - branch to-side current constraint in `constraint_current_to`
165+
- Eq. $\eqref{eq_ohms_iv}$ - Ohm's law `constraint_voltage_difference`

src/PowerModels.jl

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ include("core/admittance_matrix.jl")
5353

5454
include("io/json.jl")
5555

56+
include("form/iv.jl")
57+
5658
include("form/acp.jl")
5759
include("form/acr.jl")
5860
include("form/act.jl")
@@ -67,8 +69,10 @@ include("form/shared.jl")
6769
include("prob/opb.jl")
6870
include("prob/pf.jl")
6971
include("prob/pf_bf.jl")
72+
include("prob/pf_iv.jl")
7073
include("prob/opf.jl")
7174
include("prob/opf_bf.jl")
75+
include("prob/opf_iv.jl")
7276
include("prob/ots.jl")
7377
include("prob/tnep.jl")
7478
include("prob/test.jl")

src/core/constraint_template.jl

+132-5
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ function constraint_model_voltage_ne(pm::AbstractPowerModel; nw::Int=pm.cnw, cnd
5252
constraint_model_voltage_ne(pm, nw, cnd)
5353
end
5454

55+
"""
56+
This constraint captures problem agnostic constraints that define limits for
57+
voltage magnitudes (where variable bounds cannot be used)
58+
59+
Notable examples include IVRPowerModel and ACRPowerModel
60+
"""
61+
function constraint_voltage_magnitude_bounds(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
62+
bus = ref(pm, nw, :bus, i)
63+
constraint_voltage_magnitude_bounds(pm, nw, cnd, i, bus["vmin"][cnd], bus["vmax"][cnd])
64+
end
5565

5666
### Current Constraints ###
5767

@@ -82,12 +92,26 @@ function constraint_reactive_gen_setpoint(pm::AbstractPowerModel, i::Int; nw::In
8292
constraint_reactive_gen_setpoint(pm, nw, cnd, gen["index"], gen["qg"][cnd])
8393
end
8494

95+
""
8596
function constraint_generation_on_off(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
8697
gen = ref(pm, nw, :gen, i)
8798

8899
constraint_generation_on_off(pm, nw, cnd, i, gen["pmin"][cnd], gen["pmax"][cnd], gen["qmin"][cnd], gen["qmax"][cnd])
89100
end
90101

102+
"defines limits on active power output of a generator where bounds can't be used"
103+
function constraint_gen_active_power_limits(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
104+
gen = ref(pm, nw, :gen, i)
105+
bus = gen["gen_bus"]
106+
constraint_gen_active_power_limits(pm, nw, cnd, i, bus, gen["pmax"][cnd], gen["pmin"][cnd])
107+
end
108+
109+
"defines limits on reactive power output of a generator where bounds can't be used"
110+
function constraint_gen_reactive_power_limits(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
111+
gen = ref(pm, nw, :gen, i)
112+
bus = gen["gen_bus"]
113+
constraint_gen_reactive_power_limits(pm, nw, cnd, i, bus, gen["qmax"][cnd], gen["qmin"][cnd])
114+
end
91115

92116
### Bus - Setpoint Constraints ###
93117

@@ -202,6 +226,31 @@ function constraint_power_balance_ne(pm::AbstractPowerModel, i::Int; nw::Int=pm.
202226
constraint_power_balance_ne(pm, nw, cnd, i, bus_arcs, bus_arcs_dc, bus_arcs_sw, bus_arcs_ne, bus_gens, bus_storage, bus_pd, bus_qd, bus_gs, bus_bs)
203227
end
204228

229+
""
230+
function constraint_current_balance(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
231+
if !haskey(con(pm, nw, cnd), :kcl_cr)
232+
con(pm, nw, cnd)[:kcl_cr] = Dict{Int,JuMP.ConstraintRef}()
233+
end
234+
if !haskey(con(pm, nw, cnd), :kcl_ci)
235+
con(pm, nw, cnd)[:kcl_ci] = Dict{Int,JuMP.ConstraintRef}()
236+
end
237+
238+
bus = ref(pm, nw, :bus, i)
239+
bus_arcs = ref(pm, nw, :bus_arcs, i)
240+
bus_arcs_dc = ref(pm, nw, :bus_arcs_dc, i)
241+
bus_gens = ref(pm, nw, :bus_gens, i)
242+
bus_loads = ref(pm, nw, :bus_loads, i)
243+
bus_shunts = ref(pm, nw, :bus_shunts, i)
244+
245+
246+
bus_pd = Dict(k => ref(pm, nw, :load, k, "pd", cnd) for k in bus_loads)
247+
bus_qd = Dict(k => ref(pm, nw, :load, k, "qd", cnd) for k in bus_loads)
248+
249+
bus_gs = Dict(k => ref(pm, nw, :shunt, k, "gs", cnd) for k in bus_shunts)
250+
bus_bs = Dict(k => ref(pm, nw, :shunt, k, "bs", cnd) for k in bus_shunts)
251+
252+
constraint_current_balance(pm, nw, cnd, i, bus_arcs, bus_arcs_dc, bus_gens, bus_pd, bus_qd, bus_gs, bus_bs)
253+
end
205254

206255
### Branch - Ohm's Law Constraints ###
207256

@@ -277,6 +326,39 @@ function constraint_ohms_y_to(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw, cn
277326
end
278327

279328

329+
""
330+
function constraint_current_from(pm::AbstractIVRModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
331+
branch = ref(pm, nw, :branch, i)
332+
f_bus = branch["f_bus"]
333+
t_bus = branch["t_bus"]
334+
f_idx = (i, f_bus, t_bus)
335+
336+
tr, ti = calc_branch_t(branch)
337+
g_fr = branch["g_fr"][cnd]
338+
b_fr = branch["b_fr"][cnd]
339+
tm = branch["tap"][cnd]
340+
341+
constraint_current_from(pm, nw, cnd, f_bus, f_idx, g_fr, b_fr, tr[cnd], ti[cnd], tm)
342+
end
343+
344+
""
345+
function constraint_current_to(pm::AbstractIVRModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
346+
branch = ref(pm, nw, :branch, i)
347+
f_bus = branch["f_bus"]
348+
t_bus = branch["t_bus"]
349+
f_idx = (i, f_bus, t_bus)
350+
t_idx = (i, t_bus, f_bus)
351+
352+
tr, ti = calc_branch_t(branch)
353+
g_to = branch["g_to"][cnd]
354+
b_to = branch["b_to"][cnd]
355+
tm = branch["tap"][cnd]
356+
357+
constraint_current_to(pm, nw, cnd, t_bus, f_idx, t_idx, g_to, b_to)
358+
end
359+
360+
361+
280362
### Branch - On/Off Ohm's Law Constraints ###
281363

282364
""
@@ -363,6 +445,21 @@ function constraint_ohms_yt_to_ne(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw
363445
end
364446

365447

448+
""
449+
function constraint_voltage_drop(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
450+
branch = ref(pm, nw, :branch, i)
451+
f_bus = branch["f_bus"]
452+
t_bus = branch["t_bus"]
453+
f_idx = (i, f_bus, t_bus)
454+
455+
tr, ti = calc_branch_t(branch)
456+
r = branch["br_r"][cnd,cnd]
457+
x = branch["br_x"][cnd,cnd]
458+
tm = branch["tap"][cnd]
459+
460+
constraint_voltage_drop(pm, nw, cnd, i, f_bus, t_bus, f_idx, r, x, tr[cnd], ti[cnd], tm)
461+
end
462+
366463

367464
### Branch - Current ###
368465

@@ -621,8 +718,8 @@ function constraint_flow_losses(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw,
621718
f_idx = (i, f_bus, t_bus)
622719
t_idx = (i, t_bus, f_bus)
623720

624-
r = branch["br_r"][cnd]
625-
x = branch["br_x"][cnd]
721+
r = branch["br_r"][cnd,cnd]
722+
x = branch["br_x"][cnd,cnd]
626723
tm = branch["tap"][cnd]
627724
g_sh_fr = branch["g_fr"][cnd]
628725
g_sh_to = branch["g_to"][cnd]
@@ -640,8 +737,8 @@ function constraint_voltage_magnitude_difference(pm::AbstractPowerModel, i::Int;
640737
f_idx = (i, f_bus, t_bus)
641738
t_idx = (i, t_bus, f_bus)
642739

643-
r = branch["br_r"][cnd]
644-
x = branch["br_x"][cnd]
740+
r = branch["br_r"][cnd,cnd]
741+
x = branch["br_x"][cnd,cnd]
645742
g_sh_fr = branch["g_fr"][cnd]
646743
b_sh_fr = branch["b_fr"][cnd]
647744
tm = branch["tap"][cnd]
@@ -760,7 +857,7 @@ function constraint_storage_on_off(pm::AbstractPowerModel, i::Int; nw::Int=pm.cn
760857
storage = ref(pm, nw, :storage, i)
761858
charge_ub = storage["charge_rating"]
762859
discharge_ub = storage["discharge_rating"]
763-
860+
764861
inj_lb, inj_ub = ref_calc_storage_injection_bounds(ref(pm, nw, :storage), ref(pm, nw, :bus), cnd)
765862
pmin = inj_lb[i]
766863
pmax = inj_ub[i]
@@ -797,3 +894,33 @@ function constraint_active_dcline_setpoint(pm::AbstractPowerModel, i::Int; nw::I
797894

798895
constraint_active_dcline_setpoint(pm, nw, cnd, f_idx, t_idx, pf, pt)
799896
end
897+
898+
899+
""
900+
function constraint_dcline_power_limits_from(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
901+
dcline = ref(pm, nw, :dcline, i)
902+
f_bus = dcline["f_bus"]
903+
t_bus = dcline["t_bus"]
904+
f_idx = (i, f_bus, t_bus)
905+
906+
pmax = dcline["pmaxf"][cnd]
907+
pmin = dcline["pminf"][cnd]
908+
909+
qmax = dcline["qmaxf"][cnd]
910+
qmin = dcline["qminf"][cnd]
911+
constraint_dcline_power_limits_from(pm, nw, cnd, i, f_bus, f_idx, pmax, pmin, qmax, qmin)
912+
end
913+
914+
""
915+
function constraint_dcline_power_limits_to(pm::AbstractPowerModel, i::Int; nw::Int=pm.cnw, cnd::Int=pm.ccnd)
916+
dcline = ref(pm, nw, :dcline, i)
917+
f_bus = dcline["f_bus"]
918+
t_bus = dcline["t_bus"]
919+
t_idx = (i, t_bus, f_bus)
920+
921+
pmax = dcline["pmaxt"][cnd]
922+
pmin = dcline["pmint"][cnd]
923+
qmax = dcline["qmaxt"][cnd]
924+
qmin = dcline["qmint"][cnd]
925+
constraint_dcline_power_limits_to(pm, nw, cnd, i, t_bus, t_idx, pmax, pmin, qmax, qmin)
926+
end

0 commit comments

Comments
 (0)