Skip to content

Commit

Permalink
Add support for plotting multi-networks (#86)
Browse files Browse the repository at this point in the history
support for 1-phase and multiphase multinetwork plots
  • Loading branch information
noahrhodes authored Jan 11, 2022
1 parent b8d9dfc commit 954f65a
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ PowerPlots.jl Change Log
========================
### Staged
- Add support for plotting 3-phase grid data
- Add support for multinetwork plots

### v0.3.0
- Change PowerModelsGraph specification
Expand Down
6 changes: 5 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Memento = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9"
NLopt = "76087f3c-5699-56af-9a33-bf431cd00edd"
NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
Expand All @@ -28,19 +29,22 @@ DataFrames = "~0.21, ~0.22, ~1"
GeometryBasics = "~0.3, ~0.4"
Graphs = "~1.4"
InfrastructureModels = "~0.5, ~0.6"
Ipopt = ">= 0.4"
Memento = "~1"
NLopt = "~0.6"
NetworkLayout = "~0.4"
OrderedCollections = "~1"
PowerModels = "~0.17, ~0.18, ~0.19"
PowerModelsDistribution = "~0.11,0.12,0.13"
RecursiveArrayTools = "~2"
Setfield = "~0.7"
VegaLite = "^2.0"
julia = "^1"

[extras]
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
PowerModelsDistribution = "d7431456-977f-11e9-2de3-97ff7677985e"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "Ipopt", "PowerModelsDistribution"]
2 changes: 1 addition & 1 deletion src/PowerPlots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module PowerPlots

import InfrastructureModels
import PowerModels
import PowerModelsDistribution
import Statistics: mean, std
import LinearAlgebra
import LinearAlgebra: norm
Expand All @@ -12,6 +11,7 @@ import VegaLite
import Colors
import ColorSchemes
import DataFrames
import OrderedCollections
import Memento

import Graphs
Expand Down
229 changes: 206 additions & 23 deletions src/plots/plot.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@

function powerplot( case::Dict{String,<:Any};
layout_algorithm=kamada_kawai,
fixed=false,
invalid_keys = Dict("branch" => ["mu_angmin", "mu_angmax", "mu_sf", "shift", "rate_b", "rate_c", "g_to", "g_fr", "mu_st", "source_id", "f_bus", "t_bus", "qf", "angmin", "angmax", "qt", "tap"],#["b_fr","b_to", "xcoord_1", "xcoord_2", "ycoord_1", "ycoord_2", "pf", "src","dst","rate_a","br_r","br_x","index","br_status"],
"bus" => ["mu_vmax", "lam_q", "mu_vmin", "source_id","lam_p"],#["xcoord_1", "ycoord_1", "bus_type", "name", "vmax", "vmin", "index", "va", "vm", "base_kv"],
"gen" => ["vg","gen_bus","cost","ncost", "qc1max","qc2max", "ramp_agc", "qc1min", "qc2min", "pc1", "ramp_q", "mu_qmax", "ramp_30", "mu_qmin", "shutdown", "startup","ramp_10","source_id", "mu_pmax", "pc2", "mu_pmin","apf",]),#["xcoord_1", "ycoord_1", "pg", "qg", "pmax", "mbase", "index", "cost", "qmax", "qmin", "pmin", "gen_status"]),
kwargs...
)
"""
`powerplot(
case::Dict{String,<:Any};
layout_algorithm=kamada_kawai,
fixed=false,
invalid_keys = Dict("branch" => ["mu_angmin", "mu_angmax", "mu_sf", "shift", "rate_b", "rate_c", "g_to", "g_fr", "mu_st", "source_id", "f_bus", "t_bus", "qf", "angmin", "angmax", "qt", "tap"],#["b_fr","b_to", "xcoord_1", "xcoord_2", "ycoord_1", "ycoord_2", "pf", "src","dst","rate_a","br_r","br_x","index","br_status"],
"bus" => ["mu_vmax", "lam_q", "mu_vmin", "source_id","lam_p"],#["xcoord_1", "ycoord_1", "bus_type", "name", "vmax", "vmin", "index", "va", "vm", "base_kv"],
"gen" => ["vg","gen_bus","cost","ncost", "qc1max","qc2max", "ramp_agc", "qc1min", "qc2min", "pc1", "ramp_q", "mu_qmax", "ramp_30", "mu_qmin", "shutdown", "startup","ramp_10","source_id", "mu_pmax", "pc2", "mu_pmin","apf",]),#["xcoord_1", "ycoord_1", "pg", "qg", "pmax", "mbase", "index", "cost", "qmax", "qmin", "pmin", "gen_status"]),
kwargs...)`
Create a plower plot. Check github repo for documentation on kwarg options.
"""
function powerplot(
case::Dict{String,<:Any};
layout_algorithm=kamada_kawai,
fixed=false,
invalid_keys = Dict("branch" => ["mu_angmin", "mu_angmax", "mu_sf", "shift", "rate_b", "rate_c", "g_to", "g_fr", "mu_st", "source_id", "f_bus", "t_bus", "qf", "angmin", "angmax", "qt", "tap"],#["b_fr","b_to", "xcoord_1", "xcoord_2", "ycoord_1", "ycoord_2", "pf", "src","dst","rate_a","br_r","br_x","index","br_status"],
"bus" => ["mu_vmax", "lam_q", "mu_vmin", "source_id","lam_p"],#["xcoord_1", "ycoord_1", "bus_type", "name", "vmax", "vmin", "index", "va", "vm", "base_kv"],
"gen" => ["vg","gen_bus","cost","ncost", "qc1max","qc2max", "ramp_agc", "qc1min", "qc2min", "pc1", "ramp_q", "mu_qmax", "ramp_30", "mu_qmin", "shutdown", "startup","ramp_10","source_id", "mu_pmax", "pc2", "mu_pmin","apf",]),#["xcoord_1", "ycoord_1", "pg", "qg", "pmax", "mbase", "index", "cost", "qmax", "qmin", "pmin", "gen_status"]),
kwargs...)

if InfrastructureModels.ismultinetwork(case)
Memento.error(_LOGGER, "powerplot does not yet support multinetwork data")
return _powerplot_mn(case; layout_algorithm=layout_algorithm, fixed=fixed, invalid_keys=invalid_keys, kwargs...)
end

# modify case dictionary for distribution grid data
Expand Down Expand Up @@ -55,22 +68,35 @@ function powerplot( case::Dict{String,<:Any};
end

"""
`powerplot!(plt_layer, case::Dict{String,<:Any};`
Create a plower plot, with a different VegaLite plot as the bottom layer of the plot. Primarily
used to plot geographic map data underneath a power grid.
"""
function powerplot!(plt_layer, case::Dict{String,<:Any};
`powerplot!(
plt_layer::VegaLite.VLSpec, case::Dict{String,<:Any};
layout_algorithm=kamada_kawai,
fixed=false,
invalid_keys = Dict("branch" => ["mu_angmin", "mu_angmax", "mu_sf", "shift", "rate_b", "rate_c", "g_to", "g_fr", "mu_st", "source_id", "f_bus", "t_bus", "qf", "angmin", "angmax", "qt", "tap"],#["b_fr","b_to", "xcoord_1", "xcoord_2", "ycoord_1", "ycoord_2", "pf", "src","dst","rate_a","br_r","br_x","index","br_status"],
"bus" => ["mu_vmax", "lam_q", "mu_vmin", "source_id","lam_p"],#["xcoord_1", "ycoord_1", "bus_type", "name", "vmax", "vmin", "index", "va", "vm", "base_kv"],
"gen" => ["vg","gen_bus","cost","ncost", "qc1max","qc2max", "ramp_agc", "qc1min", "qc2min", "pc1", "ramp_q", "mu_qmax", "ramp_30", "mu_qmin", "shutdown", "startup","ramp_10","source_id", "mu_pmax", "pc2", "mu_pmin","apf",]),#["xcoord_1", "ycoord_1", "pg", "qg", "pmax", "mbase", "index", "cost", "qmax", "qmin", "pmin", "gen_status"]),
kwargs...
)
kwargs...)`
Create a plower plot, with a different VegaLite plot as the bottom layer of the plot. Primarily
used to plot geographic map data underneath a power grid.
"""
function powerplot!(plt_layer::VegaLite.VLSpec, case::Dict{String,<:Any};
layout_algorithm=kamada_kawai,
fixed=false,
invalid_keys = Dict("branch" => ["mu_angmin", "mu_angmax", "mu_sf", "shift", "rate_b", "rate_c", "g_to", "g_fr", "mu_st", "source_id", "f_bus", "t_bus", "qf", "angmin", "angmax", "qt", "tap"],#["b_fr","b_to", "xcoord_1", "xcoord_2", "ycoord_1", "ycoord_2", "pf", "src","dst","rate_a","br_r","br_x","index","br_status"],
"bus" => ["mu_vmax", "lam_q", "mu_vmin", "source_id","lam_p"],#["xcoord_1", "ycoord_1", "bus_type", "name", "vmax", "vmin", "index", "va", "vm", "base_kv"],
"gen" => ["vg","gen_bus","cost","ncost", "qc1max","qc2max", "ramp_agc", "qc1min", "qc2min", "pc1", "ramp_q", "mu_qmax", "ramp_30", "mu_qmin", "shutdown", "startup","ramp_10","source_id", "mu_pmax", "pc2", "mu_pmin","apf",]),#["xcoord_1", "ycoord_1", "pg", "qg", "pmax", "mbase", "index", "cost", "qmax", "qmin", "pmin", "gen_status"]),
kwargs...)

if InfrastructureModels.ismultinetwork(case)
Memento.error(_LOGGER, "powerplot does not yet support multinetwork data")
return _powerplot_mn!(plt_layer, case; layout_algorithm=layout_algorithm, fixed=fixed, invalid_keys=invalid_keys, kwargs...)
end

# modify case dictionary for distribution grid data
if haskey(case, "is_kron_reduced")
case = distr_data(case)
end

data = layout_network(case; layout_algorithm=layout_algorithm, fixed=fixed, kwargs...)

@prepare_plot_attributes(kwargs) # creates the plot_attributes dictionary
Expand Down Expand Up @@ -114,7 +140,138 @@ function powerplot!(plt_layer, case::Dict{String,<:Any};
end


function plot_base(plot_attributes::Dict{Symbol,Any})
function _powerplot_mn(case::Dict{String,<:Any};
layout_algorithm=kamada_kawai,
fixed=false,
invalid_keys = Dict("branch" => ["mu_angmin", "mu_angmax", "mu_sf", "shift", "rate_b", "rate_c", "g_to", "g_fr", "mu_st", "source_id", "f_bus", "t_bus", "qf", "angmin", "angmax", "qt", "tap"],#["b_fr","b_to", "xcoord_1", "xcoord_2", "ycoord_1", "ycoord_2", "pf", "src","dst","rate_a","br_r","br_x","index","br_status"],
"bus" => ["mu_vmax", "lam_q", "mu_vmin", "source_id","lam_p"],#["xcoord_1", "ycoord_1", "bus_type", "name", "vmax", "vmin", "index", "va", "vm", "base_kv"],
"gen" => ["vg","gen_bus","cost","ncost", "qc1max","qc2max", "ramp_agc", "qc1min", "qc2min", "pc1", "ramp_q", "mu_qmax", "ramp_30", "mu_qmin", "shutdown", "startup","ramp_10","source_id", "mu_pmax", "pc2", "mu_pmin","apf",]),#["xcoord_1", "ycoord_1", "pg", "qg", "pmax", "mbase", "index", "cost", "qmax", "qmin", "pmin", "gen_status"]),
kwargs... )

data = deepcopy(case)
for (nwid,net) in data["nw"]
if haskey(first(case["nw"])[2],"is_kron_reduced")
net = distr_data(net)
end
data["nw"][nwid] = layout_network(net; layout_algorithm=layout_algorithm, fixed=fixed, kwargs...)
end

PowerPlots.@prepare_plot_attributes(kwargs) # creates the plot_attributes dictionary
PowerPlots._validate_plot_attributes!(plot_attributes) # check the attributes for valid input types
for (nwid,nw) in data["nw"]
remove_information!(nw, invalid_keys)
end

PMD = PowerModelsDataFrame(data)


# validate data-related attributes
PowerPlots._validate_data_type(plot_attributes, :gen_data_type)
PowerPlots._validate_data(PMD.gen, plot_attributes[:gen_data], "generator")
PowerPlots._validate_data_type(plot_attributes, :bus_data_type)
PowerPlots._validate_data(PMD.bus, plot_attributes[:bus_data], "bus")
PowerPlots._validate_data_type(plot_attributes, :branch_data_type)
PowerPlots._validate_data(PMD.branch, plot_attributes[:branch_data], "branch")
PowerPlots._validate_data_type(plot_attributes, :dcline_data_type)
PowerPlots._validate_data(PMD.dcline, plot_attributes[:dcline_data], "DC line")

# make the plots
p = plot_base_mn(data,plot_attributes)
if !(isempty(PMD.branch))
p = p+plot_branch(PMD, plot_attributes)
end
if !(isempty(PMD.dcline))
p = p+plot_dcline(PMD, plot_attributes)
end
if !(isempty(PMD.connector))
p = p+plot_connector(PMD, plot_attributes)
end
if !(isempty(PMD.bus))
p = p+plot_bus(PMD, plot_attributes)
end
if !(isempty(PMD.gen))
p = p+plot_gen(PMD, plot_attributes)
end

for i in keys(p.layer) # add filter for nwid on each layer
p.layer[i]["transform"] = OrderedCollections.OrderedDict{String, Any}[OrderedCollections.OrderedDict("filter"=>"datum.nw_id == nwid")]
end

return p
end


function _powerplot_mn!(plt_layer::VegaLite.VLSpec, case::Dict{String,<:Any};
layout_algorithm=kamada_kawai,
fixed=false,
invalid_keys = Dict("branch" => ["mu_angmin", "mu_angmax", "mu_sf", "shift", "rate_b", "rate_c", "g_to", "g_fr", "mu_st", "source_id", "f_bus", "t_bus", "qf", "angmin", "angmax", "qt", "tap"],#["b_fr","b_to", "xcoord_1", "xcoord_2", "ycoord_1", "ycoord_2", "pf", "src","dst","rate_a","br_r","br_x","index","br_status"],
"bus" => ["mu_vmax", "lam_q", "mu_vmin", "source_id","lam_p"],#["xcoord_1", "ycoord_1", "bus_type", "name", "vmax", "vmin", "index", "va", "vm", "base_kv"],
"gen" => ["vg","gen_bus","cost","ncost", "qc1max","qc2max", "ramp_agc", "qc1min", "qc2min", "pc1", "ramp_q", "mu_qmax", "ramp_30", "mu_qmin", "shutdown", "startup","ramp_10","source_id", "mu_pmax", "pc2", "mu_pmin","apf",]),#["xcoord_1", "ycoord_1", "pg", "qg", "pmax", "mbase", "index", "cost", "qmax", "qmin", "pmin", "gen_status"]),
kwargs... )

data = deepcopy(case)
for (nwid,net) in data["nw"]
if haskey(first(case["nw"])[2],"is_kron_reduced")
net = distr_data(net)
end
data["nw"][nwid] = layout_network(net; layout_algorithm=layout_algorithm, fixed=fixed, kwargs...)
end

PowerPlots.@prepare_plot_attributes(kwargs) # creates the plot_attributes dictionary
PowerPlots._validate_plot_attributes!(plot_attributes) # check the attributes for valid input types
for (nwid,nw) in data["nw"]
remove_information!(nw, invalid_keys)
end

PMD = PowerModelsDataFrame(data)

# validate data-related attributes
PowerPlots._validate_data_type(plot_attributes, :gen_data_type)
PowerPlots._validate_data(PMD.gen, plot_attributes[:gen_data], "generator")
PowerPlots._validate_data_type(plot_attributes, :bus_data_type)
PowerPlots._validate_data(PMD.bus, plot_attributes[:bus_data], "bus")
PowerPlots._validate_data_type(plot_attributes, :branch_data_type)
PowerPlots._validate_data(PMD.branch, plot_attributes[:branch_data], "branch")
PowerPlots._validate_data_type(plot_attributes, :dcline_data_type)
PowerPlots._validate_data(PMD.dcline, plot_attributes[:dcline_data], "DC line")

# make the plots
p = plot_base_mn(data,plot_attributes)

# add layers
old_layer_count = 1 # used to only reference new powerplot layers in logic below
if haskey(plt_layer.params,"layer")
old_layer_count=length(keys(plt_layer.layer))
end
p = p+plt_layer

if !(isempty(PMD.branch))
p = p+plot_branch(PMD, plot_attributes)
end
if !(isempty(PMD.dcline))
p = p+plot_dcline(PMD, plot_attributes)
end
if !(isempty(PMD.connector))
p = p+plot_connector(PMD, plot_attributes)
end
if !(isempty(PMD.bus))
p = p+plot_bus(PMD, plot_attributes)
end
if !(isempty(PMD.gen))
p = p+plot_gen(PMD, plot_attributes)
end

for i in keys(p.layer) # add filter for nwid on each powerplot layer
if i > old_layer_count
p.layer[i]["transform"] = OrderedCollections.OrderedDict{String, Any}[OrderedCollections.OrderedDict("filter"=>"datum.nw_id == nwid")]
end
end

return p
end


function plot_base(plot_attributes::Dict{Symbol,Any})
return p = VegaLite.@vlplot(
width=plot_attributes[:width],
height=plot_attributes[:height],
Expand All @@ -127,10 +284,36 @@ end
}
},
)
end
end


function plot_branch(PMD::PowerModelsDataFrame, plot_attributes::Dict{Symbol,Any})
function plot_base_mn(case::Dict{String,Any},plot_attributes::Dict{Symbol,Any})
return p = VegaLite.@vlplot(
width=plot_attributes[:width],
height=plot_attributes[:height],
config={view={stroke=nothing}},
x={axis=nothing},
y={axis=nothing},
resolve={
scale={
color=:independent
}
},
params=[{
name="nwid",
select={type="point"},
value=minimum(parse.(Int,collect(keys(case["nw"])))),
bind={
input="range",
min=minimum(parse.(Int,collect(keys(case["nw"])))),
max=maximum((parse.(Int,collect(keys(case["nw"]))))),
step=1}
}],
)
end


function plot_branch(PMD::PowerModelsDataFrame, plot_attributes::Dict{Symbol,Any})
return VegaLite.@vlplot(
mark ={
:rule,
Expand All @@ -153,7 +336,7 @@ end
# legend={orient="bottom-right"}
},
)
end
end

function plot_dcline(PMD::PowerModelsDataFrame, plot_attributes::Dict{Symbol,Any})
return VegaLite.@vlplot(
Expand Down Expand Up @@ -181,7 +364,7 @@ function plot_dcline(PMD::PowerModelsDataFrame, plot_attributes::Dict{Symbol,Any
end

function plot_connector(PMD::PowerModelsDataFrame, plot_attributes::Dict{Symbol,Any})
return VegaLite.@vlplot(
return VegaLite.@vlplot(
mark ={
:rule,
"tooltip" =("content" => "data"),
Expand Down
35 changes: 33 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,23 @@ data = PowerModels.parse_file("$(joinpath(dirname(pathof(PowerModels)), ".."))/t
@test case["bus"]["1"]["ycoord_1"] == 2.0
end

@testset "Multinetwork plots" begin
case = PowerModels.parse_file("$(joinpath(dirname(pathof(PowerModels)), ".."))/test/data/matpower/case5.m")
case_mn = replicate(case, 2)

@testset "Basic multinetwork plot" begin
p = powerplot(case_mn)
@test true # above line does not error
end

@testset "Layered multinetwork plot" begin
p = powerplot(case_mn)
pp = powerplot!(p, case_mn)
@test length(keys(pp.layer))==5 # 1 layer first plot, 4 component layers from second plot
end
end


@testset "Experimental" begin
using PowerPlots.Experimental
case = PowerModels.parse_file("$(joinpath(dirname(pathof(PowerModels)), ".."))/test/data/matpower/case5.m")
Expand All @@ -130,12 +147,26 @@ data = PowerModels.parse_file("$(joinpath(dirname(pathof(PowerModels)), ".."))/t

@testset "Distribution Grids" begin
using PowerModelsDistribution
PowerModelsDistribution.silence!()
eng = PowerModelsDistribution.parse_file("$(joinpath(dirname(pathof(PowerModelsDistribution)), ".."))/test/data/opendss/case3_unbalanced.dss")
math = transform_data_model(eng)
powerplot(math)
p = powerplot(math)
@test true # what do I test here?
p = powerplot!(p,math)
@test true

@testset "Multinetwork Distribution Grids" begin
eng = PowerModelsDistribution.parse_file("$(joinpath(dirname(pathof(PowerModelsDistribution)), ".."))/test/data/opendss/case3_unbalanced.dss")
eng_mn = PowerModelsDistribution.make_multinetwork(eng)
math_mn = transform_data_model(eng_mn)
p = powerplot(math_mn)
@test true
pp = powerplot!(p,math_mn)
@test true
end

end

end

PowerModels.logger_config!(prev_level); # reset PowerModels logger to previous level

0 comments on commit 954f65a

Please sign in to comment.