Functionality examples

This section contains practical examples demonstrating the usage of DistrictHeatingSim.

Getting Started Examples

Geocoding Example

01_example_geocoding.py
 1"""
 2Filename: 01_example_geocoding.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2024-11-18
 5Description: This script runs the geocoding process on a given CSV file containing addresses.
 6The script imports the `process_data` function from the `geocodingETRS89` module
 7within the `districtheatingsim` package and executes it with the specified CSV file.
 8Usage:
 9    Run this script directly to process the addresses in the "data/data_ETRS89.csv" file.
10Functions:
11    process_data(file_path: str) -> None
12        Processes the geocoding of addresses from the given CSV file.
13Example:
14    $ python 01_geocoding.py
15"""
16
17import pandas as pd
18import traceback
19
20from districtheatingsim.geocoding.geocoding import process_data
21
22if __name__ == '__main__':
23    try:
24        print("Running the geocoding script.")
25
26        example_data_path = "examples/data/data_ETRS89.csv"
27
28        print(f"Using example data from {example_data_path}.")
29
30        example_data = pd.read_csv(example_data_path, sep=';')
31
32        print("Example data:")
33        print(example_data)
34
35        process_data(example_data_path)
36
37        geocode_data = pd.read_csv(example_data_path, sep=';')
38
39        print("Geocoded data:")
40        print(geocode_data)
41
42    except Exception as e:
43        print("An error occurred:")
44        print(traceback.format_exc())
45        raise e

This example demonstrates how to geocode addresses to coordinates.

Import OSM Data Example

02_example_import_osm_data_geojson.py
 1"""
 2Filename: 02_example_import_osm_data_geojson.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2024-11-18
 5Description: This script runs the OSM data import process to download street and building data.
 6The script imports the `build_query`, `download_data`, and `save_to_file` functions
 7from the `import_osm_data_geojson` module within the `districtheatingsim.osm` package
 8and executes them to download and save the OSM data.
 9Usage:
10    Run this script directly to download and save the OSM data.
11Functions:
12    osm_street_query() -> None
13        Downloads and saves OSM street data.
14    osm_building_query() -> None
15        Downloads and saves OSM building data.
16Example:
17    $ python 02_import_osm_data_geojson.py
18
19"""
20
21import traceback
22
23from districtheatingsim.osm.import_osm_data_geojson import build_query, download_data, save_to_file
24
25### OSM-Download von Straßendaten ###
26def osm_street_query():
27    city_name = "Görlitz"
28    tags = [
29            ("highway", "primary"),
30            ("highway", "secondary"),
31            ("highway", "tertiary"),
32            ("highway", "residential"),
33            ("highway", "living_street"),
34            {"highway", "service"}
35        ]
36    element_type = "way"
37
38    query = build_query(city_name, tags, element_type)
39    geojson_data = download_data(query, element_type)
40    geojson_file_name = "examples\data\osm_street_data.geojson"
41
42    save_to_file(geojson_data, geojson_file_name)
43    print("Speichern der OSM-Straßendaten erfolgreich abgeschlossen.")
44
45### OSM-Download von Gebäudedaten ###
46def osm_building_query():
47    city_name = "Zittau"
48    tags = None
49    element_type = "building"
50
51    query = build_query(city_name, tags, element_type)
52    geojson_data = download_data(query, element_type)
53    geojson_file_name = "examples\data\osm_building_data.geojson"
54
55    save_to_file(geojson_data, geojson_file_name)
56    print("Speichern der OSM-Gebäudedaten erfolgreich abgeschlossen.")
57
58if __name__ == '__main__':
59    try:
60        print("Running the OSM data import script.")
61
62        osm_street_query()
63        osm_building_query()
64
65    except Exception as e:
66        print("An error occurred:")
67        print(traceback.format_exc())
68        raise e

This example shows how to import OpenStreetMap data in GeoJSON format.

Heat Demand Examples

Simple Heat Requirement

03_example_simple_heat_requirement.py
  1"""
  2Filename: 03_example_heat_requirement.py
  3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
  4Date: 2024-11-19
  5Description: Contains the heat requirement functions necessary to calculate the heat requirement of a building.
  6Usage:
  7    Run this script directly to calculate the heat requirement of a building.
  8Functions:
  9    VDI4655() -> None
 10        Calculates the heat requirement according to VDI 4655.
 11    BDEW() -> None
 12        Calculates the heat requirement according to BDEW.
 13Example:
 14    $ python simple_heat_requirement_test.py
 15
 16"""
 17
 18import traceback
 19import numpy as np
 20import matplotlib.pyplot as plt
 21
 22from districtheatingsim.heat_requirement import heat_requirement_BDEW
 23from districtheatingsim.heat_requirement import heat_requirement_VDI4655
 24
 25# Berechnung mit VDI 4655 (Referenzlastprofile)
 26def VDI4655(TRY_filename):
 27    YEU_heating_kWh = 20000
 28    YEU_hot_water_kWh = 4000
 29    YEU_electricity_kWh = 10000
 30    building_type = "MFH"
 31
 32    # folgendes wird statisch gesetzt, muss in Zukunft noch in config-Datei ausgelagert werden oder im UI einstellbar sein
 33    year = 2021 # Jahr, für das die Berechnung durchgeführt wird (für VDI 4655, BDEW)
 34    holidays = np.array(["2021-01-01", "2021-04-02", "2021-04-05", "2021-05-01", "2021-05-24", "2021-05-13", 
 35                         "2021-06-03", "2021-10-03", "2021-11-01", "2021-12-25", "2021-12-26"]).astype('datetime64[D]') # Feiertage in Deutschland 2021 (ohne Wochenenden) als datetime64[D]-Array (YYYY-MM-DD) für VDI 4655
 36    climate_zone = "9"  # Klimazone 9: Deutschland (VDI 4655)
 37    number_people_household = 2  # Anzahl der Personen im Haushalt (VDI 4655)
 38
 39    time_15min, total_heat_kW, heating_kW, hot_water_kW, temperature, electricity_kW = heat_requirement_VDI4655.calculate(YEU_heating_kWh, YEU_hot_water_kWh, YEU_electricity_kWh, building_type, number_people_household, year, climate_zone, TRY_filename, holidays)
 40
 41    print("Ergebnisse VDI 4655")
 42    print(f"Zeitschritte: {time_15min}")
 43    print(f"Strombedarf: {electricity_kW}")    
 44    print(f"Wärmebedarf Heizung: {heating_kW}")
 45    print(f"Wärmebedarf Warmwasser: {hot_water_kW}")
 46    print(f"Wärmebedarf Gesamt: {total_heat_kW}")
 47    print(f"Temperaturen: {temperature}")
 48
 49    # Plotting
 50    fig, ax1 = plt.subplots(figsize=(10, 5))
 51
 52    ax2 = ax1.twinx()
 53    ax1.plot(time_15min, total_heat_kW, 'g-', label="Gesamt", linewidth=0.5)
 54    ax1.plot(time_15min, heating_kW, 'b-', label="Heizung", linewidth=0.5)
 55    ax1.plot(time_15min, hot_water_kW, 'r-', label="Warmwasser", linewidth=0.5)
 56    ax1.plot(time_15min, electricity_kW, 'm-', label="Strombedarf", linewidth=0.5)
 57    
 58    # temperature is hourly, so we need to repeat the values for the 15min intervals
 59    temperature = np.repeat(temperature, 4)
 60    ax2.plot(time_15min, temperature, 'k-', label="Außentemperatur", linewidth=0.5)
 61
 62    ax1.set_xlabel("Zeitschritte")
 63    ax1.set_ylabel("Wärmebedarf (kW)")
 64    ax2.set_ylabel("Temperatur (°C)")
 65
 66    ax1.legend(loc='upper left')
 67    ax2.legend(loc='upper right')
 68
 69# Berechnung nach BDEW
 70def BDEW(TRY_filename):
 71    YEU_heating_kWh = 20000
 72    building_type = "HMF"
 73    subtype = "03"
 74    real_ww_share = 0.3
 75
 76    year = 2021
 77
 78    hourly_intervals, hourly_heat_demand_total_normed, hourly_heat_demand_heating_normed, hourly_heat_demand_warmwater_normed, hourly_temperature = heat_requirement_BDEW.calculate(YEU_heating_kWh, building_type, subtype, TRY_filename, year, real_ww_share)
 79
 80    print("Ergebnisse BDEW")
 81    print(f"Zeitschritte: {hourly_intervals}")
 82    print(f"Wärmebedarf Gesamt: {hourly_heat_demand_total_normed}") 
 83    print(f"Wärmebedarf Heizung: {hourly_heat_demand_heating_normed}")
 84    print(f"Wärmebedarf Warmwasser: {hourly_heat_demand_warmwater_normed}")  
 85    print(f"Temperaturen: {hourly_temperature}")
 86
 87    # Plotting
 88    fig, ax1 = plt.subplots(figsize=(10, 5))
 89
 90    ax2 = ax1.twinx()
 91    ax1.plot(hourly_intervals, hourly_heat_demand_total_normed, 'g-', label="Gesamt", linewidth=0.5)
 92    ax1.plot(hourly_intervals, hourly_heat_demand_heating_normed, 'b-', label="Heizung", linewidth=0.5)
 93    ax1.plot(hourly_intervals, hourly_heat_demand_warmwater_normed, 'r-', label="Warmwasser", linewidth=0.5)
 94    ax2.plot(hourly_intervals, hourly_temperature, 'k-', label="Außentemperatur", linewidth=0.5)
 95
 96    ax1.set_xlabel("Zeitschritte")
 97    ax1.set_ylabel("Wärmebedarf (kW)")
 98    ax2.set_ylabel("Temperatur (°C)")
 99
100    ax1.legend(loc='upper left')
101    ax2.legend(loc='upper right')
102
103if __name__ == '__main__':
104    try:
105        print("Running the heat requirement script.")
106
107        TRY_filename = "src/districtheatingsim/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
108        
109        VDI4655(TRY_filename)
110        BDEW(TRY_filename)
111
112        plt.show()
113
114    except Exception as e:
115        print("An error occurred:")
116        print(traceback.format_exc())
117        raise e

Basic heat demand calculation example.

Data-driven Heat Requirement

04_example_data_heat_requirement.py
 1"""
 2Filename: 04_example_data_heat_requirement.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2024-11-19
 5Description: Contains the heat requirement functions necessary to calculate the heat requirement of a building.
 6Usage:
 7    Run this script directly to calculate the heat requirement of a building.
 8Functions:
 9    calculation() -> None
10        Calculates the heat requirement according to the given data.
11Example:
12    $ python 04_example_data_heat_requirement.py
13
14"""
15
16import traceback
17import matplotlib.pyplot as plt
18import pandas as pd
19
20from districtheatingsim.heat_requirement import heat_requirement_calculation_csv
21
22def calculation(data, TRY, calc_method):
23    yearly_time_steps, total_heat_W, heating_heat_W, warmwater_heat_W, max_heat_requirement_W, supply_temperature_curve, return_temperature_curve, hourly_air_temperatures = heat_requirement_calculation_csv.generate_profiles_from_csv(data, TRY, calc_method)
24    
25    print("Ergebnisse")
26    print(f"Jährliche Zeitschritte: {yearly_time_steps}")
27    print(f"Gesamtwärmebedarf: {total_heat_W}")
28    print(f"Wärmebedarf für Heizung: {heating_heat_W}")
29    print(f"Wärmebedarf für Warmwasser: {warmwater_heat_W}")
30    print(f"Maximaler Wärmebedarf: {max_heat_requirement_W}")
31    print(f"Vorlauftemperaturkurve: {supply_temperature_curve}")
32    print(f"Rücklauftemperaturkurve: {return_temperature_curve}")
33    print(f"Stündliche Außentemperaturen: {hourly_air_temperatures}")
34
35    # Plotting each element as they are 2-dimensional arrays
36    fig, axs = plt.subplots(3, 1, figsize=(10, 15))
37
38    # Plot total heat requirement
39    for i in range(total_heat_W.shape[0]):
40        axs[0].plot(yearly_time_steps, total_heat_W[i, :], label=f"Gesamtwärmebedarf {i+1}")
41    axs[0].set_xlabel("Zeitschritte")
42    axs[0].legend()
43
44    # Plot supply and return temperature curves
45    for i in range(supply_temperature_curve.shape[0]):
46        axs[1].plot(yearly_time_steps, supply_temperature_curve[i, :], label=f"Vorlauftemperatur {i+1}")
47        axs[1].plot(yearly_time_steps, return_temperature_curve[i, :], label=f"Rücklauftemperatur {i+1}")
48    axs[1].set_xlabel("Zeitschritte")
49    axs[1].legend()
50
51    # Plot hourly air temperatures
52    axs[2].plot(yearly_time_steps, hourly_air_temperatures, label=f"Außentemperatur")
53    axs[2].set_xlabel("Zeitschritte")
54    axs[2].legend()
55
56    plt.tight_layout()
57    plt.show()
58
59if __name__ == '__main__':
60    try:
61        print("Running the heat requirement script.")
62        
63        data = pd.read_csv("examples/data/data_ETRS89.csv", sep=";")
64        TRY_filename = "src/districtheatingsim/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
65        calc_method = "Datensatz"
66
67        print(f"Data: {data}")
68
69        # if data in column "Subtyp" is just one digit, add a leading zero
70        data["Subtyp"] = data["Subtyp"].apply(lambda x: f"0{x}" if len(str(x)) == 1 else x)
71
72        calculation(data, TRY_filename, calc_method)
73
74    except Exception as e:
75        print("An error occurred:")
76        print(traceback.format_exc())
77        raise e

Data-driven heat demand analysis example.

Network Design Examples

Network Generation

05_example_net_generation.py
 1"""
 2Filename: 05_example_net_generation_test.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2024-11-19
 5Description: Script for testing the net generation functions.
 6Usage: Run the script to generate a heat network based on the given inputs.
 7Functions:
 8    generate_and_export_layers(osm_street_layer_geojson_file_name, data_csv_file_name, coordinates, base_path, algorithm) -> None
 9        Generates and exports the heat network layers based on the given inputs.
10Example:
11    $ python 05_example_net_generation_test.py
12"""
13
14import geopandas as gpd
15import matplotlib.pyplot as plt
16
17from districtheatingsim.geocoding.geocoding import get_coordinates
18from districtheatingsim.net_generation.import_and_create_layers import generate_and_export_layers
19from districtheatingsim.net_generation.network_geojson_schema import NetworkGeoJSONSchema
20
21### this is an example on how to use the net generation features ###
22### Project-specific inputs ###
23
24# Data csv file can be created with the 01_example_geocoding.py script
25# therefore the data_csv_input_file_name is the output of the geocoding script
26data_csv_file_name = "examples\data\data_ETRS89.csv"
27
28# Street data is imported with the 02_example_import_osm_data_geojson.py script
29# therefore the osm_street_layer_geojson_file_name is the output of the osm import script
30#osm_street_layer_geojson_file_name = "examples\data\osm_street_data.geojson"
31# "examples\data\streets.geojson" is a reduced version of the original data, faster loading
32osm_street_layer_geojson_file_name = "examples\data\streets.geojson"
33
34# Coordinates for the heat source is needed to generate the heat network
35# The coordinates can be obtained from the geocoding script with an address input
36
37"""
38try:
39    address = "Brückenstraße 10"
40    city = "Görlitz"
41    state = "Sachsen"
42    country = "Deutschland"
43    coordinates = [get_coordinates(f"{address}, {city}, {state}, {country}")]
44    print(f"Coordinates: {coordinates}")
45except Exception as e:
46    print(f"Error getting coordinates: {e}")
47    coordinates = [(499829.047722075, 5666164.624415245)]
48    print(f"Using default coordinates: {coordinates}")
49"""
50coordinates = [(499829.047722075, 5666164.624415245)]
51print(f"Using default coordinates: {coordinates}")
52
53# Choose the algorithm to generate the network
54# mode = "MST"
55mode = "Advanced MST"
56# mode = "OSMnx Steiner Tree"
57
58base_path = "examples\data\Wärmenetz\Variante 1"
59print(f"Starte die Generierung des Wärmenetzes in {base_path} mit dem Algorithmus {mode}...")
60
61generate_and_export_layers(osm_street_layer_geojson_file_name, data_csv_file_name, coordinates, base_path, algorithm=mode)
62print("Wärmenetz-Layer erfolgreich erstellt.")
63
64# Load unified GeoJSON and extract layers for visualization
65
66unified_geojson = NetworkGeoJSONSchema.import_from_file(f"{base_path}\Wärmenetz\Wärmenetz.geojson")
67vorlauf, rücklauf, hast, erzeuger = NetworkGeoJSONSchema.split_to_legacy_format(unified_geojson)
68
69print("Layer erfolgreich geladen.")
70
71# Plotten der geographischen Daten
72fig, ax = plt.subplots(figsize=(10, 10))  # Größe des Plots anpassen
73hast.plot(ax=ax, color='green')  # Farbe und weitere Parameter anpassen
74rücklauf.plot(ax=ax, color='blue')  # Farbe und weitere Parameter anpassen
75vorlauf.plot(ax=ax, color='red')  # Farbe und weitere Parameter anpassen
76erzeuger.plot(ax=ax, color='black')  # Farbe und weitere Parameter anpassen
77plt.title('Wärmenetz')
78plt.show()

Automated network topology generation example.

Simulation Examples

Simple Pandapipes Simulation

06_example_simple_pandapipes.py
  1"""
  2Filename: 06_example_simple_pandapipes.py
  3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
  4Date: 2025-05-12
  5Description: Script for testing the pandapipes net simulation functions.
  6Usage: Run the script to generate a simple pandapipes network.
  7
  8Example:
  9    $ python 06_example_simple_pandapipes.py
 10"""
 11
 12# needs fixes due to updated functions
 13
 14import logging
 15
 16import matplotlib.pyplot as plt
 17import numpy as np
 18import traceback
 19import pandapipes as pp
 20import pandapipes.plotting as pp_plot
 21
 22from districtheatingsim.net_simulation_pandapipes.config_plot import config_plot
 23from districtheatingsim.net_simulation_pandapipes.pp_net_initialisation_geojson import *
 24from districtheatingsim.net_simulation_pandapipes.utilities import *
 25from districtheatingsim.heat_requirement import heat_requirement_calculation_csv
 26
 27# Initialize logging
 28logging.basicConfig(level=logging.INFO)
 29
 30def test_heat_consumer_result_extraction():
 31    print("Running the heat consumer result extraction script.")
 32    # Create a simple pandapipes network
 33    net = pp.create_empty_network("net", add_stdtypes=False, fluid="water")
 34
 35    juncs = pp.create_junctions(
 36        net,
 37        nr_junctions=6,
 38        pn_bar=5,
 39        tfluid_k=85+273.15,
 40        system=["flow"] * 3 + ["return"] * 3,
 41        geodata=[
 42            (0, 0),    # Junction 0 (Startpunkt)
 43            (10, 0),   # Junction 1 (Vorlauf)
 44            (20, 0),   # Junction 2 (Vorlauf)
 45            (20, -10), # Junction 3 (Rücklauf)
 46            (10, -10), # Junction 4 (Rücklauf)
 47            (0, -10),  # Junction 5 (Endpunkt Rücklauf)
 48        ],
 49        name=[
 50            "Junction 0",      # Junction 0
 51            "Junction 1", # Junction 1
 52            "Junction 2", # Junction 2
 53            "Junction 3", # Junction 3
 54            "Junction 4", # Junction 4
 55            "Junction 5"         # Junction 5
 56        ]
 57    )
 58    pp.create_pipes_from_parameters(net, juncs[[0, 1, 3, 4]], juncs[[1, 2, 4, 5]], k_mm=0.1, length_km=0.5,
 59                                            diameter_m=0.1022, system=["flow"] * 2 + ["return"] * 2, alpha_w_per_m2k=0.4,
 60                                            text_k=273.15, name=[
 61            "Flow Pipe 1",  # Pipe von Source zu Flow Node 1
 62            "Flow Pipe 2",  # Pipe von Flow Node 1 zu Flow Node 2
 63            "Return Pipe 1", # Pipe von Return Node 1 zu Return Node 2
 64            "Return Pipe 2"  # Pipe von Return Node 2 zu Sink
 65        ])
 66    pp.create_circ_pump_const_pressure(net, juncs[-1], juncs[0], 5, 2, 85+273.15, type='auto', name="Pump 1")
 67    pp.create_heat_consumer(net, juncs[1], juncs[4], treturn_k=60+273.15, qext_w=7500, name="Heat Consumer 1")
 68    pp.create_heat_consumer(net, juncs[2], juncs[3], treturn_k=77+273.15, qext_w=7500, name="Heat Consumer 2")
 69
 70    pp.pipeflow(net, mode="bidirectional", iter=100)
 71
 72    return net
 73
 74def initialize_test_net(qext_w=np.array([500000, 200000]),
 75                        return_temperature=np.array([55, 60]),
 76                        supply_temperature=85, 
 77                        flow_pressure_pump=4,
 78                        lift_pressure_pump=1.5,
 79                        pipetype="KMR 100/250-2v",
 80                        v_max_m_s=1.5):
 81    print("Running the test network initialization script.")
 82    net = pp.create_empty_network(fluid="water")
 83
 84    k = 0.1 # roughness defaults to 0.1
 85
 86    suply_temperature_k = supply_temperature + 273.15
 87    return_temperature_k = return_temperature + 273.15
 88
 89    # Junctions for pump
 90    j1 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 1", geodata=(0, 10))
 91    j2 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 2", geodata=(0, 0))
 92
 93    # Junctions for connection pipes forward line
 94    j3 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 3", geodata=(10, 0))
 95    j4 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 4", geodata=(60, 0))
 96
 97    # Junctions for heat exchangers
 98    j5 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 5", geodata=(85, 0))
 99    j6 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 6", geodata=(85, 10))
100    
101    # Junctions for connection pipes return line
102    j7 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 7", geodata=(60, 10))
103    j8 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 8", geodata=(10, 10))
104
105    pump1 = pp.create_circ_pump_const_pressure(net, j1, j2, p_flow_bar=flow_pressure_pump, plift_bar=lift_pressure_pump, 
106                                               t_flow_k=suply_temperature_k, type="auto", name="pump1")
107
108    pipe1 = pp.create_pipe(net, j2, j3, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe1", sections=5, text_k=283)
109    pipe2 = pp.create_pipe(net, j3, j4, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe2", sections=5, text_k=283)
110    pipe3 = pp.create_pipe(net, j4, j5, std_type=pipetype, length_km=0.025,k_mm=k, name="pipe3", sections=5, text_k=283)
111
112    heat_cosnumer1 = pp.create_heat_consumer(net, from_junction=j5, to_junction=j6, loss_coefficient=0, qext_w=qext_w[0], 
113                                             treturn_k=return_temperature_k[0], name="heat_consumer_1") # treturn_k=t when implemented in function
114    
115
116    heat_cosnumer2 = pp.create_heat_consumer(net, from_junction=j4, to_junction=j7, loss_coefficient=0, qext_w=qext_w[1], 
117                                             treturn_k=return_temperature_k[1], name="heat_consumer_2") # treturn_k=t when implemented in function
118    
119    pipe4 = pp.create_pipe(net, j6, j7, std_type=pipetype, length_km=0.25, k_mm=k, name="pipe4", sections=5, text_k=283)
120    pipe5 = pp.create_pipe(net, j7, j8, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe5", sections=5, text_k=283)
121    pipe6 = pp.create_pipe(net, j8, j1, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe6", sections=5, text_k=283)
122
123    pp.pipeflow(net, mode="bidirectional", iter=100)
124    
125    net = create_controllers(net, qext_w, supply_temperature, None, return_temperature, None)
126
127    run_control(net, mode="bidirectional", iter=100)
128
129    net = correct_flow_directions(net)
130    net = init_diameter_types(net, v_max_pipe=v_max_m_s, material_filter="KMR", k=k)
131    net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="KMR", k=k)
132
133    return net
134
135
136def initialize_complex_test_net(qext_w=np.array([20000, 15000, 10000, 25000]),
137                                return_temperature=np.array([55, 60, 50, 65]),
138                                supply_temperature=85,
139                                flow_pressure_pump=4,
140                                lift_pressure_pump=1.5,
141                                pipetype="KMR 100/250-2v",
142                                v_max_m_s=1.5):
143    print("Running the complex test network initialization script.")
144    net = pp.create_empty_network(fluid="water")
145    
146    k = 0.1  # roughness
147    supply_temperature_k = supply_temperature + 273.15  # convert to Kelvin
148    return_temperature_k = return_temperature + 273.15  # convert to Kelvin
149
150    # Define junctions
151    j1 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Pump Supply", geodata=(0, 0))
152    j2 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Main Split Supply", geodata=(10, 0))
153    j12 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Main Split Return", geodata=(10, 10))
154    j13 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Pump Return", geodata=(0, 10))
155
156    # Additional junctions for new branches
157    j3 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer B Supply", geodata=(20, 0))
158    j4 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer B Return", geodata=(20, 10))
159    j5 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer C Supply", geodata=(30, 0))
160    j6 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer C Return", geodata=(30, 10))
161
162    # Pump
163    pp.create_circ_pump_const_pressure(net, j13, j1, p_flow_bar=flow_pressure_pump, plift_bar=lift_pressure_pump, 
164                                       t_flow_k=supply_temperature_k, type="auto", name="Main Pump")
165
166    # Pipes for supply line
167    pp.create_pipe(net, j1, j2, std_type=pipetype, length_km=0.02, k_mm=k, name="Main Pipe Supply")
168    pp.create_pipe(net, j2, j3, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch B Pipe Supply")
169    pp.create_pipe(net, j3, j5, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch C Pipe Supply")
170
171    # Pipes for return line
172    pp.create_pipe(net, j6, j4, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch C Pipe Return")
173    pp.create_pipe(net, j4, j12, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch B Pipe Return")
174    pp.create_pipe(net, j12, j13, std_type=pipetype, length_km=0.02, k_mm=k, name="Main Pipe Return")
175
176    # Heat consumers
177    pp.create_heat_consumer(net, from_junction=j2, to_junction=j12, qext_w=qext_w[0], treturn_k=return_temperature_k[0], name="Consumer A")
178    pp.create_heat_consumer(net, from_junction=j3, to_junction=j4, qext_w=qext_w[1], treturn_k=return_temperature_k[1], name="Consumer B")
179    pp.create_heat_consumer(net, from_junction=j5, to_junction=j6, qext_w=qext_w[2], treturn_k=return_temperature_k[2], name="Consumer C")
180
181    # Simulate pipe flow
182    pp.pipeflow(net, mode="bidirectional", iter=100, alpha=0.6)
183
184    # Placeholder functions for additional processing
185    net = create_controllers(net, qext_w, supply_temperature, None, return_temperature, None)
186
187    run_control(net, mode="bidirectional", iter=100)
188
189    net = correct_flow_directions(net)
190    net = init_diameter_types(net, v_max_pipe=v_max_m_s, material_filter="KMR", k=k)
191    net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="KMR", k=k)
192
193    return net
194
195def initialize_test_net_two_pumps(qext_w=np.array([50000, 20000]),
196                                  return_temperature=np.array([60,55]),
197                                  supply_temperature=85,
198                                  flow_pressure_pump=4,
199                                  lift_pressure_pump=1.5,
200                                  pipetype="KMR 100/250-2v",
201                                  v_max_m_s=1.5,
202                                  mass_pump_mass_flow=0.5):
203    print("Running the test network with two pumps initialization script.")
204    net = pp.create_empty_network(fluid="water")
205
206    ### get pipe properties
207    k = 0.1
208
209    supply_temperature_k = supply_temperature + 273.15  # convert to Kelvin
210    return_temperature_k = return_temperature + 273.15  # convert to Kelvin
211
212    # Junctions for pump
213    j1 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 1", geodata=(0, 10))
214    j2 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 2", geodata=(0, 0))
215
216    # Junctions for connection pipes forward line
217    j3 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 3", geodata=(10, 0))
218    j4 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 4", geodata=(60, 0))
219
220    # Junctions for heat exchangers
221    j5 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 5", geodata=(85, 0))
222    j6 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 6", geodata=(85, 10))
223    
224    # Junctions for connection pipes return line
225    j7 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 7", geodata=(60, 10))
226    j8 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 8", geodata=(10, 10))
227
228    pump1 = pp.create_circ_pump_const_pressure(net, j1, j2, p_flow_bar=flow_pressure_pump,
229                                               plift_bar=lift_pressure_pump, t_flow_k=supply_temperature_k,
230                                               type="auto", name="pump1")
231
232    pipe1 = pp.create_pipe(net, j2, j3, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe1", sections=5, text_k=283)
233    pipe2 = pp.create_pipe(net, j3, j4, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe2", sections=5, text_k=283)
234    pipe3 = pp.create_pipe(net, j4, j5, std_type=pipetype, length_km=0.025, k_mm=k, name="pipe3", sections=5, text_k=283)
235
236    pp.create_heat_consumer(net, from_junction=j5, to_junction=j6, qext_w=qext_w[0], treturn_k=return_temperature_k[0], name="Consumer A")
237    pp.create_heat_consumer(net, from_junction=j4, to_junction=j7, qext_w=qext_w[1], treturn_k=return_temperature_k[1], name="Consumer B")
238    
239    pipe4 = pp.create_pipe(net, j6, j7, std_type=pipetype, length_km=0.25, k_mm=k, name="pipe4", sections=5, text_k=283)
240    pipe5 = pp.create_pipe(net, j7, j8, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe5", sections=5, text_k=283)
241    pipe6 = pp.create_pipe(net, j8, j1, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe6", sections=5, text_k=283)
242    
243    ### here comes the part with the additional circ_pump_const_mass_flow ###
244    # first of, the junctions
245    j9 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 9", geodata=(100, 0))
246    j10 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 10", geodata=(100, 10))
247    j11 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 11", geodata=(100, 5))
248
249    pipe7 = pp.create_pipe(net, j5, j9, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe7", sections=5, text_k=283)
250    pipe8 = pp.create_pipe(net, j10, j6, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe8", sections=5, text_k=283)
251
252    pump2 = pp.create_circ_pump_const_mass_flow(net, j10, j11, p_flow_bar=flow_pressure_pump, mdot_flow_kg_per_s=mass_pump_mass_flow, 
253                                                t_flow_k=supply_temperature_k, type="auto", name="pump2")
254    
255    flow_control_pump2 = pp.create_flow_control(net, j11, j9, controlled_mdot_kg_per_s=mass_pump_mass_flow)
256
257    # Simulate pipe flow
258    pp.pipeflow(net, mode="bidirectional", iter=100)
259
260    # Placeholder functions for additional processing
261    net = create_controllers(net, qext_w, supply_temperature, None, return_temperature, None)
262
263    run_control(net, mode="bidirectional", iter=100)
264
265    net = correct_flow_directions(net)
266    net = init_diameter_types(net, v_max_pipe=v_max_m_s, material_filter="KMR", k=k)
267    net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="KMR", k=k)
268
269    return net
270
271def test_profile_initiation():
272    print("Running the test_profile_initiation function.")
273        
274    data = pd.read_csv("examples/data/data_ETRS89.csv", sep=";")
275    TRY_filename = "src/districtheatingsim/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
276    calc_method = "Datensatz"
277
278    print(f"Data: {data}")
279
280    # if data in column "Subtyp" is just one digit, add a leading zero
281    data["Subtyp"] = data["Subtyp"].apply(lambda x: f"0{x}" if len(str(x)) == 1 else x)
282
283    yearly_time_steps, total_heat_W, heating_heat_W, warmwater_heat_W, max_heat_requirement_W, supply_temperature_curve, \
284        return_temperature_curve, hourly_air_temperatures = heat_requirement_calculation_csv.generate_profiles_from_csv(data, TRY_filename, calc_method)
285
286    print("Initializing Net:")
287
288    # qext_w is the max heat requirement in W
289    qext_w = max_heat_requirement_W*5
290    print(f"  → Max heat requirement (qext_w): {qext_w} W")
291    # return_temperature is the return temperature in °C, same size as qext_w array
292    # np.fill like qext_w with 55
293    return_temperature = np.full_like(qext_w, 55)
294    print(f"  → Return temperature: {return_temperature} °C")
295
296    net = initialize_test_net(qext_w=qext_w, return_temperature=return_temperature)
297
298    return net
299
300def print_results(net):
301    print(net.res_junction)
302    print(net.res_pipe)
303    print(net.res_heat_consumer)
304    print(net.res_circ_pump_pressure)
305
306if __name__ == "__main__":
307    try:
308        nets = [
309            #test_heat_consumer_result_extraction(),
310            initialize_test_net(),
311            initialize_complex_test_net(),
312            initialize_test_net_two_pumps(),
313            test_profile_initiation()
314            ]
315        
316        for net in nets:
317            print_results(net)
318
319            fig, ax = plt.subplots()
320            config_plot(net, ax, show_junctions=True, show_pipes=True, show_heat_consumers=True, show_pump=True, show_plot=False)
321
322        plt.show()
323
324    except Exception as e:
325        print("An error occurred:")
326        print(traceback.format_exc())
327        raise e

Basic pandapipes simulation example.

Time Series Simulation

07_example_timeseries_pandapipes.py
  1"""
  2Filename: 07_example_timeseries_pandapipes.py
  3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
  4Date: 2025-05-12
  5Description: Script for testing the pandapipes net simulation functions.
  6Usage: Run the script to generate a simple pandapipes network.
  7
  8Example:
  9    $ python 07_example_timeseries_pandapipes.py
 10"""
 11
 12import traceback
 13import logging
 14# Initialize logging
 15logging.basicConfig(level=logging.INFO)
 16
 17import numpy as np
 18import pandas as pd
 19import matplotlib.pyplot as plt
 20
 21import pandapipes as pp
 22import pandapipes.plotting as pp_plot
 23from pandapipes.timeseries import run_time_series
 24from pandapower.timeseries import OutputWriter
 25
 26from districtheatingsim.net_simulation_pandapipes.config_plot import config_plot
 27from districtheatingsim.net_simulation_pandapipes.pp_net_initialisation_geojson import *
 28from districtheatingsim.net_simulation_pandapipes.pp_net_time_series_simulation import *
 29from districtheatingsim.net_simulation_pandapipes.utilities import *
 30
 31from districtheatingsim.net_simulation_pandapipes.config_plot import config_plot
 32
 33def initialize_test_net(qext_w=np.array([100000, 100000, 100000]),
 34                        return_temperature=np.array([55, 60, 50]),
 35                        supply_temperature=85,
 36                        flow_pressure_pump=4, 
 37                        lift_pressure_pump=1.5,
 38                        pipetype="KMR 100/250-2v",
 39                        v_max_m_s=1.5):
 40    print("Initializing test network...")
 41    # Initialize the pandapipes network
 42    net = pp.create_empty_network(fluid="water")
 43    
 44    k = 0.1  # roughness
 45    supply_temperature_k = supply_temperature + 273.15  # convert to Kelvin
 46    return_temperature_k = return_temperature + 273.15  # convert to Kelvin
 47
 48    # Define junctions
 49    j1 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Pump Supply", geodata=(0, 0))
 50    j2 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Main Split Supply", geodata=(10, 0))
 51    j12 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Main Split Return", geodata=(10, 10))
 52    j13 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Pump Return", geodata=(0, 10))
 53
 54    # Additional junctions for new branches
 55    j3 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer B Supply", geodata=(20, 0))
 56    j4 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer B Return", geodata=(20, 10))
 57    j5 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer C Supply", geodata=(30, 0))
 58    j6 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer C Return", geodata=(30, 10))
 59
 60    # Pump
 61    pp.create_circ_pump_const_pressure(net, j13, j1, p_flow_bar=flow_pressure_pump, plift_bar=lift_pressure_pump, 
 62                                       t_flow_k=supply_temperature_k, type="auto", name="Main Pump")
 63
 64    # Pipes for supply line
 65    pp.create_pipe(net, j1, j2, std_type=pipetype, length_km=0.02, k_mm=k, name="Main Pipe Supply")
 66    pp.create_pipe(net, j2, j3, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch B Pipe Supply")
 67    pp.create_pipe(net, j3, j5, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch C Pipe Supply")
 68
 69    # Pipes for return line
 70    pp.create_pipe(net, j12, j13, std_type=pipetype, length_km=0.02, k_mm=k, name="Main Pipe Return")
 71    pp.create_pipe(net, j4, j12, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch B Pipe Return")
 72    pp.create_pipe(net, j6, j4, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch C Pipe Return")
 73
 74    # Heat consumers
 75    pp.create_heat_consumer(net, from_junction=j2, to_junction=j12, qext_w=qext_w[0], treturn_k=return_temperature_k[0], name="Consumer A")
 76    pp.create_heat_consumer(net, from_junction=j3, to_junction=j4, qext_w=qext_w[1], treturn_k=return_temperature_k[1], name="Consumer B")
 77    pp.create_heat_consumer(net, from_junction=j5, to_junction=j6, qext_w=qext_w[2], treturn_k=return_temperature_k[2], name="Consumer C")
 78
 79    # Simulate pipe flow
 80    pp.pipeflow(net, mode="bidirectional", iter=100)
 81
 82    # Placeholder functions for additional processing
 83    net = create_controllers(net, qext_w, supply_temperature, None, return_temperature, None)
 84
 85    run_control(net, mode="bidirectional", iter=100)
 86
 87    net = correct_flow_directions(net)
 88    net = init_diameter_types(net, v_max_pipe=v_max_m_s, material_filter="KMR", k=k)
 89    net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="KMR", k=k)
 90
 91    return net
 92
 93
 94def timeseries_test(net):
 95    print("Running time series test...")
 96    start = 0
 97    end = 8 # 8760 hours in a year
 98
 99    # time steps with start and end
100    time_steps = np.arange(start, end, 1)	# time steps in hours
101
102    # yearly time steps with dates beginning at 01.01.2021 00:00:00
103    yearly_time_steps = pd.date_range(start="2021-01-01 00:00:00", periods=end, freq="h")
104
105    # np.random.seed() is used to make the random numbers predictable
106    np.random.seed(0)
107    # Generate more realistic heat demand profiles with smaller variations
108    # Instead of 0-100kW, use 30-70kW to avoid extreme controller adjustments
109    base_demand = np.array([50000, 60000, 40000])  # Base demand per consumer in W
110    variation = 0.3  # ±30% variation
111    
112    qext_w_profiles = np.zeros((3, end))
113    for i in range(3):
114        # Generate random variations around base demand
115        qext_w_profiles[i, :] = base_demand[i] * (1 + variation * (2 * np.random.random(end) - 1))
116    
117    print(f"qext_w_profiles shape: {qext_w_profiles.shape}")
118    print(f"qext_w_profiles min: {np.min(qext_w_profiles):.0f} W, max: {np.max(qext_w_profiles):.0f} W")
119
120    return_temperature = np.linspace(50, 60, end).reshape(1, -1).repeat(3, axis=0)  # Generate time-dependent return temperatures as a linear gradient
121    if qext_w_profiles.shape != return_temperature.shape:
122        raise ValueError("The shape of return_temperature_profiles must match the shape of qext_w_profiles.")
123    supply_temperature = np.full_like(time_steps, 85)  # Supply temperature is constant
124    print(f"supply_temperature: {supply_temperature}") # Structure is one-dimensional array with shape (n_time_steps,)
125    
126    print(net.controller)
127
128    update_heat_consumer_qext_controller(net, qext_w_profiles, time_steps, start, end)
129    update_heat_consumer_return_temperature_controller(net, return_temperature, time_steps, start, end)
130    update_heat_generator_supply_temperature_controller(net, supply_temperature, time_steps, start, end)
131
132    print(net)
133    print(net.controller)
134    print(net.heat_consumer)
135    print(net.circ_pump_pressure)
136    #print(net.res_controller)
137    print(net.res_heat_consumer)
138    print(net.res_circ_pump_pressure)
139
140    # Log variables and run time series calculation
141    log_variables = create_log_variables(net)
142    ow = OutputWriter(net, time_steps, output_path=None, log_variables=log_variables)
143
144    run_time_series.run_timeseries(net, time_steps, mode="bidirectional", iter=100, alpha=0.6)
145
146    return yearly_time_steps, net, ow.np_results
147
148def print_results(net):
149    print(net)
150    print(net.junction)
151    print(net.pipe)
152    print(net.heat_consumer)
153    print(net.circ_pump_pressure)
154
155    print(net.res_junction)
156    print(net.res_pipe)
157    print(net.res_heat_consumer)
158    print(net.res_circ_pump_pressure)
159
160def plot_time_series_results(yearly_time_steps, np_results, net):
161    """
162    Plot time series results for junction pressures, temperatures, and heat consumer data.
163    
164    Parameters
165    ----------
166    yearly_time_steps : pd.DatetimeIndex
167        Time steps with dates
168    np_results : dict
169        Results dictionary from OutputWriter
170    net : pandapipes.pandapipesNet
171        The network object with junction names
172    """
173    print("\n" + "="*70)
174    print("TIME SERIES RESULTS ANALYSIS")
175    print("="*70)
176    
177    # Extract junction pressures
178    if 'res_junction.p_bar' in np_results:
179        p_bar = np_results['res_junction.p_bar']
180        print(f"\nJunction Pressures Shape: {p_bar.shape}")
181        print(f"Time steps: {p_bar.shape[0]}, Junctions: {p_bar.shape[1]}")
182        
183        # Plot junction pressures over time
184        fig, ax = plt.subplots(figsize=(14, 6))
185        for i in range(p_bar.shape[1]):
186            junction_name = net.junction.at[i, 'name'] if 'name' in net.junction.columns else f"Junction {i}"
187            ax.plot(yearly_time_steps, p_bar[:, i], label=junction_name, linewidth=1.5)
188        
189        ax.set_xlabel('Zeit', fontsize=12)
190        ax.set_ylabel('Druck [bar]', fontsize=12)
191        ax.set_title('Knotendruck im Zeitverlauf', fontsize=14, fontweight='bold')
192        ax.legend(loc='best', fontsize=9)
193        ax.grid(True, alpha=0.3)
194        plt.tight_layout()
195        plt.show()
196        
197        # Print statistics
198        print("\nKnotendruck Statistik:")
199        for i in range(p_bar.shape[1]):
200            junction_name = net.junction.at[i, 'name'] if 'name' in net.junction.columns else f"Junction {i}"
201            print(f"  {junction_name}:")
202            print(f"    Min: {np.min(p_bar[:, i]):.3f} bar")
203            print(f"    Max: {np.max(p_bar[:, i]):.3f} bar")
204            print(f"    Mittelwert: {np.mean(p_bar[:, i]):.3f} bar")
205    
206    # Extract junction temperatures
207    if 'res_junction.t_k' in np_results:
208        t_k = np_results['res_junction.t_k']
209        t_c = t_k - 273.15  # Convert to Celsius
210        
211        # Plot junction temperatures over time
212        fig, ax = plt.subplots(figsize=(14, 6))
213        for i in range(t_c.shape[1]):
214            junction_name = net.junction.at[i, 'name'] if 'name' in net.junction.columns else f"Junction {i}"
215            ax.plot(yearly_time_steps, t_c[:, i], label=junction_name, linewidth=1.5)
216        
217        ax.set_xlabel('Zeit', fontsize=12)
218        ax.set_ylabel('Temperatur [°C]', fontsize=12)
219        ax.set_title('Knotentemperatur im Zeitverlauf', fontsize=14, fontweight='bold')
220        ax.legend(loc='best', fontsize=9)
221        ax.grid(True, alpha=0.3)
222        plt.tight_layout()
223        plt.show()
224    
225    # Extract heat consumer results
226    if 'res_heat_consumer.qext_w' in np_results:
227        qext_w = np_results['res_heat_consumer.qext_w']
228        qext_kw = qext_w / 1000  # Convert to kW
229        
230        # Plot heat demand over time
231        fig, ax = plt.subplots(figsize=(14, 6))
232        for i in range(qext_kw.shape[1]):
233            consumer_name = net.heat_consumer.at[i, 'name'] if 'name' in net.heat_consumer.columns else f"Consumer {i}"
234            ax.plot(yearly_time_steps, qext_kw[:, i], label=consumer_name, linewidth=1.5)
235        
236        # Plot total heat demand
237        total_qext_kw = np.sum(qext_kw, axis=1)
238        ax.plot(yearly_time_steps, total_qext_kw, label='Gesamt', linewidth=2.5, 
239                color='black', linestyle='--')
240        
241        ax.set_xlabel('Zeit', fontsize=12)
242        ax.set_ylabel('Wärmebedarf [kW]', fontsize=12)
243        ax.set_title('Wärmebedarf im Zeitverlauf', fontsize=14, fontweight='bold')
244        ax.legend(loc='best', fontsize=9)
245        ax.grid(True, alpha=0.3)
246        plt.tight_layout()
247        plt.show()
248        
249        # Print statistics
250        print("\nWärmebedarf Statistik:")
251        for i in range(qext_kw.shape[1]):
252            consumer_name = net.heat_consumer.at[i, 'name'] if 'name' in net.heat_consumer.columns else f"Consumer {i}"
253            print(f"  {consumer_name}:")
254            print(f"    Min: {np.min(qext_kw[:, i]):.1f} kW")
255            print(f"    Max: {np.max(qext_kw[:, i]):.1f} kW")
256            print(f"    Mittelwert: {np.mean(qext_kw[:, i]):.1f} kW")
257        print(f"  Gesamt:")
258        print(f"    Min: {np.min(total_qext_kw):.1f} kW")
259        print(f"    Max: {np.max(total_qext_kw):.1f} kW")
260        print(f"    Mittelwert: {np.mean(total_qext_kw):.1f} kW")
261    
262    # Print available result keys
263    print("\nVerfügbare Ergebnis-Variablen:")
264    for key in sorted(np_results.keys()):
265        shape = np_results[key].shape if hasattr(np_results[key], 'shape') else 'N/A'
266        print(f"  {key}: {shape}")
267    
268    print("="*70)
269
270
271if __name__ == "__main__":
272    try:
273        net = initialize_test_net()
274
275        fig1, ax1 = plt.subplots()
276        
277        config_plot(net=net, ax=ax1, show_junctions=True, show_pipes=True, 
278                    show_heat_consumers=True, show_pump=True, show_plot=False, 
279                    show_basemap=False, map_type="OSM")
280
281        print_results(net)
282
283        print("Test network initialized successfully."
284              " Running time series simulation..." )
285        
286
287        yearly_time_steps, net, np_results = timeseries_test(net)
288
289        print_results(net)
290
291        print("\nAnalyzing time series results...")
292        plot_time_series_results(yearly_time_steps, np_results, net)
293
294        fig2, ax2 = plt.subplots()
295        
296        config_plot(net=net, ax=ax2, show_junctions=True, show_pipes=True, 
297                    show_heat_consumers=True, show_pump=True, show_plot=False, 
298                    show_basemap=False, map_type="OSM")
299
300        plt.show()
301
302    except Exception as e:
303        print("An error occurred:")
304        print(traceback.format_exc())
305        raise e

Time series simulation with pandapipes.

Complex Time Series Analysis

08_example_complex_pandapipes_timeseries.py
  1"""
  2Filename: 08_example_complex_pandapipes_timeseries.py
  3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
  4Date: 2025-01-12
  5Description: Script for testing the pandapipes net simulation functions. Aims to simulate a district heating network using GeoJSON files for the network layout and a JSON file for the building load profile. The script includes functions to create and initialize the network, perform time series calculations, and plot the results.
  6Usage: Run the script in the main directory of the repository.
  7
  8Updated to work with the current NetworkGenerationData class structure.
  9"""
 10
 11import matplotlib.pyplot as plt
 12import numpy as np
 13
 14from districtheatingsim.net_simulation_pandapipes.pp_net_initialisation_geojson import *
 15from districtheatingsim.net_simulation_pandapipes.pp_net_time_series_simulation import *
 16from districtheatingsim.net_simulation_pandapipes.utilities import *
 17
 18from districtheatingsim.net_simulation_pandapipes.config_plot import config_plot
 19
 20from districtheatingsim.net_simulation_pandapipes.NetworkDataClass import NetworkGenerationData, SecondaryProducer
 21
 22def plot(net, time_steps, qext_kW, strom_kW):
 23    """
 24    Plots the network data.
 25
 26    Args:
 27        time_steps: Array of time steps.
 28        qext_kW: Array of external heat demand in kW.
 29        strom_kW: Array of power demand in kW.
 30    """
 31    fig, ax1 = plt.subplots()
 32
 33    if np.sum(strom_kW) == 0:
 34        ax1.plot(time_steps, qext_kW, 'b-', label="Gesamtheizlast Gebäude in kW")
 35
 36    if np.sum(strom_kW) > 0:
 37        ax1.plot(time_steps, qext_kW+strom_kW, 'b-', label="Gesamtheizlast Gebäude in kW")
 38        ax1.plot(time_steps, strom_kW, 'g-', label="Gesamtstrombedarf Wärmepumpen Gebäude in kW")
 39
 40    ax1.set_xlabel("Zeit")
 41    ax1.set_ylabel("Leistung in kW", color='b')
 42    ax1.tick_params('y', colors='b')
 43    ax1.legend(loc='upper center')
 44    ax1.grid()
 45
 46    fig, ax2 = plt.subplots()
 47
 48    config_plot(net, ax2, show_junctions=True, show_pipes=True, show_heat_consumers=True, show_basemap=False, show_plot=True)
 49
 50def print_net_results(net):
 51    print("Netzdaten:")
 52    print(f"Junctions: {net.junction}")
 53    print(f"Results Junctions: {net.res_junction}")
 54    print(f"Pipes: {net.pipe}")
 55    print(f"Results Pipes: {net.res_pipe}")
 56    print(f"Heat Consumers: {net.heat_consumer}")
 57    print(f"Results Heat Consumers: {net.res_heat_consumer}")
 58    print(f"Circ Pump Pressure: {net.circ_pump_pressure}")
 59    print(f"Results Circ Pump Pressure: {net.res_circ_pump_pressure}")
 60    if 'circ_pump_mass' in net:
 61        print(f"Circ Pump Mass: {net.circ_pump_mass}")
 62        print(f"Results Circ Pump Mass: {net.res_circ_pump_mass}")
 63
 64if __name__ == "__main__":
 65    from districtheatingsim.net_generation.network_geojson_schema import NetworkGeoJSONSchema
 66    import tempfile
 67    
 68    # File paths - Load unified format
 69    unified_path = "examples/data/Wärmenetz/Variante 2/Wärmenetz.geojson"
 70    
 71    # Extract layers from unified format
 72    unified_geojson = NetworkGeoJSONSchema.import_from_file(unified_path)
 73    vorlauf_gdf, ruecklauf_gdf, hast_gdf, erzeuger_gdf = NetworkGeoJSONSchema.split_to_legacy_format(unified_geojson)
 74    
 75    # Save to temporary files for compatibility
 76    temp_dir = tempfile.mkdtemp()
 77    vorlauf_path = os.path.join(temp_dir, "vorlauf.geojson")
 78    ruecklauf_path = os.path.join(temp_dir, "ruecklauf.geojson")
 79    hast_path = os.path.join(temp_dir, "hast.geojson")
 80    erzeugeranlagen_path = os.path.join(temp_dir, "erzeuger.geojson")
 81    
 82    vorlauf_gdf.to_file(vorlauf_path, driver="GeoJSON")
 83    ruecklauf_gdf.to_file(ruecklauf_path, driver="GeoJSON")
 84    hast_gdf.to_file(hast_path, driver="GeoJSON")
 85    erzeuger_gdf.to_file(erzeugeranlagen_path, driver="GeoJSON")
 86    json_path = "examples/data/Gebäude Lastgang.json"
 87    
 88    # Optional external data files
 89    TRY_filename = "examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
 90    COP_filename = "examples/data/COP/Kennlinien WP.csv"
 91
 92    # Network configuration parameters
 93    netconfiguration = "Niedertemperaturnetz"  # or "kaltes Netz"
 94    supply_temperature_control = "Statisch"  # or "Gleitend"
 95    
 96    # Temperature control parameters
 97    max_supply_temperature_heat_generator = 85.0  # °C
 98    min_supply_temperature_heat_generator = 70.0  # °C (for sliding control)
 99    max_air_temperature_heat_generator = 15.0     # °C
100    min_air_temperature_heat_generator = -12.0    # °C
101    
102    # Pump parameters
103    flow_pressure_pump = 4.0      # bar
104    lift_pressure_pump = 1.5      # bar
105    
106    # Building temperature parameters
107    min_supply_temperature_building_checked = False
108    min_supply_temperature_building = None # 60.0  # °C
109    fixed_return_temperature_heat_consumer_checked = False
110    fixed_return_temperature_heat_consumer = None # 50.0  # °C
111    dT_RL = 5.0  # K - temperature difference between heat consumer and net due to heat exchanger
112    building_temperature_checked = False  # flag indicating if building temperatures are considered
113    
114    # Pipe parameters
115    pipetype = "KMR 100/250-2v"  # type of pipe
116    diameter_optimization_pipe_checked = True  # flag indicating if diameter optimization is checked
117    max_velocity_pipe = 2.0  # m/s - maximum flow velocity in the pipes
118    material_filter_pipe = "KMR"  # material filter for pipes
119    k_mm_pipe = 0.1  # mm - roughness of the pipe
120    
121    # Producer configuration
122    main_producer_location_index = 0  # index of the main producer location
123    #secondary_producers = []  # list of secondary producers, can be empty or contain multiple producers
124    secondary_producers = [
125        SecondaryProducer(index=1, load_percentage=5.0)  # secondary producer with 5% load
126    ]
127    
128    # Import type
129    import_type = "geoJSON"  # currently only geoJSON
130
131    # Create NetworkGenerationData object with all required parameters
132    network_data = NetworkGenerationData(
133        # Required input data paths
134        import_type=import_type,
135        flow_line_path=vorlauf_path,
136        return_line_path=ruecklauf_path,
137        heat_consumer_path=hast_path,
138        heat_generator_path=erzeugeranlagen_path,
139        heat_demand_json_path=json_path,
140        
141        # Network configuration data
142        netconfiguration=netconfiguration,
143        supply_temperature_control=supply_temperature_control,
144        max_supply_temperature_heat_generator=max_supply_temperature_heat_generator,
145        min_supply_temperature_heat_generator=min_supply_temperature_heat_generator,
146        max_air_temperature_heat_generator=max_air_temperature_heat_generator,
147        min_air_temperature_heat_generator=min_air_temperature_heat_generator,
148        flow_pressure_pump=flow_pressure_pump,
149        lift_pressure_pump=lift_pressure_pump,
150        min_supply_temperature_building_checked=min_supply_temperature_building_checked,
151        min_supply_temperature_building=min_supply_temperature_building,
152        fixed_return_temperature_heat_consumer_checked=fixed_return_temperature_heat_consumer_checked,
153        fixed_return_temperature_heat_consumer=fixed_return_temperature_heat_consumer,
154        dT_RL=dT_RL,
155        building_temperature_checked=building_temperature_checked,
156        pipetype=pipetype,
157        
158        # Optimization variables
159        diameter_optimization_pipe_checked=diameter_optimization_pipe_checked,
160        max_velocity_pipe=max_velocity_pipe,
161        material_filter_pipe=material_filter_pipe,
162        k_mm_pipe=k_mm_pipe,
163        
164        # Producer configuration
165        main_producer_location_index=main_producer_location_index,
166        secondary_producers=secondary_producers,
167        
168        # Optional external data files
169        COP_filename=COP_filename,
170        TRY_filename=TRY_filename
171    )
172
173    # Initialize network from GeoJSON data
174    network_data = initialize_geojson(network_data)      
175
176    # Optimize pipe diameters if requested
177    if network_data.diameter_optimization_pipe_checked:
178        network_data.net = optimize_diameter_types(
179            network_data.net, 
180            network_data.max_velocity_pipe, 
181            network_data.material_filter_pipe, 
182            network_data.k_mm_pipe
183        )
184    
185    # Set simulation time range
186    network_data.start_time_step = 0
187    network_data.end_time_step = 100
188    
189    # Preprocess time series data
190    network_data = time_series_preprocessing(network_data)
191    
192    # Run thermohydraulic time series simulation
193    network_data = thermohydraulic_time_series_net(network_data)
194
195    print("Simulation erfolgreich abgeschlossen.")
196
197    # Print network results
198    print_net_results(network_data.net)
199    
200    # Calculate and display key performance indicators
201    results = network_data.calculate_results()
202    print("\nKennzahlen:")
203    for key, value in results.items():
204        if value is not None:
205            if isinstance(value, float):
206                print(f"{key}: {value:.2f}")
207            else:
208                print(f"{key}: {value}")
209        else:
210            print(f"{key}: Nicht verfügbar")
211    
212    # Prepare plot data
213    network_data.prepare_plot_data()
214    
215    # Plot results
216    plot(network_data.net, 
217         network_data.yearly_time_steps[network_data.start_time_step:network_data.end_time_step], 
218         network_data.waerme_ges_kW[network_data.start_time_step:network_data.end_time_step], 
219         network_data.strombedarf_ges_kW[network_data.start_time_step:network_data.end_time_step])
220    
221    # Show additional plots using prepared plot data
222    if network_data.plot_data:
223        print("\nVerfügbare Plot-Daten:")
224        for key in network_data.plot_data.keys():
225            print(f"  - {key}")
226        
227        # Example: Plot heat demand over time
228        heat_demand_data = network_data.plot_data["Gesamtwärmebedarf Wärmeübertrager"]
229        fig, ax = plt.subplots(figsize=(12, 6))
230        ax.plot(heat_demand_data['time'], heat_demand_data['data'], 'b-')
231        ax.set_xlabel('Zeit')
232        ax.set_ylabel(heat_demand_data['label'])
233        ax.set_title('Wärmebedarf über Zeit')
234        ax.grid(True)
235        plt.show()
236
237    plt.show()

Complex time series analysis example.

Energy Systems Examples

Heat Generators

09_example_heat_generators.py
  1"""
  2Filename: 09_example_heat_generators.py
  3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
  4Date: 2025-07-12
  5Description: Script for testing the heat generator functions.
  6
  7# Not up-to-date: This script is not up-to-date with the latest version of all heat generator classes.
  8"""
  9
 10from districtheatingsim.heat_generators.solar_thermal import SolarThermal
 11from districtheatingsim.heat_generators.gas_boiler import GasBoiler
 12from districtheatingsim.heat_generators.biomass_boiler import BiomassBoiler
 13from districtheatingsim.heat_generators.chp import CHP
 14from districtheatingsim.heat_generators.waste_heat_pump import WasteHeatPump
 15from districtheatingsim.heat_generators.river_heat_pump import RiverHeatPump
 16from districtheatingsim.heat_generators.geothermal_heat_pump import Geothermal
 17from districtheatingsim.heat_generators.power_to_heat import PowerToHeat
 18from districtheatingsim.utilities.test_reference_year import import_TRY
 19
 20import numpy as np
 21
 22import matplotlib.pyplot as plt
 23
 24def test_gas_boiler(Last_L, duration, economic_parameters, gBoiler=GasBoiler(name="Gas_Boiler_1",
 25                                                                             thermal_capacity_kW=200,
 26                                                                             spez_Investitionskosten=30, 
 27                                                                             Nutzungsgrad=0.9)):
 28    results = gBoiler.calculate(economic_parameters, duration, Last_L)
 29    
 30    print(f"Wärmemenge Gaskessel: {results['Wärmemenge']:.2f} MWh")
 31    print(f"Wärmeleistung Gaskessel: {results['Wärmeleistung_L']} kW")
 32    print(f"Brennstoffbedarf Gaskessel: {results['Brennstoffbedarf']:.2f} MWh")
 33    print(f"Wärmegestehungskosten Gaskessel: {results['WGK']:.2f} €/MWh")
 34    print(f"spezifische CO2-Emissionen Gaskessel: {results['spec_co2_total']:.2f} tCO2/MWh")
 35    print(f"Primärenergiebedarf Gaskessel: {results['primärenergie']:.2f} MWh")
 36
 37def test_power_to_heat(Last_L, duration, economic_parameters, PTH=PowerToHeat(name="PowerToHeat", 
 38                                                                              thermal_capacity_kW=1000, 
 39                                                                              spez_Investitionskosten=30, 
 40                                                                              Nutzungsgrad=0.98)):
 41    results = PTH.calculate(economic_parameters, duration, Last_L)
 42
 43    print(f"Wärmemenge Power-to-Heat: {results['Wärmemenge']:.2f} MWh")
 44    print(f"Wärmeleistung Power-to-Heat: {results['Wärmeleistung_L']} kW")
 45    print(f"Strombedarf Power-to-Heat: {results['Strombedarf']:.2f} MWh")
 46    print(f"Wärmegestehungskosten Power-to-Heat: {results['WGK']:.2f} €/MWh")
 47    print(f"spezifische CO2-Emissionen Power-to-Heat: {results['spec_co2_total']:.2f} tCO2/MWh")
 48    print(f"Primärenergiebedarf Power-to-Heat: {results['primärenergie']:.2f} MWh")
 49
 50def test_biomass_boiler(Last_L, duartion, economic_parameters, bBoiler=BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200, Größe_Holzlager=40, spez_Investitionskosten=200, spez_Investitionskosten_Holzlager=400, 
 51                                           Nutzungsgrad_BMK=0.8, min_Teillast=0.3, speicher_aktiv=False, Speicher_Volumen=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, 
 52                                           min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0, 
 53                                           opt_Speicher_max=100)):
 54    results = bBoiler.calculate(economic_parameters, duartion, Last_L)
 55    
 56    print(f"Wärmemenge Biomassekessel: {results['Wärmemenge']:.2f} MWh")
 57    print(f"Wärmeleistung Biomassekessel: {results['Wärmeleistung_L']} kW")
 58    print(f"Brennstoffbedarf Biomassekessel: {results['Brennstoffbedarf']:.2f} MWh")
 59    print(f"Wärmegestehungskosten Biomassekessel: {results['WGK']:.2f} €/MWh")
 60    print(f"spezifische CO2-Emissionen Biomassekessel: {results['spec_co2_total']:.2f} tCO2/MWh")
 61    print(f"Primärenergiebedarf Biomassekessel: {results['primärenergie']:.2f} MWh")
 62    print(f"Anzahl Starts Biomassekessel: {results['Anzahl_Starts']}")
 63    print(f"Betriebsstunden Biomassekessel: {results['Betriebsstunden']} h")
 64    print(f"Betriebsstunden pro Start Biomassekessel: {results['Betriebsstunden_pro_Start']:.2f} h")
 65
 66def test_biomass_boiler_storage(Last_L, 
 67                                duration, 
 68                                economic_parameters, 
 69                                bBoiler=BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200, 
 70                                                                     Größe_Holzlager=40, spez_Investitionskosten=200, 
 71                                                                     spez_Investitionskosten_Holzlager=400, 
 72                                                                     Nutzungsgrad_BMK=0.8, min_Teillast=0.3, 
 73                                                                     speicher_aktiv=True, Speicher_Volumen=20, 
 74                                                                     T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, 
 75                                                                     min_fill=0.2, max_fill=0.8, 
 76                                                                     spez_Investitionskosten_Speicher=750, active=True, 
 77                                                                     opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0, 
 78                                                                     opt_Speicher_max=100)):
 79
 80    results = bBoiler.calculate(economic_parameters, duration, Last_L)
 81    
 82    print(f"Wärmemenge Biomassekessel mit Speicher: {results['Wärmemenge']:.2f} MWh")
 83    print(f"Wärmeleistung Biomassekessel mit Speicher: {results['Wärmeleistung_L']} kW")
 84    print(f"Wärmeleistung Speicher Biomassekessel: {results['Wärmeleistung_Speicher_L']} kW")
 85    print(f"Brennstoffbedarf Biomassekessel mit Speicher: {results['Brennstoffbedarf']:.2f} MWh")
 86    print(f"Wärmegestehungskosten Biomassekessel mit Speicher: {results['WGK']:.2f} €/MWh")
 87    print(f"spezifische CO2-Emissionen Biomassekessel mit Speicher: {results['spec_co2_total']:.2f} tCO2/MWh")
 88    print(f"Primärenergiebedarf Biomassekessel mit Speicher: {results['primärenergie']:.2f} MWh")
 89    print(f"Anzahl Starts Biomassekessel mit Speicher: {results['Anzahl_Starts']}")
 90    print(f"Betriebsstunden Biomassekessel mit Speicher: {results['Betriebsstunden']} h")
 91    print(f"Betriebsstunden pro Start Biomassekessel mit Speicher: {results['Betriebsstunden_pro_Start']:.2f} h")
 92
 93def test_chp(Last_L, duration, economic_parameters, chp=CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33, 
 94                                                                KWK_Wirkungsgrad=0.9, min_Teillast=0.7, speicher_aktiv=False, Speicher_Volumen_BHKW=20, T_vorlauf=90, T_ruecklauf=60, 
 95                                                                initial_fill=0.0, min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BHKW_min=0, opt_BHKW_max=1000, 
 96                                                                opt_BHKW_Speicher_min=0, opt_BHKW_Speicher_max=100)):
 97
 98    results = chp.calculate(economic_parameters, duration, Last_L)
 99
100    print(f"Wärmemenge BHKW: {results['Wärmemenge']:.2f} MWh")
101    print(f"Wärmeleistung BHKW: {results['Wärmeleistung_L']} kW")
102    print(f"Brennstoffbedarf BHKW: {results['Brennstoffbedarf']:.2f} MWh")
103    print(f"Wärmegestehungskosten BHKW: {results['WGK']:.2f} €/MWh")
104    print(f"Strommenge BHKW: {results['Strommenge']:.2f} MWh")
105    print(f"Stromleistung BHKW: {results['el_Leistung_L']} kW")
106    print(f"Anzahl Starts BHKW: {results['Anzahl_Starts']}")
107    print(f"Betriebsstunden BHKW: {results['Betriebsstunden']} h")
108    print(f"Betriebsstunden pro Start BHKW: {results['Betriebsstunden_pro_Start']:.2f} h")
109    print(f"spezifische CO2-Emissionen BHKW: {results['spec_co2_total']:.2f} tCO2/MWh")
110    print(f"Primärenergiebedarf BHKW: {results['primärenergie']:.2f} MWh")
111
112def test_chp_storage(Last_L, duration, economic_parameters, chp=CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33, 
113                                                                        KWK_Wirkungsgrad=0.9, min_Teillast=0.7, speicher_aktiv=True, Speicher_Volumen_BHKW=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, 
114                                                                        min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BHKW_min=0, opt_BHKW_max=1000, 
115                                                                        opt_BHKW_Speicher_min=0, opt_BHKW_Speicher_max=100)):
116
117    results = chp.calculate(economic_parameters, duration, Last_L)
118    
119    print(f"Wärmemenge BHKW mit Speicher: {results['Wärmemenge']:.2f} MWh")
120    print(f"Wärmeleistung BHKW mit Speicher: {results['Wärmeleistung_L']} kW")
121    print(f"Wärmeleistung Speicher BHKW: {results['Wärmeleistung_Speicher_L']} kW")
122    print(f"Brennstoffbedarf BHKW mit Speicher: {results['Brennstoffbedarf']:.2f} MWh")
123    print(f"Wärmegestehungskosten BHKW mit Speicher: {results['WGK']:.2f} €/MWh")
124    print(f"Strommenge BHKW mit Speicher: {results['Strommenge']:.2f} MWh")
125    print(f"Stromleistung BHKW mit Speicher: {results['el_Leistung_L']} kW")
126    print(f"Anzahl Starts BHKW mit Speicher: {results['Anzahl_Starts']}")
127    print(f"Betriebsstunden BHKW mit Speicher: {results['Betriebsstunden']} h")
128    print(f"Betriebsstunden pro Start BHKW mit Speicher: {results['Betriebsstunden_pro_Start']:.2f} h")
129    print(f"spezifische CO2-Emissionen BHKW mit Speicher: {results['spec_co2_total']:.2f} tCO2/MWh")
130    print(f"Primärenergiebedarf BHKW mit Speicher: {results['primärenergie']:.2f} MWh")
131
132def test_waste_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, wasteHeatPump=WasteHeatPump(name="Abwärmepumpe", Kühlleistung_Abwärme=50, Temperatur_Abwärme=30, spez_Investitionskosten_Abwärme=500, spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)):
133
134    results = wasteHeatPump.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, COP_data=COP_data)
135    
136    print(f"Wärmemenge Abwärme-WP: {results['Wärmemenge']:.2f} MWh")
137    print(f"Strombedarf Abwärme-WP: {results['Strombedarf']:.2f} MWh")
138    print(f"Wärmeleistung Abwärme-WP: {results['Wärmeleistung_L']} kW")
139    print(f"elektrische Leistung Abwärme-WP: {results['el_Leistung_L']} kW")
140    print(f"Wärmegestehungskosten Abwärme-WP: {results['WGK']:.2f} €/MWh")
141    print(f"spezifische CO2-Emissionen Abwärme-WP: {results['spec_co2_total']:.2f} tCO2/MWh")
142    print(f"Primärenergiebedarf Abwärme-WP: {results['primärenergie']:.2f} MWh")
143
144def test_river_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, riverHeatPump=RiverHeatPump(name="Flusswärmepumpe", Wärmeleistung_FW_WP=200, Temperatur_FW_WP=10, dT=0, spez_Investitionskosten_Flusswasser=1000, spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)):   
145    
146    results = riverHeatPump.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, COP_data=COP_data)
147    
148    print(f"Wärmemenge Fluss-WP: {results['Wärmemenge']:.2f} MWh")
149    print(f"Strombedarf Fluss-WP: {results['Strombedarf']:.2f} MWh")
150    print(f"Wärmeleistung Fluss-WP: {results['Wärmeleistung_L']} kW")
151    print(f"elektrische Leistung Fluss-WP: {results['el_Leistung_L']} kW")
152    print(f"Wärmegestehungskosten Fluss-WP: {results['WGK']:.2f} €/MWh")
153    print(f"spezifische CO2-Emissionen Fluss-WP: {results['spec_co2_total']:.2f} tCO2/MWh")
154    print(f"Primärenergiebedarf Fluss-WP: {results['primärenergie']:.2f} MWh")
155
156def test_geothermal_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, geothermalHeatPump=Geothermal(name="Geothermie", Fläche=200, Bohrtiefe=100, Temperatur_Geothermie=10, spez_Bohrkosten=100, spez_Entzugsleistung=50, Vollbenutzungsstunden=2400, Abstand_Sonden=10, spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)):
157    
158    results = geothermalHeatPump.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, COP_data=COP_data)
159
160    print(f"Wärmemenge Geothermie-WP: {results['Wärmemenge']:.2f} MWh")
161    print(f"Strombedarf Geothermie-WP: {results['Strombedarf']:.2f} MWh")
162    print(f"Wärmeleistung Geothermie-WP: {results['Wärmeleistung_L']} kW")
163    print(f"elektrische Leistung Geothermie-WP: {results['el_Leistung_L']} kW")
164    print(f"Wärmegestehungskosten Geothermie-WP: {results['WGK']:.2f} €/MWh")
165    print(f"spezifische CO2-Emissionen Geothermie-WP: {results['spec_co2_total']:.2f} tCO2/MWh")
166    print(f"Primärenergiebedarf Geothermie-WP: {results['primärenergie']:.2f} MWh")
167
168def test_solar_thermal(Last_L, duration, economic_parameters, VLT_L, RLT_L, TRY_data, time_steps, solarThermal=SolarThermal(name="Solarthermie", bruttofläche_STA=200, vs=20, Typ="Vakuumröhrenkollektor", 
169                                                                                                            kosten_speicher_spez=750, kosten_fk_spez=430, kosten_vrk_spez=590, Tsmax=90, 
170                                                                                                            Longitude=-14.4222, STD_Longitude=-15, Latitude=51.1676, East_West_collector_azimuth_angle=0, 
171                                                                                                            Collector_tilt_angle=36, Tm_rl=60, Qsa=0, Vorwärmung_K=8, DT_WT_Solar_K=5, DT_WT_Netz_K=5, 
172                                                                                                            opt_volume_min=0, opt_volume_max=200, opt_area_min=0, opt_area_max=2000)):
173
174    results = solarThermal.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, RLT_L=RLT_L, TRY_data=TRY_data, time_steps=time_steps)
175
176    print(f"Wärmemenge Solarthermie: {results['Wärmemenge']:.2f} MWh")
177    print(f"Wärmeleistung Solarthermie: {results['Wärmeleistung_L']} kW")
178    print(f"Wärmegestehungskosten Solarthermie: {results['WGK']:.2f} €/MWh")
179    print(f"spezifische CO2-Emissionen Solarthermie: {results['spec_co2_total']:.2f} tCO2/MWh")
180    print(f"Primärenergiebedarf Solarthermie: {results['primärenergie']:.2f} MWh")
181    print(f"Speicherladung Solarthermie: {results['Speicherladung_L']} kW")
182    print(f"Speicherfüllstand Solarthermie: {results['Speicherfüllstand_L']} MWh")
183
184if __name__ == "__main__":
185    # Heat demand profile for one year (8760 hours)
186    min_last = 50
187    max_last = 400
188    Last_L = np.random.randint(min_last, max_last, 8760).astype(float)
189
190    # Duration
191    duration = 1
192
193    # Arrays for VLT and RLT, assuming constant values for the example
194    VLT_L, RLT_L = np.full(8760, 80), np.full(8760, 55)
195
196    # Load COP data from CSV file
197    COP_data = np.genfromtxt("examples/data/COP/Kennlinien WP.csv", delimiter=';')
198
199    # Filename for TRY data
200    TRY_data = import_TRY("examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat")
201
202    # Time steps for the simulation
203    # Angaben zum zu betrchtende Zeitraum
204    # Erstelle ein Array mit stündlichen Zeitwerten für ein Jahr
205    start_date = np.datetime64('2019-01-01')
206    end_date = np.datetime64('2020-01-01', 'D')  # Enddatum ist exklusiv, daher 'D' für Tage
207
208    # Erstelle das Array mit stündlichen Zeitwerten für ein Jahr
209    time_steps = np.arange(start_date, end_date, dtype='datetime64[h]')
210
211
212    # Define economic parameters
213    electricity_price = 150 # €/MWh
214    gas_price = 70 # €/MWh
215    wood_price = 60 # €/MWh
216    q = 1.05
217    r = 1.03
218    T = 20
219    BEW = "Nein"
220    hourly_rate = 45
221
222    economic_parameters = {
223                "gas_price": gas_price,
224                "electricity_price": electricity_price,
225                "wood_price": wood_price,
226                "capital_interest_rate": q,
227                "inflation_rate": r,
228                "time_period": T,
229                "hourly_rate": hourly_rate,
230                "subsidy_eligibility": BEW
231            }
232    
233    # Test Gas Boiler
234    gBoiler = GasBoiler(name="Gas_Boiler_1", thermal_capacity_kW=1000, spez_Investitionskosten=30, Nutzungsgrad=0.9)
235    test_gas_boiler(Last_L, duration, economic_parameters, gBoiler)
236
237    # Test Power to Heat
238    PTH = PowerToHeat(name="PowerToHeat_1", thermal_capacity_kW=1000, spez_Investitionskosten=30, Nutzungsgrad=0.98)    
239    test_power_to_heat(Last_L, duration, economic_parameters, PTH)
240
241    # Test Biomass Boiler without storage
242    bBoiler = BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200, Größe_Holzlager=40, spez_Investitionskosten=200, 
243                                           spez_Investitionskosten_Holzlager=400, Nutzungsgrad_BMK=0.8, min_Teillast=0.3, speicher_aktiv=False, 
244                                           Speicher_Volumen=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, min_fill=0.2, max_fill=0.8, 
245                                           spez_Investitionskosten_Speicher=750, active=True, opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0, 
246                                           opt_Speicher_max=100)
247    test_biomass_boiler(Last_L, duration, economic_parameters, bBoiler)
248
249    # Test Biomass Boiler with storage
250    bBoiler_storage = BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200, Größe_Holzlager=40, spez_Investitionskosten=200, 
251                                                   spez_Investitionskosten_Holzlager=400, Nutzungsgrad_BMK=0.8, min_Teillast=0.3, speicher_aktiv=True, 
252                                                   Speicher_Volumen=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, min_fill=0.2, max_fill=0.8, 
253                                                   spez_Investitionskosten_Speicher=750, active=True, opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0, 
254                                                   opt_Speicher_max=100)
255    test_biomass_boiler_storage(Last_L, duration, economic_parameters, bBoiler_storage)
256
257    # Test CHP without storage
258    CHP_1 = CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33, 
259                  KWK_Wirkungsgrad=0.9, min_Teillast=0.7, speicher_aktiv=False, Speicher_Volumen_BHKW=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, 
260                  min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BHKW_min=0, opt_BHKW_max=1000, opt_BHKW_Speicher_min=0, 
261                  opt_BHKW_Speicher_max=100)    
262    test_chp(Last_L, duration, economic_parameters, CHP_1)
263
264    # Test CHP with storage
265    CHP_storage = CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33, 
266                          KWK_Wirkungsgrad=0.9, min_Teillast=0.7, speicher_aktiv=True, Speicher_Volumen_BHKW=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, 
267                          min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BHKW_min=0, opt_BHKW_max=1000, opt_BHKW_Speicher_min=0, 
268                          opt_BHKW_Speicher_max=100)
269    test_chp_storage(Last_L, duration, economic_parameters, CHP_storage)
270
271    # Test Waste Heat Pump
272    wasteHeatPump = WasteHeatPump(name="Abwärmepumpe", Kühlleistung_Abwärme=50, Temperatur_Abwärme=30, spez_Investitionskosten_Abwärme=500, 
273                                             spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)
274    test_waste_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, wasteHeatPump)
275
276    # Test River Heat Pump
277    riverHeatPump = RiverHeatPump(name="Flusswärmepumpe", Wärmeleistung_FW_WP=200, Temperatur_FW_WP=10, dT=0, spez_Investitionskosten_Flusswasser=1000, 
278                                             spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)
279    test_river_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, riverHeatPump) 
280
281    # Test Geothermal Heat Pump
282    geothermal_heat_pump = Geothermal(name="Geothermie", Fläche=200, Bohrtiefe=100, Temperatur_Geothermie=10, spez_Bohrkosten=100, spez_Entzugsleistung=50,
283                                                Vollbenutzungsstunden=2400, Abstand_Sonden=10, spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)
284    test_geothermal_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, geothermal_heat_pump)
285
286    # Test Solar Thermal
287    solarThermal = SolarThermal(name="Solarthermie", bruttofläche_STA=200, vs=20, Typ="Vakuumröhrenkollektor", kosten_speicher_spez=750, kosten_fk_spez=430, kosten_vrk_spez=590, 
288                                            Tsmax=90, Longitude=-14.4222, STD_Longitude=-15, Latitude=51.1676, East_West_collector_azimuth_angle=0, Collector_tilt_angle=36, Tm_rl=60, 
289                                            Qsa=0, Vorwärmung_K=8, DT_WT_Solar_K=5, DT_WT_Netz_K=5, opt_volume_min=0, opt_volume_max=200, opt_area_min=0, opt_area_max=2000)
290    test_solar_thermal(Last_L, duration, economic_parameters, VLT_L, RLT_L, TRY_data, time_steps, solarThermal)

Heat generator configuration and analysis.

Heat Generation Optimization

10_example_heat_generation_optimization.py
  1"""
  2Filename: 10_example_heat_generation_optimization.py
  3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
  4Date: 2025-07-12
  5Description: Example for the optimization of a heat generator mix
  6
  7Updated to match the latest version of all heat generator classes.
  8"""
  9
 10from districtheatingsim.heat_generators.solar_thermal import SolarThermal
 11from districtheatingsim.heat_generators.gas_boiler import GasBoiler
 12from districtheatingsim.heat_generators.biomass_boiler import BiomassBoiler
 13from districtheatingsim.heat_generators.chp import CHP
 14from districtheatingsim.heat_generators.waste_heat_pump import WasteHeatPump
 15from districtheatingsim.heat_generators.river_heat_pump import RiverHeatPump
 16from districtheatingsim.heat_generators.geothermal_heat_pump import Geothermal
 17from districtheatingsim.heat_generators.power_to_heat import PowerToHeat
 18from districtheatingsim.heat_generators.energy_system import EnergySystem
 19from districtheatingsim.utilities.test_reference_year import import_TRY
 20
 21import numpy as np
 22import matplotlib.pyplot as plt
 23
 24def test_berechnung_erzeugermix(optimize=False, plot=True, opt_method="SLSQP"):
 25    # Lastgang, z.B. in kW, muss derzeitig für korrekte wirtschaftliche Betrachtung Länge von 8760 haben
 26    min_last = 50
 27    max_last = 400
 28    Last_L = np.random.randint(min_last, max_last, 8760).astype(float)
 29
 30    # Load profile from csv file
 31    Last_L = np.genfromtxt("examples/data/Lastgang/Lastgang.csv", delimiter=';', skip_header=1)
 32    # 5 column
 33    Last_L = Last_L[:, 4]
 34    print(Last_L)
 35
 36    # Wirtschaftliche Randbedingungen
 37    electricity_price = 150 # €/MWh - Updated to match 09 example
 38    gas_price = 70 # €/MWh
 39    wood_price = 60 # €/MWh - Updated to match 09 example
 40
 41    q = 1.05
 42    r = 1.03
 43    T = 20
 44    BEW = "Nein"
 45    hourly_rate = 45
 46
 47    # Arrays für Vor- und Rücklauftemperatur
 48    VLT_L, RLT_L = np.full(8760, 80), np.full(8760, 55)
 49
 50    # Laden des COP-Kennfeldes
 51    COP_data = np.genfromtxt("examples/data/COP/Kennlinien WP.csv", delimiter=';')
 52
 53    # Dateiname Testreferenzjahr für Wetterdaten, Dateiname muss ggf. angepasst werden
 54    TRY_data = import_TRY("examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat")
 55
 56    # UPDATED: Technology definitions matching 09 example
 57    gBoiler = GasBoiler(
 58        name="Gas_Boiler_1", 
 59        thermal_capacity_kW=1000,  # Updated parameter name
 60        spez_Investitionskosten=30, 
 61        Nutzungsgrad=0.9
 62    )
 63
 64    PTH = PowerToHeat(
 65        name="PowerToHeat_1", 
 66        thermal_capacity_kW=1000,  # Updated parameter name
 67        spez_Investitionskosten=30, 
 68        Nutzungsgrad=0.98  # Updated efficiency
 69    )
 70
 71    bBoiler = BiomassBoiler(
 72        name="Biomass_Boiler_1", 
 73        thermal_capacity_kW=200, 
 74        Größe_Holzlager=40, 
 75        spez_Investitionskosten=200, 
 76        spez_Investitionskosten_Holzlager=400, 
 77        Nutzungsgrad_BMK=0.8, 
 78        min_Teillast=0.3, 
 79        speicher_aktiv=False, 
 80        Speicher_Volumen=20, 
 81        T_vorlauf=90, 
 82        T_ruecklauf=60, 
 83        initial_fill=0.0, 
 84        min_fill=0.2, 
 85        max_fill=0.8, 
 86        spez_Investitionskosten_Speicher=750, 
 87        active=True, 
 88        opt_BMK_min=0, 
 89        opt_BMK_max=1000, 
 90        opt_Speicher_min=0, 
 91        opt_Speicher_max=100
 92    )
 93
 94    bBoiler_storage = BiomassBoiler(
 95        name="Biomass_Boiler_2", 
 96        thermal_capacity_kW=200, 
 97        Größe_Holzlager=40, 
 98        spez_Investitionskosten=200, 
 99        spez_Investitionskosten_Holzlager=400, 
100        Nutzungsgrad_BMK=0.8, 
101        min_Teillast=0.3, 
102        speicher_aktiv=True,  # Updated: with storage
103        Speicher_Volumen=20, 
104        T_vorlauf=90, 
105        T_ruecklauf=60, 
106        initial_fill=0.0, 
107        min_fill=0.2, 
108        max_fill=0.8, 
109        spez_Investitionskosten_Speicher=750, 
110        active=True, 
111        opt_BMK_min=0, 
112        opt_BMK_max=1000, 
113        opt_Speicher_min=0, 
114        opt_Speicher_max=100
115    )
116
117    CHP_1 = CHP(
118        name="BHKW_1", 
119        th_Leistung_kW=100,  # Updated parameter name
120        spez_Investitionskosten_GBHKW=1500, 
121        spez_Investitionskosten_HBHKW=1850, 
122        el_Wirkungsgrad=0.33, 
123        KWK_Wirkungsgrad=0.9, 
124        min_Teillast=0.7, 
125        speicher_aktiv=False, 
126        Speicher_Volumen_BHKW=20, 
127        T_vorlauf=90, 
128        T_ruecklauf=60, 
129        initial_fill=0.0, 
130        min_fill=0.2, 
131        max_fill=0.8, 
132        spez_Investitionskosten_Speicher=750, 
133        active=True, 
134        opt_BHKW_min=0, 
135        opt_BHKW_max=1000, 
136        opt_BHKW_Speicher_min=0, 
137        opt_BHKW_Speicher_max=100
138    )
139
140    CHP_storage = CHP(
141        name="BHKW_2", 
142        th_Leistung_kW=100,  # Updated parameter name
143        spez_Investitionskosten_GBHKW=1500, 
144        spez_Investitionskosten_HBHKW=1850, 
145        el_Wirkungsgrad=0.33, 
146        KWK_Wirkungsgrad=0.9, 
147        min_Teillast=0.7, 
148        speicher_aktiv=True,  # Updated: with storage
149        Speicher_Volumen_BHKW=20, 
150        T_vorlauf=90, 
151        T_ruecklauf=60, 
152        initial_fill=0.0, 
153        min_fill=0.2, 
154        max_fill=0.8, 
155        spez_Investitionskosten_Speicher=750, 
156        active=True, 
157        opt_BHKW_min=0, 
158        opt_BHKW_max=1000, 
159        opt_BHKW_Speicher_min=0, 
160        opt_BHKW_Speicher_max=100
161    )
162
163    # UPDATED: Heat pump definitions matching 09 example
164    wasteHeatPump = WasteHeatPump(
165        name="Abwärmepumpe", 
166        Kühlleistung_Abwärme=50, 
167        Temperatur_Abwärme=30, 
168        spez_Investitionskosten_Abwärme=500, 
169        spezifische_Investitionskosten_WP=1000, 
170        min_Teillast=0.2
171    )
172
173    riverHeatPump = RiverHeatPump(
174        name="Flusswärmepumpe", 
175        Wärmeleistung_FW_WP=200, 
176        Temperatur_FW_WP=10, 
177        dT=0, 
178        spez_Investitionskosten_Flusswasser=1000, 
179        spezifische_Investitionskosten_WP=1000, 
180        min_Teillast=0.2
181    )
182
183    geothermal_heat_pump = Geothermal(
184        name="Geothermie", 
185        Fläche=200, 
186        Bohrtiefe=100, 
187        Temperatur_Geothermie=10, 
188        spez_Bohrkosten=100, 
189        spez_Entzugsleistung=50,
190        Vollbenutzungsstunden=2400, 
191        Abstand_Sonden=10, 
192        spezifische_Investitionskosten_WP=1000, 
193        min_Teillast=0.2
194    )
195
196    solarThermal = SolarThermal(
197        name="Solarthermie", 
198        bruttofläche_STA=200, 
199        vs=20, 
200        Typ="Vakuumröhrenkollektor", 
201        kosten_speicher_spez=750, 
202        kosten_fk_spez=430, 
203        kosten_vrk_spez=590, 
204        Tsmax=90, 
205        Longitude=-14.4222, 
206        STD_Longitude=-15, 
207        Latitude=51.1676, 
208        East_West_collector_azimuth_angle=0, 
209        Collector_tilt_angle=36, 
210        Tm_rl=60, 
211        Qsa=0, 
212        Vorwärmung_K=8, 
213        DT_WT_Solar_K=5, 
214        DT_WT_Netz_K=5, 
215        opt_volume_min=0, 
216        opt_volume_max=200, 
217        opt_area_min=0, 
218        opt_area_max=2000
219    )
220
221    # UPDATED: Technology selection - mix of different technologies
222    tech_objects = [
223        solarThermal,  # Solar thermal
224        CHP_1,      # CHP with storage
225        gBoiler,          # Gas boiler as backup
226    ]
227
228    # Time steps for simulation
229    start_date = np.datetime64('2019-01-01')
230    end_date = np.datetime64('2020-01-01', 'D')
231    time_steps = np.arange(start_date, end_date, dtype='datetime64[h]')
232
233    # UPDATED: Economic parameters matching 09 example
234    economic_parameters = {
235        "gas_price": gas_price,
236        "electricity_price": electricity_price,
237        "wood_price": wood_price,
238        "capital_interest_rate": q,
239        "inflation_rate": r,
240        "time_period": T,
241        "hourly_rate": hourly_rate,
242        "subsidy_eligibility": BEW
243    }
244
245    # Optimization weights
246    weights = {
247        "WGK_Gesamt": 1.0,                    # Focus on cost optimization
248        "specific_emissions_Gesamt": 0.1,     # Small weight for emissions
249        "primärenergiefaktor_Gesamt": 0.0     # No weight for primary energy
250    }
251
252    # Create energy system
253    energy_system = EnergySystem(
254        time_steps=time_steps,
255        load_profile=Last_L,
256        VLT_L=VLT_L,
257        RLT_L=RLT_L,
258        TRY_data=TRY_data,
259        COP_data=COP_data,
260        economic_parameters=economic_parameters,
261    )
262
263    # Add technologies to the system
264    for tech in tech_objects:
265        energy_system.add_technology(tech)
266
267    # Calculate the initial energy mix
268    system_results = energy_system.calculate_mix()
269    print("=== Initial Energy Mix Results ===")
270    print(f"Technologies: {system_results['techs']}")
271    print(f"Energy mix shares: {system_results['Anteile']}")
272    print(f"Heat generation costs: {system_results['WGK_Gesamt']:.2f} €/MWh")
273    print(f"Specific emissions: {system_results.get('specific_emissions_Gesamt', 'N/A'):.3f} tCO2/MWh")
274    print(f"Primary energy factor: {system_results.get('primärenergiefaktor_Gesamt', 'N/A'):.3f}")
275
276    if plot:
277        print("\nGenerating initial plots...")
278        fig1 = plt.figure(figsize=(12, 8))
279        energy_system.plot_stack_plot(fig1)
280        fig1.suptitle('Initial Energy Mix - Stack Plot')
281        
282        fig2 = plt.figure(figsize=(10, 8))
283        energy_system.plot_pie_chart(fig2)
284        fig2.suptitle('Initial Energy Mix - Pie Chart')
285
286    # Perform optimization if requested
287    if optimize:
288        print(f"\n=== Starting Optimization with {opt_method} ===")
289        
290        if opt_method == "SLSQP":
291            print("Optimizing mix with SLSQP...")
292            optimized_energy_system = energy_system.optimize_mix(weights, num_restarts=5)
293        else:
294            print(f"Unknown optimization method: {opt_method}")
295            return
296            
297        print("Optimization completed!")
298
299        # Calculate the optimized energy mix
300        optimized_system_results = optimized_energy_system.calculate_mix()
301        print("\n=== Optimized Energy Mix Results ===")
302        print(f"Technologies: {optimized_system_results['techs']}")
303        print(f"Energy mix shares: {optimized_system_results['Anteile']}")
304        print(f"Heat generation costs: {optimized_system_results['WGK_Gesamt']:.2f} €/MWh")
305        print(f"Specific emissions: {optimized_system_results.get('specific_emissions_Gesamt', 'N/A'):.3f} tCO2/MWh")
306        print(f"Primary energy factor: {optimized_system_results.get('primärenergiefaktor_Gesamt', 'N/A'):.3f}")
307
308        # Compare results
309        print("\n=== Optimization Comparison ===")
310        initial_cost = system_results['WGK_Gesamt']
311        optimized_cost = optimized_system_results['WGK_Gesamt']
312        cost_savings = initial_cost - optimized_cost
313        cost_savings_percent = (cost_savings / initial_cost) * 100
314
315        print(f"Initial costs: {initial_cost:.2f} €/MWh")
316        print(f"Optimized costs: {optimized_cost:.2f} €/MWh")
317        print(f"Cost savings: {cost_savings:.2f} €/MWh ({cost_savings_percent:.1f}%)")
318
319        if plot:
320            print("\nGenerating optimized plots...")
321            fig3 = plt.figure(figsize=(12, 8))
322            optimized_energy_system.plot_stack_plot(fig3)
323            fig3.suptitle('Optimized Energy Mix - Stack Plot')
324            
325            fig4 = plt.figure(figsize=(10, 8))
326            optimized_energy_system.plot_pie_chart(fig4)
327            fig4.suptitle('Optimized Energy Mix - Pie Chart')
328
329    print("\n=== Analysis Complete ===")
330    return energy_system
331
332if __name__ == "__main__":
333    print("Starting Heat Generation Optimization Example")
334    print("=" * 50)
335    
336    # Test with different optimization methods
337    print("\n1. Testing without optimization:")
338    energy_system_base = test_berechnung_erzeugermix(optimize=False, plot=True)
339    
340    print("\n2. Testing with SLSQP optimization:")
341    energy_system_slsqp = test_berechnung_erzeugermix(optimize=True, plot=True, opt_method="SLSQP")
342
343    print("\nShowing all plots...")
344    plt.show()

