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 ]
36 element_type = "way"
37
38 query = build_query(city_name, tags, element_type)
39 geojson_data = download_data(query, element_type)
40 geojson_file_name = "examples\data\osm_street_data.geojson"
41
42 save_to_file(geojson_data, geojson_file_name)
43 print("Speichern der OSM-Straßendaten erfolgreich abgeschlossen.")
44
45### OSM-Download von Gebäudedaten ###
46def osm_building_query():
47 city_name = "Zittau"
48 tags = None
49 element_type = "building"
50
51 query = build_query(city_name, tags, element_type)
52 geojson_data = download_data(query, element_type)
53 geojson_file_name = "examples\data\osm_building_data.geojson"
54
55 save_to_file(geojson_data, geojson_file_name)
56 print("Speichern der OSM-Gebäudedaten erfolgreich abgeschlossen.")
57
58if __name__ == '__main__':
59 try:
60 print("Running the OSM data import script.")
61
62 osm_street_query()
63 osm_building_query()
64
65 except Exception as e:
66 print("An error occurred:")
67 print(traceback.format_exc())
68 raise e
This example shows how to import OpenStreetMap data in GeoJSON format.
Heat Demand Examples
Simple Heat Requirement
1"""
2Filename: 03_example_heat_requirement.py
3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
4Date: 2024-11-19
5Description: Contains the heat requirement functions necessary to calculate the heat requirement of a building.
6Usage:
7 Run this script directly to calculate the heat requirement of a building.
8Functions:
9 VDI4655() -> None
10 Calculates the heat requirement according to VDI 4655.
11 BDEW() -> None
12 Calculates the heat requirement according to BDEW.
13Example:
14 $ python simple_heat_requirement_test.py
15
16"""
17
18import traceback
19import numpy as np
20import matplotlib.pyplot as plt
21
22from districtheatingsim.heat_requirement import heat_requirement_BDEW
23from districtheatingsim.heat_requirement import heat_requirement_VDI4655
24
25# Berechnung mit VDI 4655 (Referenzlastprofile)
26def VDI4655(TRY_filename):
27 YEU_heating_kWh = 20000
28 YEU_hot_water_kWh = 4000
29 YEU_electricity_kWh = 10000
30 building_type = "MFH"
31
32 # folgendes wird statisch gesetzt, muss in Zukunft noch in config-Datei ausgelagert werden oder im UI einstellbar sein
33 year = 2021 # Jahr, für das die Berechnung durchgeführt wird (für VDI 4655, BDEW)
34 holidays = np.array(["2021-01-01", "2021-04-02", "2021-04-05", "2021-05-01", "2021-05-24", "2021-05-13",
35 "2021-06-03", "2021-10-03", "2021-11-01", "2021-12-25", "2021-12-26"]).astype('datetime64[D]') # Feiertage in Deutschland 2021 (ohne Wochenenden) als datetime64[D]-Array (YYYY-MM-DD) für VDI 4655
36 climate_zone = "9" # Klimazone 9: Deutschland (VDI 4655)
37 number_people_household = 2 # Anzahl der Personen im Haushalt (VDI 4655)
38
39 time_15min, total_heat_kW, heating_kW, hot_water_kW, temperature, electricity_kW = heat_requirement_VDI4655.calculate(YEU_heating_kWh, YEU_hot_water_kWh, YEU_electricity_kWh, building_type, number_people_household, year, climate_zone, TRY_filename, holidays)
40
41 print("Ergebnisse VDI 4655")
42 print(f"Zeitschritte: {time_15min}")
43 print(f"Strombedarf: {electricity_kW}")
44 print(f"Wärmebedarf Heizung: {heating_kW}")
45 print(f"Wärmebedarf Warmwasser: {hot_water_kW}")
46 print(f"Wärmebedarf Gesamt: {total_heat_kW}")
47 print(f"Temperaturen: {temperature}")
48
49 # Plotting
50 fig, ax1 = plt.subplots(figsize=(10, 5))
51
52 ax2 = ax1.twinx()
53 ax1.plot(time_15min, total_heat_kW, 'g-', label="Gesamt", linewidth=0.5)
54 ax1.plot(time_15min, heating_kW, 'b-', label="Heizung", linewidth=0.5)
55 ax1.plot(time_15min, hot_water_kW, 'r-', label="Warmwasser", linewidth=0.5)
56 ax1.plot(time_15min, electricity_kW, 'm-', label="Strombedarf", linewidth=0.5)
57
58 # temperature is hourly, so we need to repeat the values for the 15min intervals
59 temperature = np.repeat(temperature, 4)
60 ax2.plot(time_15min, temperature, 'k-', label="Außentemperatur", linewidth=0.5)
61
62 ax1.set_xlabel("Zeitschritte")
63 ax1.set_ylabel("Wärmebedarf (kW)")
64 ax2.set_ylabel("Temperatur (°C)")
65
66 ax1.legend(loc='upper left')
67 ax2.legend(loc='upper right')
68
69# Berechnung nach BDEW
70def BDEW(TRY_filename):
71 YEU_heating_kWh = 20000
72 building_type = "HMF"
73 subtype = "03"
74 real_ww_share = 0.3
75
76 year = 2021
77
78 hourly_intervals, hourly_heat_demand_total_normed, hourly_heat_demand_heating_normed, hourly_heat_demand_warmwater_normed, hourly_temperature = heat_requirement_BDEW.calculate(YEU_heating_kWh, building_type, subtype, TRY_filename, year, real_ww_share)
79
80 print("Ergebnisse BDEW")
81 print(f"Zeitschritte: {hourly_intervals}")
82 print(f"Wärmebedarf Gesamt: {hourly_heat_demand_total_normed}")
83 print(f"Wärmebedarf Heizung: {hourly_heat_demand_heating_normed}")
84 print(f"Wärmebedarf Warmwasser: {hourly_heat_demand_warmwater_normed}")
85 print(f"Temperaturen: {hourly_temperature}")
86
87 # Plotting
88 fig, ax1 = plt.subplots(figsize=(10, 5))
89
90 ax2 = ax1.twinx()
91 ax1.plot(hourly_intervals, hourly_heat_demand_total_normed, 'g-', label="Gesamt", linewidth=0.5)
92 ax1.plot(hourly_intervals, hourly_heat_demand_heating_normed, 'b-', label="Heizung", linewidth=0.5)
93 ax1.plot(hourly_intervals, hourly_heat_demand_warmwater_normed, 'r-', label="Warmwasser", linewidth=0.5)
94 ax2.plot(hourly_intervals, hourly_temperature, 'k-', label="Außentemperatur", linewidth=0.5)
95
96 ax1.set_xlabel("Zeitschritte")
97 ax1.set_ylabel("Wärmebedarf (kW)")
98 ax2.set_ylabel("Temperatur (°C)")
99
100 ax1.legend(loc='upper left')
101 ax2.legend(loc='upper right')
102
103if __name__ == '__main__':
104 try:
105 print("Running the heat requirement script.")
106
107 TRY_filename = "src/districtheatingsim/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
108
109 VDI4655(TRY_filename)
110 BDEW(TRY_filename)
111
112 plt.show()
113
114 except Exception as e:
115 print("An error occurred:")
116 print(traceback.format_exc())
117 raise e
Basic heat demand calculation example.
Data-driven Heat Requirement
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="KMR 100/250-2v",
80 v_max_m_s=1.5):
81 print("Running the test network initialization script.")
82 net = pp.create_empty_network(fluid="water")
83
84 k = 0.1 # roughness defaults to 0.1
85
86 suply_temperature_k = supply_temperature + 273.15
87 return_temperature_k = return_temperature + 273.15
88
89 # Junctions for pump
90 j1 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 1", geodata=(0, 10))
91 j2 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 2", geodata=(0, 0))
92
93 # Junctions for connection pipes forward line
94 j3 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 3", geodata=(10, 0))
95 j4 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 4", geodata=(60, 0))
96
97 # Junctions for heat exchangers
98 j5 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 5", geodata=(85, 0))
99 j6 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 6", geodata=(85, 10))
100
101 # Junctions for connection pipes return line
102 j7 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 7", geodata=(60, 10))
103 j8 = pp.create_junction(net, pn_bar=1.05, tfluid_k=suply_temperature_k, name="Junction 8", geodata=(10, 10))
104
105 pump1 = pp.create_circ_pump_const_pressure(net, j1, j2, p_flow_bar=flow_pressure_pump, plift_bar=lift_pressure_pump,
106 t_flow_k=suply_temperature_k, type="auto", name="pump1")
107
108 pipe1 = pp.create_pipe(net, j2, j3, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe1", sections=5, text_k=283)
109 pipe2 = pp.create_pipe(net, j3, j4, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe2", sections=5, text_k=283)
110 pipe3 = pp.create_pipe(net, j4, j5, std_type=pipetype, length_km=0.025,k_mm=k, name="pipe3", sections=5, text_k=283)
111
112 heat_cosnumer1 = pp.create_heat_consumer(net, from_junction=j5, to_junction=j6, loss_coefficient=0, qext_w=qext_w[0],
113 treturn_k=return_temperature_k[0], name="heat_consumer_1") # treturn_k=t when implemented in function
114
115
116 heat_cosnumer2 = pp.create_heat_consumer(net, from_junction=j4, to_junction=j7, loss_coefficient=0, qext_w=qext_w[1],
117 treturn_k=return_temperature_k[1], name="heat_consumer_2") # treturn_k=t when implemented in function
118
119 pipe4 = pp.create_pipe(net, j6, j7, std_type=pipetype, length_km=0.25, k_mm=k, name="pipe4", sections=5, text_k=283)
120 pipe5 = pp.create_pipe(net, j7, j8, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe5", sections=5, text_k=283)
121 pipe6 = pp.create_pipe(net, j8, j1, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe6", sections=5, text_k=283)
122
123 pp.pipeflow(net, mode="bidirectional", iter=100)
124
125 net = create_controllers(net, qext_w, supply_temperature, None, return_temperature, None)
126
127 run_control(net, mode="bidirectional", iter=100)
128
129 net = correct_flow_directions(net)
130 net = init_diameter_types(net, v_max_pipe=v_max_m_s, material_filter="KMR", k=k)
131 net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="KMR", k=k)
132
133 return net
134
135
136def initialize_complex_test_net(qext_w=np.array([20000, 15000, 10000, 25000]),
137 return_temperature=np.array([55, 60, 50, 65]),
138 supply_temperature=85,
139 flow_pressure_pump=4,
140 lift_pressure_pump=1.5,
141 pipetype="KMR 100/250-2v",
142 v_max_m_s=1.5):
143 print("Running the complex test network initialization script.")
144 net = pp.create_empty_network(fluid="water")
145
146 k = 0.1 # roughness
147 supply_temperature_k = supply_temperature + 273.15 # convert to Kelvin
148 return_temperature_k = return_temperature + 273.15 # convert to Kelvin
149
150 # Define junctions
151 j1 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Pump Supply", geodata=(0, 0))
152 j2 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Main Split Supply", geodata=(10, 0))
153 j12 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Main Split Return", geodata=(10, 10))
154 j13 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Pump Return", geodata=(0, 10))
155
156 # Additional junctions for new branches
157 j3 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer B Supply", geodata=(20, 0))
158 j4 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer B Return", geodata=(20, 10))
159 j5 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer C Supply", geodata=(30, 0))
160 j6 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer C Return", geodata=(30, 10))
161
162 # Pump
163 pp.create_circ_pump_const_pressure(net, j13, j1, p_flow_bar=flow_pressure_pump, plift_bar=lift_pressure_pump,
164 t_flow_k=supply_temperature_k, type="auto", name="Main Pump")
165
166 # Pipes for supply line
167 pp.create_pipe(net, j1, j2, std_type=pipetype, length_km=0.02, k_mm=k, name="Main Pipe Supply")
168 pp.create_pipe(net, j2, j3, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch B Pipe Supply")
169 pp.create_pipe(net, j3, j5, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch C Pipe Supply")
170
171 # Pipes for return line
172 pp.create_pipe(net, j6, j4, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch C Pipe Return")
173 pp.create_pipe(net, j4, j12, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch B Pipe Return")
174 pp.create_pipe(net, j12, j13, std_type=pipetype, length_km=0.02, k_mm=k, name="Main Pipe Return")
175
176 # Heat consumers
177 pp.create_heat_consumer(net, from_junction=j2, to_junction=j12, qext_w=qext_w[0], treturn_k=return_temperature_k[0], name="Consumer A")
178 pp.create_heat_consumer(net, from_junction=j3, to_junction=j4, qext_w=qext_w[1], treturn_k=return_temperature_k[1], name="Consumer B")
179 pp.create_heat_consumer(net, from_junction=j5, to_junction=j6, qext_w=qext_w[2], treturn_k=return_temperature_k[2], name="Consumer C")
180
181 # Simulate pipe flow
182 pp.pipeflow(net, mode="bidirectional", iter=100, alpha=0.6)
183
184 # Placeholder functions for additional processing
185 net = create_controllers(net, qext_w, supply_temperature, None, return_temperature, None)
186
187 run_control(net, mode="bidirectional", iter=100)
188
189 net = correct_flow_directions(net)
190 net = init_diameter_types(net, v_max_pipe=v_max_m_s, material_filter="KMR", k=k)
191 net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="KMR", k=k)
192
193 return net
194
195def initialize_test_net_two_pumps(qext_w=np.array([50000, 20000]),
196 return_temperature=np.array([60,55]),
197 supply_temperature=85,
198 flow_pressure_pump=4,
199 lift_pressure_pump=1.5,
200 pipetype="KMR 100/250-2v",
201 v_max_m_s=1.5,
202 mass_pump_mass_flow=0.5):
203 print("Running the test network with two pumps initialization script.")
204 net = pp.create_empty_network(fluid="water")
205
206 ### get pipe properties
207 k = 0.1
208
209 supply_temperature_k = supply_temperature + 273.15 # convert to Kelvin
210 return_temperature_k = return_temperature + 273.15 # convert to Kelvin
211
212 # Junctions for pump
213 j1 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 1", geodata=(0, 10))
214 j2 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 2", geodata=(0, 0))
215
216 # Junctions for connection pipes forward line
217 j3 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 3", geodata=(10, 0))
218 j4 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 4", geodata=(60, 0))
219
220 # Junctions for heat exchangers
221 j5 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 5", geodata=(85, 0))
222 j6 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 6", geodata=(85, 10))
223
224 # Junctions for connection pipes return line
225 j7 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 7", geodata=(60, 10))
226 j8 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 8", geodata=(10, 10))
227
228 pump1 = pp.create_circ_pump_const_pressure(net, j1, j2, p_flow_bar=flow_pressure_pump,
229 plift_bar=lift_pressure_pump, t_flow_k=supply_temperature_k,
230 type="auto", name="pump1")
231
232 pipe1 = pp.create_pipe(net, j2, j3, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe1", sections=5, text_k=283)
233 pipe2 = pp.create_pipe(net, j3, j4, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe2", sections=5, text_k=283)
234 pipe3 = pp.create_pipe(net, j4, j5, std_type=pipetype, length_km=0.025, k_mm=k, name="pipe3", sections=5, text_k=283)
235
236 pp.create_heat_consumer(net, from_junction=j5, to_junction=j6, qext_w=qext_w[0], treturn_k=return_temperature_k[0], name="Consumer A")
237 pp.create_heat_consumer(net, from_junction=j4, to_junction=j7, qext_w=qext_w[1], treturn_k=return_temperature_k[1], name="Consumer B")
238
239 pipe4 = pp.create_pipe(net, j6, j7, std_type=pipetype, length_km=0.25, k_mm=k, name="pipe4", sections=5, text_k=283)
240 pipe5 = pp.create_pipe(net, j7, j8, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe5", sections=5, text_k=283)
241 pipe6 = pp.create_pipe(net, j8, j1, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe6", sections=5, text_k=283)
242
243 ### here comes the part with the additional circ_pump_const_mass_flow ###
244 # first of, the junctions
245 j9 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 9", geodata=(100, 0))
246 j10 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 10", geodata=(100, 10))
247 j11 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Junction 11", geodata=(100, 5))
248
249 pipe7 = pp.create_pipe(net, j5, j9, std_type=pipetype, length_km=0.05, k_mm=k, name="pipe7", sections=5, text_k=283)
250 pipe8 = pp.create_pipe(net, j10, j6, std_type=pipetype, length_km=0.01, k_mm=k, name="pipe8", sections=5, text_k=283)
251
252 pump2 = pp.create_circ_pump_const_mass_flow(net, j10, j11, p_flow_bar=flow_pressure_pump, mdot_flow_kg_per_s=mass_pump_mass_flow,
253 t_flow_k=supply_temperature_k, type="auto", name="pump2")
254
255 flow_control_pump2 = pp.create_flow_control(net, j11, j9, controlled_mdot_kg_per_s=mass_pump_mass_flow)
256
257 # Simulate pipe flow
258 pp.pipeflow(net, mode="bidirectional", iter=100)
259
260 # Placeholder functions for additional processing
261 net = create_controllers(net, qext_w, supply_temperature, None, return_temperature, None)
262
263 run_control(net, mode="bidirectional", iter=100)
264
265 net = correct_flow_directions(net)
266 net = init_diameter_types(net, v_max_pipe=v_max_m_s, material_filter="KMR", k=k)
267 net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="KMR", k=k)
268
269 return net
270
271def test_profile_initiation():
272 print("Running the test_profile_initiation function.")
273
274 data = pd.read_csv("examples/data/data_ETRS89.csv", sep=";")
275 TRY_filename = "src/districtheatingsim/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
276 calc_method = "Datensatz"
277
278 print(f"Data: {data}")
279
280 # if data in column "Subtyp" is just one digit, add a leading zero
281 data["Subtyp"] = data["Subtyp"].apply(lambda x: f"0{x}" if len(str(x)) == 1 else x)
282
283 yearly_time_steps, total_heat_W, heating_heat_W, warmwater_heat_W, max_heat_requirement_W, supply_temperature_curve, \
284 return_temperature_curve, hourly_air_temperatures = heat_requirement_calculation_csv.generate_profiles_from_csv(data, TRY_filename, calc_method)
285
286 print("Initializing Net:")
287
288 # qext_w is the max heat requirement in W
289 qext_w = max_heat_requirement_W*5
290 print(f" → Max heat requirement (qext_w): {qext_w} W")
291 # return_temperature is the return temperature in °C, same size as qext_w array
292 # np.fill like qext_w with 55
293 return_temperature = np.full_like(qext_w, 55)
294 print(f" → Return temperature: {return_temperature} °C")
295
296 net = initialize_test_net(qext_w=qext_w, return_temperature=return_temperature)
297
298 return net
299
300def print_results(net):
301 print(net.res_junction)
302 print(net.res_pipe)
303 print(net.res_heat_consumer)
304 print(net.res_circ_pump_pressure)
305
306if __name__ == "__main__":
307 try:
308 nets = [
309 #test_heat_consumer_result_extraction(),
310 initialize_test_net(),
311 initialize_complex_test_net(),
312 initialize_test_net_two_pumps(),
313 test_profile_initiation()
314 ]
315
316 for net in nets:
317 print_results(net)
318
319 fig, ax = plt.subplots()
320 config_plot(net, ax, show_junctions=True, show_pipes=True, show_heat_consumers=True, show_pump=True, show_plot=False)
321
322 plt.show()
323
324 except Exception as e:
325 print("An error occurred:")
326 print(traceback.format_exc())
327 raise e
Basic pandapipes simulation example.
Time Series Simulation
1"""
2Filename: 07_example_timeseries_pandapipes.py
3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
4Date: 2025-05-12
5Description: Script for testing the pandapipes net simulation functions.
6Usage: Run the script to generate a simple pandapipes network.
7
8Example:
9 $ python 07_example_timeseries_pandapipes.py
10"""
11
12import traceback
13import logging
14# Initialize logging
15logging.basicConfig(level=logging.INFO)
16
17import numpy as np
18import pandas as pd
19import matplotlib.pyplot as plt
20
21import pandapipes as pp
22import pandapipes.plotting as pp_plot
23from pandapipes.timeseries import run_time_series
24from pandapower.timeseries import OutputWriter
25
26from districtheatingsim.net_simulation_pandapipes.config_plot import config_plot
27from districtheatingsim.net_simulation_pandapipes.pp_net_initialisation_geojson import *
28from districtheatingsim.net_simulation_pandapipes.pp_net_time_series_simulation import *
29from districtheatingsim.net_simulation_pandapipes.utilities import *
30
31from districtheatingsim.net_simulation_pandapipes.config_plot import config_plot
32
33def initialize_test_net(qext_w=np.array([100000, 100000, 100000]),
34 return_temperature=np.array([55, 60, 50]),
35 supply_temperature=85,
36 flow_pressure_pump=4,
37 lift_pressure_pump=1.5,
38 pipetype="KMR 100/250-2v",
39 v_max_m_s=1.5):
40 print("Initializing test network...")
41 # Initialize the pandapipes network
42 net = pp.create_empty_network(fluid="water")
43
44 k = 0.1 # roughness
45 supply_temperature_k = supply_temperature + 273.15 # convert to Kelvin
46 return_temperature_k = return_temperature + 273.15 # convert to Kelvin
47
48 # Define junctions
49 j1 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Pump Supply", geodata=(0, 0))
50 j2 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Main Split Supply", geodata=(10, 0))
51 j12 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Main Split Return", geodata=(10, 10))
52 j13 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Pump Return", geodata=(0, 10))
53
54 # Additional junctions for new branches
55 j3 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer B Supply", geodata=(20, 0))
56 j4 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer B Return", geodata=(20, 10))
57 j5 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer C Supply", geodata=(30, 0))
58 j6 = pp.create_junction(net, pn_bar=1.05, tfluid_k=supply_temperature_k, name="Consumer C Return", geodata=(30, 10))
59
60 # Pump
61 pp.create_circ_pump_const_pressure(net, j13, j1, p_flow_bar=flow_pressure_pump, plift_bar=lift_pressure_pump,
62 t_flow_k=supply_temperature_k, type="auto", name="Main Pump")
63
64 # Pipes for supply line
65 pp.create_pipe(net, j1, j2, std_type=pipetype, length_km=0.02, k_mm=k, name="Main Pipe Supply")
66 pp.create_pipe(net, j2, j3, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch B Pipe Supply")
67 pp.create_pipe(net, j3, j5, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch C Pipe Supply")
68
69 # Pipes for return line
70 pp.create_pipe(net, j12, j13, std_type=pipetype, length_km=0.02, k_mm=k, name="Main Pipe Return")
71 pp.create_pipe(net, j4, j12, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch B Pipe Return")
72 pp.create_pipe(net, j6, j4, std_type=pipetype, length_km=0.03, k_mm=k, name="Branch C Pipe Return")
73
74 # Heat consumers
75 pp.create_heat_consumer(net, from_junction=j2, to_junction=j12, qext_w=qext_w[0], treturn_k=return_temperature_k[0], name="Consumer A")
76 pp.create_heat_consumer(net, from_junction=j3, to_junction=j4, qext_w=qext_w[1], treturn_k=return_temperature_k[1], name="Consumer B")
77 pp.create_heat_consumer(net, from_junction=j5, to_junction=j6, qext_w=qext_w[2], treturn_k=return_temperature_k[2], name="Consumer C")
78
79 # Simulate pipe flow
80 pp.pipeflow(net, mode="bidirectional", iter=100)
81
82 # Placeholder functions for additional processing
83 net = create_controllers(net, qext_w, supply_temperature, None, return_temperature, None)
84
85 run_control(net, mode="bidirectional", iter=100)
86
87 net = correct_flow_directions(net)
88 net = init_diameter_types(net, v_max_pipe=v_max_m_s, material_filter="KMR", k=k)
89 net = optimize_diameter_types(net, v_max=v_max_m_s, material_filter="KMR", k=k)
90
91 return net
92
93
94def timeseries_test(net):
95 print("Running time series test...")
96 start = 0
97 end = 8 # 8760 hours in a year
98
99 # time steps with start and end
100 time_steps = np.arange(start, end, 1) # time steps in hours
101
102 # yearly time steps with dates beginning at 01.01.2021 00:00:00
103 yearly_time_steps = pd.date_range(start="2021-01-01 00:00:00", periods=end, freq="h")
104
105 # np.random.seed() is used to make the random numbers predictable
106 np.random.seed(0)
107 # Generate more realistic heat demand profiles with smaller variations
108 # Instead of 0-100kW, use 30-70kW to avoid extreme controller adjustments
109 base_demand = np.array([50000, 60000, 40000]) # Base demand per consumer in W
110 variation = 0.3 # ±30% variation
111
112 qext_w_profiles = np.zeros((3, end))
113 for i in range(3):
114 # Generate random variations around base demand
115 qext_w_profiles[i, :] = base_demand[i] * (1 + variation * (2 * np.random.random(end) - 1))
116
117 print(f"qext_w_profiles shape: {qext_w_profiles.shape}")
118 print(f"qext_w_profiles min: {np.min(qext_w_profiles):.0f} W, max: {np.max(qext_w_profiles):.0f} W")
119
120 return_temperature = np.linspace(50, 60, end).reshape(1, -1).repeat(3, axis=0) # Generate time-dependent return temperatures as a linear gradient
121 if qext_w_profiles.shape != return_temperature.shape:
122 raise ValueError("The shape of return_temperature_profiles must match the shape of qext_w_profiles.")
123 supply_temperature = np.full_like(time_steps, 85) # Supply temperature is constant
124 print(f"supply_temperature: {supply_temperature}") # Structure is one-dimensional array with shape (n_time_steps,)
125
126 print(net.controller)
127
128 update_heat_consumer_qext_controller(net, qext_w_profiles, time_steps, start, end)
129 update_heat_consumer_return_temperature_controller(net, return_temperature, time_steps, start, end)
130 update_heat_generator_supply_temperature_controller(net, supply_temperature, time_steps, start, end)
131
132 print(net)
133 print(net.controller)
134 print(net.heat_consumer)
135 print(net.circ_pump_pressure)
136 #print(net.res_controller)
137 print(net.res_heat_consumer)
138 print(net.res_circ_pump_pressure)
139
140 # Log variables and run time series calculation
141 log_variables = create_log_variables(net)
142 ow = OutputWriter(net, time_steps, output_path=None, log_variables=log_variables)
143
144 run_time_series.run_timeseries(net, time_steps, mode="bidirectional", iter=100, alpha=0.6)
145
146 return yearly_time_steps, net, ow.np_results
147
148def print_results(net):
149 print(net)
150 print(net.junction)
151 print(net.pipe)
152 print(net.heat_consumer)
153 print(net.circ_pump_pressure)
154
155 print(net.res_junction)
156 print(net.res_pipe)
157 print(net.res_heat_consumer)
158 print(net.res_circ_pump_pressure)
159
160def plot_time_series_results(yearly_time_steps, np_results, net):
161 """
162 Plot time series results for junction pressures, temperatures, and heat consumer data.
163
164 Parameters
165 ----------
166 yearly_time_steps : pd.DatetimeIndex
167 Time steps with dates
168 np_results : dict
169 Results dictionary from OutputWriter
170 net : pandapipes.pandapipesNet
171 The network object with junction names
172 """
173 print("\n" + "="*70)
174 print("TIME SERIES RESULTS ANALYSIS")
175 print("="*70)
176
177 # Extract junction pressures
178 if 'res_junction.p_bar' in np_results:
179 p_bar = np_results['res_junction.p_bar']
180 print(f"\nJunction Pressures Shape: {p_bar.shape}")
181 print(f"Time steps: {p_bar.shape[0]}, Junctions: {p_bar.shape[1]}")
182
183 # Plot junction pressures over time
184 fig, ax = plt.subplots(figsize=(14, 6))
185 for i in range(p_bar.shape[1]):
186 junction_name = net.junction.at[i, 'name'] if 'name' in net.junction.columns else f"Junction {i}"
187 ax.plot(yearly_time_steps, p_bar[:, i], label=junction_name, linewidth=1.5)
188
189 ax.set_xlabel('Zeit', fontsize=12)
190 ax.set_ylabel('Druck [bar]', fontsize=12)
191 ax.set_title('Knotendruck im Zeitverlauf', fontsize=14, fontweight='bold')
192 ax.legend(loc='best', fontsize=9)
193 ax.grid(True, alpha=0.3)
194 plt.tight_layout()
195 plt.show()
196
197 # Print statistics
198 print("\nKnotendruck Statistik:")
199 for i in range(p_bar.shape[1]):
200 junction_name = net.junction.at[i, 'name'] if 'name' in net.junction.columns else f"Junction {i}"
201 print(f" {junction_name}:")
202 print(f" Min: {np.min(p_bar[:, i]):.3f} bar")
203 print(f" Max: {np.max(p_bar[:, i]):.3f} bar")
204 print(f" Mittelwert: {np.mean(p_bar[:, i]):.3f} bar")
205
206 # Extract junction temperatures
207 if 'res_junction.t_k' in np_results:
208 t_k = np_results['res_junction.t_k']
209 t_c = t_k - 273.15 # Convert to Celsius
210
211 # Plot junction temperatures over time
212 fig, ax = plt.subplots(figsize=(14, 6))
213 for i in range(t_c.shape[1]):
214 junction_name = net.junction.at[i, 'name'] if 'name' in net.junction.columns else f"Junction {i}"
215 ax.plot(yearly_time_steps, t_c[:, i], label=junction_name, linewidth=1.5)
216
217 ax.set_xlabel('Zeit', fontsize=12)
218 ax.set_ylabel('Temperatur [°C]', fontsize=12)
219 ax.set_title('Knotentemperatur im Zeitverlauf', fontsize=14, fontweight='bold')
220 ax.legend(loc='best', fontsize=9)
221 ax.grid(True, alpha=0.3)
222 plt.tight_layout()
223 plt.show()
224
225 # Extract heat consumer results
226 if 'res_heat_consumer.qext_w' in np_results:
227 qext_w = np_results['res_heat_consumer.qext_w']
228 qext_kw = qext_w / 1000 # Convert to kW
229
230 # Plot heat demand over time
231 fig, ax = plt.subplots(figsize=(14, 6))
232 for i in range(qext_kw.shape[1]):
233 consumer_name = net.heat_consumer.at[i, 'name'] if 'name' in net.heat_consumer.columns else f"Consumer {i}"
234 ax.plot(yearly_time_steps, qext_kw[:, i], label=consumer_name, linewidth=1.5)
235
236 # Plot total heat demand
237 total_qext_kw = np.sum(qext_kw, axis=1)
238 ax.plot(yearly_time_steps, total_qext_kw, label='Gesamt', linewidth=2.5,
239 color='black', linestyle='--')
240
241 ax.set_xlabel('Zeit', fontsize=12)
242 ax.set_ylabel('Wärmebedarf [kW]', fontsize=12)
243 ax.set_title('Wärmebedarf im Zeitverlauf', fontsize=14, fontweight='bold')
244 ax.legend(loc='best', fontsize=9)
245 ax.grid(True, alpha=0.3)
246 plt.tight_layout()
247 plt.show()
248
249 # Print statistics
250 print("\nWärmebedarf Statistik:")
251 for i in range(qext_kw.shape[1]):
252 consumer_name = net.heat_consumer.at[i, 'name'] if 'name' in net.heat_consumer.columns else f"Consumer {i}"
253 print(f" {consumer_name}:")
254 print(f" Min: {np.min(qext_kw[:, i]):.1f} kW")
255 print(f" Max: {np.max(qext_kw[:, i]):.1f} kW")
256 print(f" Mittelwert: {np.mean(qext_kw[:, i]):.1f} kW")
257 print(f" Gesamt:")
258 print(f" Min: {np.min(total_qext_kw):.1f} kW")
259 print(f" Max: {np.max(total_qext_kw):.1f} kW")
260 print(f" Mittelwert: {np.mean(total_qext_kw):.1f} kW")
261
262 # Print available result keys
263 print("\nVerfügbare Ergebnis-Variablen:")
264 for key in sorted(np_results.keys()):
265 shape = np_results[key].shape if hasattr(np_results[key], 'shape') else 'N/A'
266 print(f" {key}: {shape}")
267
268 print("="*70)
269
270
271if __name__ == "__main__":
272 try:
273 net = initialize_test_net()
274
275 fig1, ax1 = plt.subplots()
276
277 config_plot(net=net, ax=ax1, show_junctions=True, show_pipes=True,
278 show_heat_consumers=True, show_pump=True, show_plot=False,
279 show_basemap=False, map_type="OSM")
280
281 print_results(net)
282
283 print("Test network initialized successfully."
284 " Running time series simulation..." )
285
286
287 yearly_time_steps, net, np_results = timeseries_test(net)
288
289 print_results(net)
290
291 print("\nAnalyzing time series results...")
292 plot_time_series_results(yearly_time_steps, np_results, net)
293
294 fig2, ax2 = plt.subplots()
295
296 config_plot(net=net, ax=ax2, show_junctions=True, show_pipes=True,
297 show_heat_consumers=True, show_pump=True, show_plot=False,
298 show_basemap=False, map_type="OSM")
299
300 plt.show()
301
302 except Exception as e:
303 print("An error occurred:")
304 print(traceback.format_exc())
305 raise e
Time series simulation with pandapipes.
Complex Time Series Analysis
1"""
2Filename: 08_example_complex_pandapipes_timeseries.py
3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
4Date: 2025-01-12
5Description: Script for testing the pandapipes net simulation functions. Aims to simulate a district heating network using GeoJSON files for the network layout and a JSON file for the building load profile. The script includes functions to create and initialize the network, perform time series calculations, and plot the results.
6Usage: Run the script in the main directory of the repository.
7
8Updated to work with the current NetworkGenerationData class structure.
9"""
10
11import matplotlib.pyplot as plt
12import numpy as np
13
14from districtheatingsim.net_simulation_pandapipes.pp_net_initialisation_geojson import *
15from districtheatingsim.net_simulation_pandapipes.pp_net_time_series_simulation import *
16from districtheatingsim.net_simulation_pandapipes.utilities import *
17
18from districtheatingsim.net_simulation_pandapipes.config_plot import config_plot
19
20from districtheatingsim.net_simulation_pandapipes.NetworkDataClass import NetworkGenerationData, SecondaryProducer
21
22def plot(net, time_steps, qext_kW, strom_kW):
23 """
24 Plots the network data.
25
26 Args:
27 time_steps: Array of time steps.
28 qext_kW: Array of external heat demand in kW.
29 strom_kW: Array of power demand in kW.
30 """
31 fig, ax1 = plt.subplots()
32
33 if np.sum(strom_kW) == 0:
34 ax1.plot(time_steps, qext_kW, 'b-', label="Gesamtheizlast Gebäude in kW")
35
36 if np.sum(strom_kW) > 0:
37 ax1.plot(time_steps, qext_kW+strom_kW, 'b-', label="Gesamtheizlast Gebäude in kW")
38 ax1.plot(time_steps, strom_kW, 'g-', label="Gesamtstrombedarf Wärmepumpen Gebäude in kW")
39
40 ax1.set_xlabel("Zeit")
41 ax1.set_ylabel("Leistung in kW", color='b')
42 ax1.tick_params('y', colors='b')
43 ax1.legend(loc='upper center')
44 ax1.grid()
45
46 fig, ax2 = plt.subplots()
47
48 config_plot(net, ax2, show_junctions=True, show_pipes=True, show_heat_consumers=True, show_basemap=False, show_plot=True)
49
50def print_net_results(net):
51 print("Netzdaten:")
52 print(f"Junctions: {net.junction}")
53 print(f"Results Junctions: {net.res_junction}")
54 print(f"Pipes: {net.pipe}")
55 print(f"Results Pipes: {net.res_pipe}")
56 print(f"Heat Consumers: {net.heat_consumer}")
57 print(f"Results Heat Consumers: {net.res_heat_consumer}")
58 print(f"Circ Pump Pressure: {net.circ_pump_pressure}")
59 print(f"Results Circ Pump Pressure: {net.res_circ_pump_pressure}")
60 if 'circ_pump_mass' in net:
61 print(f"Circ Pump Mass: {net.circ_pump_mass}")
62 print(f"Results Circ Pump Mass: {net.res_circ_pump_mass}")
63
64if __name__ == "__main__":
65 from districtheatingsim.net_generation.network_geojson_schema import NetworkGeoJSONSchema
66 import tempfile
67
68 # File paths - Load unified format
69 unified_path = "examples/data/Wärmenetz/Variante 2/Wärmenetz.geojson"
70
71 # Extract layers from unified format
72 unified_geojson = NetworkGeoJSONSchema.import_from_file(unified_path)
73 vorlauf_gdf, ruecklauf_gdf, hast_gdf, erzeuger_gdf = NetworkGeoJSONSchema.split_to_legacy_format(unified_geojson)
74
75 # Save to temporary files for compatibility
76 temp_dir = tempfile.mkdtemp()
77 vorlauf_path = os.path.join(temp_dir, "vorlauf.geojson")
78 ruecklauf_path = os.path.join(temp_dir, "ruecklauf.geojson")
79 hast_path = os.path.join(temp_dir, "hast.geojson")
80 erzeugeranlagen_path = os.path.join(temp_dir, "erzeuger.geojson")
81
82 vorlauf_gdf.to_file(vorlauf_path, driver="GeoJSON")
83 ruecklauf_gdf.to_file(ruecklauf_path, driver="GeoJSON")
84 hast_gdf.to_file(hast_path, driver="GeoJSON")
85 erzeuger_gdf.to_file(erzeugeranlagen_path, driver="GeoJSON")
86 json_path = "examples/data/Gebäude Lastgang.json"
87
88 # Optional external data files
89 TRY_filename = "examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
90 COP_filename = "examples/data/COP/Kennlinien WP.csv"
91
92 # Network configuration parameters
93 netconfiguration = "Niedertemperaturnetz" # or "kaltes Netz"
94 supply_temperature_control = "Statisch" # or "Gleitend"
95
96 # Temperature control parameters
97 max_supply_temperature_heat_generator = 85.0 # °C
98 min_supply_temperature_heat_generator = 70.0 # °C (for sliding control)
99 max_air_temperature_heat_generator = 15.0 # °C
100 min_air_temperature_heat_generator = -12.0 # °C
101
102 # Pump parameters
103 flow_pressure_pump = 4.0 # bar
104 lift_pressure_pump = 1.5 # bar
105
106 # Building temperature parameters
107 min_supply_temperature_building_checked = False
108 min_supply_temperature_building = None # 60.0 # °C
109 fixed_return_temperature_heat_consumer_checked = False
110 fixed_return_temperature_heat_consumer = None # 50.0 # °C
111 dT_RL = 5.0 # K - temperature difference between heat consumer and net due to heat exchanger
112 building_temperature_checked = False # flag indicating if building temperatures are considered
113
114 # Pipe parameters
115 pipetype = "KMR 100/250-2v" # type of pipe
116 diameter_optimization_pipe_checked = True # flag indicating if diameter optimization is checked
117 max_velocity_pipe = 2.0 # m/s - maximum flow velocity in the pipes
118 material_filter_pipe = "KMR" # material filter for pipes
119 k_mm_pipe = 0.1 # mm - roughness of the pipe
120
121 # Producer configuration
122 main_producer_location_index = 0 # index of the main producer location
123 #secondary_producers = [] # list of secondary producers, can be empty or contain multiple producers
124 secondary_producers = [
125 SecondaryProducer(index=1, load_percentage=5.0) # secondary producer with 5% load
126 ]
127
128 # Import type
129 import_type = "geoJSON" # currently only geoJSON
130
131 # Create NetworkGenerationData object with all required parameters
132 network_data = NetworkGenerationData(
133 # Required input data paths
134 import_type=import_type,
135 flow_line_path=vorlauf_path,
136 return_line_path=ruecklauf_path,
137 heat_consumer_path=hast_path,
138 heat_generator_path=erzeugeranlagen_path,
139 heat_demand_json_path=json_path,
140
141 # Network configuration data
142 netconfiguration=netconfiguration,
143 supply_temperature_control=supply_temperature_control,
144 max_supply_temperature_heat_generator=max_supply_temperature_heat_generator,
145 min_supply_temperature_heat_generator=min_supply_temperature_heat_generator,
146 max_air_temperature_heat_generator=max_air_temperature_heat_generator,
147 min_air_temperature_heat_generator=min_air_temperature_heat_generator,
148 flow_pressure_pump=flow_pressure_pump,
149 lift_pressure_pump=lift_pressure_pump,
150 min_supply_temperature_building_checked=min_supply_temperature_building_checked,
151 min_supply_temperature_building=min_supply_temperature_building,
152 fixed_return_temperature_heat_consumer_checked=fixed_return_temperature_heat_consumer_checked,
153 fixed_return_temperature_heat_consumer=fixed_return_temperature_heat_consumer,
154 dT_RL=dT_RL,
155 building_temperature_checked=building_temperature_checked,
156 pipetype=pipetype,
157
158 # Optimization variables
159 diameter_optimization_pipe_checked=diameter_optimization_pipe_checked,
160 max_velocity_pipe=max_velocity_pipe,
161 material_filter_pipe=material_filter_pipe,
162 k_mm_pipe=k_mm_pipe,
163
164 # Producer configuration
165 main_producer_location_index=main_producer_location_index,
166 secondary_producers=secondary_producers,
167
168 # Optional external data files
169 COP_filename=COP_filename,
170 TRY_filename=TRY_filename
171 )
172
173 # Initialize network from GeoJSON data
174 network_data = initialize_geojson(network_data)
175
176 # Optimize pipe diameters if requested
177 if network_data.diameter_optimization_pipe_checked:
178 network_data.net = optimize_diameter_types(
179 network_data.net,
180 network_data.max_velocity_pipe,
181 network_data.material_filter_pipe,
182 network_data.k_mm_pipe
183 )
184
185 # Set simulation time range
186 network_data.start_time_step = 0
187 network_data.end_time_step = 100
188
189 # Preprocess time series data
190 network_data = time_series_preprocessing(network_data)
191
192 # Run thermohydraulic time series simulation
193 network_data = thermohydraulic_time_series_net(network_data)
194
195 print("Simulation erfolgreich abgeschlossen.")
196
197 # Print network results
198 print_net_results(network_data.net)
199
200 # Calculate and display key performance indicators
201 results = network_data.calculate_results()
202 print("\nKennzahlen:")
203 for key, value in results.items():
204 if value is not None:
205 if isinstance(value, float):
206 print(f"{key}: {value:.2f}")
207 else:
208 print(f"{key}: {value}")
209 else:
210 print(f"{key}: Nicht verfügbar")
211
212 # Prepare plot data
213 network_data.prepare_plot_data()
214
215 # Plot results
216 plot(network_data.net,
217 network_data.yearly_time_steps[network_data.start_time_step:network_data.end_time_step],
218 network_data.waerme_ges_kW[network_data.start_time_step:network_data.end_time_step],
219 network_data.strombedarf_ges_kW[network_data.start_time_step:network_data.end_time_step])
220
221 # Show additional plots using prepared plot data
222 if network_data.plot_data:
223 print("\nVerfügbare Plot-Daten:")
224 for key in network_data.plot_data.keys():
225 print(f" - {key}")
226
227 # Example: Plot heat demand over time
228 heat_demand_data = network_data.plot_data["Gesamtwärmebedarf Wärmeübertrager"]
229 fig, ax = plt.subplots(figsize=(12, 6))
230 ax.plot(heat_demand_data['time'], heat_demand_data['data'], 'b-')
231 ax.set_xlabel('Zeit')
232 ax.set_ylabel(heat_demand_data['label'])
233 ax.set_title('Wärmebedarf über Zeit')
234 ax.grid(True)
235 plt.show()
236
237 plt.show()
Complex time series analysis example.
Energy Systems Examples
Heat Generators
1"""
2Filename: 09_example_heat_generators.py
3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
4Date: 2025-07-12
5Description: Script for testing the heat generator functions.
6
7# Not up-to-date: This script is not up-to-date with the latest version of all heat generator classes.
8"""
9
10from districtheatingsim.heat_generators.solar_thermal import SolarThermal
11from districtheatingsim.heat_generators.gas_boiler import GasBoiler
12from districtheatingsim.heat_generators.biomass_boiler import BiomassBoiler
13from districtheatingsim.heat_generators.chp import CHP
14from districtheatingsim.heat_generators.waste_heat_pump import WasteHeatPump
15from districtheatingsim.heat_generators.river_heat_pump import RiverHeatPump
16from districtheatingsim.heat_generators.geothermal_heat_pump import Geothermal
17from districtheatingsim.heat_generators.power_to_heat import PowerToHeat
18from districtheatingsim.utilities.test_reference_year import import_TRY
19
20import numpy as np
21
22import matplotlib.pyplot as plt
23
24def test_gas_boiler(Last_L, duration, economic_parameters, gBoiler=GasBoiler(name="Gas_Boiler_1",
25 thermal_capacity_kW=200,
26 spez_Investitionskosten=30,
27 Nutzungsgrad=0.9)):
28 results = gBoiler.calculate(economic_parameters, duration, Last_L)
29
30 print(f"Wärmemenge Gaskessel: {results['Wärmemenge']:.2f} MWh")
31 print(f"Wärmeleistung Gaskessel: {results['Wärmeleistung_L']} kW")
32 print(f"Brennstoffbedarf Gaskessel: {results['Brennstoffbedarf']:.2f} MWh")
33 print(f"Wärmegestehungskosten Gaskessel: {results['WGK']:.2f} €/MWh")
34 print(f"spezifische CO2-Emissionen Gaskessel: {results['spec_co2_total']:.2f} tCO2/MWh")
35 print(f"Primärenergiebedarf Gaskessel: {results['primärenergie']:.2f} MWh")
36
37def test_power_to_heat(Last_L, duration, economic_parameters, PTH=PowerToHeat(name="PowerToHeat",
38 thermal_capacity_kW=1000,
39 spez_Investitionskosten=30,
40 Nutzungsgrad=0.98)):
41 results = PTH.calculate(economic_parameters, duration, Last_L)
42
43 print(f"Wärmemenge Power-to-Heat: {results['Wärmemenge']:.2f} MWh")
44 print(f"Wärmeleistung Power-to-Heat: {results['Wärmeleistung_L']} kW")
45 print(f"Strombedarf Power-to-Heat: {results['Strombedarf']:.2f} MWh")
46 print(f"Wärmegestehungskosten Power-to-Heat: {results['WGK']:.2f} €/MWh")
47 print(f"spezifische CO2-Emissionen Power-to-Heat: {results['spec_co2_total']:.2f} tCO2/MWh")
48 print(f"Primärenergiebedarf Power-to-Heat: {results['primärenergie']:.2f} MWh")
49
50def test_biomass_boiler(Last_L, duartion, economic_parameters, bBoiler=BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200, Größe_Holzlager=40, spez_Investitionskosten=200, spez_Investitionskosten_Holzlager=400,
51 Nutzungsgrad_BMK=0.8, min_Teillast=0.3, speicher_aktiv=False, Speicher_Volumen=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0,
52 min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0,
53 opt_Speicher_max=100)):
54 results = bBoiler.calculate(economic_parameters, duartion, Last_L)
55
56 print(f"Wärmemenge Biomassekessel: {results['Wärmemenge']:.2f} MWh")
57 print(f"Wärmeleistung Biomassekessel: {results['Wärmeleistung_L']} kW")
58 print(f"Brennstoffbedarf Biomassekessel: {results['Brennstoffbedarf']:.2f} MWh")
59 print(f"Wärmegestehungskosten Biomassekessel: {results['WGK']:.2f} €/MWh")
60 print(f"spezifische CO2-Emissionen Biomassekessel: {results['spec_co2_total']:.2f} tCO2/MWh")
61 print(f"Primärenergiebedarf Biomassekessel: {results['primärenergie']:.2f} MWh")
62 print(f"Anzahl Starts Biomassekessel: {results['Anzahl_Starts']}")
63 print(f"Betriebsstunden Biomassekessel: {results['Betriebsstunden']} h")
64 print(f"Betriebsstunden pro Start Biomassekessel: {results['Betriebsstunden_pro_Start']:.2f} h")
65
66def test_biomass_boiler_storage(Last_L,
67 duration,
68 economic_parameters,
69 bBoiler=BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200,
70 Größe_Holzlager=40, spez_Investitionskosten=200,
71 spez_Investitionskosten_Holzlager=400,
72 Nutzungsgrad_BMK=0.8, min_Teillast=0.3,
73 speicher_aktiv=True, Speicher_Volumen=20,
74 T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0,
75 min_fill=0.2, max_fill=0.8,
76 spez_Investitionskosten_Speicher=750, active=True,
77 opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0,
78 opt_Speicher_max=100)):
79
80 results = bBoiler.calculate(economic_parameters, duration, Last_L)
81
82 print(f"Wärmemenge Biomassekessel mit Speicher: {results['Wärmemenge']:.2f} MWh")
83 print(f"Wärmeleistung Biomassekessel mit Speicher: {results['Wärmeleistung_L']} kW")
84 print(f"Wärmeleistung Speicher Biomassekessel: {results['Wärmeleistung_Speicher_L']} kW")
85 print(f"Brennstoffbedarf Biomassekessel mit Speicher: {results['Brennstoffbedarf']:.2f} MWh")
86 print(f"Wärmegestehungskosten Biomassekessel mit Speicher: {results['WGK']:.2f} €/MWh")
87 print(f"spezifische CO2-Emissionen Biomassekessel mit Speicher: {results['spec_co2_total']:.2f} tCO2/MWh")
88 print(f"Primärenergiebedarf Biomassekessel mit Speicher: {results['primärenergie']:.2f} MWh")
89 print(f"Anzahl Starts Biomassekessel mit Speicher: {results['Anzahl_Starts']}")
90 print(f"Betriebsstunden Biomassekessel mit Speicher: {results['Betriebsstunden']} h")
91 print(f"Betriebsstunden pro Start Biomassekessel mit Speicher: {results['Betriebsstunden_pro_Start']:.2f} h")
92
93def test_chp(Last_L, duration, economic_parameters, chp=CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33,
94 KWK_Wirkungsgrad=0.9, min_Teillast=0.7, speicher_aktiv=False, Speicher_Volumen_BHKW=20, T_vorlauf=90, T_ruecklauf=60,
95 initial_fill=0.0, min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BHKW_min=0, opt_BHKW_max=1000,
96 opt_BHKW_Speicher_min=0, opt_BHKW_Speicher_max=100)):
97
98 results = chp.calculate(economic_parameters, duration, Last_L)
99
100 print(f"Wärmemenge BHKW: {results['Wärmemenge']:.2f} MWh")
101 print(f"Wärmeleistung BHKW: {results['Wärmeleistung_L']} kW")
102 print(f"Brennstoffbedarf BHKW: {results['Brennstoffbedarf']:.2f} MWh")
103 print(f"Wärmegestehungskosten BHKW: {results['WGK']:.2f} €/MWh")
104 print(f"Strommenge BHKW: {results['Strommenge']:.2f} MWh")
105 print(f"Stromleistung BHKW: {results['el_Leistung_L']} kW")
106 print(f"Anzahl Starts BHKW: {results['Anzahl_Starts']}")
107 print(f"Betriebsstunden BHKW: {results['Betriebsstunden']} h")
108 print(f"Betriebsstunden pro Start BHKW: {results['Betriebsstunden_pro_Start']:.2f} h")
109 print(f"spezifische CO2-Emissionen BHKW: {results['spec_co2_total']:.2f} tCO2/MWh")
110 print(f"Primärenergiebedarf BHKW: {results['primärenergie']:.2f} MWh")
111
112def test_chp_storage(Last_L, duration, economic_parameters, chp=CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33,
113 KWK_Wirkungsgrad=0.9, min_Teillast=0.7, speicher_aktiv=True, Speicher_Volumen_BHKW=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0,
114 min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BHKW_min=0, opt_BHKW_max=1000,
115 opt_BHKW_Speicher_min=0, opt_BHKW_Speicher_max=100)):
116
117 results = chp.calculate(economic_parameters, duration, Last_L)
118
119 print(f"Wärmemenge BHKW mit Speicher: {results['Wärmemenge']:.2f} MWh")
120 print(f"Wärmeleistung BHKW mit Speicher: {results['Wärmeleistung_L']} kW")
121 print(f"Wärmeleistung Speicher BHKW: {results['Wärmeleistung_Speicher_L']} kW")
122 print(f"Brennstoffbedarf BHKW mit Speicher: {results['Brennstoffbedarf']:.2f} MWh")
123 print(f"Wärmegestehungskosten BHKW mit Speicher: {results['WGK']:.2f} €/MWh")
124 print(f"Strommenge BHKW mit Speicher: {results['Strommenge']:.2f} MWh")
125 print(f"Stromleistung BHKW mit Speicher: {results['el_Leistung_L']} kW")
126 print(f"Anzahl Starts BHKW mit Speicher: {results['Anzahl_Starts']}")
127 print(f"Betriebsstunden BHKW mit Speicher: {results['Betriebsstunden']} h")
128 print(f"Betriebsstunden pro Start BHKW mit Speicher: {results['Betriebsstunden_pro_Start']:.2f} h")
129 print(f"spezifische CO2-Emissionen BHKW mit Speicher: {results['spec_co2_total']:.2f} tCO2/MWh")
130 print(f"Primärenergiebedarf BHKW mit Speicher: {results['primärenergie']:.2f} MWh")
131
132def test_waste_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, wasteHeatPump=WasteHeatPump(name="Abwärmepumpe", Kühlleistung_Abwärme=50, Temperatur_Abwärme=30, spez_Investitionskosten_Abwärme=500, spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)):
133
134 results = wasteHeatPump.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, COP_data=COP_data)
135
136 print(f"Wärmemenge Abwärme-WP: {results['Wärmemenge']:.2f} MWh")
137 print(f"Strombedarf Abwärme-WP: {results['Strombedarf']:.2f} MWh")
138 print(f"Wärmeleistung Abwärme-WP: {results['Wärmeleistung_L']} kW")
139 print(f"elektrische Leistung Abwärme-WP: {results['el_Leistung_L']} kW")
140 print(f"Wärmegestehungskosten Abwärme-WP: {results['WGK']:.2f} €/MWh")
141 print(f"spezifische CO2-Emissionen Abwärme-WP: {results['spec_co2_total']:.2f} tCO2/MWh")
142 print(f"Primärenergiebedarf Abwärme-WP: {results['primärenergie']:.2f} MWh")
143
144def test_river_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, riverHeatPump=RiverHeatPump(name="Flusswärmepumpe", Wärmeleistung_FW_WP=200, Temperatur_FW_WP=10, dT=0, spez_Investitionskosten_Flusswasser=1000, spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)):
145
146 results = riverHeatPump.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, COP_data=COP_data)
147
148 print(f"Wärmemenge Fluss-WP: {results['Wärmemenge']:.2f} MWh")
149 print(f"Strombedarf Fluss-WP: {results['Strombedarf']:.2f} MWh")
150 print(f"Wärmeleistung Fluss-WP: {results['Wärmeleistung_L']} kW")
151 print(f"elektrische Leistung Fluss-WP: {results['el_Leistung_L']} kW")
152 print(f"Wärmegestehungskosten Fluss-WP: {results['WGK']:.2f} €/MWh")
153 print(f"spezifische CO2-Emissionen Fluss-WP: {results['spec_co2_total']:.2f} tCO2/MWh")
154 print(f"Primärenergiebedarf Fluss-WP: {results['primärenergie']:.2f} MWh")
155
156def test_geothermal_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, geothermalHeatPump=Geothermal(name="Geothermie", Fläche=200, Bohrtiefe=100, Temperatur_Geothermie=10, spez_Bohrkosten=100, spez_Entzugsleistung=50, Vollbenutzungsstunden=2400, Abstand_Sonden=10, spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)):
157
158 results = geothermalHeatPump.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, COP_data=COP_data)
159
160 print(f"Wärmemenge Geothermie-WP: {results['Wärmemenge']:.2f} MWh")
161 print(f"Strombedarf Geothermie-WP: {results['Strombedarf']:.2f} MWh")
162 print(f"Wärmeleistung Geothermie-WP: {results['Wärmeleistung_L']} kW")
163 print(f"elektrische Leistung Geothermie-WP: {results['el_Leistung_L']} kW")
164 print(f"Wärmegestehungskosten Geothermie-WP: {results['WGK']:.2f} €/MWh")
165 print(f"spezifische CO2-Emissionen Geothermie-WP: {results['spec_co2_total']:.2f} tCO2/MWh")
166 print(f"Primärenergiebedarf Geothermie-WP: {results['primärenergie']:.2f} MWh")
167
168def test_solar_thermal(Last_L, duration, economic_parameters, VLT_L, RLT_L, TRY_data, time_steps, solarThermal=SolarThermal(name="Solarthermie", bruttofläche_STA=200, vs=20, Typ="Vakuumröhrenkollektor",
169 kosten_speicher_spez=750, kosten_fk_spez=430, kosten_vrk_spez=590, Tsmax=90,
170 Longitude=-14.4222, STD_Longitude=-15, Latitude=51.1676, East_West_collector_azimuth_angle=0,
171 Collector_tilt_angle=36, Tm_rl=60, Qsa=0, Vorwärmung_K=8, DT_WT_Solar_K=5, DT_WT_Netz_K=5,
172 opt_volume_min=0, opt_volume_max=200, opt_area_min=0, opt_area_max=2000)):
173
174 results = solarThermal.calculate(economic_parameters, duration, Last_L, VLT_L=VLT_L, RLT_L=RLT_L, TRY_data=TRY_data, time_steps=time_steps)
175
176 print(f"Wärmemenge Solarthermie: {results['Wärmemenge']:.2f} MWh")
177 print(f"Wärmeleistung Solarthermie: {results['Wärmeleistung_L']} kW")
178 print(f"Wärmegestehungskosten Solarthermie: {results['WGK']:.2f} €/MWh")
179 print(f"spezifische CO2-Emissionen Solarthermie: {results['spec_co2_total']:.2f} tCO2/MWh")
180 print(f"Primärenergiebedarf Solarthermie: {results['primärenergie']:.2f} MWh")
181 print(f"Speicherladung Solarthermie: {results['Speicherladung_L']} kW")
182 print(f"Speicherfüllstand Solarthermie: {results['Speicherfüllstand_L']} MWh")
183
184if __name__ == "__main__":
185 # Heat demand profile for one year (8760 hours)
186 min_last = 50
187 max_last = 400
188 Last_L = np.random.randint(min_last, max_last, 8760).astype(float)
189
190 # Duration
191 duration = 1
192
193 # Arrays for VLT and RLT, assuming constant values for the example
194 VLT_L, RLT_L = np.full(8760, 80), np.full(8760, 55)
195
196 # Load COP data from CSV file
197 COP_data = np.genfromtxt("examples/data/COP/Kennlinien WP.csv", delimiter=';')
198
199 # Filename for TRY data
200 TRY_data = import_TRY("examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat")
201
202 # Time steps for the simulation
203 # Angaben zum zu betrchtende Zeitraum
204 # Erstelle ein Array mit stündlichen Zeitwerten für ein Jahr
205 start_date = np.datetime64('2019-01-01')
206 end_date = np.datetime64('2020-01-01', 'D') # Enddatum ist exklusiv, daher 'D' für Tage
207
208 # Erstelle das Array mit stündlichen Zeitwerten für ein Jahr
209 time_steps = np.arange(start_date, end_date, dtype='datetime64[h]')
210
211
212 # Define economic parameters
213 electricity_price = 150 # €/MWh
214 gas_price = 70 # €/MWh
215 wood_price = 60 # €/MWh
216 q = 1.05
217 r = 1.03
218 T = 20
219 BEW = "Nein"
220 hourly_rate = 45
221
222 economic_parameters = {
223 "gas_price": gas_price,
224 "electricity_price": electricity_price,
225 "wood_price": wood_price,
226 "capital_interest_rate": q,
227 "inflation_rate": r,
228 "time_period": T,
229 "hourly_rate": hourly_rate,
230 "subsidy_eligibility": BEW
231 }
232
233 # Test Gas Boiler
234 gBoiler = GasBoiler(name="Gas_Boiler_1", thermal_capacity_kW=1000, spez_Investitionskosten=30, Nutzungsgrad=0.9)
235 test_gas_boiler(Last_L, duration, economic_parameters, gBoiler)
236
237 # Test Power to Heat
238 PTH = PowerToHeat(name="PowerToHeat_1", thermal_capacity_kW=1000, spez_Investitionskosten=30, Nutzungsgrad=0.98)
239 test_power_to_heat(Last_L, duration, economic_parameters, PTH)
240
241 # Test Biomass Boiler without storage
242 bBoiler = BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200, Größe_Holzlager=40, spez_Investitionskosten=200,
243 spez_Investitionskosten_Holzlager=400, Nutzungsgrad_BMK=0.8, min_Teillast=0.3, speicher_aktiv=False,
244 Speicher_Volumen=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, min_fill=0.2, max_fill=0.8,
245 spez_Investitionskosten_Speicher=750, active=True, opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0,
246 opt_Speicher_max=100)
247 test_biomass_boiler(Last_L, duration, economic_parameters, bBoiler)
248
249 # Test Biomass Boiler with storage
250 bBoiler_storage = BiomassBoiler(name="Biomasss_Boiler_1", thermal_capacity_kW=200, Größe_Holzlager=40, spez_Investitionskosten=200,
251 spez_Investitionskosten_Holzlager=400, Nutzungsgrad_BMK=0.8, min_Teillast=0.3, speicher_aktiv=True,
252 Speicher_Volumen=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0, min_fill=0.2, max_fill=0.8,
253 spez_Investitionskosten_Speicher=750, active=True, opt_BMK_min=0, opt_BMK_max=1000, opt_Speicher_min=0,
254 opt_Speicher_max=100)
255 test_biomass_boiler_storage(Last_L, duration, economic_parameters, bBoiler_storage)
256
257 # Test CHP without storage
258 CHP_1 = CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33,
259 KWK_Wirkungsgrad=0.9, min_Teillast=0.7, speicher_aktiv=False, Speicher_Volumen_BHKW=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0,
260 min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BHKW_min=0, opt_BHKW_max=1000, opt_BHKW_Speicher_min=0,
261 opt_BHKW_Speicher_max=100)
262 test_chp(Last_L, duration, economic_parameters, CHP_1)
263
264 # Test CHP with storage
265 CHP_storage = CHP(name="BHKW", th_Leistung_kW=100, spez_Investitionskosten_GBHKW=1500, spez_Investitionskosten_HBHKW=1850, el_Wirkungsgrad=0.33,
266 KWK_Wirkungsgrad=0.9, min_Teillast=0.7, speicher_aktiv=True, Speicher_Volumen_BHKW=20, T_vorlauf=90, T_ruecklauf=60, initial_fill=0.0,
267 min_fill=0.2, max_fill=0.8, spez_Investitionskosten_Speicher=750, active=True, opt_BHKW_min=0, opt_BHKW_max=1000, opt_BHKW_Speicher_min=0,
268 opt_BHKW_Speicher_max=100)
269 test_chp_storage(Last_L, duration, economic_parameters, CHP_storage)
270
271 # Test Waste Heat Pump
272 wasteHeatPump = WasteHeatPump(name="Abwärmepumpe", Kühlleistung_Abwärme=50, Temperatur_Abwärme=30, spez_Investitionskosten_Abwärme=500,
273 spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)
274 test_waste_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, wasteHeatPump)
275
276 # Test River Heat Pump
277 riverHeatPump = RiverHeatPump(name="Flusswärmepumpe", Wärmeleistung_FW_WP=200, Temperatur_FW_WP=10, dT=0, spez_Investitionskosten_Flusswasser=1000,
278 spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)
279 test_river_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, riverHeatPump)
280
281 # Test Geothermal Heat Pump
282 geothermal_heat_pump = Geothermal(name="Geothermie", Fläche=200, Bohrtiefe=100, Temperatur_Geothermie=10, spez_Bohrkosten=100, spez_Entzugsleistung=50,
283 Vollbenutzungsstunden=2400, Abstand_Sonden=10, spezifische_Investitionskosten_WP=1000, min_Teillast=0.2)
284 test_geothermal_heat_pump(Last_L, duration, economic_parameters, VLT_L, COP_data, geothermal_heat_pump)
285
286 # Test Solar Thermal
287 solarThermal = SolarThermal(name="Solarthermie", bruttofläche_STA=200, vs=20, Typ="Vakuumröhrenkollektor", kosten_speicher_spez=750, kosten_fk_spez=430, kosten_vrk_spez=590,
288 Tsmax=90, Longitude=-14.4222, STD_Longitude=-15, Latitude=51.1676, East_West_collector_azimuth_angle=0, Collector_tilt_angle=36, Tm_rl=60,
289 Qsa=0, Vorwärmung_K=8, DT_WT_Solar_K=5, DT_WT_Netz_K=5, opt_volume_min=0, opt_volume_max=200, opt_area_min=0, opt_area_max=2000)
290 test_solar_thermal(Last_L, duration, economic_parameters, VLT_L, RLT_L, TRY_data, time_steps, solarThermal)
Heat generator configuration and analysis.
Heat Generation Optimization
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
1import numpy as np
2from districtheatingsim.heat_generators.energy_system import EnergySystem
3from districtheatingsim.heat_generators.chp import CHP, CHPStrategy
4from districtheatingsim.heat_generators.gas_boiler import GasBoiler, GasBoilerStrategy
5from districtheatingsim.heat_generators.power_to_heat import PowerToHeat, PowerToHeatStrategy
6from districtheatingsim.heat_generators.biomass_boiler import BiomassBoiler, BiomassBoilerStrategy
7from districtheatingsim.heat_generators.river_heat_pump import RiverHeatPump
8from districtheatingsim.heat_generators.waste_heat_pump import WasteHeatPump
9from districtheatingsim.heat_generators.geothermal_heat_pump import Geothermal
10from districtheatingsim.heat_generators.base_heat_pumps import HeatPumpStrategy
11from districtheatingsim.heat_generators.solar_thermal import SolarThermal, SolarThermalStrategy
12from districtheatingsim.heat_generators.STES import STES
13
14from districtheatingsim.utilities.test_reference_year import import_TRY
15
16from matplotlib import pyplot as plt
17
18import pandas as pd
19import os
20
21# Testparameter
22time_steps = pd.date_range(start="2023-01-01", end="2023-12-31 23:00:00", freq="h").to_numpy()
23
24file_path = os.path.join('examples', 'data', 'Lastgang', 'Lastgang.csv')
25
26df = pd.read_csv(file_path, delimiter=';', encoding='utf-8')
27load_profile = df['Gesamtwärmebedarf_Gebäude_kW'].values
28VLT_L = np.random.uniform(85, 85, 8760)
29RLT_L = np.random.uniform(50, 50, 8760)
30
31TRY_filename = os.path.abspath('src/districtheatingsim/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat')
32TRY_data = import_TRY(TRY_filename)
33
34COP_filename = os.path.abspath('src/districtheatingsim/data/COP/Kennlinien WP.csv')
35COP_data = np.genfromtxt(COP_filename, delimiter=';')
36
37economic_parameters = {"gas_price": 50, "wood_price": 40, "electricity_price": 120, "capital_interest_rate": 0.05,
38 "inflation_rate": 0.03, "time_period": 20, "subsidy_eligibility": "Nein", "hourly_rate": 45} # Wirtschaftsparameter
39
40# Speicherparameter
41storage_params = {
42 "storage_type": "truncated_trapezoid", # Speichergeometrie
43 "dimensions": (20, 20, 50, 50, 15), # Geometrieparameter
44 "rho": 1000, # Dichte des Mediums (kg/m³)
45 "cp": 4180, # Spezifische Wärmekapazität (J/kg*K)
46 "T_ref": 10, # Referenztemperatur (°C)
47 "lambda_top": 0.04, # Wärmeleitfähigkeit der oberen Isolierung (W/m*K)
48 "lambda_side": 0.03, # Wärmeleitfähigkeit der seitlichen Isolierung (W/m*K)
49 "lambda_bottom": 0.05, # Wärmeleitfähigkeit der unteren Isolierung (W/m*K)
50 "lambda_soil": 1.5, # Wärmeleitfähigkeit des Bodens (W/m*K)
51 "dt_top": 0.3, # Dicke der oberen Isolierung (m)
52 "ds_side": 0.4, # Dicke der seitlichen Isolierung (m)
53 "db_bottom": 0.5, # Dicke der unteren Isolierung (m)
54 "T_amb": 10, # Umgebungstemperatur (°C)
55 "T_soil": 10, # Bodentemperatur (°C)
56 "T_max": 95, # Maximale Speichertemperatur (°C)
57 "T_min": 40, # Minimale Speichertemperatur (°C)
58 "initial_temp": 60, # Anfangstemperatur des Speichers (°C)
59 "hours": 8760, # Anzahl der Stunden in einem Jahr
60 "num_layers": 5, # Anzahl der Schichten für die Schichtung
61 "thermal_conductivity": 0.6 # Wärmeleitfähigkeit des Mediums (W/m*K)
62}
63
64# Initialisiere das Energiesystem
65energy_system = EnergySystem(time_steps, load_profile, VLT_L, RLT_L, TRY_data, COP_data, economic_parameters)
66
67# Initialisiere den Speicher
68storage = STES(name="Saisonalspeicher", **storage_params)
69energy_system.add_storage(storage)
70
71# Füge Generatoren hinzu
72chp = CHP("BHKW_1", th_Leistung_kW=500)
73energy_system.add_technology(chp)
74chp.strategy = CHPStrategy(charge_on=75, charge_off=70)
75
76#pth = PowerToHeat("PtH_1", thermal_capacity_kW=500)
77#energy_system.add_technology(pth)
78#pth.strategy = PowerToHeatStrategy(charge_on=75)
79
80# Biomasssboiler hinzufügen
81#biomass_boiler = BiomassBoiler("Biomassekessel_1", thermal_capacity_kW=500)
82#energy_system.add_technology(biomass_boiler)
83#biomass_boiler.strategy = BiomassBoilerStrategy(charge_on=75, charge_off=70)
84
85# Gasboiler hinzufügen
86#gas_boiler = GasBoiler("Gasboiler_1", thermal_capacity_kW=1000)
87#energy_system.add_technology(gas_boiler)
88#gas_boiler.strategy = GasBoilerStrategy(charge_on=70)
89
90# Erneuerbare Energien hinzufügen
91#river_heat_pump = RiverHeatPump("Flusswärmepumpe_1", Wärmeleistung_FW_WP=600, Temperatur_FW_WP=20)
92#energy_system.add_technology(river_heat_pump)
93#river_heat_pump.strategy = HeatPumpStrategy(charge_on=75, charge_off=70)
94
95#waste_heat_pump = WasteHeatPump("Abwärmepumpe_1", Kühlleistung_Abwärme=600, Temperatur_Abwärme=20)
96#energy_system.add_technology(waste_heat_pump)
97#waste_heat_pump.strategy = HeatPumpStrategy(charge_on=75, charge_off=70)
98
99#waste_water_heat_pump = WasteHeatPump("Abwasserwärmepumpe_1", Kühlleistung_Abwärme=200, Temperatur_Abwärme=20)
100#energy_system.add_technology(waste_water_heat_pump)
101#waste_water_heat_pump.strategy = HeatPumpStrategy(charge_on=75, charge_off=70)
102
103#geothermal_heat_pump = Geothermal("Geothermie_1", Fläche=10000, Bohrtiefe=100, Temperatur_Geothermie=20)
104#energy_system.add_technology(geothermal_heat_pump)
105#geothermal_heat_pump.strategy = HeatPumpStrategy(charge_on=75, charge_off=70)
106
107#solar_thermal = SolarThermal("Solarthermie_1", bruttofläche_STA=2500, vs=50, Typ="Vakuumröhrenkollektor")
108#energy_system.add_technology(solar_thermal)
109#solar_thermal.strategy = SolarThermalStrategy(charge_on=75, charge_off=70)
110
111
112# Berechne den Energiemix mit Speicher
113results = energy_system.calculate_mix()
114
115# Ergebnisse anzeigen
116print("Simulationsergebnisse:")
117print(f"Speicherwirkungsgrad: {results['storage_class'].efficiency*100:.2f}%")
118# print(f"Betriebskosten: {results['storage_class'].operational_costs:.2f} €") # Not yet implemented in STES
119print(f"Überschüssige Wärme durch Stagnation: {results['storage_class'].excess_heat:.2f} kWh")
120print(f"Nicht gedeckter Bedarf aufgrund von Speicherentleerung: {results['storage_class'].unmet_demand:.2f} kWh")
121print(f"Stagnationsdauer: {results['storage_class'].stagnation_time} h")
122
123
124# Ergebnisse plotten
125results['storage_class'].plot_results(results["Wärmeleistung_L"][0], load_profile, VLT_L, RLT_L)
126energy_system.plot_stack_plot()
127energy_system.plot_pie_chart()
128plt.show()
Seasonal energy storage systems example.
Utility Examples
STANET to Pandapipes Conversion
1"""
2Filename: 18_stanet_to_pandapipes.py
3Author: Dipl.-Ing. (FH) Jonas Pfeiffer
4Date: 2025-05-21
5Description: This script demonstrates how to use the `create_net_from_stanet_csv` function from the `feature_develop.stanet_import_pandapipes` module.
6
7"""
8
9from stanet_import_pandapipes import create_net_from_stanet_csv
10import pandapipes as pp
11
12if __name__ == "__main__":
13 # Example usage
14 stanet_csv_file_path= "examples/data/STANET/Example_STANET_ETRS89.CSV"
15 TRY_file_path = "examples/data/TRY/TRY_511676144222/TRY2015_511676144222_Jahr.dat"
16 supply_temperature = 80 # Supply temperature in Celsius
17 flow_pressure_pump = 4.0 # Flow pressure of the pump in bar
18 lift_pressure_pump = 1.5 # Lift pressure of the pump in bar
19
20 net, yearly_time_steps, total_heat_W, max_heat_requirement_W = create_net_from_stanet_csv(stanet_csv_file_path, TRY_file_path, supply_temperature, flow_pressure_pump, lift_pressure_pump)
STANET to pandapipes conversion example.
Generator Schematic Test
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.