Functionality examples
This section contains practical examples demonstrating the usage of DistrictHeatingSim.
Getting Started Examples
Geocoding Example
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
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 ("highway", "unclassified"),
36 ("highway", "track")
37 ]
38 element_type = "way"
39
40 query = build_query(city_name, tags, element_type)
41 geojson_data = download_data(query, element_type)
42 geojson_file_name = "examples\data\osm_street_data.geojson"
43
44 save_to_file(geojson_data, geojson_file_name)
45 print("Speichern der OSM-Straßendaten erfolgreich abgeschlossen.")
46
47### OSM-Download von Gebäudedaten ###
48def osm_building_query():
49 city_name = "Zittau"
50 tags = None
51 element_type = "building"
52
53 query = build_query(city_name, tags, element_type)
54 geojson_data = download_data(query, element_type)
55 geojson_file_name = "examples\data\osm_building_data.geojson"
56
57 save_to_file(geojson_data, geojson_file_name)
58 print("Speichern der OSM-Gebäudedaten erfolgreich abgeschlossen.")
59
60if __name__ == '__main__':
61 try:
62 print("Running the OSM data import script.")
63
64 osm_street_query()
65 osm_building_query()
66
67 except Exception as e:
68 print("An error occurred:")
69 print(traceback.format_exc())
70 raise e
This example shows how to import OpenStreetMap data in GeoJSON format.
Heat Demand Examples
Simple Heat Requirement
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 pyslpheat import bdew_calculate, vdi4655_calculate
23
24# Berechnung mit VDI 4655 (Referenzlastprofile)
25def VDI4655(TRY_filename):
26 YEU_heating_kWh = 20000
27 YEU_hot_water_kWh = 4000
28 YEU_electricity_kWh = 10000
29 building_type = "MFH"
30
31 # folgendes wird statisch gesetzt, muss in Zukunft noch in config-Datei ausgelagert werden oder im UI einstellbar sein
32 year = 2021 # Jahr, für das die Berechnung durchgeführt wird (für VDI 4655, BDEW)
33 holidays = np.array(["2021-01-01", "2021-04-02", "2021-04-05", "2021-05-01", "2021-05-24", "2021-05-13",
34 "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
35 climate_zone = "9" # Klimazone 9: Deutschland (VDI 4655)
36 number_people_household = 2 # Anzahl der Personen im Haushalt (VDI 4655)
37
38 df = vdi4655_calculate(
39 annual_heating_kWh=YEU_heating_kWh,
40 annual_dhw_kWh=YEU_hot_water_kWh,
41 annual_electricity_kWh=YEU_electricity_kWh,
42 building_type=building_type,
43 number_people_household=number_people_household,
44 year=year,
45 climate_zone=climate_zone,
46 TRY=TRY_filename,
47 holidays=holidays,
48 )
49 time_15min = df.index.values
50 total_heat_kW = df["Q_total_kWh"].values * 4
51 heating_kW = df["Q_heat_kWh"].values * 4
52 hot_water_kW = df["Q_dhw_kWh"].values * 4
53 electricity_kW = df["Q_electricity_kWh"].values * 4
54 temperature = df["temperature_C"].values
55
56 print("Ergebnisse VDI 4655")
57 print(f"Zeitschritte: {time_15min}")
58 print(f"Strombedarf: {electricity_kW}")
59 print(f"Wärmebedarf Heizung: {heating_kW}")
60 print(f"Wärmebedarf Warmwasser: {hot_water_kW}")
61 print(f"Wärmebedarf Gesamt: {total_heat_kW}")
62 print(f"Temperaturen: {temperature}")
63
64 # Plotting
65 fig, ax1 = plt.subplots(figsize=(10, 5))
66
67 ax2 = ax1.twinx()
68 ax1.plot(time_15min, total_heat_kW, 'g-', label="Gesamt", linewidth=0.5)
69 ax1.plot(time_15min, heating_kW, 'b-', label="Heizung", linewidth=0.5)
70 ax1.plot(time_15min, hot_water_kW, 'r-', label="Warmwasser", linewidth=0.5)
71 ax1.plot(time_15min, electricity_kW, 'm-', label="Strombedarf", linewidth=0.5)
72 ax2.plot(time_15min, temperature, 'k-', label="Außentemperatur", linewidth=0.5)
73
74 ax1.set_xlabel("Zeitschritte")
75 ax1.set_ylabel("Wärmebedarf (kW)")
76 ax2.set_ylabel("Temperatur (°C)")
77
78 ax1.legend(loc='upper left')
79 ax2.legend(loc='upper right')
80
81# Berechnung nach BDEW
82def BDEW(TRY_filename):
83 YEU_heating_kWh = 20000
84 building_type = "HMF"
85 subtype = "03"
86 real_ww_share = 0.3
87
88 year = 2021
89
90 df = bdew_calculate(
91 annual_heat_kWh=YEU_heating_kWh,
92 profile_type=building_type,
93 subtype=subtype,
94 TRY_file_path=TRY_filename,
95 year=year,
96 dhw_share=real_ww_share,
97 )
98 hourly_intervals = df.index.values
99 hourly_heat_demand_total_normed = df["Q_total_kWh"].values
100 hourly_heat_demand_heating_normed = df["Q_heat_kWh"].values
101 hourly_heat_demand_warmwater_normed = df["Q_dhw_kWh"].values
102 hourly_temperature = df["temperature_C"].values
103
104 print("Ergebnisse BDEW")
105 print(f"Zeitschritte: {hourly_intervals}")
106 print(f"Wärmebedarf Gesamt: {hourly_heat_demand_total_normed}")
107 print(f"Wärmebedarf Heizung: {hourly_heat_demand_heating_normed}")
108 print(f"Wärmebedarf Warmwasser: {hourly_heat_demand_warmwater_normed}")
109 print(f"Temperaturen: {hourly_temperature}")
110
111 # Plotting
112 fig, ax1 = plt.subplots(figsize=(10, 5))
113
114 ax2 = ax1.twinx()
115 ax1.plot(hourly_intervals, hourly_heat_demand_total_normed, 'g-', label="Gesamt", linewidth=0.5)
116 ax1.plot(hourly_intervals, hourly_heat_demand_heating_normed, 'b-', label="Heizung", linewidth=0.5)
117 ax1.plot(hourly_intervals, hourly_heat_demand_warmwater_normed, 'r-', label="Warmwasser", linewidth=0.5)
118 ax2.plot(hourly_intervals, hourly_temperature, 'k-', label="Außentemperatur", linewidth=0.5)
119
120 ax1.set_xlabel("Zeitschritte")
121 ax1.set_ylabel("Wärmebedarf (kW)")
122 ax2.set_ylabel("Temperatur (°C)")
123
124 ax1.legend(loc='upper left')
125 ax2.legend(loc='upper right')
126
127if __name__ == '__main__':
128 try:
129 print("Running the heat requirement script.")
130
131 TRY_filename = "src/districtheatingsim/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
132
133 VDI4655(TRY_filename)
134 BDEW(TRY_filename)
135
136 plt.show()
137
138 except Exception as e:
139 print("An error occurred:")
140 print(traceback.format_exc())
141 raise e
Basic heat demand calculation example.
Data-driven Heat Requirement
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
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
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="ISOPLUS_DRE100_2x",
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="P235GH/PUR/PEHD", k=k)
131 net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="P235GH/PUR/PEHD", 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="ISOPLUS_DRE100_2x",
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="P235GH/PUR/PEHD", k=k)
191 net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="P235GH/PUR/PEHD", 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="ISOPLUS_DRE100_2x",
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="P235GH/PUR/PEHD", k=k)
267 net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="P235GH/PUR/PEHD", 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
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="ISOPLUS_DRE100_2x",
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, alpha=0.6, transient=False)
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, alpha=0.6, transient=False)
86
87 net = correct_flow_directions(net)
88 net = init_diameter_types(net, v_max_pipe=v_max_m_s, material_filter="P235GH/PUR/PEHD", k=k)
89 net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="P235GH/PUR/PEHD", k=k)
90
91 return net
92
93
94def timeseries_test(net, transient=False):
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, transient=transient)
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, transient=False)
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
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 # File paths - the unified GeoJSON is read directly by initialize_geojson
66 # (no split into separate flow/return/consumer/producer files needed).
67 unified_path = "examples/data/osmnx_steiner_output/Wärmenetz.geojson"
68 json_path = "examples/data/Gebäude Lastgang.json"
69
70 # Optional external data files
71 TRY_filename = "examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
72 COP_filename = "examples/data/COP/Kennlinien WP.csv"
73
74 # Network configuration parameters
75 netconfiguration = "Niedertemperaturnetz" # or "kaltes Netz"
76 supply_temperature_control = "Statisch" # or "Gleitend"
77
78 # Temperature control parameters
79 max_supply_temperature_heat_generator = 85.0 # °C
80 min_supply_temperature_heat_generator = 70.0 # °C (for sliding control)
81 max_air_temperature_heat_generator = 15.0 # °C
82 min_air_temperature_heat_generator = -12.0 # °C
83
84 # Pump parameters
85 flow_pressure_pump = 4.0 # bar
86 lift_pressure_pump = 1.5 # bar
87
88 # Building temperature parameters
89 min_supply_temperature_building_checked = False
90 min_supply_temperature_building = None # 60.0 # °C
91 fixed_return_temperature_heat_consumer_checked = False
92 fixed_return_temperature_heat_consumer = None # 50.0 # °C
93 dT_RL = 5.0 # K - temperature difference between heat consumer and net due to heat exchanger
94 building_temperature_checked = False # flag indicating if building temperatures are considered
95
96 # Pipe parameters
97 pipetype = "ISOPLUS_DRE100_2x" # type of pipe
98 diameter_optimization_pipe_checked = True # flag indicating if diameter optimization is checked
99 max_velocity_pipe = 2.0 # m/s - maximum flow velocity in the pipes
100 material_filter_pipe = "P235GH/PUR/PEHD" # material filter for pipes
101 k_mm_pipe = 0.1 # mm - roughness of the pipe
102
103 # Producer configuration
104 main_producer_location_index = 0 # index of the main producer location
105 # This data set has a single producer; add SecondaryProducer(index=…) entries here
106 # only if the GeoJSON actually contains additional heat-producer locations.
107 secondary_producers = []
108
109 # Import type
110 import_type = "geoJSON" # currently only geoJSON
111
112 # Create NetworkGenerationData object with all required parameters
113 network_data = NetworkGenerationData(
114 # Required input data paths
115 import_type=import_type,
116 network_geojson_path=unified_path,
117 heat_demand_json_path=json_path,
118
119 # Network configuration data
120 netconfiguration=netconfiguration,
121 supply_temperature_control=supply_temperature_control,
122 max_supply_temperature_heat_generator=max_supply_temperature_heat_generator,
123 min_supply_temperature_heat_generator=min_supply_temperature_heat_generator,
124 max_air_temperature_heat_generator=max_air_temperature_heat_generator,
125 min_air_temperature_heat_generator=min_air_temperature_heat_generator,
126 flow_pressure_pump=flow_pressure_pump,
127 lift_pressure_pump=lift_pressure_pump,
128 min_supply_temperature_building_checked=min_supply_temperature_building_checked,
129 min_supply_temperature_building=min_supply_temperature_building,
130 fixed_return_temperature_heat_consumer_checked=fixed_return_temperature_heat_consumer_checked,
131 fixed_return_temperature_heat_consumer=fixed_return_temperature_heat_consumer,
132 dT_RL=dT_RL,
133 building_temperature_checked=building_temperature_checked,
134 pipetype=pipetype,
135
136 # Optimization variables
137 diameter_optimization_pipe_checked=diameter_optimization_pipe_checked,
138 max_velocity_pipe=max_velocity_pipe,
139 material_filter_pipe=material_filter_pipe,
140 k_mm_pipe=k_mm_pipe,
141
142 # Producer configuration
143 main_producer_location_index=main_producer_location_index,
144 secondary_producers=secondary_producers,
145
146 # Optional external data files
147 COP_filename=COP_filename,
148 TRY_filename=TRY_filename
149 )
150
151 # Initialize network from GeoJSON data
152 network_data = initialize_geojson(network_data)
153
154 # Optimize pipe diameters if requested
155 if network_data.diameter_optimization_pipe_checked:
156 network_data.net = optimize_diameter_types(
157 network_data.net,
158 network_data.max_velocity_pipe,
159 network_data.material_filter_pipe,
160 network_data.k_mm_pipe
161 )
162
163 # Set simulation time range
164 network_data.start_time_step = 0
165 network_data.end_time_step = 100
166
167 # Preprocess time series data
168 network_data = time_series_preprocessing(network_data)
169
170 # Run thermohydraulic time series simulation
171 network_data = thermohydraulic_time_series_net(network_data)
172
173 print("Simulation erfolgreich abgeschlossen.")
174
175 # Print network results
176 print_net_results(network_data.net)
177
178 # Calculate and display key performance indicators
179 results = network_data.calculate_results()
180 print("\nKennzahlen:")
181 for key, value in results.items():
182 if value is not None:
183 if isinstance(value, float):
184 print(f"{key}: {value:.2f}")
185 else:
186 print(f"{key}: {value}")
187 else:
188 print(f"{key}: Nicht verfügbar")
189
190 # Prepare plot data
191 network_data.prepare_plot_data()
192
193 # Plot results
194 plot(network_data.net,
195 network_data.yearly_time_steps[network_data.start_time_step:network_data.end_time_step],
196 network_data.waerme_ges_kW[network_data.start_time_step:network_data.end_time_step],
197 network_data.strombedarf_ges_kW[network_data.start_time_step:network_data.end_time_step])
198
199 # Show additional plots using prepared plot data
200 if network_data.plot_data:
201 print("\nVerfügbare Plot-Daten:")
202 for key in network_data.plot_data.keys():
203 print(f" - {key}")
204
205 # Example: Plot heat demand over time
206 heat_demand_data = network_data.plot_data["Gesamtwärmebedarf Wärmeübertrager"]
207 fig, ax = plt.subplots(figsize=(12, 6))
208 ax.plot(heat_demand_data['time'], heat_demand_data['data'], 'b-')
209 ax.set_xlabel('Zeit')
210 ax.set_ylabel(heat_demand_data['label'])
211 ax.set_title('Wärmebedarf über Zeit')
212 ax.grid(True)
213 plt.show()
214
215 plt.show()
Complex time series analysis example.
Energy Systems Examples
Heat Generators
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
8from districtheatingsim.heat_generators.solar_thermal import SolarThermal
9from districtheatingsim.heat_generators.gas_boiler import GasBoiler
10from districtheatingsim.heat_generators.biomass_boiler import BiomassBoiler
11from districtheatingsim.heat_generators.chp import CHP
12from districtheatingsim.heat_generators.waste_heat_pump import WasteHeatPump
13from districtheatingsim.heat_generators.river_heat_pump import RiverHeatPump
14from districtheatingsim.heat_generators.geothermal_heat_pump import Geothermal
15from districtheatingsim.heat_generators.power_to_heat import PowerToHeat
16from districtheatingsim.utilities.test_reference_year import import_TRY
17
18import numpy as np
19
20import matplotlib.pyplot as plt
21
22def test_gas_boiler(Last_L, duration, economic_parameters, gBoiler=GasBoiler(name="Gas_Boiler_1",
23 thermal_capacity_kW=200,
24 spez_Investitionskosten=30,
25 Nutzungsgrad=0.9)):
26 results = gBoiler.calculate(economic_parameters, duration, Last_L)
27
28 print(f"Wärmemenge Gaskessel: {results['Wärmemenge']:.2f} MWh")
29 print(f"Wärmeleistung Gaskessel: {results['Wärmeleistung_L']} kW")
30 print(f"Brennstoffbedarf Gaskessel: {results['Brennstoffbedarf']:.2f} MWh")
31 print(f"Wärmegestehungskosten Gaskessel: {results['WGK']:.2f} €/MWh")
32 print(f"spezifische CO2-Emissionen Gaskessel: {results['spec_co2_total']:.2f} tCO2/MWh")
33 print(f"Primärenergiebedarf Gaskessel: {results['primärenergie']:.2f} MWh")
34
35def test_power_to_heat(Last_L, duration, economic_parameters, PTH=PowerToHeat(name="PowerToHeat",
36 thermal_capacity_kW=1000,
37 spez_Investitionskosten=30,
38 Nutzungsgrad=0.98)):
39 results = PTH.calculate(economic_parameters, duration, Last_L)
40
41 print(f"Wärmemenge Power-to-Heat: {results['Wärmemenge']:.2f} MWh")
42 print(f"Wärmeleistung Power-to-Heat: {results['Wärmeleistung_L']} kW")
43 print(f"Strombedarf Power-to-Heat: {results['Strombedarf']:.2f} MWh")
44 print(f"Wärmegestehungskosten Power-to-Heat: {results['WGK']:.2f} €/MWh")
45 print(f"spezifische CO2-Emissionen Power-to-Heat: {results['spec_co2_total']:.2f} tCO2/MWh")
46 print(f"Primärenergiebedarf Power-to-Heat: {results['primärenergie']:.2f} MWh")
47
48def 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,
49 Nutzungsgrad_BMK=0.8, min_Teillast=0.3, speicher_aktiv=False, Speicher_Volumen=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0,
50 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,
51 opt_Speicher_max=100)):
52 results = bBoiler.calculate(economic_parameters, duartion, Last_L)
53
54 print(f"Wärmemenge Biomassekessel: {results['Wärmemenge']:.2f} MWh")
55 print(f"Wärmeleistung Biomassekessel: {results['Wärmeleistung_L']} kW")
56 print(f"Brennstoffbedarf Biomassekessel: {results['Brennstoffbedarf']:.2f} MWh")
57 print(f"Wärmegestehungskosten Biomassekessel: {results['WGK']:.2f} €/MWh")
58 print(f"spezifische CO2-Emissionen Biomassekessel: {results['spec_co2_total']:.2f} tCO2/MWh")
59 print(f"Primärenergiebedarf Biomassekessel: {results['primärenergie']:.2f} MWh")
60 print(f"Anzahl Starts Biomassekessel: {results['Anzahl_Starts']}")
61 print(f"Betriebsstunden Biomassekessel: {results['Betriebsstunden']} h")
62 print(f"Betriebsstunden pro Start Biomassekessel: {results['Betriebsstunden_pro_Start']:.2f} h")
63
64def test_biomass_boiler_storage(Last_L,
65 duration,
66 economic_parameters,
67 bBoiler=BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200,
68 Größe_Holzlager=40, spez_Investitionskosten=200,
69 spez_Investitionskosten_Holzlager=400,
70 Nutzungsgrad_BMK=0.8, min_Teillast=0.3,
71 speicher_aktiv=True, Speicher_Volumen=20,
72 T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0,
73 min_fill=0.2, max_fill=0.8,
74 spez_Investitionskosten_Speicher=750, active=True,
75 opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0,
76 opt_Speicher_max=100)):
77
78 results = bBoiler.calculate(economic_parameters, duration, Last_L)
79
80 print(f"Wärmemenge Biomassekessel mit Speicher: {results['Wärmemenge']:.2f} MWh")
81 print(f"Wärmeleistung Biomassekessel mit Speicher: {results['Wärmeleistung_L']} kW")
82 print(f"Wärmeleistung Speicher Biomassekessel: {results['Wärmeleistung_Speicher_L']} kW")
83 print(f"Brennstoffbedarf Biomassekessel mit Speicher: {results['Brennstoffbedarf']:.2f} MWh")
84 print(f"Wärmegestehungskosten Biomassekessel mit Speicher: {results['WGK']:.2f} €/MWh")
85 print(f"spezifische CO2-Emissionen Biomassekessel mit Speicher: {results['spec_co2_total']:.2f} tCO2/MWh")
86 print(f"Primärenergiebedarf Biomassekessel mit Speicher: {results['primärenergie']:.2f} MWh")
87 print(f"Anzahl Starts Biomassekessel mit Speicher: {results['Anzahl_Starts']}")
88 print(f"Betriebsstunden Biomassekessel mit Speicher: {results['Betriebsstunden']} h")
89 print(f"Betriebsstunden pro Start Biomassekessel mit Speicher: {results['Betriebsstunden_pro_Start']:.2f} h")
90
91def 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,
92 KWK_Wirkungsgrad=0.9, min_Teillast=0.7, speicher_aktiv=False, Speicher_Volumen_BHKW=20, T_vorlauf=90, T_ruecklauf=60,
93 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,
94 opt_BHKW_Speicher_min=0, opt_BHKW_Speicher_max=100)):
95
96 results = chp.calculate(economic_parameters, duration, Last_L)
97
98 print(f"Wärmemenge BHKW: {results['Wärmemenge']:.2f} MWh")
99 print(f"Wärmeleistung BHKW: {results['Wärmeleistung_L']} kW")
100 print(f"Brennstoffbedarf BHKW: {results['Brennstoffbedarf']:.2f} MWh")
101 print(f"Wärmegestehungskosten BHKW: {results['WGK']:.2f} €/MWh")
102 print(f"Strommenge BHKW: {results['Strommenge']:.2f} MWh")
103 print(f"Stromleistung BHKW: {results['el_Leistung_L']} kW")
104 print(f"Anzahl Starts BHKW: {results['Anzahl_Starts']}")
105 print(f"Betriebsstunden BHKW: {results['Betriebsstunden']} h")
106 print(f"Betriebsstunden pro Start BHKW: {results['Betriebsstunden_pro_Start']:.2f} h")
107 print(f"spezifische CO2-Emissionen BHKW: {results['spec_co2_total']:.2f} tCO2/MWh")
108 print(f"Primärenergiebedarf BHKW: {results['primärenergie']:.2f} MWh")
109
110def 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,
111 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,
112 min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BHKW_min=0, opt_BHKW_max=1000,
113 opt_BHKW_Speicher_min=0, opt_BHKW_Speicher_max=100)):
114
115 results = chp.calculate(economic_parameters, duration, Last_L)
116
117 print(f"Wärmemenge BHKW mit Speicher: {results['Wärmemenge']:.2f} MWh")
118 print(f"Wärmeleistung BHKW mit Speicher: {results['Wärmeleistung_L']} kW")
119 print(f"Wärmeleistung Speicher BHKW: {results['Wärmeleistung_Speicher_L']} kW")
120 print(f"Brennstoffbedarf BHKW mit Speicher: {results['Brennstoffbedarf']:.2f} MWh")
121 print(f"Wärmegestehungskosten BHKW mit Speicher: {results['WGK']:.2f} €/MWh")
122 print(f"Strommenge BHKW mit Speicher: {results['Strommenge']:.2f} MWh")
123 print(f"Stromleistung BHKW mit Speicher: {results['el_Leistung_L']} kW")
124 print(f"Anzahl Starts BHKW mit Speicher: {results['Anzahl_Starts']}")
125 print(f"Betriebsstunden BHKW mit Speicher: {results['Betriebsstunden']} h")
126 print(f"Betriebsstunden pro Start BHKW mit Speicher: {results['Betriebsstunden_pro_Start']:.2f} h")
127 print(f"spezifische CO2-Emissionen BHKW mit Speicher: {results['spec_co2_total']:.2f} tCO2/MWh")
128 print(f"Primärenergiebedarf BHKW mit Speicher: {results['primärenergie']:.2f} MWh")
129
130def 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)):
131
132 results = wasteHeatPump.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, COP_data=COP_data)
133
134 print(f"Wärmemenge Abwärme-WP: {results['Wärmemenge']:.2f} MWh")
135 print(f"Strombedarf Abwärme-WP: {results['Strombedarf']:.2f} MWh")
136 print(f"Wärmeleistung Abwärme-WP: {results['Wärmeleistung_L']} kW")
137 print(f"elektrische Leistung Abwärme-WP: {results['el_Leistung_L']} kW")
138 print(f"Wärmegestehungskosten Abwärme-WP: {results['WGK']:.2f} €/MWh")
139 print(f"spezifische CO2-Emissionen Abwärme-WP: {results['spec_co2_total']:.2f} tCO2/MWh")
140 print(f"Primärenergiebedarf Abwärme-WP: {results['primärenergie']:.2f} MWh")
141
142def 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)):
143
144 results = riverHeatPump.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, COP_data=COP_data)
145
146 print(f"Wärmemenge Fluss-WP: {results['Wärmemenge']:.2f} MWh")
147 print(f"Strombedarf Fluss-WP: {results['Strombedarf']:.2f} MWh")
148 print(f"Wärmeleistung Fluss-WP: {results['Wärmeleistung_L']} kW")
149 print(f"elektrische Leistung Fluss-WP: {results['el_Leistung_L']} kW")
150 print(f"Wärmegestehungskosten Fluss-WP: {results['WGK']:.2f} €/MWh")
151 print(f"spezifische CO2-Emissionen Fluss-WP: {results['spec_co2_total']:.2f} tCO2/MWh")
152 print(f"Primärenergiebedarf Fluss-WP: {results['primärenergie']:.2f} MWh")
153
154def 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)):
155
156 results = geothermalHeatPump.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, COP_data=COP_data)
157
158 print(f"Wärmemenge Geothermie-WP: {results['Wärmemenge']:.2f} MWh")
159 print(f"Strombedarf Geothermie-WP: {results['Strombedarf']:.2f} MWh")
160 print(f"Wärmeleistung Geothermie-WP: {results['Wärmeleistung_L']} kW")
161 print(f"elektrische Leistung Geothermie-WP: {results['el_Leistung_L']} kW")
162 print(f"Wärmegestehungskosten Geothermie-WP: {results['WGK']:.2f} €/MWh")
163 print(f"spezifische CO2-Emissionen Geothermie-WP: {results['spec_co2_total']:.2f} tCO2/MWh")
164 print(f"Primärenergiebedarf Geothermie-WP: {results['primärenergie']:.2f} MWh")
165
166def 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",
167 kosten_speicher_spez=750, kosten_fk_spez=430, kosten_vrk_spez=590, Tsmax=90,
168 Longitude=-14.4222, STD_Longitude=-15, Latitude=51.1676, East_West_collector_azimuth_angle=0,
169 Collector_tilt_angle=36, Tm_rl=60, Qsa=0, Vorwärmung_K=8, DT_WT_Solar_K=5, DT_WT_Netz_K=5,
170 opt_volume_min=0, opt_volume_max=200, opt_area_min=0, opt_area_max=2000)):
171
172 results = solarThermal.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, RLT_L=RLT_L, TRY_data=TRY_data, time_steps=time_steps)
173
174 print(f"Wärmemenge Solarthermie: {results['Wärmemenge']:.2f} MWh")
175 print(f"Wärmeleistung Solarthermie: {results['Wärmeleistung_L']} kW")
176 print(f"Wärmegestehungskosten Solarthermie: {results['WGK']:.2f} €/MWh")
177 print(f"spezifische CO2-Emissionen Solarthermie: {results['spec_co2_total']:.2f} tCO2/MWh")
178 print(f"Primärenergiebedarf Solarthermie: {results['primärenergie']:.2f} MWh")
179 print(f"Speicherladung Solarthermie: {results['Speicherladung_L']} kW")
180 print(f"Speicherfüllstand Solarthermie: {results['Speicherfüllstand_L']} MWh")
181
182if __name__ == "__main__":
183 # Heat demand profile for one year (8760 hours)
184 min_last = 50
185 max_last = 400
186 Last_L = np.random.randint(min_last, max_last, 8760).astype(float)
187
188 # Duration
189 duration = 1
190
191 # Arrays for VLT and RLT, assuming constant values for the example
192 VLT_L, RLT_L = np.full(8760, 80), np.full(8760, 55)
193
194 # Load COP data from CSV file
195 COP_data = np.genfromtxt("examples/data/COP/Kennlinien WP.csv", delimiter=';')
196
197 # Filename for TRY data
198 TRY_data = import_TRY("examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat")
199
200 # Time steps for the simulation
201 # Angaben zum zu betrchtende Zeitraum
202 # Erstelle ein Array mit stündlichen Zeitwerten für ein Jahr
203 start_date = np.datetime64('2019-01-01')
204 end_date = np.datetime64('2020-01-01', 'D') # Enddatum ist exklusiv, daher 'D' für Tage
205
206 # Erstelle das Array mit stündlichen Zeitwerten für ein Jahr
207 time_steps = np.arange(start_date, end_date, dtype='datetime64[h]')
208
209
210 # Define economic parameters
211 electricity_price = 150 # €/MWh
212 gas_price = 70 # €/MWh
213 wood_price = 60 # €/MWh
214 q = 1.05
215 r = 1.03
216 T = 20
217 BEW = "Nein"
218 hourly_rate = 45
219
220 economic_parameters = {
221 "gas_price": gas_price,
222 "electricity_price": electricity_price,
223 "wood_price": wood_price,
224 "capital_interest_rate": q,
225 "inflation_rate": r,
226 "time_period": T,
227 "hourly_rate": hourly_rate,
228 "subsidy_eligibility": BEW
229 }
230
231 # Test Gas Boiler
232 gBoiler = GasBoiler(name="Gas_Boiler_1", thermal_capacity_kW=1000, spez_Investitionskosten=30, Nutzungsgrad=0.9)
233 test_gas_boiler(Last_L, duration, economic_parameters, gBoiler)
234
235 # Test Power to Heat
236 PTH = PowerToHeat(name="PowerToHeat_1", thermal_capacity_kW=1000, spez_Investitionskosten=30, Nutzungsgrad=0.98)
237 test_power_to_heat(Last_L, duration, economic_parameters, PTH)
238
239 # Test Biomass Boiler without storage
240 bBoiler = BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200, Größe_Holzlager=40, spez_Investitionskosten=200,
241 spez_Investitionskosten_Holzlager=400, Nutzungsgrad_BMK=0.8, min_Teillast=0.3, speicher_aktiv=False,
242 Speicher_Volumen=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, min_fill=0.2, max_fill=0.8,
243 spez_Investitionskosten_Speicher=750, active=True, opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0,
244 opt_Speicher_max=100)
245 test_biomass_boiler(Last_L, duration, economic_parameters, bBoiler)
246
247 # Test Biomass Boiler with storage
248 bBoiler_storage = BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200, Größe_Holzlager=40, spez_Investitionskosten=200,
249 spez_Investitionskosten_Holzlager=400, Nutzungsgrad_BMK=0.8, min_Teillast=0.3, speicher_aktiv=True,
250 Speicher_Volumen=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, min_fill=0.2, max_fill=0.8,
251 spez_Investitionskosten_Speicher=750, active=True, opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0,
252 opt_Speicher_max=100)
253 test_biomass_boiler_storage(Last_L, duration, economic_parameters, bBoiler_storage)
254
255 # Test CHP without storage
256 CHP_1 = CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33,
257 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,
258 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,
259 opt_BHKW_Speicher_max=100)
260 test_chp(Last_L, duration, economic_parameters, CHP_1)
261
262 # Test CHP with storage
263 CHP_storage = CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33,
264 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,
265 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,
266 opt_BHKW_Speicher_max=100)
267 test_chp_storage(Last_L, duration, economic_parameters, CHP_storage)
268
269 # Test Waste Heat Pump
270 wasteHeatPump = WasteHeatPump(name="Abwärmepumpe", Kühlleistung_Abwärme=50, Temperatur_Abwärme=30, spez_Investitionskosten_Abwärme=500,
271 spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)
272 test_waste_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, wasteHeatPump)
273
274 # Test River Heat Pump
275 riverHeatPump = RiverHeatPump(name="Flusswärmepumpe", Wärmeleistung_FW_WP=200, Temperatur_FW_WP=10, dT=0, spez_Investitionskosten_Flusswasser=1000,
276 spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)
277 test_river_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, riverHeatPump)
278
279 # Test Geothermal Heat Pump
280 geothermal_heat_pump = Geothermal(name="Geothermie", Fläche=200, Bohrtiefe=100, Temperatur_Geothermie=10, spez_Bohrkosten=100, spez_Entzugsleistung=50,
281 Vollbenutzungsstunden=2400, Abstand_Sonden=10, spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)
282 test_geothermal_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, geothermal_heat_pump)
283
284 # Test Solar Thermal
285 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,
286 Tsmax=90, Longitude=-14.4222, STD_Longitude=-15, Latitude=51.1676, East_West_collector_azimuth_angle=0, Collector_tilt_angle=36, Tm_rl=60,
287 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)
288 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
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
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
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
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
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
1"""
2Example: Energy System with Seasonal Thermal Storage (1D model)
3================================================================
4
5Demonstrates a district heating energy system with a seasonal pit thermal
6energy storage (PTES) backed by ThermalStorage1D (via ThermalStorageAdapter).
7
8System design:
9 - CHP (600 kW thermal) as base-load generator. Summer average demand is
10 ~228 kW, so the CHP over-produces by ~372 kW that charges the storage.
11 - Gas boiler (2500 kW) as peak-load backup for winter demand peaks.
12 - PTES (15 000 m³, truncated-cone, ground loss model, 20 nodes):
13 absorbs CHP excess in summer and discharges it in winter.
14
15Storage interface (4 hydraulic ports via StorageInputs.two_port):
16 - Charge loop → hot generator supply (T_VL) enters at top;
17 storage return exits at bottom → T_bottom.
18 - Discharge loop → cold network return (T_RL) enters at bottom;
19 storage supply exits at top → T_top.
20
21The EnergySystem passes Q_in (generators) and Q_out (demand) to the storage.
22The adapter converts the net flow to mass flows:
23 - Q_charge = max(0, Q_in − Q_out) → enters storage top
24 - Q_discharge = max(0, Q_out − Q_in) → leaves storage top
25"""
26
27import numpy as np
28import pandas as pd
29import os
30import matplotlib.pyplot as plt
31
32from districtheatingsim.heat_generators.energy_system import EnergySystem
33from districtheatingsim.heat_generators.chp import CHP, CHPStrategy
34from districtheatingsim.heat_generators.gas_boiler import GasBoiler, GasBoilerStrategy
35from districtheatingsim.heat_generators.thermal_storage import ThermalStorageAdapter
36
37from districtheatingsim.utilities.test_reference_year import import_TRY
38
39# ── Simulation horizon ────────────────────────────────────────────────────────
40time_steps = pd.date_range(start="2023-01-01", end="2023-12-31 23:00:00", freq="h").to_numpy()
41
42# ── Load profile & temperatures ───────────────────────────────────────────────
43file_path = os.path.join('examples', 'data', 'Lastgang', 'Lastgang.csv')
44df = pd.read_csv(file_path, delimiter=';', encoding='utf-8')
45load_profile = df['Gesamtwärmebedarf_Gebäude_kW'].values
46# Mean ≈ 474 kW, peak ≈ 2081 kW, annual ≈ 4153 MWh, summer mean ≈ 228 kW
47
48VLT_L = np.full(8760, 85.0) # generator / network supply temperature [°C]
49RLT_L = np.full(8760, 50.0) # network return temperature [°C]
50
51# ── Climate data ──────────────────────────────────────────────────────────────
52TRY_filename = os.path.abspath(
53 'src/districtheatingsim/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat'
54)
55TRY_data = import_TRY(TRY_filename)
56
57COP_filename = os.path.abspath('src/districtheatingsim/data/COP/Kennlinien WP.csv')
58COP_data = np.genfromtxt(COP_filename, delimiter=';')
59
60# ── Economic parameters ───────────────────────────────────────────────────────
61economic_parameters = {
62 "gas_price": 50,
63 "wood_price": 40,
64 "electricity_price": 120,
65 "capital_interest_rate": 1.05, # factor = 1 + rate (5 %), per VDI 2067 annuity()
66 "inflation_rate": 1.03, # factor = 1 + rate (3 %)
67 "time_period": 20,
68 "subsidy_eligibility": "Nein",
69 "hourly_rate": 45,
70}
71
72# ── Seasonal pit thermal energy storage (PTES) ────────────────────────────────
73# 15 000 m³ truncated-cone PTES:
74# capacity ≈ 15 000 × 997 × 4200 × 50 / 3.6e9 ≈ 873 MWh
75# The CHP summer excess is ~372 kW × 4 400 h ≈ 1 637 MWh, so the storage
76# fills and empties about 1.9 times per year — a realistic seasonal cycle.
77#
78# Ground loss model: buried 2 m deep, mean ground surface temp 10 °C,
79# well-insulated lid (U_top = 0.15 W/m²K).
80# Implicit solver + TVD advection: unconditionally stable for dt = 1 h.
81storage = ThermalStorageAdapter(
82 name="Saisonaler Wärmespeicher",
83 volume=15_000.0, # m³
84 height=15.0, # m
85 geometry_type="truncated_cone",
86 n_nodes=20,
87 T_min=40.0, # °C – lower SOC bound (≈ network return temp)
88 T_max=90.0, # °C – upper SOC bound
89 initial_temp=55.0, # °C – start of year (partially charged)
90 loss_model_type="ground",
91 U_top=0.15, # W/m²K – insulated lid
92 T_ambient=10.0, # °C – mean ground surface temperature
93 z_ground=2.0, # m – burial depth
94 fluid_type="water", # temperature-dependent properties
95 solver="implicit",
96 advection_scheme="tvd",
97 buoyancy=True,
98 spez_Investitionskosten=40.0, # €/m³
99 hours=8760,
100 T_charge=90.0, # °C – fixed generator supply temperature (charge side)
101 T_discharge_return=50.0, # °C – fixed network return temperature (discharge side)
102)
103
104# ── Energy system ─────────────────────────────────────────────────────────────
105energy_system = EnergySystem(
106 time_steps, load_profile, VLT_L, RLT_L, TRY_data, COP_data, economic_parameters
107)
108energy_system.add_storage(storage)
109
110# CHP 600 kW (base-load) — strategy:
111# OFF → ON when upper storage temp ≤ 82 °C (storage not yet full)
112# ON → OFF when upper storage temp ≥ 85 °C (T_max reached)
113# In summer the CHP runs almost continuously and charges the storage.
114chp = CHP("BHKW_1", th_Leistung_kW=600)
115chp.strategy = CHPStrategy(charge_on=82, charge_off=85)
116energy_system.add_technology(chp)
117
118# Gas boiler 2500 kW — peak-load backup for winter demand spikes.
119gas_boiler = GasBoiler("Gaskessel_1", thermal_capacity_kW=2500)
120gas_boiler.strategy = GasBoilerStrategy(charge_on=70)
121energy_system.add_technology(gas_boiler)
122
123# ── Run simulation ────────────────────────────────────────────────────────────
124print("Running annual simulation …")
125results = energy_system.calculate_mix()
126
127# ── Print results ─────────────────────────────────────────────────────────────
128sto = results['storage_class']
129print("\n=== Seasonal Storage Results ===")
130print(f" Round-trip efficiency : {sto.efficiency * 100:.1f} %")
131print(f" Total heat loss : {np.sum(sto.Q_loss) / 1000:.0f} MWh")
132print(f" SOC min / max : {sto._soc.min() * 100:.1f} % / {sto._soc.max() * 100:.1f} %")
133print(f" SOC at end of year : {sto._soc[-1] * 100:.1f} %")
134print(f" T_top min / max : {sto._T_supply.min():.1f} / {sto._T_supply.max():.1f} °C")
135print(f" T_middle min / max : {sto._T_middle.min():.1f} / {sto._T_middle.max():.1f} °C")
136print(f" T_bottom min / max : {sto._T_return.min():.1f} / {sto._T_return.max():.1f} °C")
137
138print("\n=== Energy Mix ===")
139for tech, amt, share in zip(results['techs'], results['Wärmemengen'], results['Anteile']):
140 print(f" {tech:<30s}: {amt:8.0f} MWh ({share * 100:.1f} %)")
141
142# ── Plots ─────────────────────────────────────────────────────────────────────
143hours = np.arange(8760)
144months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
145 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
146month_starts = [0, 744, 1416, 2160, 2880, 3624,
147 4344, 5088, 5832, 6552, 7296, 8016]
148
149fig, axes = plt.subplots(4, 1, figsize=(13, 11), sharex=True)
150
151# Panel 1: Heat demand vs CHP generation
152ax = axes[0]
153ax.fill_between(hours, load_profile, alpha=0.3, color='gray', label='Heat demand (kW)')
154chp_profile = results['Wärmeleistung_L'][0] # CHP is first technology
155ax.plot(hours, chp_profile, color='orange', linewidth=0.7, label='CHP generation (kW)')
156ax.set_ylabel('Power (kW)')
157ax.legend(fontsize=8)
158ax.grid(True, alpha=0.3)
159
160# Panel 2: Net storage flow
161ax = axes[1]
162net_flow = sto._Q_net_storage_flow
163ax.fill_between(hours, net_flow, where=(net_flow > 0),
164 color='red', alpha=0.6, label='Discharge to network (kW)')
165ax.fill_between(hours, net_flow, where=(net_flow < 0),
166 color='royalblue', alpha=0.6, label='Charge from generators (kW)')
167ax.axhline(0, color='black', linewidth=0.5)
168ax.set_ylabel('Net storage flow (kW)')
169ax.legend(fontsize=8)
170ax.grid(True, alpha=0.3)
171
172# Panel 3: State of charge
173ax = axes[2]
174ax.fill_between(hours, sto._soc * 100, alpha=0.5, color='steelblue')
175ax.plot(hours, sto._soc * 100, color='steelblue', linewidth=0.8, label='SOC (%)')
176ax.set_ylabel('SOC (%)')
177ax.set_ylim(0, 100)
178ax.legend(fontsize=8)
179ax.grid(True, alpha=0.3)
180
181# Panel 4: Storage temperatures (top / middle / bottom)
182# T_middle is used by the generator control strategy (charge_off threshold).
183ax = axes[3]
184ax.plot(hours, sto._T_supply, color='red', linewidth=0.8, label='T_top (°C)')
185ax.plot(hours, sto._T_middle, color='orange', linewidth=0.8,
186 label='T_middle – strategy (°C)')
187ax.plot(hours, sto._T_return, color='royalblue', linewidth=0.8, label='T_bottom (°C)')
188ax.set_ylabel('Temperature (°C)')
189ax.set_xlabel('Hour of year')
190ax.legend(fontsize=8)
191ax.grid(True, alpha=0.3)
192
193# Month labels on x-axis
194for ax in axes:
195 ax.set_xticks(month_starts)
196 ax.set_xticklabels(months)
197
198fig.suptitle(
199 'Seasonal PTES – 15 000 m³ | CHP 600 kW + Gas Boiler 2 500 kW | 1D stratified model'
200)
201plt.tight_layout()
202
203energy_system.plot_stack_plot()
204energy_system.plot_pie_chart()
205plt.show()
Seasonal energy storage systems example.
Utility Examples
STANET to Pandapipes Conversion
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
11import matplotlib.pyplot as plt
12import numpy as np
13
14
15def plot_net_3d(net) -> None:
16 """3D network plot: X/Y = geographic coordinates, Z = elevation (GEOH, mNN)."""
17 geo = net.junction_geodata
18 z_arr = geo["z"].fillna(0.0).values
19
20 fig = plt.figure(figsize=(14, 8))
21 ax = fig.add_subplot(111, projection="3d")
22
23 cmap = plt.cm.terrain
24 v_min, v_max = z_arr.min(), z_arr.max()
25
26 # --- Pipes as 3D polylines, Z interpolated linearly between junctions ---
27 for _, pipe_row in net.pipe.iterrows():
28 fj = pipe_row["from_junction"]
29 tj = pipe_row["to_junction"]
30 if fj not in geo.index or tj not in geo.index:
31 continue
32
33 if pipe_row.name in net.pipe_geodata.index:
34 coords = net.pipe_geodata.at[pipe_row.name, "coords"]
35 xs = [c[0] for c in coords]
36 ys = [c[1] for c in coords]
37 else:
38 xs = [geo.at[fj, "x"], geo.at[tj, "x"]]
39 ys = [geo.at[fj, "y"], geo.at[tj, "y"]]
40
41 z_from = float(geo.at[fj, "z"])
42 z_to = float(geo.at[tj, "z"])
43 zs = np.linspace(z_from, z_to, len(xs))
44
45 ax.plot(xs, ys, zs, color="steelblue", linewidth=1.0, alpha=0.7)
46
47 # --- Junctions coloured by elevation ---
48 sc = ax.scatter(
49 geo["x"], geo["y"], z_arr,
50 c=z_arr, cmap=cmap, s=25, zorder=5,
51 vmin=v_min, vmax=v_max,
52 )
53 fig.colorbar(sc, ax=ax, shrink=0.5, pad=0.1, label="Elevation (mNN)")
54
55 ax.set_xlabel("Easting (m)")
56 ax.set_ylabel("Northing (m)")
57 ax.set_zlabel("Elevation (mNN)")
58 ax.set_title("District heating network — elevation profile")
59 plt.tight_layout()
60 plt.show()
61
62
63if __name__ == "__main__":
64 # Example usage
65 stanet_csv_file_path = "examples/data/STANET/Example_STANET_ETRS89.CSV"
66 TRY_file_path = "examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
67 supply_temperature = 80 # Supply temperature in Celsius
68 flow_pressure_pump = 4.0 # Flow pressure of the pump in bar
69 lift_pressure_pump = 1.5 # Lift pressure of the pump in bar
70
71 net, yearly_time_steps, total_heat_W, max_heat_requirement_W, crs_epsg = \
72 create_net_from_stanet_csv(stanet_csv_file_path, TRY_file_path,
73 supply_temperature, flow_pressure_pump, lift_pressure_pump)
74 print(f"CRS: {crs_epsg}")
75
76 if net is not None:
77 plot_net_3d(net)
STANET to pandapipes conversion example.
Generator Schematic Test
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
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.