Heat generation system optimization example.

Economic Analysis Examples

Annuity Calculation

15_example_annuity.py
 1"""
 2Filename: 15_example_annuity.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2024-11-19
 5Description: Example for the calculation of the annuity of a heat generator
 6
 7"""
 8
 9from districtheatingsim.heat_generators import annuity
10
11# Test Annuitätsberechnung
12def test_annuität():
13    # Investition in €
14    A0 = 10000
15
16    # Technische Nutzungsdauer in Jahren
17    TN = 20
18
19    # Kostenfaktor Installationskosten als Anteil der Investitionskosten
20    f_Inst = 0.03
21
22    # Kostenfaktor Wartungs- und Instandhaltungskosten als Anteil der Investitionskosten
23    f_W_Insp = 0.02
24
25    # Bedienaufwand in Stunden (Stundensatz aktuell fix in der Funktion mit 45 €/h hinterlegt)
26    # muss unbedingt noch weg von der fixen Definition
27    Bedienaufwand = 10
28
29    # Kapitalzinsfaktor 
30    q = 1.05 # 5 %
31
32    # Preissteigerungsfaktor
33    r = 1.03 # 3 %
34
35    # Betrachtungszeitraum in Jahren
36    T = 20
37
38    # Energiebedarf / Brennstoffbedarf in z.B. kWh
39    Energiebedarf = 15000
40
41    # Energiekosten in z.B. €/kWh --> Einheit Energie muss mit der des Energiebedarfs übereinstimmen
42    Energiekosten = 0.15
43
44    # Erlöse, könnte z.B. Stromverkauf aus BHKW sein oder auch Wärmeverkauf, kann auf 0 gesetzt werden, dann nur Aussage über Höhe der Kosten
45    E1 = 0
46
47    A_N = annuity.annuity(A0, TN, f_Inst, f_W_Insp, Bedienaufwand, q, r, T, Energiebedarf, Energiekosten, E1)
48
49    print(f"Annuität: {A_N:.2f} €")
50
51if __name__ == "__main__":
52    test_annuität()

