Source code for districtheatingsim.gui.EnergySystemTab._09_sankey_dialog

"""
Sankey Dialog Module
====================

:author: Dipl.-Ing. (FH) Jonas Pfeiffer

Displaying Sankey diagram using Plotly to visualize energy flows in district heating systems.
"""

import os
import sys
import tempfile

import numpy as np
import plotly.graph_objects as go
from PyQt6.QtCore import QUrl
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import QApplication, QDialog, QVBoxLayout


[docs] class SankeyDialog(QDialog): """ Dialog to display a Sankey diagram using Plotly in a QWebEngineView. """
[docs] def __init__(self, results=None, parent=None): super().__init__(parent) self.results = results self.initUI()
[docs] def initUI(self): """ Initialize the Sankey dialog UI and display the Plotly diagram. """ self.setWindowTitle("Sankey-Diagramm - Energieflüsse im Quartier") self.resize(800, 600) # Set the layout layout = QVBoxLayout(self) # Create a QWebEngineView to display the Plotly HTML content self.browser = QWebEngineView(self) layout.addWidget(self.browser) # Create the Plotly Sankey diagram using results self.plotSankey() self.setLayout(layout)
[docs] def plotSankey(self): """ Generate and display a Sankey diagram using Plotly based on results data. """ if self.results is None: return # Extract relevant data from results jahreswärmebedarf_gebäude = np.sum(self.results["waerme_ges_kW"]) / 1000 # Total building heat demand (input) jahreswärmebedarf_netz = self.results["Jahreswärmebedarf"] # Total network heat demand waermemengen = self.results["Wärmemengen"] # Heat generated by each generator (outputs) erzeuger_labels = self.results["techs"] # Labels for the heat generators colors = self.results["colors"] # Colors for the links # Calculate network losses and actual building heat demand netzverluste = jahreswärmebedarf_netz - jahreswärmebedarf_gebäude if netzverluste < 0: netzverluste = 0 # Ensure no negative losses # Define source and target nodes sources = list(range(len(erzeuger_labels))) # Sources: heat generators targets = [len(erzeuger_labels)] * len(erzeuger_labels) # Targets: total network heat demand node # Add additional sources and targets for network losses and building demand sources += [len(erzeuger_labels)] * 2 # From "Jahreswärmeerzeugung" targets += [len(erzeuger_labels) + 1, len(erzeuger_labels) + 2] # To "Gebäudewärmebedarf" and "Netzverluste" # Values for the Sankey links values = list(waermemengen) # Heat generated by each generator values += [jahreswärmebedarf_gebäude, netzverluste] # Add building demand and losses # Define labels for the nodes node_labels = list(erzeuger_labels) + ["Jahreswärmeerzeugung", "Gebäudewärmebedarf", "Netzverluste"] # Add colors for the new links link_colors = list(colors) + ["blue", "red"] # Add colors for building demand and losses # Create the Sankey diagram fig = go.Figure( go.Sankey( node=dict( pad=15, thickness=20, line=dict(color="black", width=0.5), label=node_labels, color="blue", # Color of nodes ), link=dict( source=sources, # Links start from each generator or network node target=targets, # Links point to network demand, building demand, and losses value=values, # The values correspond to the heat amounts color=link_colors, # Apply the custom colors for each link ), ) ) fig.update_layout(title_text="Sankey Diagram - Energieverteilung im Wärmenetz", font_size=10) # Save the figure as an HTML file in a temporary directory with tempfile.NamedTemporaryFile(suffix=".html", delete=False) as temp_file: fig.write_html(temp_file.name) self.html_file_path = temp_file.name # Load the HTML file in the QWebEngineView self.browser.setUrl(QUrl.fromLocalFile(self.html_file_path))
[docs] def closeEvent(self, event): """ Clean up the temporary HTML file when the dialog is closed. :param event: The close event :type event: QCloseEvent """ if os.path.exists(self.html_file_path): os.remove(self.html_file_path) event.accept()
if __name__ == "__main__": app = QApplication(sys.argv) # Simulated 'results' data for testing results = { "Jahreswärmebedarf": 4392.997250030184, "Wärmemengen": [200.34259576, 1505.44989018, 1313.92519592, 1373.27956817], "techs": ["Solarthermie_1", "BHKW_1", "Biomassekessel_1", "Gaskessel_1"], "colors": ["red", "yellow", "green", "saddlebrown"], } dialog = SankeyDialog(results=results) dialog.show() sys.exit(app.exec())