Skip to content
Snippets Groups Projects
PlotPFC.jl 7.02 KiB
#* Functions that allow to compare different power flow solutions
#*------------------------------------------------------------------------------

#* NOTE: Utility functions used are defined in PlotUtils.jl

#*------------------------------------------------------------------------------

#=
Scatters branch loads contained in y_ndd versus branch loads in x_ndd. Possible plot settings can be seen in PlotUtils.jl.
=#
function scatter_branchloads(
        x_ndd::Dict{String,<:Any},
        y_ndd::Dict{String,<:Any},
        settings::Dict{String,<:Any}; # plot settings
        figpath = "" # where to save the figure
    )

    ### Set plot settings
    settings = _recursive_merge(
        _default_settings(:scatter_branchloads), settings
    )
    
    ### Get branch loadings from both network data dictionaries
    x_bl_dict = Dict(
        i => br[settings["x_pf_type"]] for (i, br) in x_ndd["branch"] 
        if br["br_status"] == 1
    )
    y_bl_dict = Dict(
        i => br[settings["y_pf_type"]] for (i, br) in y_ndd["branch"]
        if br["br_status"] == 1
    )

    # @assert length(x_bl_dict) == length(y_bl_dict) # same number of branches?
    L = length(y_bl_dict)
    # println(L)
    x_bl, y_bl = zeros(L), zeros(L) # arrays for branch loads
    ### Write branch loads with correct ordering into arrays    
    for (n, key) in enumerate(keys(y_bl_dict))
        x_bl[n] = x_bl_dict[key]
        y_bl[n] = y_bl_dict[key]
    end

    ### Plot straight lines with slope 1 as reference
    x = [i for i in 0:0.01:1]
    plt.plot(x, x, color="k", alpha=.7) # plot line with slope 1

    ### Scatter branch loads versus each other
    diff = y_bl - x_bl # deviation from ratio 1 for coloring
    diff[diff .== 0.0] .= -10 # map exact zero values to -10 for distinct color
    # mcolors = pyimport("matplotlib.colors")
    # offset = mcolors.TwoSlopeNorm(
    #     vcenter=0, vmin=minimum(diff), vmax=maximum(diff)
    # ) # match diff to values between 0 and 1
    cmap = plt.get_cmap(settings["cmap"])
    cmap.set_under("tab:green")
    sc = plt.scatter(
        x_bl, y_bl,       
        s = settings["size"],
        c = diff,
        # c = offset(diff),
        cmap = cmap,
        vmin = -1.,
        vmax = 1.,
        edgecolors = settings["ec"],
        linewidths = settings["lw"],
        alpha = settings["alpha"]
    )

    ### Add colorbar
    sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(-1., 1.))
    # sm = plt.cm.ScalarMappable(cmap=cmap, offset)
    cbar = plt.colorbar(sm)
    cbar.ax.plot([-1, 1], [0.0, 0.0], "tab:green")
    # cbar.add_lines(CS2)
    cbar.ax.set_ylabel(
        L"Difference $y-x$", rotation=-90, va="bottom"
    )

    ### Plot labels
    plt.xlabel(settings["xlabel"])
    plt.ylabel(settings["ylabel"])

    plt.savefig(figpath, bbox_inches="tight") # save figure
    plt.close("all") # close figure
    return nothing
end

#*------------------------------------------------------------------------------

#=
Plots a power grid similar to plot_pg in PlotPG.jl and colors the edges according to the difference in branch loads in ndd and ref_ndd. Possible plot settings can be seen in PlotUtils.jl.
=#
function plot_pg_pf_diff(
        ndd::Dict{String,<:Any}, # NDD with (new) power flow
        ref_ndd::Dict{String,<:Any}, # NDD with reference power flow
        settings = Dict{String,Any}(); # plot settings
        figpath = "" # where to save the figure
    )
    
    ### Setup figure
    figure::Figure, ax::PyObject = plt.subplots()::Tuple{Figure,PyObject}
    w::Float64, h::Float64 = plt.figaspect(2/3)::Vector{Float64}
    figure.set_size_inches(1.5w, 1.5h)
    ax.set_aspect("equal")

    ### Set plot settings and plot power grid
    settings = _recursive_merge(_default_settings(:plot_pg_pf_diff), settings)
    _plot_pg_pf_diff!(ax, ndd, ref_ndd, settings, figpath)

    return nothing
