| | import streamlit as st |
| | import wntr |
| | import geopandas as gpd |
| | import pandas as pd |
| | import tempfile |
| | import zipfile |
| | import os |
| | import numpy as np |
| |
|
| | st.set_page_config(layout="wide") |
| | st.title("π EPANET INP β Simulation β Shapefiles") |
| |
|
| | uploaded_file = st.file_uploader("Upload EPANET .inp file", type=["inp"]) |
| |
|
| | |
| | st.subheader("π₯ Or Import from Shapefiles") |
| |
|
| | node_files = st.file_uploader("Upload all Node Shapefile components (.shp, .shx, .dbf, .prj)", accept_multiple_files=True) |
| | link_files = st.file_uploader("Upload all Link Shapefile components (.shp, .shx, .dbf, .prj)", accept_multiple_files=True) |
| |
|
| | def extract_shapefile(files, temp_dir, layer_name): |
| | shp_path = None |
| | for file in files: |
| | suffix = os.path.splitext(file.name)[-1] |
| | file_path = os.path.join(temp_dir, f"{layer_name}{suffix}") |
| | with open(file_path, "wb") as f: |
| | f.write(file.read()) |
| | if suffix == ".shp": |
| | shp_path = file_path |
| | return shp_path |
| |
|
| | if node_files and link_files: |
| | try: |
| | temp_shape_dir = tempfile.mkdtemp() |
| | node_shp_path = extract_shapefile(node_files, temp_shape_dir, "nodes") |
| | link_shp_path = extract_shapefile(link_files, temp_shape_dir, "links") |
| | node_gdf = gpd.read_file(node_shp_path) |
| | link_gdf = gpd.read_file(link_shp_path) |
| |
|
| | wn = wntr.network.WaterNetworkModel() |
| | for _, row in node_gdf.iterrows(): |
| | wn.add_junction(str(row['ID']), base_demand=0.0, elevation=float(row['elevation']), |
| | coordinates=(row.geometry.x, row.geometry.y)) |
| | for _, row in link_gdf.iterrows(): |
| | wn.add_pipe(str(row['ID']), |
| | start_node_name=str(row['Node1']), |
| | end_node_name=str(row['Node2']), |
| | length=float(row['length']), |
| | diameter=float(row['diameter']), |
| | roughness=float(row['roughness']), |
| | minor_loss=0.0, status='OPEN') |
| | st.success("Water network built from shapefiles!") |
| |
|
| | sim = wntr.sim.WNTRSimulator(wn) |
| | results = sim.run_sim() |
| | st.success("Simulation from shapefile model complete.") |
| | except Exception as e: |
| | st.error(f"Error loading shapefiles: {e}") |
| |
|
| | if uploaded_file: |
| | temp_dir = tempfile.mkdtemp() |
| | inp_path = os.path.join(temp_dir, uploaded_file.name) |
| | with open(inp_path, "wb") as f: |
| | f.write(uploaded_file.read()) |
| |
|
| | st.success("INP file uploaded. Running simulation...") |
| |
|
| | try: |
| | wn = wntr.network.WaterNetworkModel(inp_path) |
| | sim = wntr.sim.EpanetSimulator(wn) |
| | results = sim.run_sim() |
| | st.success("Simulation complete.") |
| |
|
| | gis = wn.to_gis() |
| | node_gdf = pd.concat([gis.junctions, gis.tanks, gis.reservoirs]) |
| | link_gdf = pd.concat([gis.pipes, gis.pumps, gis.valves]) |
| |
|
| | node_gdf["pressure"] = results.node["pressure"].mean(axis=0) |
| | node_gdf["demand"] = results.node["demand"].mean(axis=0) |
| |
|
| | link_gdf["flow"] = results.link["flowrate"].mean(axis=0) |
| | link_gdf["velocity"] = results.link["velocity"].mean(axis=0) |
| |
|
| | node_gdf = node_gdf.set_geometry("geometry") |
| | link_gdf = link_gdf.set_geometry("geometry") |
| |
|
| | if node_gdf.crs is None: |
| | crs_choice = st.selectbox("Select CRS for shapefiles", ["EPSG:4326", "EPSG:3857", "None"]) |
| | if crs_choice != "None": |
| | node_gdf = node_gdf.set_crs(crs_choice) |
| | link_gdf = link_gdf.set_crs(crs_choice) |
| | else: |
| | st.warning("No CRS selected β export may fail.") |
| |
|
| | node_path = os.path.join(temp_dir, "nodes_with_results.shp") |
| | link_path = os.path.join(temp_dir, "links_with_results.shp") |
| | node_gdf.to_file(node_path, driver="ESRI Shapefile") |
| | link_gdf.to_file(link_path, driver="ESRI Shapefile") |
| |
|
| | zip_path = os.path.join(temp_dir, "simulation_shapefiles.zip") |
| | with zipfile.ZipFile(zip_path, 'w') as zipf: |
| | for f in os.listdir(temp_dir): |
| | if f.endswith((".shp", ".shx", ".dbf", ".prj")): |
| | zipf.write(os.path.join(temp_dir, f), arcname=f) |
| |
|
| | with open(zip_path, "rb") as f: |
| | st.download_button("π¦ Download Shapefiles with Results", f.read(), "simulation_shapefiles.zip") |
| |
|
| | st.subheader("π§ͺ Preview of Node Results") |
| | st.dataframe(node_gdf[["pressure", "demand"]].head()) |
| |
|
| | st.subheader("π Preview of Link Results") |
| | st.dataframe(link_gdf[["flow", "velocity"]].head()) |
| |
|
| | |
| | st.subheader("β οΈ Pipe Criticality Analysis") |
| | if st.button("Run Criticality Analysis on Large Pipes"): |
| | try: |
| | wn.options.time.duration = 72 * 3600 |
| | wn.options.hydraulic.demand_model = 'PDD' |
| | wn.options.hydraulic.required_pressure = 17.57 |
| | wn.options.hydraulic.minimum_pressure = 0 |
| |
|
| | pipes = wn.query_link_attribute('diameter', np.greater_equal, 24 * 0.0254, |
| | link_type=wntr.network.model.Pipe) |
| | pipes = list(pipes.index) |
| | pressure_threshold = 14.06 |
| |
|
| | sim = wntr.sim.WNTRSimulator(wn) |
| | results = sim.run_sim() |
| | min_pressure = results.node['pressure'].loc[:, wn.junction_name_list].min() |
| | below_threshold_normal_conditions = set(min_pressure[min_pressure < pressure_threshold].index) |
| |
|
| | junctions_impacted = {} |
| | for pipe_name in pipes: |
| | wn.reset_initial_values() |
| | pipe = wn.get_link(pipe_name) |
| | act = wntr.network.controls.ControlAction(pipe, 'status', wntr.network.LinkStatus.Closed) |
| | cond = wntr.network.controls.SimTimeCondition(wn, '=', '24:00:00') |
| | ctrl = wntr.network.controls.Control(cond, act) |
| | wn.add_control('close pipe ' + pipe_name, ctrl) |
| |
|
| | try: |
| | sim = wntr.sim.WNTRSimulator(wn) |
| | results = sim.run_sim() |
| | min_pressure = results.node['pressure'].loc[:, wn.junction_name_list].min() |
| | below_threshold = set(min_pressure[min_pressure < pressure_threshold].index) |
| | junctions_impacted[pipe_name] = below_threshold - below_threshold_normal_conditions |
| | except: |
| | junctions_impacted[pipe_name] = set() |
| |
|
| | wn.remove_control('close pipe ' + pipe_name) |
| |
|
| | number_of_junctions_impacted = {k: len(v) for k, v in junctions_impacted.items()} |
| | st.write("Junctions impacted per pipe:", number_of_junctions_impacted) |
| |
|
| | if number_of_junctions_impacted: |
| | worst_pipe = max(number_of_junctions_impacted, key=number_of_junctions_impacted.get) |
| | st.write(f"π¨ Most critical pipe: {worst_pipe} impacts {number_of_junctions_impacted[worst_pipe]} junctions") |
| | except Exception as e: |
| | st.error(f"Criticality analysis failed: {e}") |
| | |
| |
|
| | except Exception as e: |
| | st.error(f"Simulation failed: {e}") |
| |
|