def find_residuals(pressure_predictions_L: pd.core.frame.DataFrame, leak_start: int, leak_end: int) -> pd.core.frame.Series: """ For every modeled node calculate residuum used for leak detection. Residuum is a diffrence between: y - actual pressure values coming from a simulation WITHOUT leaks y_hat - values predicted by linear regression models using data from simulations WITH leaks The function calculates diffrences in leak timeframe and returns the minimal value as a residual Arguments: pressure_predictions_L - y mentioned above, from predict_pressure_on_leaks() leak_start - leak start time in hours (int) leak_end - leak end time in hours (int) Returns: pd.core.frame.Series - Series of residuals for each modeled node """ G = ng.create_graph() # get simulation results for the network WITHOUT leaks sim_results_nL = wntr_WSN.get_sim_results() # select the leak timeframe (index values in seconds) nodes_pressure_nL = sim_results_nL.node['pressure'].loc[[3600*i for i in range(leak_start, leak_end+1)]] # select columns corresponding to nodes that have predictions ready (to subtract DataFrames cleanly) nodes_pressure_nL = nodes_pressure_nL[pressure_predictions_L.columns.tolist()] # get absolute diffrence, then choose the minima for each node (column) #results = (nodes_pressure_nL - pressure_predictions_L).abs() #print(results) results = (nodes_pressure_nL - pressure_predictions_L).abs().min(axis=0) return results
def predict_pressure_on_leaks(linreg_models: list, norm_params: dict, leak_start = 10, leak_end = 40, leak_area = 0.0001) -> pd.core.frame.DataFrame: """ For every modeled node in the network simulate hydraulics with it leaking Arguments: linreg_models - list of dicts from model_network_with_linreg() norm_params - dict of parameters, also from model_network_with_linreg() leak_start - leak start time in hours (int) leak_end - leak end time in hours (int) leak_area - area of the leak (meters squared) Returns: DataFrame where each node has its separate column with pressures predicted for a scenario of its leak, index is the time in seconds """ G = ng.create_graph() leak_predictions = {} temp = 0#TEMP simulate leak only for first 4 nodes, to save time in debugging--------------------------------------------------------- # for each node simulate its leak, and predict its pressure using its linear regression model for node in G.nodes(): print(f'Simulating node {node} with leak') temp += 1#TEMP simulate leak only for first 4 nodes, to save time in debugging--------------------------------------------------------- if temp>10:#TEMP simulate leak only for first 4 nodes, to save time in debugging--------------------------------------------------------- break#TEMP simulate leak only for first 4 nodes, to save time in debugging--------------------------------------------------------- try: # simulate hydraulics with a leak on this particular node sim_results_L = wntr_WSN.get_sim_results_LEAK(node=node, area=leak_area, start_time=leak_start, end_time=leak_end) # find linear regression model for this particular node for model in linreg_models: if model['node'] == node: linreg_dict = model # join flowrate and pressure measurements into a single DataFrame sim_results_L = pd.concat([sim_results_L.node['pressure'], sim_results_L.link['flowrate']], axis=1) # choose only the available sensors sim_results_L = sim_results_L[linreg_dict['sensors']] # choose only leak timeframe (index values in seconds) sim_results_L = sim_results_L.loc[[3600*i for i in range(leak_start, leak_end+1)]] # normalise the input data with respective parameters sim_results_L = (sim_results_L - norm_params['mean'].loc[linreg_dict['sensors']]) / norm_params['std'].loc[linreg_dict['sensors']] # predict pressure for each node leak_predictions[node] = linreg_dict['linreg'].predict(sim_results_L) except Exception as e: # sometimes an internal WNTR error happens, I didn't manage to find out why (function convergence error) print(f'Runtime error on simulation of node {node} with leak (convergence failed)\nError message:\n{e}') # create a DataFrame of results, index is time of leak in seconds index = [3600*i for i in range(leak_start, leak_end+1)] results = pd.DataFrame(data=leak_predictions, index=index) return results
def model_network_with_linreg(n: any) -> list: """ Model every node in the water network with an sklearn.linear_regression() model based on readings from n closest sensors Arguments: n - amount of closest sensors to read data from. Uses n of each, eg. if n=1 readings from the closest flowmeter AND the closest pressure meter are used (int or str 'all') Returns: list of dicts describing each node in the network {'node', 'regression', 'msq', 'r2'} where: node - node name (str) regression - sklearn regression object msq - mean squared error (float) r2 - R2 score (float) sensors - list of strings, names of sensors used """ print("Modeling the network with linreg (linear_regression.py - model_network_with_linregression())") # collect data G = ng.create_graph() # get NetworkX graphs amount_of_junctions = len(G.nodes()) # for printing progress amount_of_pipes = len(G.edges()) # for printing progress shortest_paths = list(all_pairs_dijkstra_path_length(G)) # get shortest distances between all node pairs sim_results = wntr_WSN.get_sim_results() # no leak simulation results all_pressure_readings, all_flowrate_readings = get_all_sensor_readings(sim_results) # extract readings for sensors we have # preprocess X data data_X = pd.concat([all_pressure_readings, all_flowrate_readings], axis=1, join='inner') # join readings into a single pandas DF data_X = data_X.loc[:, ~data_X.columns.duplicated()] # remove duplicate columns normalization_params = {'mean': data_X.mean(), 'std': data_X.std()} # collect normalization params data_X = (data_X - normalization_params['mean']) / normalization_params['std'] # normalise X set # for each node in graph create a separate sklearn linear_regression() object linreg_models = [] for i, node in enumerate(G.nodes()): print(f'\tModeling junction {node}: ({i+1} out of {amount_of_junctions})') # printing progress data_X_scope = data_X # copy of data_X inside for scope sensors = ng.get_closest_sensors(G=G, # retrieve sensors closest to node central_node=node, n=n, # nr of sensors shortest_paths=shortest_paths) sensors_names = [i[0] for i in sensors] # and flowrate readings, get rid of distances data_X_scope = data_X_scope[sensors_names] # extract data for the closest sensors data_y = sim_results.node['pressure'][node] # get y data for supervised learning train_X, test_X, train_y, test_y = train_test_split(data_X_scope, # split and shuffle dataset data_y, test_size=0.1) linreg_models.append(create_linreg_model(node, train_X, test_X, train_y, test_y)) # model the node and add it to the list return linreg_models, normalization_params
def check_network_for_leaks_v1_eval(): """ Setup for evaluation of leak finding algorithm - check_network_for_leaks_v1() Arguments: None Returns: None """ # params for finding residuals between simulation with leaks and linreg predictions leak_start = 1 leak_end = 30 leak_area = 0.0001 # model the whole network with linreg models models, normalization_params = model_network_with_linreg(n='all') # get pressure predictions for each node when there's a leak on this exact node pressure_predicted_L = predict_pressure_on_leaks(linreg_models=models, norm_params=normalization_params, leak_start=leak_start, leak_end=leak_end, leak_area=leak_area) # calculate the residuals residuals = find_residuals(pressure_predictions_L=pressure_predicted_L, leak_start=leak_start, leak_end=leak_end) # create NetworkX graph of water network G = ng.create_graph() # get simulation results for the network WITHOUT leaks sim_results_nL = wntr_WSN.get_sim_results() # params for simulation of the node we'll try to detect a leak on node = 'J4' test_leak_start = 10 test_leak_end = 60 test_leak_area = 0.0001 # simulate leak on a node we'll try to detect a leak on network_to_be_checked = wntr_WSN.get_sim_results_LEAK(node=node, area=test_leak_area, start_time=test_leak_start, end_time=test_leak_end) # check said network for leaks, get graphs of v1 algorithm values over time temp = check_network_for_leaks(residuals, sim_results_nL, sim_results_nL, models, normalization_params) return
from dataclasses import dataclass import matplotlib.pyplot as plt import dash import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output import networkx_graph as ng # local file network_graph.py import wntr_WSN as WSN # local file WTNR Water Supply Network # https://towardsdatascience.com/python-interactive-network-visualization-using-networkx-plotly-and-dash-e44749161ed7 # https://www.youtube.com/watch?v=hSPmj7mK6ng app = dash.Dash(__name__) sim_results = WSN.get_sim_results() G = ng.create_graph() # create nodes/edges edge_x = [] edge_y = [] for edge in G.edges(): x0, y0 = G.nodes[edge[0]]['pos'] x1, y1 = G.nodes[edge[1]]['pos'] edge_x.append(x0) edge_x.append(x1) edge_x.append(None) edge_y.append(y0) edge_y.append(y1) edge_y.append(None) edge_trace = go.Scatter(x=edge_x,