Skip to content

Commit 109bc42

Browse files
User extension API revision, adding to GOC3 model (#26)
1 parent 645f9b7 commit 109bc42

File tree

6 files changed

+210
-26
lines changed

6 files changed

+210
-26
lines changed

README.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,21 @@ result = madnlp(model; tol=1e-6)
3030
#the other input data to generate a structure of named tuples which can then interface with
3131
#ExaModels to generate the full model. We do not make any relaxations or decompositions for this problem
3232

33-
model, sc_data, vars, lengths = goc3_model(
33+
model, cons, vars, lengths, sc_data_array = goc3_model(
3434
"data/C3E4N00073D1_scenario_303.json", "data/C3E4N00073D1_scenario_303_solution.json";
3535
backend = CUDABackend()
3636
)
3737
result = madnlp(model; tol=1e-4)
3838

3939
#Solution from GPU can be used to warm start a CPU solution or vice versa
40-
model_cpu, sc_data, vars, lengths = goc3_model(
40+
model, cons, vars, lengths, sc_data_array = goc3_model(
4141
"data/C3E4N00073D1_scenario_303.json", "data/C3E4N00073D1_scenario_303_solution.json";
4242
result_set = [result, vars]
4343
)
4444
result_cpu = ipopt(model_cpu; tol=1e-8)
4545

4646
#Additionally, the SC problem can be evaluated without contingencies
47-
model, sc_data, vars, lengths = goc3_model(
47+
model, cons, vars, lengths, sc_data_array = goc3_model(
4848
"data/C3E4N00073D1_scenario_303.json", "data/C3E4N00073D1_scenario_303_solution.json";
4949
backend = CUDABackend(), include_ctg = false
5050
)
@@ -99,6 +99,39 @@ result = madnlp(model; tol=1e-6)
9999
#https://github.com/mit-shin-group/multi-period-opf-data
100100
```
101101

102+
### User extension modeling
103+
ExaModelsPower also supports the user arbitrarily extending any prebuilt models
104+
```julia
102105

106+
curve = [1, .9, .95]
107+
# Create vector of NamedTuples elec\_data w/ device data
108+
untimed_elec_data = [(i = 1, bus = 1, cost = -5000), (i = 2, bus = 2, cost = -2000)]
109+
Ntime = 3; Nbus = 2
110+
elec_data = [(;b..., t = t) for b in untimed_elec_data, t in 1:Ntime]
111+
elec_min = zeros(size(elec_data)); elec_max = fill(50, size(elec_data)); elec_scale = Float64(10)
112+
113+
# User-defined model modifications go here
114+
function add_electrolyzers(core, vars, cons)
115+
# Add new variable to core
116+
p_elec = variable(core, size(elec_data, 1), size(elec_data, 2); lvar = elec_min, uvar = elec_max)
117+
118+
# Objectives are additive. Add secondary objective
119+
o2 = objective(core, e.cost*p_elec[e.i, e.t] for e in elec_data)
120+
121+
# Add electrolyzer load to power balance for each bus
122+
c_elec_power_balance = constraint!(core, cons.c_active_power_balance, e.bus + Nbus*(e.t-1) => p_elec[e.i, e.t] for e in elec_data)
123+
124+
# Ramping limit over time
125+
c_elec_ramp = constraint(core, p_elec[e.i, e.t] - p_elec[e.i, e.t - 1] for e in elec_data[:, 2:Ntime]; lcon = fill(-elec_scale, size(elec_data[:, 2:Ntime])), ucon = fill(elec_scale, size(elec_data[:, 2:Ntime])))
126+
127+
# Set initial electrolyzer power to 0
128+
c_elec_ramp_init = constraint(core, p_elec[e.i, e.t] for e in elec_data[:, 1];)
129+
130+
# Return new variables and constraints to be tracked
131+
return ((p_elec=p_elec,), (c_elec_ramp=c_elec_ramp, c_elec_ramp_init=c_elec_ramp_init))
132+
end
133+
# Generate model
134+
model, vars, cons = mpopf_model("pglib_opf_case3_lmbd.m", curve; user_callback = add_electrolyzers) # user_callback function added after initial mpopf model is constructed
135+
```
103136

104137

src/mpopf.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,11 @@ function build_mpopf(data, Nbus, N, form, user_callback; backend = nothing, T =
234234
vars, cons = add_piecewise_cons(core, data, N, vars, cons, storage_complementarity_constraint)
235235
end
236236

237-
vars, cons = user_callback(core, vars, cons)
238-
model = ExaModel(core; kwargs...)
237+
vars2, cons2 = user_callback(core, vars, cons)
238+
model =ExaModel(core; kwargs...)
239+
240+
vars = (;vars..., vars2...)
241+
cons = (;cons..., cons2...)
239242
return model, vars, cons
240243
end
241244

@@ -251,8 +254,11 @@ function build_mpopf(data, Nbus, N, discharge_func::Function, form, user_callbac
251254
vars, cons = add_smooth_cons(core, data, N, vars, cons, discharge_func)
252255
end
253256

254-
vars, cons = user_callback(core, vars, cons)
255-
model = ExaModel(core; kwargs...)
257+
vars2, cons2 = user_callback(core, vars, cons)
258+
model =ExaModel(core; kwargs...)
259+
260+
vars = (;vars..., vars2...)
261+
cons = (;cons..., cons2...)
256262
return model, vars, cons
257263
end
258264

src/opf.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
function dummy_extension(core, vars, cons)
2-
return vars, cons
2+
return (;), (;)
33
end
44

55
function build_polar_opf(data, user_callback; backend = nothing, T=Float64, kwargs...)
@@ -87,9 +87,12 @@ function build_polar_opf(data, user_callback; backend = nothing, T=Float64, kwar
8787
c_to_thermal_limit = c_to_thermal_limit
8888
)
8989

90-
vars, cons = user_callback(core, vars, cons)
90+
vars2, cons2 = user_callback(core, vars, cons)
9191
model =ExaModel(core; kwargs...)
9292

93+
vars = (;vars..., vars2...)
94+
cons = (;cons..., cons2...)
95+
9396
return model, vars, cons
9497
end
9598

@@ -182,8 +185,11 @@ function build_rect_opf(data, user_callback; backend = nothing, T=Float64, kwarg
182185
c_voltage_magnitude = c_voltage_magnitude
183186
)
184187

185-
vars, cons = user_callback(core, vars, cons)
186-
model = ExaModel(core; kwargs...)
188+
vars2, cons2 = user_callback(core, vars, cons)
189+
model =ExaModel(core; kwargs...)
190+
191+
vars = (;vars..., vars2...)
192+
cons = (;cons..., cons2...)
187193

188194
return model, vars, cons
189195
end

src/scopf.jl

Lines changed: 150 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ function goc3_model(
44
T = Float64,
55
include_ctg = true, #contingencies will be specified in input data
66
result_set = [],
7+
user_callback = dummy_extension,
78
kwargs...
89
)
910

@@ -241,10 +242,11 @@ function goc3_model(
241242
end
242243

243244
#4.2.1 Bus power mismatch and penalized mismatch definitions
244-
c_11 = constraint(core, p_it_plus[b.i, b.t] - p_it[b.i, b.t] for b in sc_data.busarray; ucon = fill(Inf, size(sc_data.busarray)))
245-
c_12 = constraint(core, p_it_plus[b.i, b.t] + p_it[b.i, b.t] for b in sc_data.busarray; ucon = fill(Inf, size(sc_data.busarray)))
246-
c_13 = constraint(core, q_it_plus[b.i, b.t] - q_it[b.i, b.t] for b in sc_data.busarray; ucon = fill(Inf, size(sc_data.busarray)))
247-
c_14 = constraint(core, q_it_plus[b.i, b.t] + q_it[b.i, b.t] for b in sc_data.busarray; ucon = fill(Inf, size(sc_data.busarray)))
245+
c11 = constraint(core, p_it_plus[b.i, b.t] - p_it[b.i, b.t] for b in sc_data.busarray; ucon = fill(Inf, size(sc_data.busarray)))
246+
c12 = constraint(core, p_it_plus[b.i, b.t] + p_it[b.i, b.t] for b in sc_data.busarray; ucon = fill(Inf, size(sc_data.busarray)))
247+
c13 = constraint(core, q_it_plus[b.i, b.t] - q_it[b.i, b.t] for b in sc_data.busarray; ucon = fill(Inf, size(sc_data.busarray)))
248+
c14 = constraint(core, q_it_plus[b.i, b.t] + q_it[b.i, b.t] for b in sc_data.busarray; ucon = fill(Inf, size(sc_data.busarray)))
249+
cons = (c11=c11, c12=c12, c13=c13, c14=c14)
248250
#4.2.2 Bus pwoer mismatch penalty
249251
c15 = constraint(core, z_it_p[b.i, b.t] - b.dt*sum(sc_data.c_p)*p_it_plus[b.i, b.t] for b in sc_data.busarray)
250252
c16 = constraint(core, z_it_q[b.i, b.t] - b.dt*sum(sc_data.c_q)*q_it_plus[b.i, b.t] for b in sc_data.busarray)
@@ -254,6 +256,7 @@ function goc3_model(
254256
c17_cs = constraint!(core, c17, cs.bus + I*(cs.t-1) => -p_jt_cs[cs.j_cs, cs.t] for cs in sc_data.csarray)
255257
c17_sh = constraint!(core, c17, sh.bus + I*(sh.t-1) => -p_jt_sh[sh.j_sh, sh.t] for sh in sc_data.shuntarray)
256258

259+
257260
#Reminder: fr and to split for ln, xf, and dc
258261
c17_fr_ln = constraint!(core, c17, ln.fr_bus + I*(ln.t-1) => -p_jt_fr_ln[ln.j_ln, ln.t] for ln in sc_data.aclbrancharray)
259262
c17_fr_xf = constraint!(core, c17, xf.fr_bus + I*(xf.t-1) => -p_jt_fr_xf[xf.j_xf, xf.t] for xf in sc_data.acxbrancharray)
@@ -272,7 +275,6 @@ function goc3_model(
272275
c18_to_ln = constraint!(core, c18, ln.to_bus + I*(ln.t-1) => -q_jt_to_ln[ln.j_ln, ln.t] for ln in sc_data.aclbrancharray)
273276
c18_to_xf = constraint!(core, c18, xf.to_bus + I*(xf.t-1) => -q_jt_to_xf[xf.j_xf, xf.t] for xf in sc_data.acxbrancharray)
274277
c18_to_dc = constraint!(core, c18, dc.to_bus + I*(dc.t-1) => -q_jt_to_dc[dc.j_dc, dc.t] for dc in sc_data.dclinearray)
275-
276278

277279
#4.3.2 Reserve shortfall penalties
278280
c28 = constraint(core, z_nt_rgu[n.n_p, n.t] - n.dt*n.c_rgu*p_nt_rgu_plus[n.n_p, n.t] for n in sc_data.preservearray)
@@ -283,7 +285,6 @@ function goc3_model(
283285
c33 = constraint(core, z_nt_rrd[n.n_p, n.t] - n.dt*n.c_rrd*p_nt_rrd_plus[n.n_p, n.t] for n in sc_data.preservearray)
284286
c34 = constraint(core, z_nt_qru[n.n_q, n.t] - n.dt*n.c_qru*q_nt_qru_plus[n.n_q, n.t] for n in sc_data.qreservearray)
285287
c35 = constraint(core, z_nt_qrd[n.n_q, n.t] - n.dt*n.c_qrd*q_nt_qrd_plus[n.n_q, n.t] for n in sc_data.qreservearray)
286-
287288

288289
#4.3.3 Reserve requirements
289290
c36 = constraint(core, p_nt_rgu_req[n.n_p, n.t]/n.σ_rgu for n in sc_data.preservearray)
@@ -477,7 +478,6 @@ function goc3_model(
477478

478479
#4.8.3 AC branch flows
479480

480-
481481
c148_ln = constraint(core, -p_jt_fr_ln[ln.j_ln, ln.t] + ln.u_on*((ln.g_sr+ln.g_fr)*v_it[ln.fr_bus, ln.t]^2 + (-ln.g_sr*cos(θ_it[ln.fr_bus, ln.t] - θ_it[ln.to_bus, ln.t])
482482
- ln.b_sr*sin(θ_it[ln.fr_bus, ln.t] - θ_it[ln.to_bus, ln.t]))*v_it[ln.fr_bus, ln.t]*v_it[ln.to_bus, ln.t]) for ln in sc_data.aclbrancharray)
483483
c148_xf = constraint(core, -p_jt_fr_xf[xf.j_xf, xf.t] + xf.u_on*((xf.g_sr+xf.g_fr)*v_it[xf.fr_bus, xf.t]^2/(τ_jt_xf[xf.j_xf, xf.t]^2) + (-xf.g_sr*cos(θ_it[xf.fr_bus, xf.t] - θ_it[xf.to_bus, xf.t] - φ_jt_xf[xf.j_xf, xf.t])
@@ -494,7 +494,6 @@ function goc3_model(
494494
+ ln.g_sr*sin(θ_it[ln.fr_bus, ln.t] - θ_it[ln.to_bus, ln.t]))*v_it[ln.fr_bus, ln.t]*v_it[ln.to_bus, ln.t]) for ln in sc_data.aclbrancharray)
495495
c151_xf = constraint(core, -q_jt_to_xf[xf.j_xf, xf.t] + xf.u_on*((-xf.b_sr-xf.b_to-xf.b_ch/2)*v_it[xf.to_bus, xf.t]^2 + (xf.b_sr*cos(θ_it[xf.fr_bus, xf.t] - θ_it[xf.to_bus, xf.t] - φ_jt_xf[xf.j_xf, xf.t])
496496
+ xf.g_sr*sin(θ_it[xf.fr_bus, xf.t] - θ_it[xf.to_bus, xf.t] - φ_jt_xf[xf.j_xf, xf.t]))*v_it[xf.fr_bus, xf.t]*v_it[xf.to_bus, xf.t]/τ_jt_xf[xf.j_xf, xf.t]) for xf in sc_data.acxbrancharray)
497-
498497

499498
#4.8.4 DC lines
500499
c156 = constraint(core, p_jt_fr_dc[dc.j_dc, dc.t] + p_jt_to_dc[dc.j_dc, dc.t] for dc in sc_data.dclinearray)
@@ -535,11 +534,10 @@ function goc3_model(
535534
c163_sh = constraint!(core, c163, sh.bus + I*(sh.t-1) + I*L_T*(sh.k-1) => -p_jt_sh[sh.j_sh, sh.t] for sh in sc_data.k_shuntarray)
536535
c163_dc_fr = constraint!(core, c163, dc.fr_bus + I*(dc.t-1) + I*L_T*(dc.ctg-1) => -p_jt_fr_dc[dc.j_dc, dc.t] for dc in sc_data.jtk_dc_flattened)
537536
c163_dc_to = constraint!(core, c163, dc.to_bus + I*(dc.t-1) + I*L_T*(dc.ctg-1) => -p_jt_fr_dc[dc.j_dc, dc.t] for dc in sc_data.jtk_dc_flattened)
537+
538538
end
539539

540540

541-
model = ExaModel(core; kwargs...)
542-
543541
vars = (b_jt_sh = b_jt_sh,
544542
g_jt_sh = g_jt_sh,
545543
#Split e_w_plus into separate sets for W_en_min and W_en_max ad for pr, cs
@@ -657,6 +655,128 @@ function goc3_model(
657655
τ_jt_xf = τ_jt_xf,
658656
φ_jt_xf = φ_jt_xf)
659657

658+
cons = (
659+
c11 = c11,
660+
c12 = c12,
661+
c13 = c13,
662+
c14 = c14,
663+
c15 = c15,
664+
c16 = c16,
665+
c17 = c17,
666+
c18 = c18,
667+
c28 = c28,
668+
c29 = c29,
669+
c30 = c30,
670+
c31 = c31,
671+
c32 = c32,
672+
c33 = c33,
673+
c34 = c34,
674+
c35 = c35,
675+
c36 = c36,
676+
c37 = c37,
677+
cmax38 = cmax38,
678+
c38 = c38,
679+
c39 = c39,
680+
c40 = c40,
681+
c41 = c41,
682+
c42 = c42,
683+
c43 = c43,
684+
c44 = c44,
685+
c45 = c45,
686+
c46 = c46,
687+
c47 = c47,
688+
c68_pr = c68_pr,
689+
c68_cs = c68_cs,
690+
c69_pr = c69_pr,
691+
c69_cs = c69_cs,
692+
c70_pr = c70_pr,
693+
c70_cs = c70_cs,
694+
c71_pr = c71_pr,
695+
c71_cs = c71_cs,
696+
c72_pr = c72_pr,
697+
c72_cs = c72_cs,
698+
c73_pr = c73_pr,
699+
c73_cs = c73_cs,
700+
c74_pr = c74_pr,
701+
c74_cs = c74_cs,
702+
c75_pr = c75_pr,
703+
c75_cs = c75_cs,
704+
c76_pr = c76_pr,
705+
c76_cs = c76_cs,
706+
c78_pr = c78_pr,
707+
c78_cs = c78_cs,
708+
c79_pr = c79_pr,
709+
c79_cs = c79_cs,
710+
c94_pr = c94_pr,
711+
c94_cs = c94_cs,
712+
c95_pr = c95_pr,
713+
c95_cs = c95_cs,
714+
c96_pr = c96_pr,
715+
c96_cs = c96_cs,
716+
c97_pr = c97_pr,
717+
c97_cs = c97_cs,
718+
c98_pr = c98_pr,
719+
c98_cs = c98_cs,
720+
c99_pr = c99_pr,
721+
c99_cs = c99_cs,
722+
c100_pr = c100_pr,
723+
c100_cs = c100_cs,
724+
c101_pr = c101_pr,
725+
c102_pr = c102_pr,
726+
c102_cs = c102_cs,
727+
c103_pr = c103_pr,
728+
c104_pr = c104_pr,
729+
c104_cs = c104_cs,
730+
c105_cs = c105_cs,
731+
c109 = c109,
732+
c110 = c110,
733+
c111 = c111,
734+
c112 = c112,
735+
c113 = c113,
736+
c114 = c114,
737+
c115 = c115,
738+
c116 = c116,
739+
c117 = c117,
740+
c118 = c118,
741+
c119 = c119,
742+
c120 = c120,
743+
c121 = c121,
744+
c122 = c122,
745+
c123 = c123,
746+
c124 = c124,
747+
c125 = c125,
748+
c126 = c126,
749+
c127 = c127,
750+
c128 = c128,
751+
c130_pr = c130_pr,
752+
c130_cs = c130_cs,
753+
c131_pr = c131_pr,
754+
c131_cs = c131_cs,
755+
c132 = c132,
756+
c133 = c133,
757+
c134 = c134,
758+
c135 = c135,
759+
c139_ln = c139_ln,
760+
c139_xf = c139_xf,
761+
c140_ln = c140_ln,
762+
c140_xf = c140_xf,
763+
c141_ln = c141_ln,
764+
c141_xf = c141_xf,
765+
c144 = c144,
766+
c145 = c145,
767+
c146 = c146,
768+
c147 = c147,
769+
c148_ln = c148_ln,
770+
c148_xf = c148_xf,
771+
c149_ln = c149_ln,
772+
c149_xf = c149_xf,
773+
c150_ln = c150_ln,
774+
c150_xf = c150_xf,
775+
c151_ln = c151_ln,
776+
c151_xf = c151_xf,
777+
c156 = c156
778+
)
779+
660780
if include_ctg
661781
vars = (;vars...,
662782
s_jtk_plus_ln = s_jtk_plus_ln,
@@ -670,11 +790,30 @@ function goc3_model(
670790
z_tk_ctg = z_tk_ctg,
671791
z_t_ctg_min = z_t_ctg_min,
672792
z_t_ctg_avg = z_t_ctg_avg)
793+
cons = (;cons...,
794+
c4=c4,
795+
c5=c5,
796+
c10=c10,
797+
c158_ln=c158_ln,
798+
c158_xf=c158_xf,
799+
c159_ln=c159_ln,
800+
c159_xf=c159_xf,
801+
c160_ln=c160_ln,
802+
c160_xf=c160_xf,
803+
c161_ln=c161_ln,
804+
c161_xf=c161_xf,
805+
c162=c162,
806+
c163=c163)
673807
end
674808

809+
vars2, cons2 = user_callback(core, vars, cons)
810+
model =ExaModel(core; kwargs...)
811+
812+
vars = (;vars..., vars2...)
813+
cons = (;cons..., cons2...)
675814

676815
@info "built model"
677-
return model, sc_data_array, vars, lengths
816+
return model, cons, vars, lengths, sc_data_array
678817

679818
end
680819

test/opf_tests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,6 @@ end
6969
function sc_tests(filename, backend, T)
7070
uc_filename = filename*"_solution.json"
7171
filename = filename*".json"
72-
model, sc_data_array, vars, lengths = ExaModelsPower.goc3_model(filename, uc_filename; backend=backend, T=T)
72+
model, cons, vars, lengths, sc_data_array = ExaModelsPower.goc3_model(filename, uc_filename; backend=backend, T=T)
7373
result = madnlp(model; max_iter=5, tol=1e-2)
7474
end

test/runtests.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ function add_electrolyzers(core, vars, cons)
8383
length(elec_data)), -elec_scale),
8484
ucon = fill!(similar(elec_data, Float64,
8585
length(elec_data)), elec_scale))
86-
vars = (;vars..., p_elec=p_elec)
87-
cons = (;cons..., c_elec_ramp=c_elec_ramp)
86+
vars = (p_elec=p_elec,)
87+
cons = (c_elec_ramp=c_elec_ramp,)
8888
return vars, cons
8989
end
9090

@@ -224,7 +224,7 @@ function runtests()
224224

225225
@testset "User callback, $(T), $(backend)" begin
226226
model, vars, cons = mpopf_model(
227-
"../data/pglib_opf_case3_lmbd.m", elec_curve;
227+
"../data/pglib_opf_case3_lmbd_mod.m", elec_curve;
228228
user_callback = add_electrolyzers, T=T, backend=backend)
229229
end
230230

0 commit comments

Comments
 (0)