end

function _plot_pg_pf_diff!(
        ax::PyObject, # axes to draw power grid onto
        ndd::Dict{String,<:Any}, # NDD with (new) power flow
        ref_ndd::Dict{String,<:Any}, # NDD with reference power flow
        settings = Dict{String,Any}(), # plot settings
        figpath = "" # where to save the figure
    )
    
    ### Draw power grid graph
    nx = pyimport("networkx")
    G::PyObject = nx.Graph() # empty graph

    ### Plot buses into graph
    G, bus_markers, bus_labels = _draw_buses!(G, ndd, settings)

    ### Plot branches into graph
    G, br_markers, br_labels, br_cbar = _draw_br_pf_diff!(
        G, ndd, ref_ndd, settings
    )

    ### Check for a predefined area to show
    if haskey(settings, "area")
        area = settings["area"]
        plt.xlim(area[1], area[2])
        plt.ylim(area[3], area[4])
    end

    ### Axes settings
    ax.tick_params(
        left = settings["draw_ticks"][1],
        bottom = settings["draw_ticks"][2], 
        labelleft = settings["draw_ticks"][3], 
        labelbottom = settings["draw_ticks"][4]
    )
    plt.xlabel(settings["xlabel"])
    plt.ylabel(settings["ylabel"], rotation=90)

    ### Draw legend, if wanted
    if settings["draw_legend"] == true
        all_markers = vcat(bus_markers, br_markers)
        all_labels = vcat(bus_labels, br_labels)
        plt.legend(all_markers, all_labels)
    end

    plt.savefig(figpath, bbox_inches="tight") # save figure
    plt.close("all") # close figure
    return ax, G
end

function _draw_br_pf_diff!(
        G::PyObject, # power grid graph
        ndd::Dict{String,<:Any}, # NDD with (new) power flow
        ref_ndd::Dict{String,<:Any}, # NDD with reference power flow
        settings::Dict{String,<:Any} # dictionary containing plot settings
    )
        
    pos = Dict(
        b["index"] => (b["bus_lon"], b["bus_lat"]) 
        for b in collect(values(ndd["bus"]))
    ) # geographic bus locations
    branches = collect(values(ndd["branch"])) # branch dictionaries

    ### Get edges contained in the NDD and the loading difference
    edges = Array{Tuple{Int64,Int64},1}() # array for edges
    bl_diff = Array{Float64,1}() # array for branch load differences
    for (i, br) in ndd["branch"]
        if br["br_status"] == 1
            push!(edges, (br["f_bus"], br["t_bus"]))
            pf_type = settings["Branches"]["pf_type"]
            push!(bl_diff, br[pf_type] - ref_ndd["branch"][i][pf_type])
        end
    end
    
    ### Draw edges and color them according to bl_diff
    mcolors = pyimport("matplotlib.colors")
    offset = mcolors.TwoSlopeNorm(
        vcenter=0, vmin=minimum(bl_diff), vmax=maximum(bl_diff)
    )
    vmin, vmax = minimum(offset(bl_diff)), maximum(offset(bl_diff))
    cmap = plt.get_cmap(settings["Branches"]["cmap"])
    nx = pyimport("networkx")
    drawnedges = nx.draw_networkx_edges(
        G, pos, 
        width = settings["Branches"]["br_lw"],
        edgelist = edges,
        edge_color = offset(bl_diff),
        edge_vmin = vmin,
        edge_vmax = vmax,
        edge_cmap = cmap
    )

    ### Add colorbar
    sm = plt.cm.ScalarMappable(cmap=cmap, offset)
    cbar = plt.colorbar(sm)

    cbar.ax.set_ylabel(
        settings["Branches"]["cbar_label"], rotation=-90, va="bottom"
    )

    return G, [], [], cbar
end