Tutorial 62: Pandapipes

This Example demonstrates how to generate a pandapipes network based on a SIR 3S model. When working with SIR 3S, pandapipes used to offer some freedom in creating simple DH models via Python, but with the introduction of the SIR 3S Toolkit, pandapipes is no longer necessary for this. Therefore the main benefit of pandapipes for SIR 3S users is for comparing calculations to SIR 3S results.

Note that the SIR 3S model we are using in this Example is quite simple.

In this Example we will first generate the pandapipes network manually by obtaining element dataframes for pipes and nodes via SIR 3S Toolkit. Then we will perform the same steps but built into one single Toolkit function.

Toolkit Release

[1]:
#pip install

Imports

SIR 3S Toolkit

Regular Import/Init

[2]:
SIR3S_SIRGRAF_DIR = r"C:\3S\SIR 3S\SirGraf-90-15-00-20x64_Quebec-Upd1" #change to local path
[3]:
import sir3stoolkit
[4]:
from sir3stoolkit.core import wrapper
[5]:
sir3stoolkit
[5]:
<module 'sir3stoolkit' from 'C:\\Users\\aUsername\\3S\\sir3stoolkit\\src\\sir3stoolkit\\__init__.py'>
[6]:
wrapper.Initialize_Toolkit(SIR3S_SIRGRAF_DIR)

Additional Import/Init for Alternative_Models class

[7]:
from sir3stoolkit.mantle import alternative_models
[8]:
s3s = alternative_models.SIR3S_Model_Alternative_Models()
Initialization complete

Additional

[9]:
import pandas as pd
[10]:
import pandapipes as pp
[11]:
from shapely import wkt
[12]:
import pandapipes.plotting as pp_plot

Open SIR 3S Model

[13]:
s3s.OpenModel(dbName=r"C:\Users\aUsername\3S\PT3S\PT3S\Examples\Example9_2.db3",
              providerType=s3s.ProviderTypes.SQLite,
              Mid="M-1-0-1",
              saveCurrentlyOpenModel=False,
              namedInstance="",
              userID="",
              password="")
Model is open for further operation

Generate Pandapipes network manually

Create pandapipes network

Nodes

[14]:
net = pp.create_empty_network(fluid="water")
[15]:
df_nodes_model_data = s3s.generate_element_model_data_dataframe(element_type=s3s.ObjectTypes.Node, properties=['Name', 'QmEin', 'bz.PhEin', 'Zkor', 'Ktyp'], geometry=True)
[2026-01-09 15:38:16,057] INFO in sir3stoolkit.mantle.dataframes: [model_data] Generating model_data dataframe for element type: ObjectTypes.Node
[2026-01-09 15:38:16,064] INFO in sir3stoolkit.mantle.dataframes: [model_data] Retrieved 9 element(s) of element type ObjectTypes.Node.
[2026-01-09 15:38:16,079] INFO in sir3stoolkit.mantle.dataframes: [Resolving model_data Properties] Using 5 model_data properties.
[2026-01-09 15:38:16,080] INFO in sir3stoolkit.mantle.dataframes: [model_data] Retrieving model_data properties ['Name', 'QmEin', 'bz.PhEin', 'Zkor', 'Ktyp'], geometry...
[2026-01-09 15:38:16,116] INFO in sir3stoolkit.mantle.dataframes: [model_data] Transforming DataFrame to GeoDataFrame successful with EPSG: 25832
[2026-01-09 15:38:16,116] INFO in sir3stoolkit.mantle.dataframes: [model_data] Done. Shape: (9, 7)
[16]:
df_nodes_model_data.head(3)
[16]:
tk Name QmEin bz.PhEin Zkor Ktyp geometry
0 5136506604482101815 K0000 0 100 10 PKON POINT (150 500)
1 5174640379525019821 K0001 0 0 10 QKON POINT (350 600)
2 5665004361761998834 K0002 0 0 10 QKON POINT (550 600)
[17]:
df_nodes_results = s3s.generate_element_results_dataframe(element_type=s3s.ObjectTypes.Node, properties=['PH', 'T', 'QM'], timestamps=s3s.GetTimeStamps()[0])
[2026-01-09 15:38:16,163] INFO in sir3stoolkit.mantle.dataframes: [results] Generating results dataframe for element type: ObjectTypes.Node
[2026-01-09 15:38:16,167] INFO in sir3stoolkit.mantle.dataframes: [Resolving Timestamps] Only static timestamp 2025-06-05 15:27:46.000 +02:00 is used
[2026-01-09 15:38:16,167] INFO in sir3stoolkit.mantle.dataframes: [Resolving Timestamps] 1 valid timestamp(s) will be used.
[2026-01-09 15:38:16,173] INFO in sir3stoolkit.mantle.dataframes: [Resolving tks] Retrieved 9 element(s) of element type ObjectTypes.Node.
[2026-01-09 15:38:16,174] INFO in sir3stoolkit.mantle.dataframes: [results] Using 3 result properties.
[2026-01-09 15:38:16,244] INFO in sir3stoolkit.mantle.dataframes: [results] Retrieving result values...
[2026-01-09 15:38:16,266] INFO in sir3stoolkit.mantle.dataframes: [results] Done. Shape: (1, 27)
[18]:
df_nodes_results.head(3)
[18]:
tk 5136506604482101815 5174640379525019821 5665004361761998834 5185493728872360834 4776931467066465913 5229509832448527475 5073490478136313655 5417314986988666587 5366461594623940805
name K0000 K0001 K0002 K0003 K0004 K0005 K0006 K0007 K0008
end_nodes No end nodes on element type No end nodes on element type No end nodes on element type No end nodes on element type No end nodes on element type No end nodes on element type No end nodes on element type No end nodes on element type No end nodes on element type
property PH T QM PH T QM PH T QM PH T QM PH T QM PH T QM PH T QM PH T QM PH T QM
timestamp
2025-06-05 15:27:46.000 +02:00 100.0 0.0 400.0 99.98262 0.0 0.0 99.91648 0.0 0.0 99.77149 0.0 -100.0 99.77149 0.0 -100.0 99.95844 0.0 0.0 99.91686 0.0 0.0 99.90903 0.0 -100.0 99.90903 0.0 -100.0
[19]:
df_nodes_results.columns = df_nodes_results.columns.droplevel([1, 2])
df_nodes_results = df_nodes_results.T.unstack(level=0).T
df_nodes_results = df_nodes_results.droplevel(0, axis=0)
df_nodes = df_nodes_model_data.merge(on="tk",
                    how="outer",
                    right=df_nodes_results)
[20]:
df_nodes.head(3)
[20]:
tk Name QmEin bz.PhEin Zkor Ktyp geometry PH QM T
0 4776931467066465913 K0004 -100 0 10 QKON POINT (750 550) 99.77149 -100.0 0.0
1 5073490478136313655 K0006 0 0 10 QKON POINT (550 400) 99.91686 0.0 0.0
2 5136506604482101815 K0000 0 100 10 PKON POINT (150 500) 100.00000 400.0 0.0
[21]:
js = {}

for idx, row in df_nodes.iterrows():
    geom = row["geometry"]
    x, y = geom.x, geom.y

    j = pp.create_junction(
        net,
        pn_bar=1 + float(row['PH']),
        tfluid_k=273.15 + float(row['T']),
        height_m=float(row['Zkor']),
        name=f"{row['Name']}~{row['tk']}"
    )

    # Assign geodata to junction_geodata table
    net.junction_geodata.at[j, "x"] = x
    net.junction_geodata.at[j, "y"] = y

    js[row['tk']] = j
[22]:
net.junction
[22]:
name pn_bar tfluid_k height_m in_service type
0 K0004~4776931467066465913 100.77149 273.15 10.0 True junction
1 K0006~5073490478136313655 100.91686 273.15 10.0 True junction
2 K0000~5136506604482101815 101.00000 273.15 10.0 True junction
3 K0001~5174640379525019821 100.98262 273.15 10.0 True junction
4 K0003~5185493728872360834 100.77149 273.15 10.0 True junction
5 K0005~5229509832448527475 100.95844 273.15 10.0 True junction
6 K0008~5366461594623940805 100.90903 273.15 10.0 True junction
7 K0007~5417314986988666587 100.90903 273.15 10.0 True junction
8 K0002~5665004361761998834 100.91648 273.15 10.0 True junction

pipes

