{ "cells": [ { "cell_type": "markdown", "metadata": { "vscode": { "languageId": "plaintext" } }, "source": [ "# Getting Started\n", "To get started with `swmmio`, you'll need a EPA SWMM input file (.inp). We'll use an example model provided within `swmmio` and see what it looks like to get a {py:obj}`Model.links ` DataFrame, " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
InletNodeOutletNodeLengthRoughnessInOffsetOutOffsetInitFlow...ShapeGeom1Geom2Geom3Geom4Barrelscoords
Name
C1:C2J1J2244.630.010.00.00.0...CIRCULAR1.00.00.00.01.0[(0.0, 0.0), (238.75, -53.332)]
C3J3J4NaNNaNNaNNaNNaN...RECT_OPEN5.01.00.00.0NaN[(459.058, -113.145), (671.391, -163.985)]
C2J2J3NaNNaNNaNNaNNaN...NaNNaNNaNNaNNaNNaN[(238.75, -53.332), (459.058, -113.145)]
\n", "

3 rows × 26 columns

\n", "
" ], "text/plain": [ " InletNode OutletNode Length Roughness InOffset OutOffset InitFlow \\\n", "Name \n", "C1:C2 J1 J2 244.63 0.01 0.0 0.0 0.0 \n", "C3 J3 J4 NaN NaN NaN NaN NaN \n", "C2 J2 J3 NaN NaN NaN NaN NaN \n", "\n", " ... Shape Geom1 Geom2 Geom3 Geom4 Barrels \\\n", "Name ... \n", "C1:C2 ... CIRCULAR 1.0 0.0 0.0 0.0 1.0 \n", "C3 ... RECT_OPEN 5.0 1.0 0.0 0.0 NaN \n", "C2 ... NaN NaN NaN NaN NaN NaN \n", "\n", " coords \n", "Name \n", "C1:C2 [(0.0, 0.0), (238.75, -53.332)] \n", "C3 [(459.058, -113.145), (671.391, -163.985)] \n", "C2 [(238.75, -53.332), (459.058, -113.145)] \n", "\n", "[3 rows x 26 columns]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from swmmio.tests.data import MODEL_FULL_FEATURES_PATH\n", "import swmmio\n", "\n", "# instantiate a model object\n", "model = swmmio.Model(MODEL_FULL_FEATURES_PATH)\n", "\n", "# get the data related to links\n", "model.links.dataframe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Edit Model Parameters\n", "\n", "Now let's use the lower-level {py:obj}`Model.inp ` API to access and modify the sections of the model. We'll change the outfall type to FIXED and set a stage elevation. \n", "\n", ":::{note}\n", "The {py:class}`Model.inp ` API has coverage for almost all sections of the INP file. If you find that a section isn't covered that you need, raise an issue [here](https://github.com/pyswmm/swmmio/issues)!\n", ":::" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
InvertElevOutfallTypeStageOrTimeseries
Name
J40FREENO
\n", "
" ], "text/plain": [ " InvertElev OutfallType StageOrTimeseries\n", "Name \n", "J4 0 FREE NO" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# here is the exsiting outfalls DataFrame\n", "model.inp.outfalls" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
InvertElevOutfallTypeStageOrTimeseries
Name
J40FIXED3.0
\n", "
" ], "text/plain": [ " InvertElev OutfallType StageOrTimeseries\n", "Name \n", "J4 0 FIXED 3.0" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# modify the outfall DataFrame\n", "model.inp.outfalls.loc['J4', 'OutfallType'] = 'FIXED'\n", "model.inp.outfalls.loc['J4', 'StageOrTimeseries'] = 3.0\n", "\n", "model.inp.outfalls" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can then save our updated model to a new .inp file, then instantiate a new {py:class}`~swmmio.core.Model` object. " ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
InvertElevOutfallTypeStageOrTimeseries
Name
J120.728NaNNaN
J36.547NaNNaN
J40.000FIXED3.0
J213.392NaNNaN
\n", "
" ], "text/plain": [ " InvertElev OutfallType StageOrTimeseries\n", "Name \n", "J1 20.728 NaN NaN\n", "J3 6.547 NaN NaN\n", "J4 0.000 FIXED 3.0\n", "J2 13.392 NaN NaN" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "model.inp.save('new_model.inp')\n", "\n", "# instantiate a new Model object with the modified inp file\n", "new_model = swmmio.Model('new_model.inp')\n", "\n", "# see the changes in the higher-level nodes DataFrame\n", "new_model.nodes.dataframe[['InvertElev','OutfallType', 'StageOrTimeseries']]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Building Variations of Models\n", "Starting with a base SWMM model, other models can be created by inserting altered data into a new inp file. Useful for sensitivity analysis or varying boundary conditions, models can be created using a fairly simple loop.\n", "\n", "For example, climate change impacts can be investigated by creating a set of models with varying outfall Fixed Stage elevations:\n" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
InvertElevOutfallTypeStageOrTimeseries
Name
KRO2005574.32FIXED581.0
PSO548.36FIXED581.0
\n", "
" ], "text/plain": [ " InvertElev OutfallType StageOrTimeseries\n", "Name \n", "KRO2005 574.32 FIXED 581.0\n", "PSO 548.36 FIXED 581.0" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import os\n", "\n", "# path to a SWMM model from swmm-nrtestsuite\n", "model_path = 'https://raw.githubusercontent.com/USEPA/swmm-nrtestsuite/refs/heads/dev/public/examples/Example3.inp'\n", "\n", "# initialize a model object\n", "model = swmmio.Model(model_path)\n", "\n", "sea_level_rise = 0.0 # set the starting sea level rise condition\n", "rise_increment = 0.25 # set the increment of sea level rise for each iteration\n", "\n", "# set the outfall type and initial outfall stage elevation\n", "model.inp.outfalls.loc[:, 'OutfallType'] = 'FIXED'\n", "model.inp.outfalls.loc[:, 'StageOrTimeseries'] = 576\n", "\n", "# create models up to 5ft of sea level rise.\n", "while sea_level_rise < 5:\n", "\n", " # create a dataframe of the model's outfalls\n", " outfalls = model.inp.outfalls\n", " # add the current rise to the outfalls' stage elevation\n", " outfalls.loc[:, 'StageOrTimeseries'] = outfalls.loc[:, 'StageOrTimeseries'] + rise_increment\n", " model.inp.outfalls = outfalls\n", " \n", " # create the filename for the new model\n", " newfilepath = os.path.join(model.inp.dir, f'{model.inp.name}_{sea_level_rise}_SLR.inp')\n", " \n", " # Overwrite the OUTFALLS section of the new model with the adjusted data\n", " model.inp.save(newfilepath)\n", "\n", " # increase sea level rise for the next loop\n", " sea_level_rise += rise_increment\n", "\n", "# check the outfalls of the last sea level rise \n", "# scenario that includes 5 feet of sea level rise\n", "model_slr_5 = swmmio.Model(newfilepath)\n", "model_slr_5.inp.outfalls" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Nodes and Links\n", "Specific sections of data from the inp and rpt can be extracted with `Nodes` and `Links` objects. Although these are the same object-type of the {py:meth}`swmmio.core.Model.nodes` and {py:meth}`swmmio.core.Model.links`, accessing them directly allows for custom control over what sections of data are retrieved. " ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
InvertElevMaxDepthInitDepthSurchargeDepthgeometry
Name
KRO3001556.1910.00.00.0POINT (1362408.25 431113.81)
KRO6015585.988.00.00.0POINT (1362748.63 428675.19)
KRO6016584.149.00.00.0POINT (1362767 428813.59)
KRO6017582.0114.00.00.0POINT (1363087.25 428778.19)
KRO1002594.893.00.00.0POINT (1362361.78 429189.37)
\n", "
" ], "text/plain": [ " InvertElev MaxDepth InitDepth SurchargeDepth \\\n", "Name \n", "KRO3001 556.19 10.0 0.0 0.0 \n", "KRO6015 585.98 8.0 0.0 0.0 \n", "KRO6016 584.14 9.0 0.0 0.0 \n", "KRO6017 582.01 14.0 0.0 0.0 \n", "KRO1002 594.89 3.0 0.0 0.0 \n", "\n", " geometry \n", "Name \n", "KRO3001 POINT (1362408.25 431113.81) \n", "KRO6015 POINT (1362748.63 428675.19) \n", "KRO6016 POINT (1362767 428813.59) \n", "KRO6017 POINT (1363087.25 428778.19) \n", "KRO1002 POINT (1362361.78 429189.37) " ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from swmmio import Model, Nodes\n", "m = Model(model_path)\n", "\n", "# pass custom init arguments into the Nodes object instead of using default settings referenced by m.nodes() \n", "nodes = Nodes(\n", " model=m, \n", " inp_sections=['junctions', 'storage', 'outfalls'],\n", " rpt_sections=['Node Depth Summary', 'Node Inflow Summary'],\n", " columns=[ 'InvertElev', 'MaxDepth', 'InitDepth', 'SurchargeDepth', 'MaxTotalInflow', 'coords']\n", ")\n", "\n", "# access data \n", "nodes.geodataframe.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Access Model Network as Graph\n", "The {py:obj}`~swmmio.core.Model` class returns a Networkx MultiDiGraph representation of the model via that {py:meth}`Model.network ` parameter:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "KRO3001 SU1 KRO3001-KRO3002 {'Length': 176.7171053, 'Roughness': 0.013, 'InOffset': 0.0, 'OutOffset': 5.0, 'InitFlow': 0.0, 'PumpCurve': nan, 'InitStatus': nan, 'Shape': 'CIRCULAR', 'Geom1': 1.0, 'Geom2': 0.0, 'Geom3': 0.0, 'Geom4': 0.0, 'Barrels': 1.0, 'coords': [(np.float64(1362408.25), np.float64(431113.81)), (np.float64(1362652.04), np.float64(431078.91))], 'facilityid': 'KRO3001-KRO3002', 'geometry': {\"coordinates\": [[1362408.25, 431113.81], [1362652.04, 431078.91]], \"type\": \"LineString\"}}\n" ] } ], "source": [ "# access the model as a Networkx MutliDiGraph\n", "model = Model(model_path)\n", "G = model.network\n", "\n", "# iterate through links\n", "for u, v, key, data in model.network.edges(data=True, keys=True):\n", " # do stuff with the network\n", " print(u, v, key, data)\n", " break\n" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# visualize the graph\n", "import matplotlib.pyplot as plt\n", "import networkx as nx\n", "# Draw the graph\n", "pos = nx.spring_layout(G, k=30)\n", "\n", "plt.figure(figsize=(5, 2))\n", "nx.draw(G, node_size=10, node_color='blue', with_labels=False)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For a more interesting example takes advantage of the graph-representation of a model, see [Visualizing SWMM Models](visualizing_models.ipynb). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running Models\n", "Using the command line tool, individual SWMM5 models can be run by invoking the swmmio module in your shell as such:\n", "```shell\n", "python -m swmmio --run path/to/mymodel.inp\n", "```\n", "If you have many models to run and would like to take advantage of your machine's cores, you can start a pool of simulations with the `--start_pool` (or `-sp`) command. After pointing `-sp` to one or more directories, swmmio will search for SWMM .inp files and add all them to a multiprocessing pool. By default, `-sp` leaves 4 of your machine's cores unused. This can be changed via the `-cores_left` argument.\n", "```shell\n", "# run all models in models in directories Model_Dir1 Model_Dir2\n", "python -m swmmio -sp Model_Dir1 Model_Dir2 \n", "\n", "# leave 1 core unused\n", "python -m swmmio -sp Model_Dir1 Model_Dir2 -cores_left=1\n", "```\n", "\n", ":::{warning}\n", "Using all cores for simultaneous model runs can put your machine's CPU usage at 100% for extended periods of time. This probably puts stress on your hardware. Use at your own risk.\n", ":::\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] } ], "metadata": { "kernelspec": { "display_name": "venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.4" } }, "nbformat": 4, "nbformat_minor": 2 }