Economic evaluation and annuity calculation example.

Visualization Examples

Photovoltaics Integration

14_example_photovoltaics.py
 1"""
 2Filename: 14_example_photovoltaics.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2024-11-21
 5Description: 
 6
 7"""
 8
 9from districtheatingsim.heat_generators.photovoltaics import Calculate_PV
10
11if __name__ == '__main__':
12    # Define the input parameters for the photovoltaic calculation.
13    TRY_data = "examples\\data\\TRY\\TRY_511676144222\\TRY2015_511676144222_Jahr.dat"
14    Gross_area = 100 # m²
15    Longitude = 11.581981
16    STD_Longitude = 15
17    Latitude = 48.135125
18    Albedo = 0.2
19    East_West_collector_azimuth_angle = 90
20    Collector_tilt_angle = 30
21
22    # Calculate the photovoltaic power output.
23    Annual_PV_yield, Max_power, Power_output = Calculate_PV(TRY_data, Gross_area, Longitude, STD_Longitude, Latitude, Albedo,
24                                                            East_West_collector_azimuth_angle, Collector_tilt_angle)
25
26    print(f'Annual PV yield: {Annual_PV_yield} kWh')
27    print(f'Maximum power: {Max_power} kW')
28    print(f'Power output array: {Power_output} W')