[23]:
df_pipes_model_data = s3s.generate_element_model_data_dataframe(element_type=s3s.ObjectTypes.Pipe, properties=['L', 'Di', 'Rau', 'Name'], end_nodes=True, geometry=True)
[2026-01-09 15:38:16,395] INFO in sir3stoolkit.mantle.dataframes: [model_data] Generating model_data dataframe for element type: ObjectTypes.Pipe
[2026-01-09 15:38:16,395] INFO in sir3stoolkit.mantle.dataframes: [model_data] Retrieved 9 element(s) of element type ObjectTypes.Pipe.
[2026-01-09 15:38:16,399] INFO in sir3stoolkit.mantle.dataframes: [Resolving model_data Properties] Using 4 model_data properties.
[2026-01-09 15:38:16,399] INFO in sir3stoolkit.mantle.dataframes: [model_data] Retrieving model_data properties ['L', 'Di', 'Rau', 'Name'], geometry, end nodes...
[2026-01-09 15:38:16,447] INFO in sir3stoolkit.mantle.dataframes: [model_data] 2 non-empty end node columns were created.
[2026-01-09 15:38:16,466] INFO in sir3stoolkit.mantle.dataframes: [model_data] Transforming DataFrame to GeoDataFrame successful with EPSG: 25832
[2026-01-09 15:38:16,467] INFO in sir3stoolkit.mantle.dataframes: [model_data] Done. Shape: (9, 8)
[24]:
df_pipes_model_data.head(3)
[24]:
tk L Di Rau Name geometry fkKI fkKK
0 4762947358005495341 1000 450 0,25 Rohr K0000 K0001 LINESTRING (150 500, 350 600) 5136506604482101815 5174640379525019821
1 5479123649362439650 1000 250 0,25 Rohr K0002 K0003 LINESTRING (550 600, 750 650) 5665004361761998834 5185493728872360834
2 5367303884852200682 1000 250 0,25 Rohr K0002 K0004 LINESTRING (550 600, 750 550) 5665004361761998834 4776931467066465913
[25]:
df_pipes_model_data['Rau'] = df_pipes_model_data['Rau'].str.replace(',', '.', regex=False)
df_pipes_model_data['L'] = df_pipes_model_data['L'].str.replace(',', '.', regex=False)
[26]:
df_pipes_model_data['L'] = df_pipes_model_data['L'].astype(float)
[27]:
ps = {}

for idx, row in df_pipes_model_data.iterrows():
    geom = row["geometry"]
    coords = list(geom.coords)

    # Create pipe
    p = pp.create_pipe_from_parameters(
        net,
        from_junction=js[row['fkKI']],
        to_junction=js[row['fkKK']],
        length_km=float(row['L']) / 1000.,
        diameter_m=float(row['Di']) / 1000.,
        k_mm=float(row['Rau']),
        name=f"{row['Name']}~{row['tk']}"
    )
    ps[row['tk']] = p

    net.pipe_geodata.at[p, "coords"] = coords
[28]:
net.pipe.head(3)
[28]:
name from_junction to_junction std_type length_km diameter_m k_mm loss_coefficient u_w_per_m2k text_k qext_w sections in_service type
0 Rohr K0000 K0001~4762947358005495341 2 3 None 1.0 0.45 0.25 0.0 0.0 NaN 0.0 1 True pipe
1 Rohr K0002 K0003~5479123649362439650 8 4 None 1.0 0.25 0.25 0.0 0.0 NaN 0.0 1 True pipe
2 Rohr K0002 K0004~5367303884852200682 8 0 None 1.0 0.25 0.25 0.0 0.0 NaN 0.0 1 True pipe

sources/sinks

[29]:
for idx, row in df_nodes.iterrows():
    ktyp = (row.get("Ktyp"))
    tk = row.get("tk")

    # Create source if Ktyp is PKON and PH > 0
    if ktyp == "PKON" and float(row.get("PH", 0)) > 0:
        pp.create_ext_grid(
            net,
            junction=js[tk],
            p_bar=1 + float(row["PH"]),
            t_k=273.15 + float(row["T"]),
            name=f"Src: {row['Name']}~{tk}"
        )

    # Create sink if Ktyp is QKON and QM < 0
    elif ktyp == "QKON" and float(row.get("QM", 0)) < 0:
        pp.create_sink(
            net,
            junction=js[tk],
            mdot_kg_per_s=abs(float(row["QM"])),
            name=f"Snk: {row['Name']}~{tk}"
        )
[30]:
net.ext_grid
[30]:
name junction p_bar t_k in_service type
0 Src: K0000~5136506604482101815 2 101.0 273.15 True pt
[31]:
net.sink
[31]:
name junction mdot_kg_per_s scaling in_service type
0 Snk: K0004~4776931467066465913 0 100.0 1.0 True sink
1 Snk: K0003~5185493728872360834 4 100.0 1.0 True sink
2 Snk: K0008~5366461594623940805 6 100.0 1.0 True sink
3 Snk: K0007~5417314986988666587 7 100.0 1.0 True sink

Calculate

[32]:
pp.pipeflow(net)

View Results

[33]:
net.res_junction
[33]:
p_bar t_k
0 98.417039 273.15
1 100.068190 273.15
2 101.000000 273.15
3 100.823662 273.15
4 98.417039 273.15
5 100.534095 273.15
6 99.991334 273.15
7 99.991334 273.15
8 100.064571 273.15
[34]:
net.res_pipe
[34]:
v_mean_m_per_s p_from_bar p_to_bar t_from_k t_to_k t_outlet_k mdot_from_kg_per_s mdot_to_kg_per_s vdot_m3_per_s reynolds lambda
0 0.956300 101.000000 100.823662 273.15 273.15 273.15 152.074654 -152.074654 0.152093 241084.757059 0.017356
1 2.037427 100.064571 98.417039 273.15 273.15 273.15 100.000000 -100.000000 0.100012 285354.955734 0.019847
2 2.037427 100.064571 98.417039 273.15 273.15 273.15 100.000000 -100.000000 0.100012 285354.955734 0.019847
3 1.559043 101.000000 100.534095 273.15 273.15 273.15 247.925346 -247.925346 0.247955 393037.366793 0.017253
4 1.559043 100.534095 100.068190 273.15 273.15 273.15 247.925346 -247.925346 0.247955 393037.366793 0.017253
5 0.628836 100.068190 99.991334 273.15 273.15 273.15 100.000000 -100.000000 0.100012 158530.530963 0.017494
6 0.628836 100.068190 99.991334 273.15 273.15 273.15 100.000000 -100.000000 0.100012 158530.530963 0.017494
7 3.098411 100.823662 100.064571 273.15 273.15 273.15 152.074654 -152.074654 0.152093 433952.562707 0.019770
8 -0.301372 100.064571 100.068190 273.15 273.15 273.15 -47.925346 47.925346 -0.047931 75976.304867 0.017933
[35]:
net.res_sink
[35]:
mdot_kg_per_s
0 100.0
1 100.0
2 100.0
3 100.0
[36]:
net.res_ext_grid
[36]:
mdot_kg_per_s
0 -400.0

Plot

[37]:
pp_plot.simple_plot(net)
../../_images/tutorials_SIR3S_Model_Mantle_ToolkitTutorial062_55_0.png
[37]:
<Axes: >
[38]:
import plotly.express as px

junctions = net.res_junction.copy()
junctions["x"] = net.junction_geodata["x"]
junctions["y"] = net.junction_geodata["y"]

fig = px.scatter(
    junctions,
    x="x", y="y",
    color="p_bar",
    hover_data=["p_bar"],
    title="Junctions: Pressure and Temperature",
    labels={"p_bar": "Pressure [bar]"}
)
fig.show()

Data type cannot be displayed: application/vnd.plotly.v1+json

The above depiction is not displayed in the online documentation.

Generating Pandapipes network using alternative_models() class from SIR 3S Toolkit

Create

Now we can perform the same steps above confined into one method using SIR_3S_to_pandapipes().

[39]:
pp_net = s3s.SIR_3S_to_pandapipes()
[2026-01-09 15:38:25,372] INFO in sir3stoolkit.mantle.dataframes: [model_data] Generating model_data dataframe for element type: ObjectTypes.Node
[2026-01-09 15:38:25,374] INFO in sir3stoolkit.mantle.dataframes: [model_data] Retrieved 9 element(s) of element type ObjectTypes.Node.
[2026-01-09 15:38:25,376] INFO in sir3stoolkit.mantle.dataframes: [Resolving model_data Properties] Using 5 model_data properties.
[2026-01-09 15:38:25,377] INFO in sir3stoolkit.mantle.dataframes: [model_data] Retrieving model_data properties ['Name', 'Zkor', 'QmEin', 'bz.PhEin', 'Ktyp'], geometry...
[2026-01-09 15:38:25,387] INFO in sir3stoolkit.mantle.dataframes: [model_data] Transforming DataFrame to GeoDataFrame successful with EPSG: 25832
[2026-01-09 15:38:25,388] INFO in sir3stoolkit.mantle.dataframes: [model_data] Done. Shape: (9, 7)
[2026-01-09 15:38:25,402] INFO in sir3stoolkit.mantle.dataframes: [results] Generating results dataframe for element type: ObjectTypes.Node
[2026-01-09 15:38:25,413] INFO in sir3stoolkit.mantle.dataframes: [Resolving Timestamps] Only static timestamp 2025-06-05 15:27:46.000 +02:00 is used
[2026-01-09 15:38:25,414] INFO in sir3stoolkit.mantle.dataframes: [Resolving Timestamps] 1 valid timestamp(s) will be used.
[2026-01-09 15:38:25,416] INFO in sir3stoolkit.mantle.dataframes: [Resolving tks] Retrieved 9 element(s) of element type ObjectTypes.Node.
[2026-01-09 15:38:25,418] INFO in sir3stoolkit.mantle.dataframes: [results] Using 3 result properties.
[2026-01-09 15:38:25,424] INFO in sir3stoolkit.mantle.dataframes: [results] Retrieving result values...
[2026-01-09 15:38:25,435] INFO in sir3stoolkit.mantle.dataframes: [results] Done. Shape: (1, 27)
[2026-01-09 15:38:25,462] INFO in sir3stoolkit.mantle.dataframes: [model_data] Generating model_data dataframe for element type: ObjectTypes.Pipe
[2026-01-09 15:38:25,467] INFO in sir3stoolkit.mantle.dataframes: [model_data] Retrieved 9 element(s) of element type ObjectTypes.Pipe.
[2026-01-09 15:38:25,468] INFO in sir3stoolkit.mantle.dataframes: [Resolving model_data Properties] Using 4 model_data properties.
[2026-01-09 15:38:25,468] INFO in sir3stoolkit.mantle.dataframes: [model_data] Retrieving model_data properties ['L', 'Di', 'Rau', 'Name'], geometry, end nodes...
[2026-01-09 15:38:25,474] INFO in sir3stoolkit.mantle.dataframes: [model_data] 2 non-empty end node columns were created.
[2026-01-09 15:38:25,486] INFO in sir3stoolkit.mantle.dataframes: [model_data] Transforming DataFrame to GeoDataFrame successful with EPSG: 25832
[2026-01-09 15:38:25,486] INFO in sir3stoolkit.mantle.dataframes: [model_data] Done. Shape: (9, 8)

Calculate

[40]:
pp.pipeflow(net)

View Results

[41]:
net.res_junction
[41]:
p_bar t_k
0 98.417039 273.15
1 100.068190 273.15
2 101.000000 273.15
3 100.823662 273.15
4 98.417039 273.15
5 100.534095 273.15
6 99.991334 273.15
7 99.991334 273.15
8 100.064571 273.15
[42]:
net.res_pipe
[42]:
v_mean_m_per_s p_from_bar p_to_bar t_from_k t_to_k t_outlet_k mdot_from_kg_per_s mdot_to_kg_per_s vdot_m3_per_s reynolds lambda
0 0.956300 101.000000 100.823662 273.15 273.15 273.15 152.074654 -152.074654 0.152093 241084.757059 0.017356
1 2.037427 100.064571 98.417039 273.15 273.15 273.15 100.000000 -100.000000 0.100012 285354.955734 0.019847
2 2.037427 100.064571 98.417039 273.15 273.15 273.15 100.000000 -100.000000 0.100012 285354.955734 0.019847
3 1.559043 101.000000 100.534095 273.15 273.15 273.15 247.925346 -247.925346 0.247955 393037.366793 0.017253
4 1.559043 100.534095 100.068190 273.15 273.15 273.15 247.925346 -247.925346 0.247955 393037.366793 0.017253
5 0.628836 100.068190 99.991334 273.15 273.15 273.15 100.000000 -100.000000 0.100012 158530.530963 0.017494
6 0.628836 100.068190 99.991334 273.15 273.15 273.15 100.000000 -100.000000 0.100012 158530.530963 0.017494
7 3.098411 100.823662 100.064571 273.15 273.15 273.15 152.074654 -152.074654 0.152093 433952.562707 0.019770
8 -0.301372 100.064571 100.068190 273.15 273.15 273.15 -47.925346 47.925346 -0.047931 75976.304867 0.017933
[43]:
net.res_sink
[43]:
mdot_kg_per_s
0 100.0
1 100.0
2 100.0
3 100.0
[44]:
net.res_ext_grid
[44]:
mdot_kg_per_s
0 -400.0

Plot

[45]:
import plotly.express as px

junctions = net.res_junction.copy()
junctions["x"] = net.junction_geodata["x"]
junctions["y"] = net.junction_geodata["y"]

fig = px.scatter(
    junctions,
    x="x", y="y",
    color="p_bar",
    hover_data=["p_bar"],
    title="Junctions: Pressure and Temperature",
    labels={"p_bar": "Pressure [bar]"}
)
fig.show()

Data type cannot be displayed: application/vnd.plotly.v1+json

The above depiction is not displayed in the online documentation.