Skip to content

Commit 2e01739

Browse files
authored
Core Multiphase Features (#297)
* adding multiphase flag to data parsers * adding ismultiphase test to build_generic_model * making network and phase kwargs in constraint templates * making network and phase kwargs in variable constructors * change usage of pm.ref[:nw] to nws() * exporting multinetwork and multiphase helper functions * minor fix to psse test * multi-phase solution nominally working * adding JSON output for multiphase data * making phases optional to support array-based single phase models * adding isapprox so compare_dict works with multi-phase * adding ref file and making data functions work with multiphase * adding MultiPhaseMatrix to support branch parameters * add support for multiphase duals * minor bugfix to apply_func * adding area, zone, and base_kv to phaseless parameters * adding basic multiphase tests * adding multinetwork multiphase tests * adding run_mn_pf formulation for testing * removed multinetwork models defined in tests * fixing dcline cost function unit conversion * replace getstart with getval, closes #252 * ADD: operators for MultiPhaseValues * REF: multiphasevalues operations * ADD: Unit tests for MultiPhaseValue operations * FIX: DistFlow formulation for multiphase (#289) * Adds more `getmpv` variants * ADD: Unit tests for Multphase and Multinetwork (#300) * ADD: Unit tests for Multphase and Multinetwork * REF: Updated Memento log tests * ADD: Missing unit tests for check_connectivity (#301) * ADD: Missing unit tests for check_connectivity * ADD: Missing ismultiphase test * UPD: updates network checks for multiphase networks (#302) * UPD: updates network checks for multiphase networks * update to changelog
1 parent 31e7627 commit 2e01739

29 files changed

+3216
-1834
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ PowerModels.jl Change Log
22
=================
33

44
### Staged
5-
- nothing
5+
- Added support for network data with multiple phases
6+
- Improved helper functions ref, var, con to work with multiple networks and phases
67

78
### v0.7.2
89
- Removed Memento depreciation warnings

src/PowerModels.jl

+3
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ include("io/pti.jl")
2727
include("io/psse.jl")
2828

2929
include("core/data.jl")
30+
include("core/ref.jl")
3031
include("core/base.jl")
3132
include("core/variable.jl")
3233
include("core/constraint_template.jl")
3334
include("core/constraint.jl")
3435
include("core/relaxation_scheme.jl")
3536
include("core/objective.jl")
3637
include("core/solution.jl")
38+
include("core/multiphase.jl")
3739

3840
include("form/acp.jl")
3941
include("form/acr.jl")
@@ -50,5 +52,6 @@ include("prob/opf.jl")
5052
include("prob/opf_bf.jl")
5153
include("prob/ots.jl")
5254
include("prob/tnep.jl")
55+
include("prob/test.jl")
5356

5457
end

src/core/base.jl

+97-47
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ export
44
GenericPowerModel,
55
setdata, setsolver, solve,
66
run_generic_model, build_generic_model, solve_generic_model,
7+
ismultinetwork, nw_ids, nws,
8+
ismultiphase, phase_ids,
79
ids, ref, var, ext
810

11+
912
""
1013
abstract type AbstractPowerFormulation end
1114

@@ -49,6 +52,7 @@ mutable struct GenericPowerModel{T<:AbstractPowerFormulation}
4952
var::Dict{Symbol,Any} # JuMP variables
5053
con::Dict{Symbol,Any} # JuMP constraint references
5154
cnw::Int # current network index value
55+
cph::Int # current phase index value
5256

5357
# Extension dictionary
5458
# Extensions should define a type to hold information particular to
@@ -67,55 +71,87 @@ function GenericPowerModel(data::Dict{String,Any}, T::DataType; ext = Dict{Strin
6771

6872
var = Dict{Symbol,Any}(:nw => Dict{Int,Any}())
6973
con = Dict{Symbol,Any}(:nw => Dict{Int,Any}())
70-
for nw_id in keys(ref[:nw])
71-
var[:nw][nw_id] = Dict{Symbol,Any}()
72-
con[:nw][nw_id] = Dict{Symbol,Any}()
74+
for (nw_id, nw) in ref[:nw]
75+
nw_var = var[:nw][nw_id] = Dict{Symbol,Any}()
76+
nw_con = con[:nw][nw_id] = Dict{Symbol,Any}()
77+
78+
nw_var[:ph] = Dict{Int,Any}()
79+
nw_con[:ph] = Dict{Int,Any}()
80+
81+
for ph_id in nw[:phase_ids]
82+
nw_var[:ph][ph_id] = Dict{Symbol,Any}()
83+
nw_con[:ph][ph_id] = Dict{Symbol,Any}()
84+
end
7385
end
7486

75-
cnw = minimum([k for k in keys(ref[:nw])])
87+
cnw = minimum([k for k in keys(var[:nw])])
88+
cph = minimum([k for k in keys(var[:nw][cnw][:ph])])
7689

7790
pm = GenericPowerModel{T}(
7891
Model(solver = solver), # model
79-
data, # data
80-
setting, # setting
92+
data,
93+
setting,
8194
Dict{String,Any}(), # solution
8295
ref,
83-
var, # vars
96+
var,
8497
con,
8598
cnw,
86-
ext # ext
99+
cph,
100+
ext
87101
)
88102

89103
return pm
90104
end
91105

92-
93-
### Helper functions for working with multinetworks
106+
### Helper functions for working with multinetworks and multiphases
94107
ismultinetwork(pm::GenericPowerModel) = (length(pm.ref[:nw]) > 1)
95-
nws(pm::GenericPowerModel) = keys(pm.ref[:nw])
108+
nw_ids(pm::GenericPowerModel) = keys(pm.ref[:nw])
109+
nws(pm::GenericPowerModel) = pm.ref[:nw]
110+
111+
ismultiphase(pm::GenericPowerModel, nw::Int) = haskey(pm.ref[:nw][nw], :phases)
112+
ismultiphase(pm::GenericPowerModel; nw::Int=pm.cnw) = haskey(pm.ref[:nw][nw], :phases)
113+
phase_ids(pm::GenericPowerModel, nw::Int) = pm.ref[:nw][nw][:phase_ids]
114+
phase_ids(pm::GenericPowerModel; nw::Int=pm.cnw) = pm.ref[:nw][nw][:phase_ids]
115+
96116

97-
ids(pm::GenericPowerModel, key::Symbol) = ids(pm, pm.cnw, key)
98-
ids(pm::GenericPowerModel, n::Int, key::Symbol) = keys(pm.ref[:nw][n][key])
117+
ids(pm::GenericPowerModel, nw::Int, key::Symbol) = keys(pm.ref[:nw][nw][key])
118+
ids(pm::GenericPowerModel, key::Symbol; nw::Int=pm.cnw) = keys(pm.ref[:nw][nw][key])
99119

100-
ref(pm::GenericPowerModel, key::Symbol) = ref(pm, pm.cnw, key)
101-
ref(pm::GenericPowerModel, key::Symbol, idx) = ref(pm, pm.cnw, key, idx)
102-
ref(pm::GenericPowerModel, n::Int, key::Symbol) = pm.ref[:nw][n][key]
103-
ref(pm::GenericPowerModel, n::Int, key::Symbol, idx) = pm.ref[:nw][n][key][idx]
104120

105-
Base.var(pm::GenericPowerModel, key::Symbol) = var(pm, pm.cnw, key)
106-
Base.var(pm::GenericPowerModel, key::Symbol, idx) = var(pm, pm.cnw, key, idx)
107-
Base.var(pm::GenericPowerModel, n::Int, key::Symbol) = pm.var[:nw][n][key]
108-
Base.var(pm::GenericPowerModel, n::Int, key::Symbol, idx) = pm.var[:nw][n][key][idx]
121+
ref(pm::GenericPowerModel, nw::Int) = pm.ref[:nw][nw]
122+
ref(pm::GenericPowerModel, nw::Int, key::Symbol) = pm.ref[:nw][nw][key]
123+
ref(pm::GenericPowerModel, nw::Int, key::Symbol, idx) = pm.ref[:nw][nw][key][idx]
124+
ref(pm::GenericPowerModel, nw::Int, key::Symbol, idx, param::String) = pm.ref[:nw][nw][key][idx][param]
125+
ref(pm::GenericPowerModel, nw::Int, key::Symbol, idx, param::String, ph::Int) = pm.ref[:nw][nw][key][idx][param][ph]
109126

110-
con(pm::GenericPowerModel, key::Symbol) = con(pm, pm.cnw, key)
111-
con(pm::GenericPowerModel, key::Symbol, idx) = con(pm, pm.cnw, key, idx)
112-
con(pm::GenericPowerModel, n::Int, key::Symbol) = pm.con[:nw][n][key]
113-
con(pm::GenericPowerModel, n::Int, key::Symbol, idx) = pm.con[:nw][n][key][idx]
127+
ref(pm::GenericPowerModel; nw::Int=pm.cnw) = pm.ref[:nw][nw]
128+
ref(pm::GenericPowerModel, key::Symbol; nw::Int=pm.cnw) = pm.ref[:nw][nw][key]
129+
ref(pm::GenericPowerModel, key::Symbol, idx; nw::Int=pm.cnw) = pm.ref[:nw][nw][key][idx]
130+
ref(pm::GenericPowerModel, key::Symbol, idx, param::String; nw::Int=pm.cnw, ph::Int=pm.cph) = pm.ref[:nw][nw][key][idx][param][ph]
131+
132+
133+
Base.var(pm::GenericPowerModel, nw::Int) = pm.var[:nw][nw]
134+
Base.var(pm::GenericPowerModel, nw::Int, key::Symbol) = pm.var[:nw][nw][key]
135+
Base.var(pm::GenericPowerModel, nw::Int, key::Symbol, idx) = pm.var[:nw][nw][key][idx]
136+
Base.var(pm::GenericPowerModel, nw::Int, ph::Int) = pm.var[:nw][nw][:ph][ph]
137+
Base.var(pm::GenericPowerModel, nw::Int, ph::Int, key::Symbol) = pm.var[:nw][nw][:ph][ph][key]
138+
Base.var(pm::GenericPowerModel, nw::Int, ph::Int, key::Symbol, idx) = pm.var[:nw][nw][:ph][ph][key][idx]
139+
140+
Base.var(pm::GenericPowerModel; nw::Int=pm.cnw, ph::Int=pm.cph) = pm.var[:nw][nw][:ph][ph]
141+
Base.var(pm::GenericPowerModel, key::Symbol; nw::Int=pm.cnw, ph::Int=pm.cph) = pm.var[:nw][nw][:ph][ph][key]
142+
Base.var(pm::GenericPowerModel, key::Symbol, idx; nw::Int=pm.cnw, ph::Int=pm.cph) = pm.var[:nw][nw][:ph][ph][key][idx]
143+
144+
con(pm::GenericPowerModel, nw::Int) = pm.con[:nw][nw]
145+
con(pm::GenericPowerModel, nw::Int, key::Symbol) = pm.con[:nw][nw][key]
146+
con(pm::GenericPowerModel, nw::Int, key::Symbol, idx) = pm.con[:nw][nw][key][idx]
147+
con(pm::GenericPowerModel, nw::Int, ph::Int) = pm.con[:nw][nw][:ph][ph]
148+
con(pm::GenericPowerModel, nw::Int, ph::Int, key::Symbol) = pm.con[:nw][nw][:ph][ph][key]
149+
con(pm::GenericPowerModel, nw::Int, ph::Int, key::Symbol, idx) = pm.con[:nw][nw][:ph][ph][key][idx]
150+
151+
con(pm::GenericPowerModel; nw::Int=pm.cnw, ph::Int=pm.cph) = pm.con[:nw][nw][:ph][ph]
152+
con(pm::GenericPowerModel, key::Symbol; nw::Int=pm.cnw, ph::Int=pm.cph) = pm.con[:nw][nw][:ph][ph][key]
153+
con(pm::GenericPowerModel, key::Symbol, idx; nw::Int=pm.cnw, ph::Int=pm.cph) = pm.con[:nw][nw][:ph][ph][key][idx]
114154

115-
ext(pm::GenericPowerModel, key::Symbol) = ext(pm, pm.cnw, key)
116-
ext(pm::GenericPowerModel, key::Symbol, idx) = ext(pm, pm.cnw, key, idx)
117-
ext(pm::GenericPowerModel, n::Int, key::Symbol) = pm.ext[:nw][n][key]
118-
ext(pm::GenericPowerModel, n::Int, key::Symbol, idx) = pm.ext[:nw][n][key][idx]
119155

120156

121157
# TODO Ask Miles, why do we need to put JuMP. here? using at top level should bring it in
@@ -157,14 +193,18 @@ function build_generic_model(file::String, model_constructor, post_method; kwar
157193
end
158194

159195
""
160-
function build_generic_model(data::Dict{String,Any}, model_constructor, post_method; multinetwork=false, kwargs...)
196+
function build_generic_model(data::Dict{String,Any}, model_constructor, post_method; multinetwork=false, multiphase=false, kwargs...)
161197
# NOTE, this model constructor will build the ref dict using the latest info from the data
162198
pm = model_constructor(data; kwargs...)
163199

164200
if !multinetwork && ismultinetwork(pm)
165201
error(LOGGER, "attempted to build a single-network model with multi-network data")
166202
end
167203

204+
if !multiphase && ismultiphase(pm)
205+
error(LOGGER, "attempted to build a single-phase model with multi-phase data")
206+
end
207+
168208
post_method(pm)
169209

170210
return pm
@@ -210,6 +250,7 @@ If `:ne_branch` exists, then the following keys are also available with similar
210250
"""
211251
function build_ref(data::Dict{String,Any})
212252
refs = Dict{Symbol,Any}()
253+
213254
nws = refs[:nw] = Dict{Int,Any}()
214255

215256
if InfrastructureModels.ismultinetwork(data)
@@ -218,22 +259,24 @@ function build_ref(data::Dict{String,Any})
218259
nws_data = Dict{String,Any}("0" => data)
219260
end
220261

221-
for (n,nw_data) in nws_data
262+
for (n, nw_data) in nws_data
222263
nw_id = parse(Int, n)
223264
ref = nws[nw_id] = Dict{Symbol,Any}()
224265

225266
for (key, item) in nw_data
226-
if isa(item, Dict)
227-
item_lookup = Dict([(parse(Int, k), v) for (k,v) in item])
267+
if isa(item, Dict{String,Any})
268+
item_lookup = Dict{Int,Any}([(parse(Int, k), v) for (k,v) in item])
228269
ref[Symbol(key)] = item_lookup
229270
else
230271
ref[Symbol(key)] = item
231272
end
232273
end
233274

234-
off_angmin, off_angmax = calc_theta_delta_bounds(nw_data)
235-
ref[:off_angmin] = off_angmin
236-
ref[:off_angmax] = off_angmax
275+
if !haskey(ref, :phases)
276+
ref[:phase_ids] = 1:1
277+
else
278+
ref[:phase_ids] = 1:ref[:phases]
279+
end
237280

238281
# filter turned off stuff
239282
ref[:bus] = filter((i, bus) -> bus["bus_type"] != 4, ref[:bus])
@@ -324,9 +367,11 @@ function build_ref(data::Dict{String,Any})
324367

325368
ref[:ref_buses] = ref_buses
326369

370+
ref[:buspairs] = buspair_parameters(ref[:arcs_from], ref[:branch], ref[:bus], ref[:phase_ids])
327371

328-
ref[:buspairs] = buspair_parameters(ref[:arcs_from], ref[:branch], ref[:bus])
329-
372+
off_angmin, off_angmax = calc_theta_delta_bounds(nw_data)
373+
ref[:off_angmin] = off_angmin
374+
ref[:off_angmax] = off_angmax
330375

331376
if haskey(ref, :ne_branch)
332377
ref[:ne_branch] = filter((i, branch) -> branch["br_status"] == 1 && branch["f_bus"] in keys(ref[:bus]) && branch["t_bus"] in keys(ref[:bus]), ref[:ne_branch])
@@ -341,7 +386,7 @@ function build_ref(data::Dict{String,Any})
341386
end
342387
ref[:ne_bus_arcs] = ne_bus_arcs
343388

344-
ref[:ne_buspairs] = buspair_parameters(ref[:ne_arcs_from], ref[:ne_branch], ref[:bus])
389+
ref[:ne_buspairs] = buspair_parameters(ref[:ne_arcs_from], ref[:ne_branch], ref[:bus], ref[:phase_ids])
345390
end
346391

347392
end
@@ -355,9 +400,10 @@ function biggest_generator(gens)
355400
biggest_gen = nothing
356401
biggest_value = -Inf
357402
for (k,gen) in gens
358-
if gen["pmax"] > biggest_value
403+
pmax = maximum(gen["pmax"])
404+
if pmax > biggest_value
359405
biggest_gen = gen
360-
biggest_value = gen["pmax"]
406+
biggest_value = pmax
361407
end
362408
end
363409
assert(biggest_gen != nothing)
@@ -366,19 +412,21 @@ end
366412

367413

368414
"compute bus pair level structures"
369-
function buspair_parameters(arcs_from, branches, buses)
415+
function buspair_parameters(arcs_from, branches, buses, phase_ids)
370416
buspair_indexes = collect(Set([(i,j) for (l,i,j) in arcs_from]))
371417

372-
bp_angmin = Dict([(bp, -Inf) for bp in buspair_indexes])
373-
bp_angmax = Dict([(bp, Inf) for bp in buspair_indexes])
418+
bp_angmin = Dict([(bp, MultiPhaseVector([-Inf for h in phase_ids])) for bp in buspair_indexes])
419+
bp_angmax = Dict([(bp, MultiPhaseVector([ Inf for h in phase_ids])) for bp in buspair_indexes])
374420
bp_branch = Dict([(bp, Inf) for bp in buspair_indexes])
375421

376422
for (l,branch) in branches
377423
i = branch["f_bus"]
378424
j = branch["t_bus"]
379425

380-
bp_angmin[(i,j)] = max(bp_angmin[(i,j)], branch["angmin"])
381-
bp_angmax[(i,j)] = min(bp_angmax[(i,j)], branch["angmax"])
426+
for h in phase_ids
427+
bp_angmin[(i,j)][h] = max(bp_angmin[(i,j)][h], branch["angmin"][h])
428+
bp_angmax[(i,j)][h] = min(bp_angmax[(i,j)][h], branch["angmax"][h])
429+
end
382430
bp_branch[(i,j)] = min(bp_branch[(i,j)], l)
383431
end
384432

@@ -392,7 +440,9 @@ function buspair_parameters(arcs_from, branches, buses)
392440
"vm_fr_max"=>buses[i]["vmax"],
393441
"vm_to_min"=>buses[j]["vmin"],
394442
"vm_to_max"=>buses[j]["vmax"]
395-
)) for (i,j) in buspair_indexes])
443+
)) for (i,j) in buspair_indexes]
444+
)
396445

397446
return buspairs
398447
end
448+

0 commit comments

Comments
 (0)