Photovoltaic system integration example.

Annuity Calculation

15_example_annuity.py
 1"""
 2Filename: 15_example_annuity.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2024-11-19
 5Description: Example for the calculation of the annuity of a heat generator
 6
 7"""
 8
 9from districtheatingsim.heat_generators import annuity
10
11# Test Annuitätsberechnung
12def test_annuität():
13    # Investition in €
14    A0 = 10000
15
16    # Technische Nutzungsdauer in Jahren
17    TN = 20
18
19    # Kostenfaktor Installationskosten als Anteil der Investitionskosten
20    f_Inst = 0.03
21
22    # Kostenfaktor Wartungs- und Instandhaltungskosten als Anteil der Investitionskosten
23    f_W_Insp = 0.02
24
25    # Bedienaufwand in Stunden (Stundensatz aktuell fix in der Funktion mit 45 €/h hinterlegt)
26    # muss unbedingt noch weg von der fixen Definition
27    Bedienaufwand = 10
28
29    # Kapitalzinsfaktor 
30    q = 1.05 # 5 %
31
32    # Preissteigerungsfaktor
33    r = 1.03 # 3 %
34
35    # Betrachtungszeitraum in Jahren
36    T = 20
37
38    # Energiebedarf / Brennstoffbedarf in z.B. kWh
39    Energiebedarf = 15000
40
41    # Energiekosten in z.B. €/kWh --> Einheit Energie muss mit der des Energiebedarfs übereinstimmen
42    Energiekosten = 0.15
43
44    # Erlöse, könnte z.B. Stromverkauf aus BHKW sein oder auch Wärmeverkauf, kann auf 0 gesetzt werden, dann nur Aussage über Höhe der Kosten
45    E1 = 0
46
47    A_N = annuity.annuity(A0, TN, f_Inst, f_W_Insp, Bedienaufwand, q, r, T, Energiebedarf, Energiekosten, E1)
48
49    print(f"Annuität: {A_N:.2f} €")
50
51if __name__ == "__main__":
52    test_annuität()

Annuity and financial calculations example.

Interactive Plotting

16_interactive_matplotlib.py
 1"""
 2Filename: 16_interactive_matplotlib.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2024-11-21
 5Description: Not really an example, but a test for interactive matplotlib plots.
 6
 7"""
 8
 9import sys
10import numpy as np
11from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QCheckBox, QColorDialog)
12from matplotlib.figure import Figure
13from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
14
15class MainWindow(QMainWindow):
16    def __init__(self):
17        super().__init__()
18
19        self.central_widget = QWidget()
20        self.setCentralWidget(self.central_widget)
21
22        self.layout = QVBoxLayout(self.central_widget)
23
24        self.canvas = FigureCanvas(Figure(figsize=(8, 6)))
25        self.layout.addWidget(self.canvas)
26
27        self.ax = self.canvas.figure.add_subplot(111)
28
29        self.checkboxes_layout = QHBoxLayout()
30        self.layout.addLayout(self.checkboxes_layout)
31
32        self.checkboxes = {}
33        self.colors = {}
34        self.data = {
35            'Dataset 1': np.random.rand(10),
36            'Dataset 2': np.random.rand(10),
37            'Dataset 3': np.random.rand(10)
38        }
39
40        for label in self.data.keys():
41            checkbox = QCheckBox(label)
42            checkbox.setChecked(True)
43            checkbox.stateChanged.connect(self.update_plot)
44            self.checkboxes_layout.addWidget(checkbox)
45            self.checkboxes[label] = checkbox
46
47            color_button = QPushButton('Color')
48            color_button.clicked.connect(lambda _, l=label: self.change_color(l))
49            self.checkboxes_layout.addWidget(color_button)
50            self.colors[label] = 'blue'
51
52        self.update_plot()
53
54    def update_plot(self):
55        self.ax.clear()
56
57        for label, data in self.data.items():
58            if self.checkboxes[label].isChecked():
59                self.ax.plot(data, label=label, color=self.colors[label])
60
61        self.ax.legend()
62        self.canvas.draw()
63
64    def change_color(self, label):
65        color = QColorDialog.getColor()
66
67        if color.isValid():
68            self.colors[label] = color.name()
69            self.update_plot()
70
71if __name__ == '__main__':
72    app = QApplication(sys.argv)
73    window = MainWindow()
74    window.show()
75    sys.exit(app.exec())

Interactive plotting and visualization example.

Seasonal Storage

17_energy_system_seasonal_storage.py
  1import numpy as np
  2from districtheatingsim.heat_generators.energy_system import EnergySystem
  3from districtheatingsim.heat_generators.chp import CHP, CHPStrategy
  4from districtheatingsim.heat_generators.gas_boiler import GasBoiler, GasBoilerStrategy
  5from districtheatingsim.heat_generators.power_to_heat import PowerToHeat, PowerToHeatStrategy
  6from districtheatingsim.heat_generators.biomass_boiler import BiomassBoiler, BiomassBoilerStrategy
  7from districtheatingsim.heat_generators.river_heat_pump import RiverHeatPump
  8from districtheatingsim.heat_generators.waste_heat_pump import WasteHeatPump
  9from districtheatingsim.heat_generators.geothermal_heat_pump import Geothermal
 10from districtheatingsim.heat_generators.base_heat_pumps import HeatPumpStrategy
 11from districtheatingsim.heat_generators.solar_thermal import SolarThermal, SolarThermalStrategy
 12from districtheatingsim.heat_generators.STES import STES
 13
 14from districtheatingsim.utilities.test_reference_year import import_TRY
 15
 16from matplotlib import pyplot as plt
 17
 18import pandas as pd
 19import os
 20
 21# Testparameter
 22time_steps = pd.date_range(start="2023-01-01", end="2023-12-31 23:00:00", freq="h").to_numpy()
 23
 24file_path = os.path.join('examples', 'data', 'Lastgang', 'Lastgang.csv')
 25
 26df = pd.read_csv(file_path, delimiter=';', encoding='utf-8')
 27load_profile = df['Gesamtwärmebedarf_Gebäude_kW'].values
 28VLT_L = np.random.uniform(85, 85, 8760)
 29RLT_L = np.random.uniform(50, 50, 8760)
 30
 31TRY_filename = os.path.abspath('src/districtheatingsim/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat')
 32TRY_data = import_TRY(TRY_filename)
 33
 34COP_filename = os.path.abspath('src/districtheatingsim/data/COP/Kennlinien WP.csv')
 35COP_data = np.genfromtxt(COP_filename, delimiter=';')
 36
 37economic_parameters = {"gas_price": 50, "wood_price": 40, "electricity_price": 120, "capital_interest_rate": 0.05, 
 38                       "inflation_rate": 0.03, "time_period": 20, "subsidy_eligibility": "Nein", "hourly_rate": 45}  # Wirtschaftsparameter
 39
 40# Speicherparameter
 41storage_params = {
 42    "storage_type": "truncated_trapezoid",  # Speichergeometrie
 43    "dimensions": (20, 20, 50, 50, 15),  # Geometrieparameter
 44    "rho": 1000,  # Dichte des Mediums (kg/m³)
 45    "cp": 4180,  # Spezifische Wärmekapazität (J/kg*K)
 46    "T_ref": 10,  # Referenztemperatur (°C)
 47    "lambda_top": 0.04,  # Wärmeleitfähigkeit der oberen Isolierung (W/m*K)
 48    "lambda_side": 0.03,  # Wärmeleitfähigkeit der seitlichen Isolierung (W/m*K)
 49    "lambda_bottom": 0.05,  # Wärmeleitfähigkeit der unteren Isolierung (W/m*K)
 50    "lambda_soil": 1.5,  # Wärmeleitfähigkeit des Bodens (W/m*K)
 51    "dt_top": 0.3,  # Dicke der oberen Isolierung (m)
 52    "ds_side": 0.4,  # Dicke der seitlichen Isolierung (m)
 53    "db_bottom": 0.5,  # Dicke der unteren Isolierung (m)
 54    "T_amb": 10,  # Umgebungstemperatur (°C)
 55    "T_soil": 10,  # Bodentemperatur (°C)
 56    "T_max": 95,  # Maximale Speichertemperatur (°C)
 57    "T_min": 40,  # Minimale Speichertemperatur (°C)
 58    "initial_temp": 60,  # Anfangstemperatur des Speichers (°C)
 59    "hours": 8760,  # Anzahl der Stunden in einem Jahr
 60    "num_layers": 5,  # Anzahl der Schichten für die Schichtung
 61    "thermal_conductivity": 0.6  # Wärmeleitfähigkeit des Mediums (W/m*K)
 62}
 63
 64# Initialisiere das Energiesystem
 65energy_system = EnergySystem(time_steps, load_profile, VLT_L, RLT_L, TRY_data, COP_data, economic_parameters)
 66
 67# Initialisiere den Speicher
 68storage = STES(name="Saisonalspeicher", **storage_params)
 69energy_system.add_storage(storage)
 70
 71# Füge Generatoren hinzu
 72chp = CHP("BHKW_1", th_Leistung_kW=500)
 73energy_system.add_technology(chp)
 74chp.strategy = CHPStrategy(charge_on=75, charge_off=70)
 75
 76#pth = PowerToHeat("PtH_1", thermal_capacity_kW=500)
 77#energy_system.add_technology(pth)
 78#pth.strategy = PowerToHeatStrategy(charge_on=75)
 79
 80# Biomasssboiler hinzufügen
 81#biomass_boiler = BiomassBoiler("Biomassekessel_1", thermal_capacity_kW=500)
 82#energy_system.add_technology(biomass_boiler)
 83#biomass_boiler.strategy = BiomassBoilerStrategy(charge_on=75, charge_off=70)
 84
 85# Gasboiler hinzufügen
 86#gas_boiler = GasBoiler("Gasboiler_1", thermal_capacity_kW=1000)
 87#energy_system.add_technology(gas_boiler)
 88#gas_boiler.strategy = GasBoilerStrategy(charge_on=70)
 89
 90# Erneuerbare Energien hinzufügen
 91#river_heat_pump = RiverHeatPump("Flusswärmepumpe_1", Wärmeleistung_FW_WP=600, Temperatur_FW_WP=20)
 92#energy_system.add_technology(river_heat_pump)
 93#river_heat_pump.strategy = HeatPumpStrategy(charge_on=75, charge_off=70)
 94
 95#waste_heat_pump = WasteHeatPump("Abwärmepumpe_1", Kühlleistung_Abwärme=600, Temperatur_Abwärme=20)
 96#energy_system.add_technology(waste_heat_pump)
 97#waste_heat_pump.strategy = HeatPumpStrategy(charge_on=75, charge_off=70)
 98
 99#waste_water_heat_pump = WasteHeatPump("Abwasserwärmepumpe_1", Kühlleistung_Abwärme=200, Temperatur_Abwärme=20)
