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 sys
import plotly.graph_objects as go
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QApplication
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtCore import QUrl
import tempfile
import os
import numpy as np

[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 print(f"{netzverluste}, {jahreswärmebedarf_gebäude}, {jahreswärmebedarf_netz}") # 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 print(f"Erzeuger_labels: {erzeuger_labels}, waermemengen: {waermemengen}") # 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())