Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,21 @@ result = madnlp(model; tol=1e-6)
#the other input data to generate a structure of named tuples which can then interface with
#ExaModels to generate the full model. We do not make any relaxations or decompositions for this problem

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

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

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

### User extension modeling
ExaModelsPower also supports the user arbitrarily extending any prebuilt models
```julia

curve = [1, .9, .95]
# Create vector of NamedTuples elec\_data w/ device data
untimed_elec_data = [(i = 1, bus = 1, cost = -5000), (i = 2, bus = 2, cost = -2000)]
Ntime = 3; Nbus = 2
elec_data = [(;b..., t = t) for b in untimed_elec_data, t in 1:Ntime]
elec_min = zeros(size(elec_data)); elec_max = fill(50, size(elec_data)); elec_scale = Float64(10)

# User-defined model modifications go here
function add_electrolyzers(core, vars, cons)
# Add new variable to core
p_elec = variable(core, size(elec_data, 1), size(elec_data, 2); lvar = elec_min, uvar = elec_max)

# Objectives are additive. Add secondary objective
o2 = objective(core, e.cost*p_elec[e.i, e.t] for e in elec_data)

# Add electrolyzer load to power balance for each bus
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)

# Ramping limit over time
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])))

# Set initial electrolyzer power to 0
c_elec_ramp_init = constraint(core, p_elec[e.i, e.t] for e in elec_data[:, 1];)

# Return new variables and constraints to be tracked
return ((p_elec=p_elec,), (c_elec_ramp=c_elec_ramp, c_elec_ramp_init=c_elec_ramp_init))
end
# Generate model
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
```


14 changes: 10 additions & 4 deletions src/mpopf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,11 @@ function build_mpopf(data, Nbus, N, form, user_callback; backend = nothing, T =
vars, cons = add_piecewise_cons(core, data, N, vars, cons, storage_complementarity_constraint)
end

vars, cons = user_callback(core, vars, cons)
model = ExaModel(core; kwargs...)
vars2, cons2 = user_callback(core, vars, cons)
model =ExaModel(core; kwargs...)

vars = (;vars..., vars2...)
cons = (;cons..., cons2...)
return model, vars, cons
end

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

vars, cons = user_callback(core, vars, cons)
model = ExaModel(core; kwargs...)
vars2, cons2 = user_callback(core, vars, cons)
model =ExaModel(core; kwargs...)

vars = (;vars..., vars2...)
cons = (;cons..., cons2...)
return model, vars, cons
end

Expand Down
14 changes: 10 additions & 4 deletions src/opf.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function dummy_extension(core, vars, cons)
return vars, cons
return (;), (;)
end

function build_polar_opf(data, user_callback; backend = nothing, T=Float64, kwargs...)
Expand Down Expand Up @@ -87,9 +87,12 @@ function build_polar_opf(data, user_callback; backend = nothing, T=Float64, kwar
c_to_thermal_limit = c_to_thermal_limit
)

vars, cons = user_callback(core, vars, cons)
vars2, cons2 = user_callback(core, vars, cons)
model =ExaModel(core; kwargs...)

vars = (;vars..., vars2...)
cons = (;cons..., cons2...)

return model, vars, cons
end

Expand Down Expand Up @@ -182,8 +185,11 @@ function build_rect_opf(data, user_callback; backend = nothing, T=Float64, kwarg
c_voltage_magnitude = c_voltage_magnitude
)

vars, cons = user_callback(core, vars, cons)
model = ExaModel(core; kwargs...)
vars2, cons2 = user_callback(core, vars, cons)
model =ExaModel(core; kwargs...)

vars = (;vars..., vars2...)
cons = (;cons..., cons2...)

return model, vars, cons
end
Expand Down
161 changes: 150 additions & 11 deletions src/scopf.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ function goc3_model(
T = Float64,
include_ctg = true, #contingencies will be specified in input data
result_set = [],
user_callback = dummy_extension,
kwargs...
)

Expand Down Expand Up @@ -241,10 +242,11 @@ function goc3_model(
end

#4.2.1 Bus power mismatch and penalized mismatch definitions
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)))
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)))
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)))
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)))
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)))
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)))
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)))
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)))
cons = (c11=c11, c12=c12, c13=c13, c14=c14)
#4.2.2 Bus pwoer mismatch penalty
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)
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)
Expand All @@ -254,6 +256,7 @@ function goc3_model(
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)
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)


#Reminder: fr and to split for ln, xf, and dc
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)
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)
Expand All @@ -272,7 +275,6 @@ function goc3_model(
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)
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)
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)


#4.3.2 Reserve shortfall penalties
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)
Expand All @@ -283,7 +285,6 @@ function goc3_model(
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)
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)
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)


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

#4.8.3 AC branch flows


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])
- 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)
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])
Expand All @@ -494,7 +494,6 @@ function goc3_model(
+ 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)
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])
+ 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)


#4.8.4 DC lines
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)
Expand Down Expand Up @@ -535,11 +534,10 @@ function goc3_model(
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)
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)
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)

end


model = ExaModel(core; kwargs...)