100#energy_system.add_technology(waste_water_heat_pump)
101#waste_water_heat_pump.strategy = HeatPumpStrategy(charge_on=75, charge_off=70)
102
103#geothermal_heat_pump = Geothermal("Geothermie_1", Fläche=10000, Bohrtiefe=100, Temperatur_Geothermie=20)
104#energy_system.add_technology(geothermal_heat_pump)
105#geothermal_heat_pump.strategy = HeatPumpStrategy(charge_on=75, charge_off=70)
106
107#solar_thermal = SolarThermal("Solarthermie_1", bruttofläche_STA=2500, vs=50, Typ="Vakuumröhrenkollektor")
108#energy_system.add_technology(solar_thermal)
109#solar_thermal.strategy = SolarThermalStrategy(charge_on=75, charge_off=70)
110
111
112# Berechne den Energiemix mit Speicher
113results = energy_system.calculate_mix()
114
115# Ergebnisse anzeigen
116print("Simulationsergebnisse:")
117print(f"Speicherwirkungsgrad: {results['storage_class'].efficiency*100:.2f}%")
118# print(f"Betriebskosten: {results['storage_class'].operational_costs:.2f} €")  # Not yet implemented in STES
119print(f"Überschüssige Wärme durch Stagnation: {results['storage_class'].excess_heat:.2f} kWh")
120print(f"Nicht gedeckter Bedarf aufgrund von Speicherentleerung: {results['storage_class'].unmet_demand:.2f} kWh")
121print(f"Stagnationsdauer: {results['storage_class'].stagnation_time} h")
122
123
124# Ergebnisse plotten
125results['storage_class'].plot_results(results["Wärmeleistung_L"][0], load_profile, VLT_L, RLT_L)
126energy_system.plot_stack_plot()
127energy_system.plot_pie_chart()
128plt.show()

Seasonal energy storage systems example.

Utility Examples

STANET to Pandapipes Conversion

18_stanet_to_pandapipes.py
 1"""
 2Filename: 18_stanet_to_pandapipes.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2025-05-21
 5Description: This script demonstrates how to use the `create_net_from_stanet_csv` function from the `feature_develop.stanet_import_pandapipes` module.
 6
 7"""
 8
 9from stanet_import_pandapipes import create_net_from_stanet_csv
10import pandapipes as pp
11
12if __name__ == "__main__":
13    # Example usage
14    stanet_csv_file_path= "examples/data/STANET/Example_STANET_ETRS89.CSV"
15    TRY_file_path = "examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
16    supply_temperature = 80  # Supply temperature in Celsius
17    flow_pressure_pump = 4.0  # Flow pressure of the pump in bar
18    lift_pressure_pump = 1.5  # Lift pressure of the pump in bar
19
20    net, yearly_time_steps, total_heat_W, max_heat_requirement_W = create_net_from_stanet_csv(stanet_csv_file_path, TRY_file_path, supply_temperature, flow_pressure_pump, lift_pressure_pump)

STANET to pandapipes conversion example.

Generator Schematic Test

19_generator_schematic_test_window.py
 1"""
 2Filename: generator_schematic_test_window.py
 3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
 4Date: 2024-09-27
 5Description: Test window for the schematic scene with buttons to add components.
 6"""
 7
 8import sys
 9
10from PyQt5.QtWidgets import QMainWindow, QVBoxLayout, QPushButton, QWidget, QHBoxLayout, QApplication
11from districtheatingsim.gui.EnergySystemTab._11_generator_schematic import SchematicScene, CustomGraphicsView
12
13class SchematicWindow(QMainWindow):
14    def __init__(self):
15        super().__init__()
16
17        # Set up main window
18        self.setWindowTitle('Complex Heat Generator Schematic')
19        self.setGeometry(100, 100, 1000, 1000)
20
21        self.centralWidget = QWidget()
22        self.setCentralWidget(self.centralWidget)
23
24        layout = QHBoxLayout(self.centralWidget)
25
26        # Instantiate SchematicScene (now decoupled from the window)
27        self.scene = SchematicScene(1000, 1000)
28        self.view = CustomGraphicsView(self.scene)  # Use custom view with zoom and pan functionality
29        layout.addWidget(self.view)
30
31        # Button panel for UI
32        button_layout = QVBoxLayout()
33        self.add_solar_button = QPushButton("Add Solar")
34        self.add_solar_storage_button = QPushButton("Add Solar + Storage")
35        self.add_chp_button = QPushButton("Add CHP")
36        self.add_chp_storage_button = QPushButton("Add CHP + Storage")
37        self.add_wood_chp_button = QPushButton("Add Wood-CHP")
38        self.add_wood_chp_storage_button = QPushButton("Add Wood-CHP + Storage")
39        self.add_biomass_boiler_button = QPushButton("Add Biomass Boiler")
40        self.add_biomass_boiler_storage_button = QPushButton("Add Biomass Boiler + Storage")
41        self.add_gas_boiler_button = QPushButton("Add Gas Boiler")
42        self.add_geothermal_hp_button = QPushButton("Add Geothermal Heat Pump")
43        self.add_river_hp_button = QPushButton("Add River Heat Pump")
44        self.add_waste_hp_button = QPushButton("Add Waste Heat Pump")
45        self.add_aqva_hp_button = QPushButton("Add Aqva Heat Pump")
46        self.delete_button = QPushButton("Delete Selected")
47        self.delete_all_button = QPushButton("Delete All")
48
49        # Add the buttons to the layout
50        button_layout.addWidget(self.add_solar_button)
51        button_layout.addWidget(self.add_solar_storage_button)
52        button_layout.addWidget(self.add_chp_button)
53        button_layout.addWidget(self.add_chp_storage_button)
54        button_layout.addWidget(self.add_wood_chp_button)
55        button_layout.addWidget(self.add_wood_chp_storage_button)
56        button_layout.addWidget(self.add_biomass_boiler_button)
57        button_layout.addWidget(self.add_biomass_boiler_storage_button)
58        button_layout.addWidget(self.add_gas_boiler_button)
59        button_layout.addWidget(self.add_geothermal_hp_button)
60        button_layout.addWidget(self.add_river_hp_button)
61        button_layout.addWidget(self.add_waste_hp_button)
62        button_layout.addWidget(self.add_aqva_hp_button)
63        button_layout.addWidget(self.delete_button)
64        button_layout.addWidget(self.delete_all_button)
65
66        layout.addLayout(button_layout)
67
68        # Button signals with new add_component logic
69        self.add_solar_button.clicked.connect(lambda: self.scene.add_component(item_name="Solar", storage=False))
70        self.add_solar_storage_button.clicked.connect(lambda: self.scene.add_component(item_name="Solar", storage=True))
71        self.add_chp_button.clicked.connect(lambda: self.scene.add_component(item_name="CHP", storage=False))
72        self.add_chp_storage_button.clicked.connect(lambda: self.scene.add_component(item_name="CHP", storage=True))
73        self.add_wood_chp_button.clicked.connect(lambda: self.scene.add_component(item_name="Wood-CHP", storage=False))
74        self.add_wood_chp_storage_button.clicked.connect(lambda: self.scene.add_component(item_name="Wood-CHP", storage=True))
75        self.add_biomass_boiler_button.clicked.connect(lambda: self.scene.add_component(item_name="Biomass Boiler", storage=False))
76        self.add_biomass_boiler_storage_button.clicked.connect(lambda: self.scene.add_component(item_name="Biomass Boiler", storage=True))
77        self.add_gas_boiler_button.clicked.connect(lambda: self.scene.add_component(item_name="Gas Boiler", storage=False))
78        self.add_geothermal_hp_button.clicked.connect(lambda: self.scene.add_component(item_name="Geothermal Heat Pump", storage=False))
79        self.add_river_hp_button.clicked.connect(lambda: self.scene.add_component(item_name="River Heat Pump", storage=False))
80        self.add_waste_hp_button.clicked.connect(lambda: self.scene.add_component(item_name="Waste Heat Pump", storage=False))
81        self.add_aqva_hp_button.clicked.connect(lambda: self.scene.add_component(item_name="Aqva Heat Pump", storage=False))
82        self.delete_button.clicked.connect(self.scene.delete_selected)
83        self.delete_all_button.clicked.connect(self.scene.delete_all)
84
85if __name__ == '__main__':
86    app = QApplication(sys.argv)
87    window = SchematicWindow()
88    window.show()
89    sys.exit(app.exec())

Generator schematic testing example.

Leaflet Map Visualization

20_leaflet_test.py
  1"""
  2Filename: PyQt5_Leaflet.py
  3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
  4Date: 2024-09-19
  5Description: Contains the LeafTab class for displaying a Leaflet map in a PyQt5 application.
  6"""
  7
  8import sys
  9import os
 10import json
 11import geopandas as gpd
 12import os
 13
 14from PyQt5.QtWidgets import QApplication, QVBoxLayout, QPushButton, QWidget, QFileDialog
 15from PyQt5.QtWebEngineWidgets import QWebEngineView
 16from PyQt5.QtCore import QUrl, QObject, pyqtSlot
 17from PyQt5.QtWebChannel import QWebChannel
 18
 19class GeoJsonReceiver(QObject):
 20    @pyqtSlot(str)
 21    def sendGeoJSONToPython(self, geojson_str):
 22        print("Received GeoJSON from JavaScript")
 23        
 24        # Konvertiere den JSON-String in ein Python-Objekt
 25        geojson_data = json.loads(geojson_str)
 26        
 27        # Erstelle ein GeoDataFrame aus dem GeoJSON
 28        gdf = gpd.GeoDataFrame.from_features(geojson_data['features'])
 29        
 30        # Setze das ursprüngliche CRS (EPSG:4326)
 31        gdf.set_crs(epsg=4326, inplace=True)
 32        
 33        # Konvertiere das CRS in das gewünschte Ziel-CRS (z.B. EPSG:25833)
 34        target_crs = 'EPSG:25833'
 35        gdf.to_crs(target_crs, inplace=True)
 36        
 37        # Speichere die Daten als GeoJSON
 38        output_file = 'exported_data.geojson'
 39        gdf.to_file(output_file, driver="GeoJSON")
 40        print(f"GeoJSON gespeichert in {output_file}")
 41
 42class LeafletTab(QWidget):
 43    def __init__(self):
 44        super().__init__()
 45
 46        self.setWindowTitle('Leaflet.draw in PyQt5')
 47        self.showMaximized()
 48
 49        # Erstelle eine QWebEngineView, um die HTML-Karte anzuzeigen
 50        self.web_view = QWebEngineView()
 51
 52        # Lade die HTML-Datei
 53        map_file_path = os.path.join(os.getcwd(), 'src\\districtheatingsim\\leaflet\\map.html')
 54        self.web_view.setUrl(QUrl.fromLocalFile(map_file_path))
 55
 56        # Erstelle den WebChannel und registriere das Python-Objekt
 57        self.channel = QWebChannel()
 58        self.receiver = GeoJsonReceiver()
 59        self.channel.registerObject('pywebchannel', self.receiver)
 60        self.web_view.page().setWebChannel(self.channel)
 61
 62        # Set up layout
 63        layout = QVBoxLayout()
 64        layout.addWidget(self.web_view)
 65        self.setLayout(layout)
 66
 67        # Add export button
 68        export_button = QPushButton('Export GeoJSON')
 69        export_button.clicked.connect(self.export_geojson)
 70        layout.addWidget(export_button)
 71
 72        # Add import button
 73        import_button = QPushButton('Import GeoJSON')
 74        import_button.clicked.connect(self.import_geojson)
 75        layout.addWidget(import_button)
 76
 77    def export_geojson(self):
 78        # Ruft die Funktion aus JavaScript auf
 79        self.web_view.page().runJavaScript("exportGeoJSON()")
 80
 81    def import_geojson(self):
 82        # Öffne ein Dialogfeld, um eine GeoJSON-Datei auszuwählen
 83        options = QFileDialog.Options()
 84        geojson_file, _ = QFileDialog.getOpenFileName(self, 'Open GeoJSON File', '', 'GeoJSON Files (*.geojson)', options=options)
 85
 86        # isolating the file name
 87        geojson_filename = geojson_file.split('/')[-1]
 88
 89        if geojson_file:
 90            # Lese den Inhalt der GeoJSON-Datei
 91            with open(geojson_file, 'r', encoding='utf-8') as f:
 92                geojson_data = json.load(f)
 93
 94            # Übergebe die GeoJSON-Daten an JavaScript
 95            geojson_str = json.dumps(geojson_data)
 96            # geojson_filename is passed to the JavaScript function, necessary for the function to work, transformed to a string
 97            self.web_view.page().runJavaScript(f"window.importGeoJSON({geojson_str}, '{geojson_filename}');")
 98
 99if __name__ == '__main__':
100    app = QApplication(sys.argv)
101    window = LeafletTab()
102    window.show()
103    sys.exit(app.exec())

Interactive map visualization with Leaflet example.

Running Examples

To run the examples, navigate to the project root directory and execute:

# Navigate to project root
cd DistrictHeatingSim

# Run a specific example
python examples/01_example_geocoding.py

# Run heat demand calculation example
python examples/03_example_simple_heat_requirement.py

# Run network generation example
python examples/05_example_net_generation.py

Note

Make sure you have installed DistrictHeatingSim in development mode before running examples:

pip install -e .

Tip

All example files are located in the examples/ directory of the project repository. Each example is self-contained and demonstrates specific functionality of DistrictHeatingSim.