Skip to content

Add new graphics to results view #395

@paulapreuss

Description

@paulapreuss

New graphics have been postponed due to likely switching from MVS to raw oemof, which will change the post-processing workflow. This issue is to keep track of the new graphics that have been proposed.

One grafic for the top/overview:

'''An Overview of all energy sources, good as a first graphic in the summery''' 

from oemof.solph import EnergySystem
from oemof.solph import views
import plotly.graph_objects as go
from oemof.solph.components import Source

ensys = EnergySystem()
ensys.restore(dpath=r"C:\pythonstuff\Jupyter", filename="results_dump")

sources = [n for n in ensys.nodes if isinstance(n, Source)]

labels = []
values = []

for source in sources:
    total = 0
    for key, entry in results["Main"].items():
        if isinstance(entry, dict) and "sequences" in entry:
            if "flow" in entry["sequences"].columns:
                if str(source) in str(key):
                    total += entry["sequences"]["flow"].sum(skipna=True)
    if total > 0.1:  # nur wenn größer als 0.1
        labels.append(str(source.label))
        values.append(total)

fig = go.Figure(
    data=[go.Pie(
        labels=labels,
        values=[v / 1000 for v in values],
        hole=0.3,
        textinfo="label+percent",
        textposition="outside",     # Beschriftung außen
        hoverinfo="label+value+percent",
        hovertemplate="%{label}: %{value:.1f} MWh (%{percent})<extra></extra>"
    )]
)


fig.update_layout(
    title="Accumulated energy supply of the system for one year",
    legend_title="Energy source"
)

fig.show()
Image

Originally posted by @mstich0 in #334

Currently we have multiple graphics for different energytypes but still not for all buses. It is difficulat to say what Bus is actually the interesting one for the user. So instead we can just make one graphic in which any bus is selectable. This is done here. Also it would be nice for a report if this graphic was duplicable, so two buses could be viewed in parallel.

'''So this graphic could be used for taking a look into all timeseries. Only Buses are available if any flow is above 0.1, Only flows are shown that are above 0.1'''

import plotly.graph_objects as go
from itertools import cycle
import re
import pandas as pd
from oemof.solph import EnergySystem, Bus

# System laden
ensys = EnergySystem()
ensys.restore(dpath=r"C:\pythonstuff\Jupyter", filename="results_dump")

# Ergebnisse extrahieren
results = ensys.results

# Alle Busse holen
buses = [n for n in ensys.nodes if isinstance(n, Bus)]
bus_labels = [str(b.label) for b in buses]

# Farben für die Kanten (colors were chose by chat GPT)
color_cycle_template = [
    "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
    "#9467bd", "#8c564b", "#e377c2", "#7f7f7f",
    "#bcbd22", "#17becf"
]

fig = go.Figure()
visibility_matrix = []  # keep track of which traces belong to which bus

for bus_label in bus_labels:
    flow_data = {}
    for key, entry in results["Main"].items():
        if isinstance(entry, dict) and "sequences" in entry:
            if "flow" in entry["sequences"].columns:
                if bus_label in str(key):
                    flow_data[str(key)] = entry["sequences"]["flow"]
    edge_colors = {}
    trace_visibility = []

    # reset color cycle for each bus
    color_cycle = cycle(color_cycle_template)

    for key, flow in flow_data.items():
        matches = re.findall(r"'(.*?)'", key)
        if len(matches) < 2:
            continue
        start, end = matches[0], matches[1]

        edge_key = f"{start}->{end}"
        if edge_key not in edge_colors:
            edge_colors[edge_key] = next(color_cycle)
        color = edge_colors[edge_key]
        
        if flow.sum()>0.01:
        
            if end == bus_label:
                fig.add_trace(go.Scatter(
                    x=flow.index,
                    y=flow.clip(lower=0),
                    mode='none',
                    fill='tozeroy',
                    stackgroup=f'{bus_label}_inputs',
                    name=f'from {start}',
                    fillcolor=f'rgba{tuple(int(color.lstrip("#")[i:i+2],16) for i in (0,2,4)) + (0.3,)}',
                    line=dict(color=color),
                    visible=False,
                    # ----- Hover customization -----
                    hovertemplate="from " + start + ": %{y:.1f} kWh<extra></extra>",
                    hoverlabel=dict(align="left")
                ))
                trace_visibility.append(True)
            elif start == bus_label:
                fig.add_trace(go.Scatter(
                    x=flow.index,
                    y=flow.clip(lower=0),
                    mode='lines',
                    stackgroup=f'{bus_label}_outputs',
                    line=dict(color=color, width=2),
                    name=f'to {end}',
                    fill='none',
                    visible=False,
                    # ----- Hover customization -----
                    hovertemplate="to " + end + ": %{y:.1f} kWh<extra></extra>",
                    hoverlabel=dict(align="left")
                ))
                trace_visibility.append(True)

    visibility_matrix.append(trace_visibility)

# Filter only buses that actually have traces
valid_bus_labels = []
valid_visibility_matrix = []

for bus_label, traces in zip(bus_labels, visibility_matrix):
    if len(traces) > 0:  # keep only non-empty buses
        valid_bus_labels.append(bus_label)
        valid_visibility_matrix.append(traces)

# Set first valid bus visible
if valid_visibility_matrix:
    for i in range(len(valid_visibility_matrix[0])):
        fig.data[i].visible = True

# Dropdown menu only for valid buses
buttons = []
for i, bus_label in enumerate(valid_bus_labels):
    vis = [False] * len(fig.data)
    start_idx = sum(len(tr) for tr in valid_visibility_matrix[:i])
    for k in range(len(valid_visibility_matrix[i])):
        vis[start_idx + k] = True

    buttons.append(dict(
        label=bus_label,
        method="update",
        args=[{"visible": vis},{"title": {"text": f"Energy flows in {bus_label}"}}]
    ))
    
initial_title = "Bus Flows"
if valid_bus_labels:
    initial_title = f"Energy flows for {valid_bus_labels[0]}"
    
fig.update_layout(
    title={"text": initial_title},
    xaxis_title="",
    yaxis_title="Energy flow [kWh]",
    hovermode="x unified",
    legend_title="Time series",
    updatemenus=[dict(
        active=0,
        buttons=buttons,
        x=1.26,
        y=1.2
    )]
)

fig.show()
Image

Originally posted by @mstich0 in #334

This graph is nice to have but has not a high priority. We should focus on other things before we tackle this graph.

In addition to the list of graphs that we already approved on, we should also add a graph that visualises the net present value. This graph provides information about when the investment will pay for itself. Here is an example how this graph could look like:
Image

In this example the blue bar shows the full investment costs in the first year. The red bars from year 2 until year 20 are showing the revenue minus the operational costs for every year. The black line gives a cumulative sum of investment costs, anual revenue and anual operational costs. In this example the green line and the plant output is not needed.

Originally posted by @FredericHirschmueller in #334

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions