Example 7: Source Spectrum and Fluid Age

This example demonstrates how GeoDataFrames (gdfs) and V3_dataframe created by PT3S can be used with matplotlib to create an interactive depiction of a source spectrum.

Example 7.1: Source Spectrum

PT3S Release

[1]:
#pip install PT3S -U --no-deps

Necessary packages for this Example

When running this example for the first time on your machine, please execute the cell below. Afterward, you may need to restart the kernel (using the ‘fast-forward’ button).

[2]:
#!pip install -q shapely ipywidgets

Imports

[3]:
import os
import logging
import pandas as pd
from pandas import Timestamp
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import geopandas as gpd
from shapely.geometry import LineString, Point
from matplotlib.patches import Circle
import ipywidgets as widgets
from matplotlib.collections import LineCollection
from matplotlib.cm import get_cmap
import networkx
import re
from collections import deque
from shapely.geometry import LineString
import networkx
from scipy.sparse import csc_matrix
from matplotlib.colors import Normalize



#...

try:
    from PT3S import dxAndMxHelperFcts
except:
    import dxAndMxHelperFcts

try:
    from PT3S import Rm
except:
    import Rm

try:
    from PT3S import ncd
except:
    import ncd
#...
[4]:
import importlib
from importlib import resources
[5]:
#importlib.reload(ncd)

Logging

[7]:
logger = logging.getLogger()

if not logger.handlers:
    logFileName = r"Example7.log"
    logledirection = logging.DEBUG

    logging.basicConfig(
        filename=logFileName,
        filemode='w',
        ledirection=logledirection,
        format="%(asctime)s ; %(name)-60s ; %(ledirectionname)-7s ; %(message)s"
    )

    fileHandler = logging.FileHandler(logFileName)
    logger.addHandler(fileHandler)

    consoleHandler = logging.StreamHandler()
    consoleHandler.setFormatter(logging.Formatter("%(ledirectionname)-7s ; %(message)s"))
    consoleHandler.setLedirection(logging.INFO)
    logger.addHandler(consoleHandler)

Read Model and Results

[8]:
dbFilename="Example7"
dbFile = resources.files("PT3S").joinpath("Examples", f"{dbFilename}.db3")
[9]:
m=dxAndMxHelperFcts.readDxAndMx(dbFile=dbFile
                                ,preventPklDump=True
                                ,maxRecords=-1
                                #,SirCalcExePath=r"C:\3S\SIR 3S\SirCalc-90-14-02-12_Potsdam.fix1_x64\SirCalc.exe"
                                )

Preparing Data

[10]:
dfKNOT=m.gdf_KNOT
[11]:
dfROHR=m.gdf_ROHR
[12]:
# Get soure signatures for start and end knot
dfROHR['srcvector_fkKI'] = dfROHR['fkKI'].map(dfKNOT.set_index('tk')['srcvector'])
dfROHR['srcvector_fkKK'] = dfROHR['fkKK'].map(dfKNOT.set_index('tk')['srcvector'])
[13]:
QM=('STAT',
  'ROHR~*~*~*~QMAV',
  Timestamp('2025-09-23 22:00:00'),
  Timestamp('2025-09-23 22:00:00'))
[14]:
dfROHR['srcvector_plot'] = np.where(dfROHR[QM] > 0, dfROHR['srcvector_fkKI'], dfROHR['srcvector_fkKK'])
[15]:
dfROHR = dfROHR[dfROHR['KVR'] != 2.0]

Plotting

[16]:
colors = [np.array([255, 0, 0]), np.array([0, 0, 255])]

DH Feeder

[17]:
pA=dfKNOT[dfKNOT['pk']=="5130118215602471518"]['geometry']
[18]:
pB=dfKNOT[dfKNOT['pk']=="5428490852894646653"]['geometry']
[19]:
src_A_coords = (pA.x, pA.y)
src_B_coords = (pB.x, pB.y)
[20]:
edge_colors = [c / 255.0 for c in colors]

Plot

[21]:
fig, ax = plt.subplots(figsize=Rm.DINA3q)
ncd.plot_src_spectrum(ax, dfROHR,'srcvector_plot', colors, dn_col='DN', lw_min=0.5, lw_max=6.0)

# Points
pts = np.array([src_A_coords, src_B_coords])
ax.scatter(pts[:, 0], pts[:, 1],
           s=300, marker='*',
           c=edge_colors,
           edgecolors='white', linewidths=1.0,
           zorder=10, label='Sources')

plt.show()

xlim = ax.get_xlim()
ylim = ax.get_ylim()
canvas_center = ((xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2)

fig.savefig('Example7_Output_1.pdf')
_images/Example7_33_0.png

For each pipe a color (between A: red and B: blue) is mixed based on the srcvector present in its start and end node.

[22]:
ax = ncd.plot_src_spectrum(
    gdf=dfROHR,
    attribute="srcvector_plot",
    colors=[np.array([255,0,0]), np.array([0,0,255])],
    dn_col='DN', lw_min=0.5, lw_max=6.0,
    plot_pies=True,
    n_pies=7,
    ratio_decimals=2,
    pie_radius_rel=0.04,
    connector_kwargs=dict(color='black', lw=1.2, ls='--', alpha=0.7),
    debug_pie_centers=True,
    force_equal_aspect=True,
    draw_mixture_scale=False
)
_images/Example7_35_0.png

Example 7.2 Fluid age

Preparing Data

[23]:
dfVBEL=m.V3_VBEL
[24]:
dfVBEL=dfVBEL.reset_index()
[25]:
lookup_TTR = dfKNOT.set_index('tk')['TTR']
[26]:
dfVBEL['TTR_KI'] = dfVBEL['fkKI'].map(lookup_TTR)
dfVBEL['TTR_KK'] = dfVBEL['fkKK'].map(lookup_TTR)

Geometry

[27]:
lookup_geometry = dfROHR.set_index('tk')['geometry']
lookup_XKOR = dfKNOT.set_index('tk')['XKOR']
lookup_YKOR = dfKNOT.set_index('tk')['YKOR']
[28]:
dfVBEL['geometry']=dfVBEL['OBJID'].map(lookup_geometry)
dfVBEL['XKOR_KI']=dfVBEL['fkKI'].map(lookup_XKOR)
dfVBEL['XKOR_KK']=dfVBEL['fkKK'].map(lookup_XKOR)
dfVBEL['YKOR_KI']=dfVBEL['fkKI'].map(lookup_YKOR)
dfVBEL['YKOR_KK']=dfVBEL['fkKK'].map(lookup_YKOR)
[29]:
dfVBEL['geometry'] = dfVBEL.apply(
    lambda row: row['geometry'] if pd.notnull(row['geometry']) else LineString([(row['XKOR_KI'], row['YKOR_KI']), (row['XKOR_KK'], row['YKOR_KK'])]),
    axis=1
)

KVR

[30]:
lookup_kvr = dfKNOT.set_index('tk')['KVR']
[31]:
dfVBEL['KVR_KI']=dfVBEL['fkKI'].map(lookup_kvr)
[32]:
dfVBEL['KVR_KK']=dfVBEL['fkKK'].map(lookup_kvr)

DN

[33]:
lookup_DN = dfROHR.set_index('tk')['DN']
[34]:
dfVBEL['DN']=dfVBEL['OBJID'].map(lookup_DN)
[35]:
dfVBEL['DN']=dfVBEL['DN'].fillna(100) # assume DN=100mm for all non-pipe edges
[36]:
dfVBEL = dfVBEL[(dfVBEL['XKOR_KI'].isna()) | (dfVBEL['XKOR_KI'] > 40000)]
dfVBEL = dfVBEL[(dfVBEL['XKOR_KK'].isna()) | (dfVBEL['XKOR_KK'] > 40000)]
[37]:
dfVBEL.head(3)
[37]:
OBJTYPE OBJID pk fkDE rk fkKI BESCHREIBUNG DGR DKL ALPHA ... mlc_k TTR_KI TTR_KK geometry XKOR_KI XKOR_KK YKOR_KI YKOR_KK KVR_KI KVR_KK
2 FWVB 4615167946623235098 4727459786481949539 5613149064237404433 4727459786481949539 5748663417295233893 groesster FWVB von MEHRFACH-FWVB am selben Kno... NaN NaN NaN ... 88.827044 0.388912 0.0 LINESTRING (48964.7865681636 97889.1867773421,... 48964.786568 48964.786568 97889.186777 97889.186777 1.0 2.0
3 FWVB 4615393182465694100 5282002010269240713 5613149064237404433 5282002010269240713 5096987444312174224 groesster FWVB von MEHRFACH-FWVB am selben Kno... NaN NaN NaN ... 95.780795 1.873637 0.0 LINESTRING (46651.6069416646 97713.81722454, 4... 46651.606942 46651.606942 97713.817225 97713.817225 1.0 2.0
4 FWVB 4616022158288753538 4762579314203629624 5613149064237404433 4762579314203629624 5724334807077180319 groesster FWVB von MEHRFACH-FWVB am selben Kno... NaN NaN NaN ... 82.566185 1.233922 0.353222 LINESTRING (54845.3976874947 97480.8326112069,... 54845.397687 54845.397687 97480.832611 97480.832611 1.0 2.0

3 rows × 618 columns

Plotting

[38]:
ax, nodes = ncd.plot_ttr_network(
    dfVBEL,
    dn_col="DN",
    geometry_col="geometry",
    ttr_norm="percentile", ttr_percentiles=(5, 95),
    linewidth_range=(0.5, 8),
    highlight_keys=[5011622445515665493, 5152316559510921719],
    highlight_match="both",
    highlight_marker_size=180
)
plt.show()
_images/Example7_57_0.png