vars = (b_jt_sh = b_jt_sh,
g_jt_sh = g_jt_sh,
#Split e_w_plus into separate sets for W_en_min and W_en_max ad for pr, cs
Expand Down Expand Up @@ -657,6 +655,128 @@ function goc3_model(
τ_jt_xf = τ_jt_xf,
φ_jt_xf = φ_jt_xf)

cons = (
c11 = c11,
c12 = c12,
c13 = c13,
c14 = c14,
c15 = c15,
c16 = c16,
c17 = c17,
c18 = c18,
c28 = c28,
c29 = c29,
c30 = c30,
c31 = c31,
c32 = c32,
c33 = c33,
c34 = c34,
c35 = c35,
c36 = c36,
c37 = c37,
cmax38 = cmax38,
c38 = c38,
c39 = c39,
c40 = c40,
c41 = c41,
c42 = c42,
c43 = c43,
c44 = c44,
c45 = c45,
c46 = c46,
c47 = c47,
c68_pr = c68_pr,
c68_cs = c68_cs,
c69_pr = c69_pr,
c69_cs = c69_cs,
c70_pr = c70_pr,
c70_cs = c70_cs,
c71_pr = c71_pr,
c71_cs = c71_cs,
c72_pr = c72_pr,
c72_cs = c72_cs,
c73_pr = c73_pr,
c73_cs = c73_cs,
c74_pr = c74_pr,
c74_cs = c74_cs,
c75_pr = c75_pr,
c75_cs = c75_cs,
c76_pr = c76_pr,
c76_cs = c76_cs,
c78_pr = c78_pr,
c78_cs = c78_cs,
c79_pr = c79_pr,
c79_cs = c79_cs,
c94_pr = c94_pr,
c94_cs = c94_cs,
c95_pr = c95_pr,
c95_cs = c95_cs,
c96_pr = c96_pr,
c96_cs = c96_cs,
c97_pr = c97_pr,
c97_cs = c97_cs,
c98_pr = c98_pr,
c98_cs = c98_cs,
c99_pr = c99_pr,
c99_cs = c99_cs,
c100_pr = c100_pr,
c100_cs = c100_cs,
c101_pr = c101_pr,
c102_pr = c102_pr,
c102_cs = c102_cs,
c103_pr = c103_pr,
c104_pr = c104_pr,
c104_cs = c104_cs,
c105_cs = c105_cs,
c109 = c109,
c110 = c110,
c111 = c111,
c112 = c112,
c113 = c113,
c114 = c114,
c115 = c115,
c116 = c116,
c117 = c117,
c118 = c118,
c119 = c119,
c120 = c120,
c121 = c121,
c122 = c122,
c123 = c123,
c124 = c124,
c125 = c125,
c126 = c126,
c127 = c127,
c128 = c128,
c130_pr = c130_pr,
c130_cs = c130_cs,
c131_pr = c131_pr,
c131_cs = c131_cs,
c132 = c132,
c133 = c133,
c134 = c134,
c135 = c135,
c139_ln = c139_ln,
c139_xf = c139_xf,
c140_ln = c140_ln,
c140_xf = c140_xf,
c141_ln = c141_ln,
c141_xf = c141_xf,
c144 = c144,
c145 = c145,
c146 = c146,
c147 = c147,
c148_ln = c148_ln,
c148_xf = c148_xf,
c149_ln = c149_ln,
c149_xf = c149_xf,
c150_ln = c150_ln,
c150_xf = c150_xf,
c151_ln = c151_ln,
c151_xf = c151_xf,
c156 = c156
)

if include_ctg
vars = (;vars...,
s_jtk_plus_ln = s_jtk_plus_ln,
Expand All @@ -670,11 +790,30 @@ function goc3_model(
z_tk_ctg = z_tk_ctg,
z_t_ctg_min = z_t_ctg_min,
z_t_ctg_avg = z_t_ctg_avg)
cons = (;cons...,
c4=c4,
c5=c5,
c10=c10,
c158_ln=c158_ln,
c158_xf=c158_xf,
c159_ln=c159_ln,
c159_xf=c159_xf,
c160_ln=c160_ln,
c160_xf=c160_xf,
c161_ln=c161_ln,
c161_xf=c161_xf,
c162=c162,
c163=c163)
end

vars2, cons2 = user_callback(core, vars, cons)
model =ExaModel(core; kwargs...)

vars = (;vars..., vars2...)
cons = (;cons..., cons2...)

@info "built model"
return model, sc_data_array, vars, lengths
return model, cons, vars, lengths, sc_data_array

end

2 changes: 1 addition & 1 deletion test/opf_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,6 @@ end
function sc_tests(filename, backend, T)
uc_filename = filename*"_solution.json"
filename = filename*".json"
model, sc_data_array, vars, lengths = ExaModelsPower.goc3_model(filename, uc_filename; backend=backend, T=T)
model, cons, vars, lengths, sc_data_array = ExaModelsPower.goc3_model(filename, uc_filename; backend=backend, T=T)
result = madnlp(model; max_iter=5, tol=1e-2)
end
6 changes: 3 additions & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ function add_electrolyzers(core, vars, cons)
length(elec_data)), -elec_scale),
ucon = fill!(similar(elec_data, Float64,
length(elec_data)), elec_scale))
vars = (;vars..., p_elec=p_elec)
cons = (;cons..., c_elec_ramp=c_elec_ramp)
vars = (p_elec=p_elec,)
cons = (c_elec_ramp=c_elec_ramp,)
return vars, cons
end

Expand Down Expand Up @@ -224,7 +224,7 @@ function runtests()

@testset "User callback, $(T), $(backend)" begin
model, vars, cons = mpopf_model(
"../data/pglib_opf_case3_lmbd.m", elec_curve;
"../data/pglib_opf_case3_lmbd_mod.m", elec_curve;
user_callback = add_electrolyzers, T=T, backend=backend)
end

Expand Down
Loading