Skip to content

Commit

Permalink
Add plotting functions (#360)
Browse files Browse the repository at this point in the history
  • Loading branch information
clizbe committed Jan 4, 2024
1 parent 1b5010f commit 070b88d
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ version = "0.5.1"

[deps]
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
GraphMakie = "1ecd5474-83a3-4783-bb4f-06765db800d2"
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
MetaGraphsNext = "fa8bd995-216d-47f1-8a91-f3b68fbeb377"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"

Expand Down
6 changes: 6 additions & 0 deletions src/TulipaEnergyModel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ using JuMP
using MathOptInterface
using MetaGraphsNext
using TOML
using Plots
using Colors
using GraphMakie
using GraphMakie.NetworkLayout
using CairoMakie

include("input-tables.jl")
include("structures.jl")
Expand All @@ -18,5 +23,6 @@ include("solver-parameters.jl")
include("solve-model.jl")
include("run-scenario.jl")
include("time-resolution.jl")
include("plot.jl")

end
163 changes: 163 additions & 0 deletions src/plot.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
export plot_single_flow, plot_graph, plot_assets_capacity

"""
plot_single_flow(graph, asset_from, asset_to, rp)
plot_single_flow(energy_problem, asset_from, asset_to, rp)
Plot a single flow over a single representative period, given a graph or energy problem,
the "from" (exporting) asset, the "to" (importing) asset, and the representative period.
"""
#TODO Rework using CairoMakie instead of Plots
function plot_single_flow(graph::MetaGraph, asset_from::String, asset_to::String, rp::Int64)
rp_partition = graph[asset_from, asset_to].partitions[rp]
time_dimension = 1:length(rp_partition)
flow_value = [graph[asset_from, asset_to].flow[(rp, B)] for B in rp_partition]

Plots.plot(
time_dimension,
flow_value;
title = string("Flow from ", asset_from, " to ", asset_to, " for RP", rp),
titlefontsize = 10,
legend = false,
)
end

function plot_single_flow(
energy_problem::EnergyProblem,
asset_from::String,
asset_to::String,
rp::Int64,
)
plot_single_flow(energy_problem.graph, asset_from, asset_to, rp)
end

"""
plot_final_flow_graph(graph)
plot_final_flow_graph(energy_problem)
Given a graph or energy problem, plot the graph with the "final" (initial + investment) flow capacities,
represented by the thickness of the graph edges, as well as displayed values.
"""
function plot_graph(graph::MetaGraph)
# Choose colors
nodelabelcolor = RGB(0.345, 0.164, 0.376)
transportflowcolor = RGB(1, 0.647, 0)
assetflowcolor = RGB(0.988, 0.525, 0.110)
nodecolor = RGBA(0.345, 0.164, 0.376, 0.5)

node_labels = labels(graph) |> collect

# Create a temporary graph that has arrows both directions for transport
temp_graph = DiGraph(nv(graph))
for e in edges(graph)
add_edge!(temp_graph, e.src, e.dst)
if graph[node_labels[e.src], node_labels[e.dst]].is_transport
add_edge!(temp_graph, e.dst, e.src)
end
end

# Calculate node size based on initial and investment capacity
node_size = []
node_names = []
for a in node_labels
node_total_capacity =
graph[a].initial_capacity +
graph[a].investable * graph[a].investment * graph[a].capacity
push!(node_names, "$a \n $(node_total_capacity)") # "Node Name, Capacity: ##"
push!(node_size, node_total_capacity)
end
node_size = node_size * 98 / maximum(node_size) .+ 5

# Calculate edge width (and set color) depending on type and (if transport) capacity
edge_colors = []
edge_width = []
edge_labels = []
for e in edges(temp_graph)
from = node_labels[e.src]
to = node_labels[e.dst]
edge_data = has_edge(graph, e.src, e.dst) ? graph[from, to] : graph[to, from]

if edge_data.is_transport
# Compare temp_graph to graph to determine export vs. import edges
total_trans_cap =
(
if has_edge(graph, e.src, e.dst)
edge_data.initial_export_capacity
else
edge_data.initial_import_capacity
end
) + edge_data.investable * edge_data.investment * edge_data.capacity
push!(edge_labels, string(total_trans_cap))
push!(edge_width, total_trans_cap)
push!(edge_colors, transportflowcolor)
else
push!(edge_labels, "")
push!(edge_width, 0)
push!(edge_colors, assetflowcolor)
end
end
edge_width = edge_width * 0.9 / (maximum(edge_width) == 0 ? 1 : maximum(edge_width)) .+ 0.1 # Normalize edge_width with minimum of 0.1 (just visible)

#TODO Show initial vs investment somehow (couldn't get node stroke to work)
f, ax, p = graphplot(
temp_graph;
node_size = node_size,
node_strokewidth = 0,
node_color = nodecolor,
ilabels = node_names,
ilabels_color = nodelabelcolor,
ilabels_fontsize = 10.0,
edge_width = edge_width * 10,
arrow_size = (edge_width .+ 2) * 10,
edge_color = edge_colors,
elabels = edge_labels,
elabels_fontsize = 10.0,
elabels_color = edge_colors,
)

#TODO Make these axis adjustments a calculation (so labels aren't cut off)
CairoMakie.xlims!(ax, -5.5, 5.5)
CairoMakie.ylims!(ax, -5, 6)
hidedecorations!(ax)
hidespines!(ax)

return f
end

function plot_graph(energy_problem::EnergyProblem)
plot_graph(energy_problem.graph)
end

"""
plot_assets_capacity(graph)
plot_assets_capacity(energy_problem)
Given a graph or energy problem, display a stacked bar graph of
the initial and invested capacity of each asset (initial + invested = total).
"""
#TODO Rework using CairoMakie instead of Plots
function plot_assets_capacity(graph::MetaGraph)
asset_labels = labels(graph) |> collect

total_asset_cap = [
graph[a].initial_capacity + graph[a].investable * graph[a].investment * graph[a].capacity for a in asset_labels
]
initial_cap = [graph[a].initial_capacity for a in asset_labels]
investment_cap =
[graph[a].investable * graph[a].investment * graph[a].capacity for a in asset_labels]

Plots.plot(
[initial_cap, investment_cap];
seriestype = :bar,
xticks = (1:length(total_asset_cap), asset_labels),
xrotation = 90,
title = string("Total Capacity of Assets after Investment"),
titlefontsize = 10,
label = ["Initial Capacity" "Invested Capacity"],
)
end

function plot_assets_capacity(energy_problem::EnergyProblem)
plot_assets_capacity(energy_problem.graph)
end
5 changes: 5 additions & 0 deletions test/test-case-studies.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
optimizer_list = [HiGHS.Optimizer, Cbc.Optimizer, GLPK.Optimizer]
for optimizer in optimizer_list
energy_problem = run_scenario(dir; optimizer = optimizer)
@testset "Codecov Demands Graphs" begin
plot_single_flow(energy_problem, "Asgard_Solar", "Asgard_Battery", 1)
plot_graph(energy_problem)
plot_assets_capacity(energy_problem)
end
@test energy_problem.objective_value 1.791715212196092e8 atol = 1e-5
end
end
Expand Down

0 comments on commit 070b88d

Please sign in to comment.